[GStreamer] Adaptive streaming playback broken with GStreamer < 1.12
authorphiln@webkit.org <philn@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Apr 2019 09:14:18 +0000 (09:14 +0000)
committerphiln@webkit.org <philn@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Apr 2019 09:14:18 +0000 (09:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196765

Reviewed by Xabier Rodriguez-Calvar.

Without the following patch in gst-plugins-bad, the uridownloader
doesn't relay need-context messages to its parent, so in our case
the player can't share its context with secondary webkitwebsrc
elements and a RELEASE_ASSERT is hit in the WebProcess.

So the workaround is to use again webkit+ protocol prefixes for
GStreamer versions older than 1.12.

https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/commit/8cf858fb27919e1d631223375f81b98055623733

* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
(WebCore::convertToInternalProtocol):
(WebCore::MediaPlayerPrivateGStreamer::setPlaybinURL):
(WebCore::MediaPlayerPrivateGStreamer::loadFull):
(WebCore::MediaPlayerPrivateGStreamer::handleMessage):
(WebCore::MediaPlayerPrivateGStreamer::wouldTaintOrigin const):
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:
* platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp:
(webKitWebSrcStart):
(webKitWebSrcGetProtocols):
(convertPlaybinURI):
(webKitWebSrcSetUri):
(CachedResourceStreamingClient::responseReceived):
(webKitSrcWouldTaintOrigin):
* platform/graphics/gstreamer/WebKitWebSourceGStreamer.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244109 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h
Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp
Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.h

index 6ef3806..d7aeee8 100644 (file)
@@ -1,3 +1,36 @@
+2019-04-10  Philippe Normand  <pnormand@igalia.com>
+
+        [GStreamer] Adaptive streaming playback broken with GStreamer < 1.12
+        https://bugs.webkit.org/show_bug.cgi?id=196765
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Without the following patch in gst-plugins-bad, the uridownloader
+        doesn't relay need-context messages to its parent, so in our case
+        the player can't share its context with secondary webkitwebsrc
+        elements and a RELEASE_ASSERT is hit in the WebProcess.
+
+        So the workaround is to use again webkit+ protocol prefixes for
+        GStreamer versions older than 1.12.
+
+        https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/commit/8cf858fb27919e1d631223375f81b98055623733
+
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp:
+        (WebCore::convertToInternalProtocol):
+        (WebCore::MediaPlayerPrivateGStreamer::setPlaybinURL):
+        (WebCore::MediaPlayerPrivateGStreamer::loadFull):
+        (WebCore::MediaPlayerPrivateGStreamer::handleMessage):
+        (WebCore::MediaPlayerPrivateGStreamer::wouldTaintOrigin const):
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.h:
+        * platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp:
+        (webKitWebSrcStart):
+        (webKitWebSrcGetProtocols):
+        (convertPlaybinURI):
+        (webKitWebSrcSetUri):
+        (CachedResourceStreamingClient::responseReceived):
+        (webKitSrcWouldTaintOrigin):
+        * platform/graphics/gstreamer/WebKitWebSourceGStreamer.h:
+
 2019-04-10  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [ATK] Defer the emision of AtkObject::children-changed signal after layout is done
index de78a7c..3f5263d 100644 (file)
@@ -225,6 +225,14 @@ MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
     }
 }
 
+static void convertToInternalProtocol(URL& url)
+{
+    if (webkitGstCheckVersion(1, 12, 0))
+        return;
+    if (url.protocolIsInHTTPFamily() || url.protocolIsBlob())
+        url.setProtocol("webkit+" + url.protocol());
+}
+
 void MediaPlayerPrivateGStreamer::setPlaybinURL(const URL& url)
 {
     // Clean out everything after file:// url path.
@@ -233,6 +241,7 @@ void MediaPlayerPrivateGStreamer::setPlaybinURL(const URL& url)
         cleanURLString = cleanURLString.substring(0, url.pathEnd());
 
     m_url = URL(URL(), cleanURLString);
+    convertToInternalProtocol(m_url);
     GST_INFO_OBJECT(pipeline(), "Load %s", m_url.string().utf8().data());
     g_object_set(m_pipeline.get(), "uri", m_url.string().utf8().data(), nullptr);
 }
