Title: [216067] trunk/Source/WebCore
Revision
216067
Author
[email protected]
Date
2017-05-02 03:05:58 -0700 (Tue, 02 May 2017)

Log Message

[GStreamer] Dailymotion live stream videos don't play
https://bugs.webkit.org/show_bug.cgi?id=170767

Reviewed by Sergio Villar Senin.

The video shows a message saying that an error occurred and nothing is played. There are actually several
problems with dailymotion. The main issue is that URLs are redirected by the server, and GStreamer needs to
know the redirected URL. Once GStreamer knows the redirected URL the error message no longer appears, the video
starts but it always stops after a few seconds. This is because the source receives an early EOS while still
downloading the fragments. The reason of the early EOS is because dailymotion sends a wrong Content-Length header,
something that is expected to happen and we correctly handle that case when receiving the data, by updating the
size accordingly if the bytes received are longer than the expected content length. This particular case
doesn't work well with GStreamer automatic EOS handling, which is the default. At some point, GStreamer detects
that the bytes received are at least the expected ones and emits a GST_EVENT_EOS that the GstUriDownloader
handles finishing the download early. We should always disable automatic EOS, since we know when EOS actually
happens and we already call gst_app_src_end_of_stream(). This patch also emits a GST_EVENT_CUSTOM_DOWNSTREAM_STICKY
event to let GStreamer know about the HTTP headers sent and received.

* platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp:
(webkit_web_src_init): Disable automatic EOS.
(webKitWebSrcGetProperty): Return the redirected URL in case of redirection.
(webKitWebSrcStart): Pass the ResourceRequest to the stream clients.
(webKitWebSrcQueryWithParent): Set the redirected URL in the query in case of redirection.
(webKitWebSrcSetUri): Clear also the redirected URL when setting a new URI.
(StreamingClient::StreamingClient): Use GRefPtr for the source and initialize the request too.
(StreamingClient::~StreamingClient): Remove explicit unref.
(StreamingClient::createReadBuffer):
(StreamingClient::handleResponseReceived): Initialize the redirected URL in case of redirection. Create and push
the HTTP headers event.
(StreamingClient::handleDataReceived):
(StreamingClient::handleNotifyFinished):
(CachedResourceStreamingClient::CachedResourceStreamingClient):
(CachedResourceStreamingClient::responseReceived):
(CachedResourceStreamingClient::accessControlCheckFailed):
(CachedResourceStreamingClient::loadFailed):
(ResourceHandleStreamingClient::ResourceHandleStreamingClient):
(ResourceHandleStreamingClient::didFail):
(ResourceHandleStreamingClient::wasBlocked):
(ResourceHandleStreamingClient::cannotShowURL):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (216066 => 216067)


