Changeset 243058 in webkit
- Timestamp:
- Mar 18, 2019 3:34:44 AM (5 years ago)
- Location:
- trunk
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r243056 r243058 1 2019-03-18 Philippe Normand <pnormand@igalia.com> 2 3 [GStreamer] Rewrite HTTP source element using pushsrc base class 4 https://bugs.webkit.org/show_bug.cgi?id=195631 5 6 Reviewed by Xabier Rodriguez-Calvar. 7 8 * platform/gtk/TestExpectations: 9 * platform/gtk/http/tests/media/hls/video-controls-live-stream-expected.txt: 10 Update expectations, though it's not really related with this 11 patch. 12 1 13 2019-03-18 Claudio Saavedra <csaavedra@igalia.com> 2 14 -
trunk/LayoutTests/http/tests/media/video-play-stall-seek-expected.txt
r41942 r243058 1 1 2 Test that playback can be resumed by seeking backwards after load stalls. 2 3 -
trunk/LayoutTests/platform/gtk/TestExpectations
r243056 r243058 2211 2211 2212 2212 Bug(GTK) http/tests/media/video-preload.html [ Pass Slow ] 2213 webkit.org/b/143989 [ Release ] http/tests/media/hls/video-controls-live-stream.html [ Pass Slow Failure ]2214 2213 2215 2214 webkit.org/b/116958 http/tests/navigation/slowmetaredirect-basic.html [ Pass Slow ] … … 2736 2735 webkit.org/b/121995 media/video-controls-captions-trackmenu-includes-enabled-track.html [ Failure ] 2737 2736 2738 Bug(GTK) http/tests/media/video-play-stall-seek.html [ Skip ]2739 2740 2737 Bug(GTK) http/tests/misc/acid3.html [ Failure ] 2741 2738 -
trunk/LayoutTests/platform/gtk/http/tests/media/hls/video-controls-live-stream-expected.txt
r177077 r243058 1 1 2 EVENT(canplaythrough) 2 3 EVENT(play) 3 4 EXPECTED (video.duration == 'Infinity') OK 4 5 -webkit-media-text-track-container: classes: [hidden] 5 6 -webkit-media-controls-enclosure: classes: [] 6 -webkit-media-controls-panel: classes: [paused ]7 -webkit-media-controls-panel: classes: [paused show] 7 8 -webkit-media-controls-play-button: classes: [paused] 8 9 -webkit-media-controls-timeline: classes: [] 9 10 -webkit-media-controls-current-time-display: classes: [hour-long-time] 10 -webkit-media-controls-time-remaining-display: classes: [h idden hour-long-time]11 -webkit-media-controls-time-remaining-display: classes: [hour-long-time hidden] 11 12 -webkit-media-controls-toggle-closed-captions-button: classes: [hidden] 12 13 -webkit-media-controls-fullscreen-button: classes: [] -
trunk/Source/WebCore/ChangeLog
r243057 r243058 1 2019-03-18 Philippe Normand <pnormand@igalia.com> 2 3 [GStreamer] Rewrite HTTP source element using pushsrc base class 4 https://bugs.webkit.org/show_bug.cgi?id=195631 5 6 Reviewed by Xabier Rodriguez-Calvar. 7 8 If we want to use webkitwebsrc in adaptivedemux (HLS, DASH, etc) 9 we need a source element that behaves like souphttpsrc, which is 10 implemented using pushsrc. This rewrite might also fix some seek 11 issues. 12 13 No new tests, existing http/tests/media tests cover this patch. 14 15 * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp: 16 (WebCore::MediaPlayerPrivateGStreamer::handleMessage): 17 * platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp: 18 (webkit_web_src_class_init): 19 (webkitWebSrcReset): 20 (webkit_web_src_init): 21 (webKitWebSrcCreate): 22 (webKitWebSrcStart): 23 (webKitWebSrcCloseSession): 24 (webKitWebSrcStop): 25 (webKitWebSrcGetSize): 26 (webKitWebSrcIsSeekable): 27 (webKitWebSrcDoSeek): 28 (webKitWebSrcQuery): 29 (webKitWebSrcUnLock): 30 (webKitWebSrcUnLockStop): 31 (webKitWebSrcChangeState): 32 (CachedResourceStreamingClient::checkUpdateBlocksize): 33 (CachedResourceStreamingClient::responseReceived): 34 (CachedResourceStreamingClient::dataReceived): 35 (CachedResourceStreamingClient::accessControlCheckFailed): 36 (CachedResourceStreamingClient::loadFailed): 37 (CachedResourceStreamingClient::loadFinished): 38 * platform/graphics/gstreamer/WebKitWebSourceGStreamer.h: 39 1 40 2019-03-18 Gyuyoung Kim <gyuyoung.kim@webkit.org> 2 41 -
trunk/Source/WebCore/platform/graphics/gstreamer/MainThreadNotifier.h
r235732 r243058 64 64 } 65 65 66 template<typename F> 67 void notifyAndWait(T notificationType, F&& callbackFunctor) 68 { 69 Lock mutex; 70 Condition condition; 71 72 notify(notificationType, [functor = WTFMove(callbackFunctor), &condition, &mutex] { 73 functor(); 74 LockHolder holder(mutex); 75 condition.notifyOne(); 76 }); 77 78 if (!isMainThread()) { 79 LockHolder holder(mutex); 80 condition.wait(mutex); 81 } 82 } 83 66 84 void cancelPendingNotifications(unsigned mask = 0) 67 85 { -
trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
r242793 r243058 534 534 535 535 // Avoid useless seeking. 536 if (mediaTime == currentMediaTime()) 537 return; 536 if (mediaTime == currentMediaTime()) { 537 GST_DEBUG_OBJECT(pipeline(), "[Seek] seek to EOS position unhandled"); 538 return; 539 } 538 540 539 541 MediaTime time = std::min(mediaTime, durationMediaTime()); 540 542 541 if (isLiveStream()) 542 return; 543 if (isLiveStream()) { 544 GST_DEBUG_OBJECT(pipeline(), "[Seek] Live stream seek unhandled"); 545 return; 546 } 543 547 544 548 GST_INFO_OBJECT(pipeline(), "[Seek] seeking to %s", toString(time).utf8().data()); … … 1197 1201 bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_pipeline.get()); 1198 1202 1199 GST_LOG ("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message));1203 GST_LOG_OBJECT(pipeline(), "Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message)); 1200 1204 switch (GST_MESSAGE_TYPE(message)) { 1201 1205 case GST_MESSAGE_ERROR: … … 1343 1347 #endif 1344 1348 else if (gst_structure_has_name(structure, "http-headers")) { 1345 GstStructure* responseHeaders; 1346 if (gst_structure_get(structure, "response-headers", GST_TYPE_STRUCTURE, &responseHeaders, nullptr)) { 1347 if (!gst_structure_has_field(responseHeaders, httpHeaderNameString(HTTPHeaderName::ContentLength).utf8().data())) { 1348 GST_INFO_OBJECT(pipeline(), "Live stream detected. Disabling on-disk buffering"); 1349 if (const char* uri = gst_structure_get_string(structure, "uri")) { 1350 URL url(URL(), uri); 1351 convertToInternalProtocol(url); 1352 if (url != m_url) { 1353 GST_DEBUG_OBJECT(pipeline(), "Ignoring HTTP response headers for non-main URI."); 1354 break; 1355 } 1356 } 1357 GUniqueOutPtr<GstStructure> responseHeaders; 1358 if (gst_structure_get(structure, "response-headers", GST_TYPE_STRUCTURE, &responseHeaders.outPtr(), nullptr)) { 1359 const char* contentLengthHeaderName = httpHeaderNameString(HTTPHeaderName::ContentLength).utf8().data(); 1360 uint64_t contentLength = 0; 1361 if (!gst_structure_get_uint64(responseHeaders.get(), contentLengthHeaderName, &contentLength)) { 1362 // souphttpsrc sets a string for Content-Length, so 1363 // handle it here, until we remove the webkit+ protocol 1364 // prefix from webkitwebsrc. 1365 if (const char* contentLengthAsString = gst_structure_get_string(responseHeaders.get(), contentLengthHeaderName)) { 1366 contentLength = g_ascii_strtoull(contentLengthAsString, nullptr, 10); 1367 if (contentLength == G_MAXUINT64) 1368 contentLength = 0; 1369 } 1370 } 1371 GST_INFO_OBJECT(pipeline(), "%s stream detected", !contentLength ? "Live" : "Non-live"); 1372 if (!contentLength) { 1349 1373 m_isStreaming = true; 1350 1374 setDownloadBuffering(); 1351 1375 } 1352 gst_structure_free(responseHeaders);1353 1376 } 1354 1377 } else if (gst_structure_has_name(structure, "adaptive-streaming-statistics")) { … … 1648 1671 MediaTime MediaPlayerPrivateGStreamer::maxMediaTimeSeekable() const 1649 1672 { 1673 GST_TRACE_OBJECT(pipeline(), "errorOccured: %s, isLiveStream: %s", boolForPrinting(m_errorOccured), boolForPrinting(isLiveStream())); 1650 1674 if (m_errorOccured) 1675 return MediaTime::zeroTime(); 1676 1677 if (isLiveStream()) 1651 1678 return MediaTime::zeroTime(); 1652 1679 -
trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp
r241444 r243058 2 2 * Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> 3 3 * Copyright (C) 2013 Collabora Ltd. 4 * Copyright (C) 2019 Igalia S.L. 4 5 * 5 6 * This library is free software; you can redistribute it and/or … … 33 34 #include "SecurityOrigin.h" 34 35 #include <cstdint> 35 #include <gst/app/gstappsrc.h> 36 #include <gst/pbutils/missing-plugins.h> 36 #include <wtf/Condition.h> 37 37 #include <wtf/text/CString.h> 38 38 … … 74 74 Start = 1 << 0, 75 75 Stop = 1 << 1, 76 NeedData = 1 << 2,77 EnoughData = 1 << 3,78 Seek = 1 << 479 76 }; 80 77 81 78 #define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate)) 82 79 struct _WebKitWebSrcPrivate { 83 GstAppSrc* appsrc;84 GstPad* srcpad;85 80 CString originalURI; 86 81 CString redirectedURI; … … 89 84 bool compress; 90 85 GUniquePtr<gchar> httpMethod; 91 92 86 WebCore::MediaPlayer* player; 93 94 87 RefPtr<PlatformMediaResourceLoader> loader; 95 88 RefPtr<PlatformMediaResource> resource; 96 89 RefPtr<MainThreadNotifier<MainThreadSourceNotification>> notifier; 97 90 bool didPassAccessControlCheck; 98 99 guint64 offset; 91 bool wereHeadersReceived; 92 Condition headersCondition; 93 Lock responseLock; 94 bool wasResponseReceived; 95 Condition responseCondition; 96 bool doesHaveEOS; 97 bool isFlushing { false }; 98 uint64_t readPosition; 99 uint64_t requestedPosition; 100 uint64_t stopPosition; 101 bool isDurationSet; 100 102 bool haveSize; 101 guint64 size; 102 gboolean seekable; 103 bool paused; 103 uint64_t size; 104 bool isSeekable; 104 105 bool isSeeking; 105 106 guint64 requestedOffset; 107 106 bool wasSeeking { false }; 108 107 uint64_t minimumBlocksize; 109 110 RefPtr<MainThreadNotifier<MainThreadSourceNotification>> notifier; 108 Lock adapterLock; 109 Condition adapterCondition; 110 uint64_t queueSize { 0 }; 111 GRefPtr<GstAdapter> adapter; 112 GRefPtr<GstEvent> httpHeadersEvent; 111 113 }; 112 114 … … 121 123 }; 122 124 123 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", 124 GST_PAD_SRC, 125 GST_PAD_ALWAYS, 126 GST_STATIC_CAPS_ANY); 125 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY); 127 126 128 127 GST_DEBUG_CATEGORY_STATIC(webkit_web_src_debug); … … 136 135 static void webKitWebSrcGetProperty(GObject*, guint propertyID, GValue*, GParamSpec*); 137 136 static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange); 138 139 static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*); 140 141 static void webKitWebSrcNeedData(WebKitWebSrc*); 142 static void webKitWebSrcEnoughData(WebKitWebSrc*); 143 static gboolean webKitWebSrcSeek(WebKitWebSrc*, guint64); 144 145 static GstAppSrcCallbacks appsrcCallbacks = { 146 // need_data 147 [](GstAppSrc*, guint, gpointer userData) { 148 webKitWebSrcNeedData(WEBKIT_WEB_SRC(userData)); 149 }, 150 // enough_data 151 [](GstAppSrc*, gpointer userData) { 152 webKitWebSrcEnoughData(WEBKIT_WEB_SRC(userData)); 153 }, 154 // seek_data 155 [](GstAppSrc*, guint64 offset, gpointer userData) -> gboolean { 156 return webKitWebSrcSeek(WEBKIT_WEB_SRC(userData), offset); 157 }, 158 { nullptr } 159 }; 137 static GstFlowReturn webKitWebSrcCreate(GstPushSrc*, GstBuffer**); 138 static gboolean webKitWebSrcStart(GstBaseSrc*); 139 static gboolean webKitWebSrcStop(GstBaseSrc*); 140 static gboolean webKitWebSrcGetSize(GstBaseSrc*, guint64* size); 141 static gboolean webKitWebSrcIsSeekable(GstBaseSrc*); 142 static gboolean webKitWebSrcDoSeek(GstBaseSrc*, GstSegment*); 143 static gboolean webKitWebSrcQuery(GstBaseSrc*, GstQuery*); 144 static gboolean webKitWebSrcUnLock(GstBaseSrc*); 145 static gboolean webKitWebSrcUnLockStop(GstBaseSrc*); 160 146 161 147 #define webkit_web_src_parent_class parent_class 162 148 // We split this out into another macro to avoid a check-webkit-style error. 163 149 #define WEBKIT_WEB_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_web_src_debug, "webkitwebsrc", 0, "websrc element"); 164 G_DEFINE_TYPE_WITH_CODE(WebKitWebSrc, webkit_web_src, GST_TYPE_ BIN,165 166 150 G_DEFINE_TYPE_WITH_CODE(WebKitWebSrc, webkit_web_src, GST_TYPE_PUSH_SRC, 151 G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitWebSrcUriHandlerInit); 152 WEBKIT_WEB_SRC_CATEGORY_INIT); 167 153 168 154 static void webkit_web_src_class_init(WebKitWebSrcClass* klass) 169 155 { 170 156 GObjectClass* oklass = G_OBJECT_CLASS(klass); 171 GstElementClass* eklass = GST_ELEMENT_CLASS(klass);172 157 173 158 oklass->dispose = webKitWebSrcDispose; … … 176 161 oklass->get_property = webKitWebSrcGetProperty; 177 162 178 gst_element_class_add_pad_template(eklass, 179 gst_static_pad_template_get(&srcTemplate)); 163 GstElementClass* eklass = GST_ELEMENT_CLASS(klass); 164 gst_element_class_add_static_pad_template(eklass, &srcTemplate); 165 180 166 gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris", 181 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");167 "Philippe Normand <philn@igalia.com>"); 182 168 183 169 /* Allows setting the uri using the 'location' property, which is used … … 207 193 nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); 208 194 209 eklass->change_state = webKitWebSrcChangeState; 195 eklass->change_state = GST_DEBUG_FUNCPTR(webKitWebSrcChangeState); 196 197 GstBaseSrcClass* baseSrcClass = GST_BASE_SRC_CLASS(klass); 198 baseSrcClass->start = GST_DEBUG_FUNCPTR(webKitWebSrcStart); 199 baseSrcClass->stop = GST_DEBUG_FUNCPTR(webKitWebSrcStop); 200 baseSrcClass->unlock = GST_DEBUG_FUNCPTR(webKitWebSrcUnLock); 201 baseSrcClass->unlock_stop = GST_DEBUG_FUNCPTR(webKitWebSrcUnLockStop); 202 baseSrcClass->get_size = GST_DEBUG_FUNCPTR(webKitWebSrcGetSize); 203 baseSrcClass->is_seekable = GST_DEBUG_FUNCPTR(webKitWebSrcIsSeekable); 204 baseSrcClass->do_seek = GST_DEBUG_FUNCPTR(webKitWebSrcDoSeek); 205 baseSrcClass->query = GST_DEBUG_FUNCPTR(webKitWebSrcQuery); 206 207 GstPushSrcClass* pushSrcClass = GST_PUSH_SRC_CLASS(klass); 208 pushSrcClass->create = GST_DEBUG_FUNCPTR(webKitWebSrcCreate); 210 209 211 210 g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate)); 211 } 212 213 214 static void webkitWebSrcReset(WebKitWebSrc* src) 215 { 216 WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src); 217 218 priv->haveSize = false; 219 priv->wereHeadersReceived = false; 220 priv->isSeekable = false; 221 priv->readPosition = 0; 222 priv->requestedPosition = 0; 223 priv->stopPosition = -1; 224 priv->size = 0; 212 225 } 213 226 … … 220 233 221 234 priv->notifier = MainThreadNotifier<MainThreadSourceNotification>::create(); 222 223 priv->haveSize = FALSE; 224 priv->size = 0; 225 226 priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", nullptr)); 227 if (!priv->appsrc) { 228 GST_ERROR_OBJECT(src, "Failed to create appsrc"); 229 return; 230 } 231 232 gst_bin_add(GST_BIN(src), GST_ELEMENT(priv->appsrc)); 233 234 GRefPtr<GstPad> targetPad = adoptGRef(gst_element_get_static_pad(GST_ELEMENT(priv->appsrc), "src")); 235 priv->srcpad = webkitGstGhostPadFromStaticTemplate(&srcTemplate, "src", targetPad.get()); 236 237 gst_element_add_pad(GST_ELEMENT(src), priv->srcpad); 238 239 GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT); 240 gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent); 241 242 gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, nullptr); 243 gst_app_src_set_emit_signals(priv->appsrc, FALSE); 244 gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_SEEKABLE); 245 246 // 512k is a abitrary number but we should choose a value 247 // here to not pause/unpause the SoupMessage too often and 248 // to make sure there's always some data available for 249 // GStreamer to handle. 250 gst_app_src_set_max_bytes(priv->appsrc, 512 * 1024); 251 252 // Emit the need-data signal if the queue contains less 253 // than 20% of data. Without this the need-data signal 254 // is emitted when the queue is empty, we then dispatch 255 // the soup message unpausing to the main loop and from 256 // there unpause the soup message. This already takes 257 // quite some time and libsoup even needs some more time 258 // to actually provide data again. If we do all this 259 // already if the queue is 20% empty, it's much more 260 // likely that libsoup already provides new data before 261 // the queue is really empty. 262 // This might need tweaking for ports not using libsoup. 263 g_object_set(priv->appsrc, "min-percent", 20, nullptr); 264 265 gst_base_src_set_automatic_eos(GST_BASE_SRC(priv->appsrc), FALSE); 266 267 gst_app_src_set_caps(priv->appsrc, nullptr); 268 269 priv->minimumBlocksize = gst_base_src_get_blocksize(GST_BASE_SRC_CAST(priv->appsrc)); 235 priv->adapter = adoptGRef(gst_adapter_new()); 236 priv->minimumBlocksize = gst_base_src_get_blocksize(GST_BASE_SRC_CAST(src)); 237 238 webkitWebSrcReset(src); 239 gst_base_src_set_automatic_eos(GST_BASE_SRC_CAST(src), FALSE); 270 240 } 271 241 … … 348 318 } 349 319 350 static void webKitWebSrcStop(WebKitWebSrc* src) 351 { 352 WebKitWebSrcPrivate* priv = src->priv; 353 354 if (priv->resource || (priv->loader && !priv->keepAlive)) { 355 GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); 356 priv->notifier->cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek); 357 priv->notifier->notify(MainThreadSourceNotification::Stop, [protector, keepAlive = priv->keepAlive] { 358 WebKitWebSrcPrivate* priv = protector->priv; 359 360 if (priv->resource) { 361 priv->resource->stop(); 362 priv->resource->setClient(nullptr); 363 priv->resource = nullptr; 364 } 365 366 if (!keepAlive) 367 priv->loader = nullptr; 320 static GstFlowReturn webKitWebSrcCreate(GstPushSrc* pushSrc, GstBuffer** buffer) 321 { 322 GstBaseSrc* baseSrc = GST_BASE_SRC_CAST(pushSrc); 323 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 324 WebKitWebSrcPrivate* priv = src->priv; 325 326 GST_TRACE_OBJECT(src, "readPosition = %" G_GUINT64_FORMAT " requestedPosition = %" G_GUINT64_FORMAT, priv->readPosition, priv->requestedPosition); 327 328 if (priv->requestedPosition != priv->readPosition) { 329 { 330 LockHolder adapterLocker(priv->adapterLock); 331 GST_DEBUG_OBJECT(src, "Seeking, flushing adapter"); 332 // Discard all the buffers coming before the requested seek position. 333 gst_adapter_flush(priv->adapter.get(), priv->queueSize); 334 priv->queueSize = 0; 335 } 336 uint64_t requestedPosition = priv->requestedPosition; 337 webKitWebSrcStop(baseSrc); 338 priv->requestedPosition = requestedPosition; 339 webKitWebSrcStart(baseSrc); 340 } 341 342 { 343 LockHolder locker(priv->responseLock); 344 priv->responseCondition.wait(priv->responseLock, [priv] () { 345 return priv->wasResponseReceived || priv->isFlushing; 368 346 }); 369 347 } 370 348 371 bool wasSeeking = std::exchange(priv->isSeeking, false); 372 373 priv->paused = false; 374 375 priv->offset = 0; 376 377 if (!wasSeeking) { 378 priv->requestedOffset = 0; 379 priv->player = nullptr; 380 priv->seekable = FALSE; 381 } 382 383 if (priv->appsrc) { 384 gst_app_src_set_caps(priv->appsrc, nullptr); 385 if (!wasSeeking) 386 gst_app_src_set_size(priv->appsrc, -1); 387 } 388 389 GST_DEBUG_OBJECT(src, "Stopped request"); 349 GST_TRACE_OBJECT(src, "flushing: %s, doesHaveEOS: %s, queueSize: %" G_GSIZE_FORMAT, boolForPrinting(priv->isFlushing), boolForPrinting(priv->doesHaveEOS), priv->queueSize); 350 351 if (priv->isFlushing) { 352 GST_DEBUG_OBJECT(src, "Flushing"); 353 return GST_FLOW_FLUSHING; 354 } 355 356 if (priv->doesHaveEOS) { 357 GST_DEBUG_OBJECT(src, "EOS"); 358 return GST_FLOW_EOS; 359 } 360 361 unsigned size = gst_base_src_get_blocksize(baseSrc); 362 bool isAdapterDrained = false; 363 { 364 LockHolder adapterLocker(priv->adapterLock); 365 unsigned retries = 0; 366 size_t available = gst_adapter_available_fast(priv->adapter.get()); 367 while (available < size && !isAdapterDrained) { 368 priv->adapterCondition.waitFor(priv->adapterLock, Seconds(1)); 369 retries++; 370 available = gst_adapter_available_fast(priv->adapter.get()); 371 if (available && available < size) 372 size = available; 373 else if (retries > 3) 374 isAdapterDrained = true; 375 } 376 } 377 378 if (isAdapterDrained) { 379 GST_DEBUG_OBJECT(src, "Adapter still empty after 3 seconds of waiting, assuming EOS"); 380 return GST_FLOW_EOS; 381 } 382 383 if (priv->haveSize && !priv->isDurationSet) { 384 GST_DEBUG_OBJECT(src, "Setting duration to %" G_GUINT64_FORMAT, priv->size); 385 baseSrc->segment.duration = priv->size; 386 priv->isDurationSet = true; 387 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_duration_changed(GST_OBJECT_CAST(src))); 388 } 389 390 if (priv->httpHeadersEvent) 391 gst_pad_push_event(GST_BASE_SRC_PAD(baseSrc), priv->httpHeadersEvent.leakRef()); 392 393 { 394 GST_TRACE_OBJECT(src, "Taking %u bytes from adapter", size); 395 LockHolder adapterLocker(priv->adapterLock); 396 if (size) { 397 *buffer = gst_adapter_take_buffer_fast(priv->adapter.get(), size); 398 RELEASE_ASSERT(*buffer); 399 400 priv->queueSize -= size; 401 402 GST_BUFFER_OFFSET(*buffer) = baseSrc->segment.position; 403 GST_BUFFER_OFFSET_END(*buffer) = GST_BUFFER_OFFSET(*buffer) + size; 404 GST_TRACE_OBJECT(src, "Buffer bounds set to %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT, GST_BUFFER_OFFSET(*buffer), GST_BUFFER_OFFSET_END(*buffer)); 405 GST_TRACE_OBJECT(src, "doesHaveEOS: %s, wasSeeking: %s, seeking: %s, size: %u", boolForPrinting(priv->doesHaveEOS), boolForPrinting(priv->wasSeeking), boolForPrinting(priv->isSeeking), size); 406 if (priv->haveSize && GST_BUFFER_OFFSET_END(*buffer) >= priv->size) { 407 if (priv->wasSeeking) 408 priv->wasSeeking = false; 409 else 410 priv->doesHaveEOS = true; 411 } else if (priv->wasSeeking) 412 priv->wasSeeking = false; 413 } else 414 GST_ERROR_OBJECT(src, "Empty adapter?"); 415 } 416 417 return GST_FLOW_OK; 390 418 } 391 419 … … 441 469 } 442 470 443 static void webKitWebSrcStart(WebKitWebSrc* src) 444 { 471 static gboolean webKitWebSrcStart(GstBaseSrc* baseSrc) 472 { 473 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 445 474 WebKitWebSrcPrivate* priv = src->priv; 446 475 ASSERT(priv->player); 476 477 priv->wereHeadersReceived = false; 478 priv->wasResponseReceived = false; 479 priv->isDurationSet = false; 480 priv->doesHaveEOS = false; 481 priv->isFlushing = false; 447 482 448 483 priv->didPassAccessControlCheck = false; … … 450 485 if (priv->originalURI.isNull()) { 451 486 GST_ERROR_OBJECT(src, "No URI provided"); 452 webKitWebSrcStop(src); 453 return; 487 webKitWebSrcStop(baseSrc); 488 return FALSE; 489 } 490 491 if (priv->requestedPosition == priv->stopPosition) { 492 GST_DEBUG_OBJECT(src, "Empty segment, signaling EOS"); 493 priv->doesHaveEOS = true; 494 return FALSE; 454 495 } 455 496 … … 485 526 request.setHTTPUserAgent("Quicktime/7.6.6"); 486 527 487 if (priv->requestedOffset) { 488 GUniquePtr<gchar> val(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset)); 489 request.setHTTPHeaderField(HTTPHeaderName::Range, val.get()); 490 } 491 priv->offset = priv->requestedOffset; 528 if (priv->requestedPosition) { 529 GUniquePtr<char> formatedRange(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedPosition)); 530 GST_DEBUG_OBJECT(src, "Range request: %s", formatedRange.get()); 531 request.setHTTPHeaderField(HTTPHeaderName::Range, formatedRange.get()); 532 } 533 priv->readPosition = priv->requestedPosition; 492 534 493 535 GST_DEBUG_OBJECT(src, "Persistent connection support %s", priv->keepAlive ? "enabled" : "disabled"); 494 if (!priv->keepAlive) {536 if (!priv->keepAlive) 495 537 request.setHTTPHeaderField(HTTPHeaderName::Connection, "close"); 496 }497 538 498 539 if (priv->extraHeaders) … … 503 544 504 545 GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); 505 priv->notifier->notify (MainThreadSourceNotification::Start, [protector, request = WTFMove(request)] {546 priv->notifier->notifyAndWait(MainThreadSourceNotification::Start, [protector, request = WTFMove(request)] { 506 547 WebKitWebSrcPrivate* priv = protector->priv; 507 508 548 if (!priv->loader) 509 549 priv->loader = priv->player->createResourceLoader(); … … 519 559 GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client"); 520 560 priv->loader = nullptr; 521 webKitWebSrcStop(protector.get());522 561 } 523 562 }); 524 } 525 526 static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition) 527 { 528 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; 529 WebKitWebSrc* src = WEBKIT_WEB_SRC(element); 530 WebKitWebSrcPrivate* priv = src->priv; 531 532 switch (transition) { 533 case GST_STATE_CHANGE_NULL_TO_READY: 534 if (!priv->appsrc) { 535 gst_element_post_message(element, 536 gst_missing_element_message_new(element, "appsrc")); 537 GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (nullptr), ("no appsrc")); 538 return GST_STATE_CHANGE_FAILURE; 539 } 540 break; 541 default: 542 break; 543 } 544 545 ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); 546 if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) { 547 GST_DEBUG_OBJECT(src, "State change failed"); 548 return ret; 549 } 550 551 switch (transition) { 552 case GST_STATE_CHANGE_READY_TO_PAUSED: 563 564 GST_DEBUG_OBJECT(src, "Resource loader started"); 565 return TRUE; 566 } 567 568 static void webKitWebSrcCloseSession(WebKitWebSrc* src) 569 { 570 WebKitWebSrcPrivate* priv = src->priv; 571 GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); 572 573 priv->notifier->notifyAndWait(MainThreadSourceNotification::Stop, [protector, keepAlive = priv->keepAlive] { 574 WebKitWebSrcPrivate* priv = protector->priv; 575 576 GST_DEBUG_OBJECT(protector.get(), "Stopping resource loader"); 577 578 if (priv->resource) { 579 priv->resource->stop(); 580 priv->resource->setClient(nullptr); 581 priv->resource = nullptr; 582 } 583 584 if (!keepAlive) 585 priv->loader = nullptr; 586 }); 587 588 GST_DEBUG_OBJECT(src, "Resource loader stopped"); 589 } 590 591 static gboolean webKitWebSrcStop(GstBaseSrc* baseSrc) 592 { 593 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 594 WebKitWebSrcPrivate* priv = src->priv; 595 596 if (priv->resource || (priv->loader && !priv->keepAlive)) 597 webKitWebSrcCloseSession(src); 598 553 599 { 554 GST_DEBUG_OBJECT(src, "READY->PAUSED"); 555 webKitWebSrcStart(src); 556 break; 557 } 558 case GST_STATE_CHANGE_PAUSED_TO_READY: 559 { 560 GST_DEBUG_OBJECT(src, "PAUSED->READY"); 561 webKitWebSrcStop(src); 562 break; 563 } 564 default: 565 break; 566 } 567 568 return ret; 569 } 570 571 static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query) 572 { 573 WebKitWebSrc* src = WEBKIT_WEB_SRC(GST_ELEMENT(parent)); 600 LockHolder adapterLocker(priv->adapterLock); 601 gst_adapter_clear(priv->adapter.get()); 602 priv->queueSize = 0; 603 } 604 605 webkitWebSrcReset(src); 606 GST_DEBUG_OBJECT(src, "Stopped request"); 607 return TRUE; 608 } 609 610 static gboolean webKitWebSrcGetSize(GstBaseSrc* baseSrc, guint64* size) 611 { 612 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 613 WebKitWebSrcPrivate* priv = src->priv; 614 615 GST_DEBUG_OBJECT(src, "haveSize: %s, size: %" G_GUINT64_FORMAT, boolForPrinting(priv->haveSize), priv->size); 616 if (priv->haveSize) { 617 *size = priv->size; 618 return TRUE; 619 } 620 621 return FALSE; 622 } 623 624 static gboolean webKitWebSrcIsSeekable(GstBaseSrc* baseSrc) 625 { 626 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 627 628 GST_DEBUG_OBJECT(src, "isSeekable: %s", boolForPrinting(src->priv->isSeekable)); 629 return src->priv->isSeekable; 630 } 631 632 static gboolean webKitWebSrcDoSeek(GstBaseSrc* baseSrc, GstSegment* segment) 633 { 634 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 635 WebKitWebSrcPrivate* priv = src->priv; 636 LockHolder locker(priv->responseLock); 637 638 GST_DEBUG_OBJECT(src, "Seek segment: (%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")", segment->start, segment->stop); 639 if (priv->readPosition == segment->start && priv->requestedPosition == priv->readPosition && priv->stopPosition == segment->stop) { 640 GST_DEBUG_OBJECT(src, "Seek to current read/end position and no seek pending"); 641 return TRUE; 642 } 643 644 if (priv->wereHeadersReceived && !priv->isSeekable) { 645 GST_WARNING_OBJECT(src, "Not seekable"); 646 return FALSE; 647 } 648 649 if (segment->rate < 0.0 || segment->format != GST_FORMAT_BYTES) { 650 GST_WARNING_OBJECT(src, "Invalid seek segment"); 651 return FALSE; 652 } 653 654 if (priv->haveSize && segment->start >= priv->size) 655 GST_WARNING_OBJECT(src, "Potentially seeking behind end of file, might EOS immediately"); 656 657 priv->isSeeking = true; 658 priv->requestedPosition = segment->start; 659 priv->stopPosition = segment->stop; 660 return TRUE; 661 } 662 663 static gboolean webKitWebSrcQuery(GstBaseSrc* baseSrc, GstQuery* query) 664 { 665 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 574 666 WebKitWebSrcPrivate* priv = src->priv; 575 667 gboolean result = FALSE; 576 668 577 switch (GST_QUERY_TYPE(query)) { 578 case GST_QUERY_URI: { 669 if (GST_QUERY_TYPE(query) == GST_QUERY_URI) { 579 670 gst_query_set_uri(query, priv->originalURI.data()); 580 671 if (!priv->redirectedURI.isNull()) 581 672 gst_query_set_uri_redirection(query, priv->redirectedURI.data()); 582 673 result = TRUE; 583 break; 584 } 585 case GST_QUERY_SCHEDULING: { 674 } 675 676 if (!result) 677 result = GST_BASE_SRC_CLASS(parent_class)->query(baseSrc, query); 678 679 if (GST_QUERY_TYPE(query) == GST_QUERY_SCHEDULING) { 586 680 GstSchedulingFlags flags; 587 681 int minSize, maxSize, align; … … 589 683 gst_query_parse_scheduling(query, &flags, &minSize, &maxSize, &align); 590 684 gst_query_set_scheduling(query, static_cast<GstSchedulingFlags>(flags | GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED), minSize, maxSize, align); 591 result = TRUE;592 break;593 }594 default: {595 GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));596 597 // Forward the query to the proxy target pad.598 if (target)599 result = gst_pad_query(target.get(), query);600 break;601 }602 685 } 603 686 604 687 return result; 688 } 689 690 static gboolean webKitWebSrcUnLock(GstBaseSrc* baseSrc) 691 { 692 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 693 LockHolder locker(src->priv->responseLock); 694 695 GST_DEBUG_OBJECT(src, "Unlock"); 696 src->priv->isFlushing = true; 697 src->priv->responseCondition.notifyOne(); 698 return TRUE; 699 } 700 701 static gboolean webKitWebSrcUnLockStop(GstBaseSrc* baseSrc) 702 { 703 WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc); 704 LockHolder locker(src->priv->responseLock); 705 GST_DEBUG_OBJECT(src, "Unlock stop"); 706 src->priv->isFlushing = false; 707 708 return TRUE; 709 } 710 711 static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition) 712 { 713 WebKitWebSrc* src = WEBKIT_WEB_SRC(element); 714 715 #if GST_CHECK_VERSION(1, 14, 0) 716 GST_DEBUG_OBJECT(src, gst_state_change_get_name(transition)); 717 #endif 718 switch (transition) { 719 case GST_STATE_CHANGE_READY_TO_NULL: 720 webKitWebSrcCloseSession(src); 721 break; 722 default: 723 break; 724 } 725 726 return GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); 605 727 } 606 728 … … 673 795 } 674 796 675 static void webKitWebSrcNeedData(WebKitWebSrc* src)676 {677 WebKitWebSrcPrivate* priv = src->priv;678 679 GST_LOG_OBJECT(src, "Need more data");680 681 if (!priv->paused)682 return;683 priv->paused = false;684 685 GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);686 priv->notifier->notify(MainThreadSourceNotification::NeedData, [protector] { });687 }688 689 static void webKitWebSrcEnoughData(WebKitWebSrc* src)690 {691 WebKitWebSrcPrivate* priv = src->priv;692 693 GST_DEBUG_OBJECT(src, "Have enough data");694 695 if (priv->paused)696 return;697 priv->paused = true;698 699 GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);700 priv->notifier->notify(MainThreadSourceNotification::EnoughData, [protector] {701 WebKitWebSrcPrivate* priv = protector->priv;702 if (priv->resource)703 priv->resource->stop();704 });705 }706 707 static gboolean webKitWebSrcSeek(WebKitWebSrc* src, guint64 offset)708 {709 WebKitWebSrcPrivate* priv = src->priv;710 711 if (offset == priv->offset && priv->requestedOffset == priv->offset)712 return TRUE;713 714 if (!priv->seekable)715 return FALSE;716 717 priv->isSeeking = true;718 priv->requestedOffset = offset;719 720 GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, src->priv->requestedOffset);721 722 GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);723 priv->notifier->notify(MainThreadSourceNotification::Seek, [protector] {724 webKitWebSrcStop(protector.get());725 webKitWebSrcStart(protector.get());726 });727 return TRUE;728 }729 730 797 void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player) 731 798 { … … 750 817 { 751 818 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); 752 WebKitWebSrcPrivate* priv = src->priv; 753 754 uint64_t blocksize = gst_base_src_get_blocksize(GST_BASE_SRC_CAST(priv->appsrc)); 755 GST_LOG_OBJECT(src, "Checking to update blocksize. Read:%" PRIu64 " blocksize:%" PRIu64, bytesRead, blocksize); 819 GstBaseSrc* baseSrc = GST_BASE_SRC_CAST(src); 820 WebKitWebSrcPrivate* priv = src->priv; 821 822 uint64_t blocksize = gst_base_src_get_blocksize(baseSrc); 823 GST_LOG_OBJECT(src, "Checking to update blocksize. Read: %" PRIu64 ", current blocksize: %" PRIu64, bytesRead, blocksize); 756 824 757 825 if (bytesRead >= blocksize * s_growBlocksizeLimit) { … … 762 830 blocksize *= s_growBlocksizeFactor; 763 831 GST_DEBUG_OBJECT(src, "Increased blocksize to %" PRIu64, blocksize); 764 gst_base_src_set_blocksize( GST_BASE_SRC_CAST(priv->appsrc), blocksize);832 gst_base_src_set_blocksize(baseSrc, blocksize); 765 833 m_increaseBlocksizeCount = 0; 766 834 } … … 773 841 blocksize = std::max(blocksize, priv->minimumBlocksize); 774 842 GST_DEBUG_OBJECT(src, "Decreased blocksize to %" PRIu64, blocksize); 775 gst_base_src_set_blocksize( GST_BASE_SRC_CAST(priv->appsrc), blocksize);843 gst_base_src_set_blocksize(baseSrc, blocksize); 776 844 m_reduceBlocksizeCount = 0; 777 845 } … … 799 867 if (response.httpStatusCode() >= 400) { 800 868 GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr)); 801 gst_app_src_end_of_stream(priv->appsrc); 802 webKitWebSrcStop(src); 803 return completionHandler(ShouldContinue::No); 804 } 805 806 if (priv->isSeeking) { 807 GST_DEBUG_OBJECT(src, "Seek in progress, ignoring response"); 808 return completionHandler(ShouldContinue::Yes); 809 } 810 811 if (priv->requestedOffset) { 869 priv->doesHaveEOS = true; 870 webKitWebSrcStop(GST_BASE_SRC_CAST(src)); 871 completionHandler(ShouldContinue::No); 872 return; 873 } 874 875 if (priv->requestedPosition) { 812 876 // Seeking ... we expect a 206 == PARTIAL_CONTENT 813 877 if (response.httpStatusCode() == 200) { 814 878 // Range request didn't have a ranged response; resetting offset. 815 priv-> offset= 0;879 priv->readPosition = 0; 816 880 } else if (response.httpStatusCode() != 206) { 817 881 // Range request completely failed. 818 882 GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received unexpected %d HTTP status code", response.httpStatusCode()), (nullptr)); 819 gst_app_src_end_of_stream(priv->appsrc); 820 webKitWebSrcStop(src); 821 return completionHandler(ShouldContinue::No); 883 priv->doesHaveEOS = true; 884 webKitWebSrcStop(GST_BASE_SRC_CAST(src)); 885 completionHandler(ShouldContinue::No); 886 return; 887 } else { 888 GST_DEBUG_OBJECT(src, "Range request succeeded"); 889 priv->isSeeking = false; 890 priv->wasSeeking = true; 822 891 } 823 892 } 824 893 825 894 long long length = response.expectedContentLength(); 826 if (length > 0 && priv->requestedOffset && response.httpStatusCode() == 206) 827 length += priv->requestedOffset; 828 829 priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data()); 830 831 GST_DEBUG_OBJECT(src, "Size: %lld, seekable: %s", length, priv->seekable ? "yes" : "no"); 832 // notify size/duration 895 if (length > 0 && priv->requestedPosition && response.httpStatusCode() == 206) 896 length += priv->requestedPosition; 897 898 priv->isSeekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data()); 899 900 GST_DEBUG_OBJECT(src, "Size: %lld, isSeekable: %s", length, boolForPrinting(priv->isSeekable)); 833 901 if (length > 0) { 834 902 if (!priv->haveSize || (static_cast<long long>(priv->size) != length)) { 835 priv->haveSize = TRUE;903 priv->haveSize = true; 836 904 priv->size = length; 837 gst_app_src_set_size(priv->appsrc, length); 838 } 839 } else { 840 gst_app_src_set_size(priv->appsrc, -1); 841 if (!priv->seekable) 842 gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_STREAM); 905 priv->isDurationSet = false; 906 } 843 907 } 844 908 … … 855 919 GST_DEBUG_OBJECT(src, "Response ContentType: %s", contentType.utf8().data()); 856 920 gst_caps_set_simple(caps.get(), "content-type", G_TYPE_STRING, contentType.utf8().data(), nullptr); 857 858 gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_STREAM); 859 } 860 } 861 862 gst_app_src_set_caps(priv->appsrc, caps.get()); 863 864 // Emit a GST_EVENT_CUSTOM_DOWNSTREAM_STICKY event and message to let 865 // GStreamer know about the HTTP headers sent and received. 866 GstStructure* httpHeaders = gst_structure_new_empty("http-headers"); 867 gst_structure_set(httpHeaders, "uri", G_TYPE_STRING, priv->originalURI.data(), 868 "http-status-code", G_TYPE_UINT, response.httpStatusCode(), nullptr); 869 if (!priv->redirectedURI.isNull()) 870 gst_structure_set(httpHeaders, "redirection-uri", G_TYPE_STRING, priv->redirectedURI.data(), nullptr); 871 GUniquePtr<GstStructure> headers(gst_structure_new_empty("request-headers")); 872 for (const auto& header : m_request.httpHeaderFields()) 873 gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr); 874 GST_DEBUG_OBJECT(src, "Request headers going downstream: %" GST_PTR_FORMAT, headers.get()); 875 gst_structure_set(httpHeaders, "request-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr); 876 headers.reset(gst_structure_new_empty("response-headers")); 877 for (const auto& header : response.httpHeaderFields()) 878 gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr); 879 gst_structure_set(httpHeaders, "response-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr); 880 GST_DEBUG_OBJECT(src, "Response headers going downstream: %" GST_PTR_FORMAT, headers.get()); 881 882 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_element(GST_OBJECT_CAST(src), 883 gst_structure_copy(httpHeaders))); 884 gst_pad_push_event(GST_BASE_SRC_PAD(priv->appsrc), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, httpHeaders)); 885 921 } 922 } 923 924 if (caps) { 925 GST_DEBUG_OBJECT(src, "Set caps to %" GST_PTR_FORMAT, caps.get()); 926 gst_base_src_set_caps(GST_BASE_SRC_CAST(src), caps.get()); 927 } 928 929 { 930 LockHolder locker(priv->responseLock); 931 932 // Emit a GST_EVENT_CUSTOM_DOWNSTREAM_STICKY event and message to let 933 // GStreamer know about the HTTP headers sent and received. 934 GstStructure* httpHeaders = gst_structure_new_empty("http-headers"); 935 gst_structure_set(httpHeaders, "uri", G_TYPE_STRING, priv->originalURI.data(), 936 "http-status-code", G_TYPE_UINT, response.httpStatusCode(), nullptr); 937 if (!priv->redirectedURI.isNull()) 938 gst_structure_set(httpHeaders, "redirection-uri", G_TYPE_STRING, priv->redirectedURI.data(), nullptr); 939 GUniquePtr<GstStructure> headers(gst_structure_new_empty("request-headers")); 940 for (const auto& header : m_request.httpHeaderFields()) 941 gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr); 942 GST_DEBUG_OBJECT(src, "Request headers going downstream: %" GST_PTR_FORMAT, headers.get()); 943 gst_structure_set(httpHeaders, "request-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr); 944 headers.reset(gst_structure_new_empty("response-headers")); 945 for (const auto& header : response.httpHeaderFields()) { 946 bool ok = false; 947 uint64_t convertedValue = header.value.toUInt64(&ok); 948 if (ok) 949 gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_UINT64, convertedValue, nullptr); 950 else 951 gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr); 952 } 953 auto contentLengthFieldName(httpHeaderNameString(HTTPHeaderName::ContentLength).toString()); 954 if (!gst_structure_has_field(headers.get(), contentLengthFieldName.utf8().data())) 955 gst_structure_set(headers.get(), contentLengthFieldName.utf8().data(), G_TYPE_UINT64, static_cast<uint64_t>(length), nullptr); 956 gst_structure_set(httpHeaders, "response-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr); 957 GST_DEBUG_OBJECT(src, "Response headers going downstream: %" GST_PTR_FORMAT, headers.get()); 958 959 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_element(GST_OBJECT_CAST(src), gst_structure_copy(httpHeaders))); 960 961 priv->httpHeadersEvent = adoptGRef(gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, httpHeaders)); 962 priv->wereHeadersReceived = true; 963 priv->headersCondition.notifyOne(); 964 } 886 965 completionHandler(ShouldContinue::Yes); 887 966 } … … 890 969 { 891 970 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); 892 WebKitWebSrcPrivate* priv = src->priv; 893 894 if (priv->isSeeking) { 895 GST_DEBUG_OBJECT(src, "Seek in progress, ignoring data"); 896 return; 897 } 898 899 if (priv->offset < priv->requestedOffset) { 900 // Range request failed; seeking manually. 901 if (priv->offset + length <= priv->requestedOffset) { 902 // Discard all the buffers coming before the requested seek position. 903 priv->offset += length; 904 return; 905 } 906 907 if (priv->offset + length > priv->requestedOffset) { 908 guint64 offset = priv->requestedOffset - priv->offset; 909 data += offset; 910 length -= offset; 911 priv->offset = priv->requestedOffset; 912 } 913 914 priv->requestedOffset = 0; 971 GstBaseSrc* baseSrc = GST_BASE_SRC_CAST(src); 972 WebKitWebSrcPrivate* priv = src->priv; 973 974 GST_LOG_OBJECT(src, "Have %d bytes of data", length); 975 LockHolder locker(priv->responseLock); 976 977 uint64_t newPosition = priv->readPosition + length; 978 if (LIKELY (priv->requestedPosition == priv->readPosition)) 979 priv->requestedPosition = newPosition; 980 priv->readPosition = newPosition; 981 982 uint64_t newSize = 0; 983 if (priv->haveSize && (newPosition > priv->size)) { 984 GST_DEBUG_OBJECT(src, "Got position previous estimated content size (%" G_GINT64_FORMAT " > %" G_GINT64_FORMAT ")", newPosition, priv->size); 985 newSize = newPosition; 986 } else if (!priv->haveSize) { 987 GST_DEBUG_OBJECT(src, "Got initial response without Content-Length, assuming response size as duration."); 988 newSize = length; 989 priv->haveSize = true; 990 } 991 992 if (newSize) { 993 priv->size = newSize; 994 baseSrc->segment.duration = priv->size; 995 gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_duration_changed(GST_OBJECT_CAST(src))); 915 996 } 916 997 917 998 checkUpdateBlocksize(length); 918 999 919 uint64_t startingOffset = priv->offset; 920 921 if (priv->requestedOffset == priv->offset) 922 priv->requestedOffset += length; 923 priv->offset += length; 924 // priv->size == 0 if received length on didReceiveResponse < 0. 925 if (priv->size > 0 && priv->offset > priv->size) { 926 GST_DEBUG_OBJECT(src, "Updating internal size from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT, priv->size, priv->offset); 927 gst_app_src_set_size(priv->appsrc, priv->offset); 928 priv->size = priv->offset; 929 } 930 931 // Now split the recv'd buffer into buffers that are of a size basesrc suggests. It is important not 932 // to push buffers that are too large, otherwise incorrect buffering messages can be sent from the 933 // pipeline. 934 uint64_t bufferSize = static_cast<uint64_t>(length); 935 uint64_t blockSize = static_cast<uint64_t>(gst_base_src_get_blocksize(GST_BASE_SRC_CAST(priv->appsrc))); 936 GST_LOG_OBJECT(src, "Splitting the received buffer into %" PRIu64 " blocks", bufferSize / blockSize); 937 for (uint64_t currentOffset = 0; currentOffset < bufferSize; currentOffset += blockSize) { 938 uint64_t subBufferOffset = startingOffset + currentOffset; 939 uint64_t currentOffsetSize = std::min(blockSize, bufferSize - currentOffset); 940 941 GstBuffer* subBuffer = gst_buffer_new_wrapped(g_memdup(data + currentOffset, currentOffsetSize), currentOffsetSize); 942 if (UNLIKELY(!subBuffer)) { 943 GST_ELEMENT_ERROR(src, CORE, FAILED, ("Failed to allocate sub-buffer"), (nullptr)); 944 break; 945 } 946 947 GST_TRACE_OBJECT(src, "Sub-buffer bounds: %" PRIu64 " -- %" PRIu64, subBufferOffset, subBufferOffset + currentOffsetSize); 948 GST_BUFFER_OFFSET(subBuffer) = subBufferOffset; 949 GST_BUFFER_OFFSET_END(subBuffer) = subBufferOffset + currentOffsetSize; 950 951 if (priv->isSeeking) { 952 GST_TRACE_OBJECT(src, "Stopping buffer appends due to seek"); 953 // A seek has happened in the middle of us breaking the 954 // incoming data up from a previous request. Stop pushing 955 // buffers that are now from the incorrect offset. 956 break; 957 } 958 959 // It may be tempting to use a GstBufferList here, but note 960 // that there is a race condition in GstDownloadBuffer during 961 // seek flushes that can cause decoders to read at incorrect 962 // offsets. 963 GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, subBuffer); 964 965 if (UNLIKELY(ret != GST_FLOW_OK && ret != GST_FLOW_EOS && ret != GST_FLOW_FLUSHING)) { 966 GST_ELEMENT_ERROR(src, CORE, FAILED, (nullptr), (nullptr)); 967 break; 968 } 1000 if (!priv->wasResponseReceived) 1001 priv->wasResponseReceived = true; 1002 priv->responseCondition.notifyOne(); 1003 1004 { 1005 LockHolder adapterLocker(priv->adapterLock); 1006 GstBuffer* buffer = gst_buffer_new_wrapped(g_memdup(data, length), length); 1007 priv->queueSize += length; 1008 gst_adapter_push(priv->adapter.get(), buffer); 1009 priv->adapterCondition.notifyOne(); 969 1010 } 970 1011 } … … 974 1015 WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); 975 1016 GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr)); 976 gst_app_src_end_of_stream(src->priv->appsrc);977 webKitWebSrcStop( src);1017 src->priv->doesHaveEOS = true; 1018 webKitWebSrcStop(GST_BASE_SRC_CAST(src)); 978 1019 } 979 1020 … … 987 1028 } 988 1029 989 gst_app_src_end_of_stream(src->priv->appsrc);1030 src->priv->doesHaveEOS = true; 990 1031 } 991 1032 … … 995 1036 WebKitWebSrcPrivate* priv = src->priv; 996 1037 997 GST_DEBUG_OBJECT(src, "Have EOS"); 998 999 if (!priv->isSeeking) 1000 gst_app_src_end_of_stream(priv->appsrc); 1038 if (priv->isSeeking && !priv->isFlushing) 1039 priv->isSeeking = false; 1001 1040 } 1002 1041 -
trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.h
r236961 r243058 21 21 #if ENABLE(VIDEO) && USE(GSTREAMER) 22 22 23 #include <gst/base/gstpushsrc.h> 23 24 #include <gst/gst.h> 24 25 … … 41 42 42 43 struct _WebKitWebSrc { 43 Gst Binparent;44 GstPushSrc parent; 44 45 45 46 WebKitWebSrcPrivate *priv; … … 47 48 48 49 struct _WebKitWebSrcClass { 49 Gst BinClass parentClass;50 GstPushSrcClass parentClass; 50 51 }; 51 52
Note: See TracChangeset
for help on using the changeset viewer.