@@ -302,6 +311,7 @@ void MediaPlayerPrivateGStreamer::loadFull(const String& urlString, const gchar*
     m_readyState = MediaPlayer::HaveNothing;
     m_player->readyStateChanged();
     m_volumeAndMuteInitialized = false;
+    m_hasTaintedOrigin = WTF::nullopt;
 
     if (!m_delayingLoad)
         commitLoad();
@@ -1349,7 +1359,7 @@ void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
             const char* uri = redirectionUri ? redirectionUri : gst_structure_get_string(structure, "uri");
             if (uri) {
                 URL url(URL(), uri);
-
+                convertToInternalProtocol(url);
                 m_origins.add(SecurityOrigin::create(url));
 
                 if (url != m_url) {
@@ -1361,7 +1371,16 @@ void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
             if (gst_structure_get(structure, "response-headers", GST_TYPE_STRUCTURE, &responseHeaders.outPtr(), nullptr)) {
                 const char* contentLengthHeaderName = httpHeaderNameString(HTTPHeaderName::ContentLength).utf8().data();
                 uint64_t contentLength = 0;
-                gst_structure_get_uint64(responseHeaders.get(), contentLengthHeaderName, &contentLength);
+                if (!gst_structure_get_uint64(responseHeaders.get(), contentLengthHeaderName, &contentLength)) {
+                    // souphttpsrc sets a string for Content-Length, so
+                    // handle it here, until we remove the webkit+ protocol
+                    // prefix from webkitwebsrc.
+                    if (const char* contentLengthAsString = gst_structure_get_string(responseHeaders.get(), contentLengthHeaderName)) {
+                        contentLength = g_ascii_strtoull(contentLengthAsString, nullptr, 10);
+                        if (contentLength == G_MAXUINT64)
+                            contentLength = 0;
+                    }
+                }
                 GST_INFO_OBJECT(pipeline(), "%s stream detected", !contentLength ? "Live" : "Non-live");
                 if (!contentLength) {
                     m_isStreaming = true;
@@ -1371,6 +1390,11 @@ void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
         } else if (gst_structure_has_name(structure, "webkit-network-statistics")) {
             if (gst_structure_get(structure, "read-position", G_TYPE_UINT64, &m_networkReadPosition, "size", G_TYPE_UINT64, &m_httpResponseTotalSize, nullptr))
                 GST_DEBUG_OBJECT(pipeline(), "Updated network read position %" G_GUINT64_FORMAT ", size: %" G_GUINT64_FORMAT, m_networkReadPosition, m_httpResponseTotalSize);
+        } else if (gst_structure_has_name(structure, "adaptive-streaming-statistics")) {
+            if (WEBKIT_IS_WEB_SRC(m_source.get()) && !webkitGstCheckVersion(1, 12, 0)) {
+                if (const char* uri = gst_structure_get_string(structure, "uri"))
+                    m_hasTaintedOrigin = webKitSrcWouldTaintOrigin(WEBKIT_WEB_SRC(m_source.get()), SecurityOrigin::create(URL(URL(), uri)));
+            }
         } else
             GST_DEBUG_OBJECT(pipeline(), "Unhandled element message: %" GST_PTR_FORMAT, structure);
         break;
@@ -2489,15 +2513,23 @@ bool MediaPlayerPrivateGStreamer::canSaveMediaData() const
 
 Optional<bool> MediaPlayerPrivateGStreamer::wouldTaintOrigin(const SecurityOrigin& origin) const
 {
-    GST_TRACE_OBJECT(pipeline(), "Checking %u origins", m_origins.size());
-    for (auto& responseOrigin : m_origins) {
-        if (!origin.canAccess(*responseOrigin)) {
-            GST_DEBUG_OBJECT(pipeline(), "Found reachable response origin");
-            return true;
+    if (webkitGstCheckVersion(1, 12, 0)) {
+        GST_TRACE_OBJECT(pipeline(), "Checking %u origins", m_origins.size());
+        for (auto& responseOrigin : m_origins) {
+            if (!origin.canAccess(*responseOrigin)) {
+                GST_DEBUG_OBJECT(pipeline(), "Found reachable response origin");
+                return true;
+            }
         }
+        GST_DEBUG_OBJECT(pipeline(), "No valid response origin found");
+        return false;
     }
-    GST_DEBUG_OBJECT(pipeline(), "No valid response origin found");
-    return false;
+
+    // GStreamer < 1.12 has an incomplete uridownloader implementation so we
+    // can't use WebKitWebSrc for adaptive fragments downloading if this
+    // version is detected.
+    UNUSED_PARAM(origin);
+    return m_hasTaintedOrigin;
 }
 
 }
index bc1a512..1f574b2 100644 (file)
@@ -292,6 +292,7 @@ private:
     mutable uint64_t m_readPositionAtLastDidLoadingProgress { 0 };
 
     HashSet<RefPtr<WebCore::SecurityOrigin>> m_origins;
+    Optional<bool> m_hasTaintedOrigin { WTF::nullopt };
 };
 
 }
index 483fd65..47e7e20 100644 (file)
@@ -44,6 +44,8 @@ public:
     CachedResourceStreamingClient(WebKitWebSrc*, ResourceRequest&&);
     virtual ~CachedResourceStreamingClient();
 
+    const HashSet<RefPtr<WebCore::SecurityOrigin>>& securityOrigins() const { return m_origins; }
+
 private:
     void checkUpdateBlocksize(uint64_t bytesRead);
 
@@ -65,6 +67,7 @@ private:
 
     GRefPtr<GstElement> m_src;
     ResourceRequest m_request;
+    HashSet<RefPtr<WebCore::SecurityOrigin>> m_origins;
 };
 
 enum MainThreadSourceNotification {
@@ -486,7 +489,7 @@ static gboolean webKitWebSrcStart(GstBaseSrc* baseSrc)
     WebKitWebSrc* src = WEBKIT_WEB_SRC(baseSrc);
     WebKitWebSrcPrivate* priv = src->priv;
 
-    if (!priv->player) {
+    if (webkitGstCheckVersion(1, 12, 0) && !priv->player) {
         GRefPtr<GstQuery> query = adoptGRef(gst_query_new_context(WEBKIT_WEB_SRC_PLAYER_CONTEXT_TYPE_NAME));
         if (gst_pad_peer_query(GST_BASE_SRC_PAD(baseSrc), query.get())) {
             GstContext* context;
@@ -765,10 +768,30 @@ static GstURIType webKitWebSrcUriGetType(GType)
 
 const gchar* const* webKitWebSrcGetProtocols(GType)
 {
-    static const char* protocols[] = {"http", "https", "blob", nullptr };
+    static const char* protocols[4];
+    if (webkitGstCheckVersion(1, 12, 0)) {
+        protocols[0] = "http";
+        protocols[1] = "https";
+        protocols[2] = "blob";
+    } else {
+        protocols[0] = "webkit+http";
+        protocols[1] = "webkit+https";
+        protocols[2] = "webkit+blob";
+    }
+    protocols[3] = nullptr;
     return protocols;
 }
 
+static URL convertPlaybinURI(const char* uriString)
+{
+    URL url(URL(), uriString);
+    if (!webkitGstCheckVersion(1, 12, 0)) {
+        ASSERT(url.protocol().substring(0, 7) == "webkit+");
+        url.setProtocol(url.protocol().substring(7).toString());
+    }
+    return url;
+}
+
 static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
 {
     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
@@ -796,7 +819,8 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr
         return FALSE;
     }
 
-    URL url(URL(), uri);
+    URL url = convertPlaybinURI(uri);
+
     if (!urlHasSupportedProtocol(url)) {
         g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri);
         return FALSE;
@@ -879,6 +903,8 @@ void CachedResourceStreamingClient::responseReceived(PlatformMediaResource&, con
 
     GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode());
 
+    m_origins.add(SecurityOrigin::create(response.url()));
+
     auto responseURI = response.url().string().utf8();
     if (priv->originalURI != responseURI)
         priv->redirectedURI = WTFMove(responseURI);
@@ -1062,4 +1088,16 @@ void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&)
         priv->isSeeking = false;
 }
 
+bool webKitSrcWouldTaintOrigin(WebKitWebSrc* src, const SecurityOrigin& origin)
+{
+    WebKitWebSrcPrivate* priv = src->priv;
+
+    auto* cachedResourceStreamingClient = reinterpret_cast<CachedResourceStreamingClient*>(priv->resource->client());
+    for (auto& responseOrigin : cachedResourceStreamingClient->securityOrigins()) {
+        if (!origin.canAccess(*responseOrigin))
+            return true;
+    }
+    return false;
+}
+
 #endif // ENABLE(VIDEO) && USE(GSTREAMER)
index 709984c..ab85183 100644 (file)
@@ -25,6 +25,7 @@
 
 namespace WebCore {
 class MediaPlayer;
+class SecurityOrigin;
 }
 
 G_BEGIN_DECLS
@@ -54,6 +55,7 @@ struct _WebKitWebSrcClass {
 GType webkit_web_src_get_type(void);
 void webKitWebSrcSetMediaPlayer(WebKitWebSrc*, WebCore::MediaPlayer*);
 bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc*);
+bool webKitSrcWouldTaintOrigin(WebKitWebSrc*, const WebCore::SecurityOrigin&);
 
 G_END_DECLS