--- trunk/Source/WebCore/ChangeLog	2017-05-02 08:55:43 UTC (rev 216066)
+++ trunk/Source/WebCore/ChangeLog	2017-05-02 10:05:58 UTC (rev 216067)
@@ -1,3 +1,45 @@
+2017-05-02  Carlos Garcia Campos  <[email protected]>
+
+        [GStreamer] Dailymotion live stream videos don't play
+        https://bugs.webkit.org/show_bug.cgi?id=170767
+
+        Reviewed by Sergio Villar Senin.
+
+        The video shows a message saying that an error occurred and nothing is played. There are actually several
+        problems with dailymotion. The main issue is that URLs are redirected by the server, and GStreamer needs to
+        know the redirected URL. Once GStreamer knows the redirected URL the error message no longer appears, the video
+        starts but it always stops after a few seconds. This is because the source receives an early EOS while still
+        downloading the fragments. The reason of the early EOS is because dailymotion sends a wrong Content-Length header,
+        something that is expected to happen and we correctly handle that case when receiving the data, by updating the
+        size accordingly if the bytes received are longer than the expected content length. This particular case
+        doesn't work well with GStreamer automatic EOS handling, which is the default. At some point, GStreamer detects
+        that the bytes received are at least the expected ones and emits a GST_EVENT_EOS that the GstUriDownloader
+        handles finishing the download early. We should always disable automatic EOS, since we know when EOS actually
+        happens and we already call gst_app_src_end_of_stream(). This patch also emits a GST_EVENT_CUSTOM_DOWNSTREAM_STICKY
+        event to let GStreamer know about the HTTP headers sent and received.
+
+        * platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp:
+        (webkit_web_src_init): Disable automatic EOS.
+        (webKitWebSrcGetProperty): Return the redirected URL in case of redirection.
+        (webKitWebSrcStart): Pass the ResourceRequest to the stream clients.
+        (webKitWebSrcQueryWithParent): Set the redirected URL in the query in case of redirection.
+        (webKitWebSrcSetUri): Clear also the redirected URL when setting a new URI.
+        (StreamingClient::StreamingClient): Use GRefPtr for the source and initialize the request too.
+        (StreamingClient::~StreamingClient): Remove explicit unref.
+        (StreamingClient::createReadBuffer):
+        (StreamingClient::handleResponseReceived): Initialize the redirected URL in case of redirection. Create and push
+        the HTTP headers event.
+        (StreamingClient::handleDataReceived):
+        (StreamingClient::handleNotifyFinished):
+        (CachedResourceStreamingClient::CachedResourceStreamingClient):
+        (CachedResourceStreamingClient::responseReceived):
+        (CachedResourceStreamingClient::accessControlCheckFailed):
+        (CachedResourceStreamingClient::loadFailed):
+        (ResourceHandleStreamingClient::ResourceHandleStreamingClient):
+        (ResourceHandleStreamingClient::didFail):
+        (ResourceHandleStreamingClient::wasBlocked):
+        (ResourceHandleStreamingClient::cannotShowURL):
+
 2017-05-02  Youenn Fablet  <[email protected]>
 
         Adding logging to RTCPeerConnection to allow WebRTC application debugging

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp (216066 => 216067)


--- trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp	2017-05-02 08:55:43 UTC (rev 216066)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp	2017-05-02 10:05:58 UTC (rev 216067)
@@ -53,77 +53,78 @@
 using namespace WebCore;
 
 class StreamingClient {
-    public:
-        StreamingClient(WebKitWebSrc*);
-        virtual ~StreamingClient();
+public:
+    StreamingClient(WebKitWebSrc*, ResourceRequest&&);
+    virtual ~StreamingClient();
 
-    protected:
-        char* createReadBuffer(size_t requestedSize, size_t& actualSize);
-        void handleResponseReceived(const ResourceResponse&);
-        void handleDataReceived(const char*, int);
-        void handleNotifyFinished();
+protected:
+    char* createReadBuffer(size_t requestedSize, size_t& actualSize);
+    void handleResponseReceived(const ResourceResponse&);
+    void handleDataReceived(const char*, int);
+    void handleNotifyFinished();
 
-        GstElement* m_src;
+    GRefPtr<GstElement> m_src;
+    ResourceRequest m_request;
 };
 
 class CachedResourceStreamingClient final : public PlatformMediaResourceClient, public StreamingClient {
     WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient);
-    public:
-        CachedResourceStreamingClient(WebKitWebSrc*);
-        virtual ~CachedResourceStreamingClient();
+public:
+    CachedResourceStreamingClient(WebKitWebSrc*, ResourceRequest&&);
+    virtual ~CachedResourceStreamingClient();
 
-    private:
-        // PlatformMediaResourceClient virtual methods.
+private:
+    // PlatformMediaResourceClient virtual methods.
 #if USE(SOUP)
-        char* getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize) override;
+    char* getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize) override;
 #endif
-        void responseReceived(PlatformMediaResource&, const ResourceResponse&) override;
-        void dataReceived(PlatformMediaResource&, const char*, int) override;
-        void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) override;
-        void loadFailed(PlatformMediaResource&, const ResourceError&) override;
-        void loadFinished(PlatformMediaResource&) override;
+    void responseReceived(PlatformMediaResource&, const ResourceResponse&) override;
+    void dataReceived(PlatformMediaResource&, const char*, int) override;
+    void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) override;
+    void loadFailed(PlatformMediaResource&, const ResourceError&) override;
+    void loadFinished(PlatformMediaResource&) override;
 };
 
 class ResourceHandleStreamingClient : public ThreadSafeRefCounted<ResourceHandleStreamingClient>, public ResourceHandleClient, public StreamingClient {
-    public:
-        static Ref<ResourceHandleStreamingClient> create(WebKitWebSrc* src, ResourceRequest&& request)
-        {
-            return adoptRef(*new ResourceHandleStreamingClient(src, WTFMove(request)));
-        }
-        virtual ~ResourceHandleStreamingClient();
+public:
+    static Ref<ResourceHandleStreamingClient> create(WebKitWebSrc* src, ResourceRequest&& request)
+    {
+        return adoptRef(*new ResourceHandleStreamingClient(src, WTFMove(request)));
+    }
+    virtual ~ResourceHandleStreamingClient();
 
-        void invalidate();
+    void invalidate();
 
-        // StreamingClient virtual methods.
-        bool loadFailed() const;
-        void setDefersLoading(bool);
+    // StreamingClient virtual methods.
+    bool loadFailed() const;
+    void setDefersLoading(bool);
 
-    private:
-        ResourceHandleStreamingClient(WebKitWebSrc*, ResourceRequest&&);
-        void cleanupAndStopRunLoop();
+private:
+    ResourceHandleStreamingClient(WebKitWebSrc*, ResourceRequest&&);
+    void cleanupAndStopRunLoop();
 
-        // ResourceHandleClient virtual methods.
+    // ResourceHandleClient virtual methods.
 #if USE(SOUP)
-        char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override;
+    char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override;
 #endif
-        ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&&, ResourceResponse&&) override;
-        void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override;
-        void didReceiveData(ResourceHandle*, const char*, unsigned, int) override;
-        void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int encodedLength) override;
-        void didFinishLoading(ResourceHandle*) override;
-        void didFail(ResourceHandle*, const ResourceError&) override;
-        void wasBlocked(ResourceHandle*) override;
-        void cannotShowURL(ResourceHandle*) override;
+    ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&&, ResourceResponse&&) override;
+    void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override;
+    void didReceiveData(ResourceHandle*, const char*, unsigned, int) override;
+    void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int encodedLength) override;
+    void didFinishLoading(ResourceHandle*) override;
+    void didFail(ResourceHandle*, const ResourceError&) override;
+    void wasBlocked(ResourceHandle*) override;
+    void cannotShowURL(ResourceHandle*) override;
 
-        RefPtr<Thread> m_thread;
-        Lock m_initializeRunLoopConditionMutex;
-        Condition m_initializeRunLoopCondition;
-        RunLoop* m_runLoop { nullptr };
-        Lock m_terminateRunLoopConditionMutex;
-        Condition m_terminateRunLoopCondition;
-        RefPtr<ResourceHandle> m_resource;
+    RefPtr<Thread> m_thread;
+    Lock m_initializeRunLoopConditionMutex;
+    Condition m_initializeRunLoopCondition;
+    RunLoop* m_runLoop { nullptr };
+    Lock m_terminateRunLoopConditionMutex;
+    Condition m_terminateRunLoopCondition;
+    RefPtr<ResourceHandle> m_resource;
 #if USE(SOUP)
-        std::unique_ptr<SoupNetworkSession> m_session;
+    std::unique_ptr<SoupNetworkSession> m_session;
 #endif
 };
 
@@ -140,7 +141,7 @@
     GstAppSrc* appsrc;
     GstPad* srcpad;
     CString originalURI;
-    CString resolvedURI;
+    CString redirectedURI;
     bool keepAlive;
     GUniquePtr<GstStructure> extraHeaders;
     bool compress;
@@ -317,6 +318,8 @@
     // This might need tweaking for ports not using libsoup.
     g_object_set(priv->appsrc, "min-percent", 20, nullptr);
 
+    gst_base_src_set_automatic_eos(GST_BASE_SRC(priv->appsrc), FALSE);
+
     gst_app_src_set_caps(priv->appsrc, nullptr);
     gst_app_src_set_size(priv->appsrc, -1);
 }
@@ -379,7 +382,7 @@
         g_value_set_string(value, priv->originalURI.data());
         break;
     case PROP_RESOLVED_LOCATION:
-        g_value_set_string(value, priv->resolvedURI.data());
+        g_value_set_string(value, priv->redirectedURI.isNull() ? priv->originalURI.data() : priv->redirectedURI.data());
         break;
     case PROP_KEEP_ALIVE:
         g_value_set_boolean(value, priv->keepAlive);
@@ -599,10 +602,9 @@
         PlatformMediaResourceLoader::LoadOptions loadOptions = 0;
         if (request.url().protocolIsBlob())
             loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData;
-        // FIXME: request should be moved for efficiency
         priv->resource = priv->loader->requestResource(ResourceRequest(request), loadOptions);
         if (priv->resource) {
-            priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(protector.get()));
+            priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(protector.get(), ResourceRequest(request)));
             GST_DEBUG_OBJECT(protector.get(), "Started request");
         } else {
             GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client");
@@ -680,6 +682,8 @@
     case GST_QUERY_URI: {
         WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
         gst_query_set_uri(query, src->priv->originalURI.data());
+        if (!src->priv->redirectedURI.isNull())
+            gst_query_set_uri_redirection(query, src->priv->redirectedURI.data());
         result = TRUE;
         break;
     }
@@ -745,6 +749,7 @@
 
     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
 
+    priv->redirectedURI = CString();
     priv->originalURI = CString();
     if (!uri)
         return TRUE;
@@ -863,19 +868,19 @@
     return src->priv->didPassAccessControlCheck;
 }
 
-StreamingClient::StreamingClient(WebKitWebSrc* src)
-    : m_src(static_cast<GstElement*>(gst_object_ref(src)))
+StreamingClient::StreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
+    : m_src(GST_ELEMENT(src))
+    , m_request(WTFMove(request))
 {
 }
 
 StreamingClient::~StreamingClient()
 {
-    gst_object_unref(m_src);
 }
 
 char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize)
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
     WebKitWebSrcPrivate* priv = src->priv;
 
     ASSERT(!priv->buffer);
@@ -894,12 +899,14 @@
 
 void StreamingClient::handleResponseReceived(const ResourceResponse& response)
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
     WebKitWebSrcPrivate* priv = src->priv;
 
     GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode());
 
-    priv->resolvedURI = response.url().string().utf8();
+    auto responseURI = response.url().string().utf8();
+    if (priv->originalURI != responseURI)
+        priv->redirectedURI = WTFMove(responseURI);
 
     if (response.httpStatusCode() >= 400) {
         GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr));
@@ -946,11 +953,26 @@
         gst_app_src_set_size(priv->appsrc, -1);
 
     gst_app_src_set_caps(priv->appsrc, nullptr);
+
+    // Emit a GST_EVENT_CUSTOM_DOWNSTREAM_STICKY event to let GStreamer know about the HTTP headers sent and received.
+    GstStructure* httpHeaders = gst_structure_new_empty("http-headers");
+    gst_structure_set(httpHeaders, "uri", G_TYPE_STRING, priv->originalURI.data(), nullptr);
+    if (!priv->redirectedURI.isNull())
+        gst_structure_set(httpHeaders, "redirection-uri", G_TYPE_STRING, priv->redirectedURI.data(), nullptr);
+    GUniquePtr<GstStructure> headers(gst_structure_new_empty("request-headers"));
+    for (const auto& header : m_request.httpHeaderFields())
+        gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr);
+    gst_structure_set(httpHeaders, "request-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr);
+    headers.reset(gst_structure_new_empty("response-headers"));
+    for (const auto& header : response.httpHeaderFields())
+        gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr);
+    gst_structure_set(httpHeaders, "response-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr);
+    gst_pad_push_event(GST_BASE_SRC_PAD(priv->appsrc), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, httpHeaders));
 }
 
 void StreamingClient::handleDataReceived(const char* data, int length)
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
     WebKitWebSrcPrivate* priv = src->priv;
 
     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
@@ -1017,7 +1039,7 @@
 
 void StreamingClient::handleNotifyFinished()
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
     WebKitWebSrcPrivate* priv = src->priv;
 
     GST_DEBUG_OBJECT(src, "Have EOS");
@@ -1029,8 +1051,8 @@
     }
 }
 
-CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src)
-    : StreamingClient(src)
+CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
+    : StreamingClient(src, WTFMove(request))
 {
 }
 
@@ -1047,7 +1069,7 @@
 
 void CachedResourceStreamingClient::responseReceived(PlatformMediaResource&, const ResourceResponse& response)
 {
-    WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(m_src)->priv;
+    WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(m_src.get())->priv;
     priv->didPassAccessControlCheck = priv->resource->didPassAccessControlCheck();
     handleResponseReceived(response);
 }
@@ -1059,7 +1081,7 @@
 
 void CachedResourceStreamingClient::accessControlCheckFailed(PlatformMediaResource&, const ResourceError& error)
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
     GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr));
     gst_app_src_end_of_stream(src->priv->appsrc);
     webKitWebSrcStop(src);
@@ -1067,7 +1089,7 @@
 
 void CachedResourceStreamingClient::loadFailed(PlatformMediaResource&, const ResourceError& error)
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
 
     if (!error.isCancellation()) {
         GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
@@ -1083,19 +1105,19 @@
 }
 
 ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
-    : StreamingClient(src)
+    : StreamingClient(src, WTFMove(request))
 {
     LockHolder locker(m_initializeRunLoopConditionMutex);
-    m_thread = Thread::create("ResourceHandleStreamingClient", [this, request = WTFMove(request)] {
+    m_thread = Thread::create("ResourceHandleStreamingClient", [this] {
         {
             LockHolder locker(m_initializeRunLoopConditionMutex);
             m_runLoop = &RunLoop::current();
 #if USE(SOUP)
             m_session = std::make_unique<SoupNetworkSession>();
-            m_resource = ResourceHandle::create(*m_session, request, this, true, false);
+            m_resource = ResourceHandle::create(*m_session, m_request, this, true, false);
 #else
             // FIXME: This create will hit an assert in debug builds. See https://bugs.webkit.org/show_bug.cgi?id=167003.
-            m_resource = ResourceHandle::create(nullptr /*context*/, request, this, true, false);
+            m_resource = ResourceHandle::create(nullptr, m_request, this, true, false);
 #endif
             m_initializeRunLoopCondition.notifyOne();
         }
@@ -1196,7 +1218,7 @@
 
 void ResourceHandleStreamingClient::didFail(ResourceHandle*, const ResourceError& error)
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
 
     GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
     GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr));
@@ -1205,7 +1227,7 @@
 
 void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*)
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
     GUniquePtr<gchar> uri;
 
     GST_ERROR_OBJECT(src, "Request was blocked");
@@ -1219,7 +1241,7 @@
 
 void ResourceHandleStreamingClient::cannotShowURL(ResourceHandle*)
 {
-    WebKitWebSrc* src = ""
+    WebKitWebSrc* src = ""
     GUniquePtr<gchar> uri;
 
     GST_ERROR_OBJECT(src, "Cannot show URL");
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to