diff options
Diffstat (limited to 'Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp')
-rw-r--r-- | Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp | 951 |
1 files changed, 434 insertions, 517 deletions
diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp index a6d961f17..d2ae28f2c 100644 --- a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp @@ -22,68 +22,62 @@ #if ENABLE(VIDEO) && USE(GSTREAMER) -#include "CachedRawResource.h" -#include "CachedRawResourceClient.h" -#include "CachedResourceHandle.h" -#include "CachedResourceLoader.h" -#include "CachedResourceRequest.h" -#include "Frame.h" #include "GRefPtrGStreamer.h" -#include "GStreamerVersioning.h" +#include "GStreamerUtilities.h" +#include "GUniquePtrGStreamer.h" +#include "HTTPHeaderNames.h" +#include "MainThreadNotifier.h" #include "MediaPlayer.h" #include "NotImplemented.h" +#include "PlatformMediaResourceLoader.h" +#include "ResourceError.h" #include "ResourceHandle.h" #include "ResourceHandleClient.h" #include "ResourceRequest.h" #include "ResourceResponse.h" +#include "SharedBuffer.h" #include <gst/app/gstappsrc.h> #include <gst/gst.h> #include <gst/pbutils/missing-plugins.h> +#include <wtf/MainThread.h> #include <wtf/Noncopyable.h> -#include <wtf/gobject/GMutexLocker.h> -#include <wtf/gobject/GOwnPtr.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GMutexLocker.h> +#include <wtf/glib/GRefPtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> using namespace WebCore; -static CachedResourceLoader* s_cachedResourceLoader = 0; - class StreamingClient { public: StreamingClient(WebKitWebSrc*); virtual ~StreamingClient(); - virtual bool loadFailed() const = 0; - virtual void setDefersLoading(bool) = 0; - protected: char* createReadBuffer(size_t requestedSize, size_t& actualSize); void handleResponseReceived(const ResourceResponse&); void handleDataReceived(const char*, int); void handleNotifyFinished(); - GRefPtr<GstElement> m_src; + GstElement* m_src; }; -class CachedResourceStreamingClient : public CachedRawResourceClient, public StreamingClient { - WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient); WTF_MAKE_FAST_ALLOCATED; +class CachedResourceStreamingClient final : public PlatformMediaResourceClient, public StreamingClient { + WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient); public: - CachedResourceStreamingClient(WebKitWebSrc*, CachedResourceLoader*, const ResourceRequest&); + CachedResourceStreamingClient(WebKitWebSrc*); virtual ~CachedResourceStreamingClient(); - // StreamingClient virtual methods. - virtual bool loadFailed() const; - virtual void setDefersLoading(bool); - private: - // CachedResourceClient virtual methods. - virtual char* getOrCreateReadBuffer(CachedResource*, size_t requestedSize, size_t& actualSize); - virtual void responseReceived(CachedResource*, const ResourceResponse&); - virtual void dataReceived(CachedResource*, const char*, int); - virtual void notifyFinished(CachedResource*); - - CachedResourceHandle<CachedRawResource> m_resource; + // PlatformMediaResourceClient virtual methods. +#if USE(SOUP) + virtual char* getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize) override; +#endif + virtual void responseReceived(PlatformMediaResource&, const ResourceResponse&) override; + virtual void dataReceived(PlatformMediaResource&, const char*, int) override; + virtual void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) override; + virtual void loadFailed(PlatformMediaResource&, const ResourceError&) override; + virtual void loadFinished(PlatformMediaResource&) override; }; class ResourceHandleStreamingClient : public ResourceHandleClient, public StreamingClient { @@ -93,15 +87,16 @@ class ResourceHandleStreamingClient : public ResourceHandleClient, public Stream virtual ~ResourceHandleStreamingClient(); // StreamingClient virtual methods. - virtual bool loadFailed() const; - virtual void setDefersLoading(bool); + bool loadFailed() const; + void setDefersLoading(bool); private: // ResourceHandleClient virtual methods. virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize); virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&); virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); - virtual void didReceiveData(ResourceHandle*, const char*, int, int); + virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int); + virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer>, int encodedLength); virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); virtual void didFail(ResourceHandle*, const ResourceError&); virtual void wasBlocked(ResourceHandle*); @@ -110,50 +105,51 @@ class ResourceHandleStreamingClient : public ResourceHandleClient, public Stream RefPtr<ResourceHandle> m_resource; }; +enum MainThreadSourceNotification { + Start = 1 << 0, + Stop = 1 << 1, + NeedData = 1 << 2, + EnoughData = 1 << 3, + Seek = 1 << 4 +}; + #define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate)) struct _WebKitWebSrcPrivate { GstAppSrc* appsrc; GstPad* srcpad; gchar* uri; + bool keepAlive; + GUniquePtr<GstStructure> extraHeaders; + bool compress; + GUniquePtr<gchar> httpMethod; WebCore::MediaPlayer* player; - StreamingClient* client; + RefPtr<PlatformMediaResourceLoader> loader; + RefPtr<PlatformMediaResource> resource; + ResourceHandleStreamingClient* client; + + bool didPassAccessControlCheck; guint64 offset; guint64 size; gboolean seekable; gboolean paused; + bool isSeeking; guint64 requestedOffset; - guint startID; - guint stopID; - guint needDataID; - guint enoughDataID; - guint seekID; - + MainThreadNotifier<MainThreadSourceNotification> notifier; GRefPtr<GstBuffer> buffer; - - // icecast stuff - gboolean iradioMode; - gchar* iradioName; - gchar* iradioGenre; - gchar* iradioUrl; - gchar* iradioTitle; - - // TRUE if appsrc's version is >= 0.10.27, see - // https://bugzilla.gnome.org/show_bug.cgi?id=609423 - gboolean haveAppSrc27; }; enum { - PROP_IRADIO_MODE = 1, - PROP_IRADIO_NAME, - PROP_IRADIO_GENRE, - PROP_IRADIO_URL, - PROP_IRADIO_TITLE, - PROP_LOCATION + PROP_0, + PROP_LOCATION, + PROP_KEEP_ALIVE, + PROP_EXTRA_HEADERS, + PROP_COMPRESS, + PROP_METHOD }; static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", @@ -173,19 +169,62 @@ static void webKitWebSrcGetProperty(GObject*, guint propertyID, GValue*, GParamS static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange); static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*); -#ifndef GST_API_VERSION_1 -static gboolean webKitWebSrcQuery(GstPad*, GstQuery*); -#endif -static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData); -static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData); -static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData); +static void webKitWebSrcNeedData(WebKitWebSrc*); +static void webKitWebSrcEnoughData(WebKitWebSrc*); +static void webKitWebSrcSeek(WebKitWebSrc*); static GstAppSrcCallbacks appsrcCallbacks = { - webKitWebSrcNeedDataCb, - webKitWebSrcEnoughDataCb, - webKitWebSrcSeekDataCb, - { 0 } + // need_data + [](GstAppSrc*, guint, gpointer userData) { + WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); + WebKitWebSrcPrivate* priv = src->priv; + + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (!priv->paused) + return; + } + + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::NeedData, [protector] { webKitWebSrcNeedData(protector.get()); }); + }, + // enough_data + [](GstAppSrc*, gpointer userData) { + WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); + WebKitWebSrcPrivate* priv = src->priv; + + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (priv->paused) + return; + } + + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::EnoughData, [protector] { webKitWebSrcEnoughData(protector.get()); }); + }, + // seek_data + [](GstAppSrc*, guint64 offset, gpointer userData) -> gboolean { + WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); + WebKitWebSrcPrivate* priv = src->priv; + + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (offset == priv->offset && priv->requestedOffset == priv->offset) + return TRUE; + + if (!priv->seekable) + return FALSE; + + priv->isSeeking = true; + priv->requestedOffset = offset; + } + + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::Seek, [protector] { webKitWebSrcSeek(protector.get()); }); + return TRUE; + }, + { nullptr } }; #define webkit_web_src_parent_class parent_class @@ -207,51 +246,9 @@ static void webkit_web_src_class_init(WebKitWebSrcClass* klass) gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate)); - setGstElementClassMetadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris", + gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris", "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); - // icecast stuff - g_object_class_install_property(oklass, - PROP_IRADIO_MODE, - g_param_spec_boolean("iradio-mode", - "iradio-mode", - "Enable internet radio mode (extraction of shoutcast/icecast metadata)", - FALSE, - (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(oklass, - PROP_IRADIO_NAME, - g_param_spec_string("iradio-name", - "iradio-name", - "Name of the stream", - 0, - (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(oklass, - PROP_IRADIO_GENRE, - g_param_spec_string("iradio-genre", - "iradio-genre", - "Genre of the stream", - 0, - (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(oklass, - PROP_IRADIO_URL, - g_param_spec_string("iradio-url", - "iradio-url", - "Homepage URL for radio stream", - 0, - (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(oklass, - PROP_IRADIO_TITLE, - g_param_spec_string("iradio-title", - "iradio-title", - "Name of currently playing song", - 0, - (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - /* Allows setting the uri using the 'location' property, which is used * for example by gst_element_make_from_uri() */ g_object_class_install_property(oklass, @@ -261,6 +258,23 @@ static void webkit_web_src_class_init(WebKitWebSrcClass* klass) "Location to read from", 0, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_KEEP_ALIVE, + g_param_spec_boolean("keep-alive", "keep-alive", "Use HTTP persistent connections", + FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_EXTRA_HEADERS, + g_param_spec_boxed("extra-headers", "Extra Headers", "Extra headers to append to the HTTP request", + GST_TYPE_STRUCTURE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_COMPRESS, + g_param_spec_boolean("compress", "Compress", "Allow compressed content encodings", + FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property(oklass, PROP_METHOD, + g_param_spec_string("method", "method", "The HTTP method to use (default: GET)", + nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + eklass->change_state = webKitWebSrcChangeState; g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate)); @@ -271,6 +285,7 @@ static void webkit_web_src_init(WebKitWebSrc* src) WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src); src->priv = priv; + new (priv) WebKitWebSrcPrivate(); priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", 0)); if (!priv->appsrc) { @@ -278,9 +293,6 @@ static void webkit_web_src_init(WebKitWebSrc* src) return; } - GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(priv->appsrc)->elementfactory); - priv->haveAppSrc27 = gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 27); - gst_bin_add(GST_BIN(src), GST_ELEMENT(priv->appsrc)); @@ -289,12 +301,8 @@ static void webkit_web_src_init(WebKitWebSrc* src) gst_element_add_pad(GST_ELEMENT(src), priv->srcpad); -#ifdef GST_API_VERSION_1 GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT); gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent); -#else - gst_pad_set_query_function(priv->srcpad, webKitWebSrcQuery); -#endif gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, 0); gst_app_src_set_emit_signals(priv->appsrc, FALSE); @@ -317,8 +325,7 @@ static void webkit_web_src_init(WebKitWebSrc* src) // likely that libsoup already provides new data before // the queue is really empty. // This might need tweaking for ports not using libsoup. - if (priv->haveAppSrc27) - g_object_set(priv->appsrc, "min-percent", 20, NULL); + g_object_set(priv->appsrc, "min-percent", 20, NULL); gst_app_src_set_caps(priv->appsrc, 0); gst_app_src_set_size(priv->appsrc, -1); @@ -329,10 +336,7 @@ static void webKitWebSrcDispose(GObject* object) WebKitWebSrc* src = WEBKIT_WEB_SRC(object); WebKitWebSrcPrivate* priv = src->priv; - if (priv->player) { - priv->player = 0; - s_cachedResourceLoader = 0; - } + priv->player = 0; GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object)); } @@ -343,6 +347,7 @@ static void webKitWebSrcFinalize(GObject* object) WebKitWebSrcPrivate* priv = src->priv; g_free(priv->uri); + priv->~WebKitWebSrcPrivate(); GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); } @@ -350,20 +355,24 @@ static void webKitWebSrcFinalize(GObject* object) static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec) { WebKitWebSrc* src = WEBKIT_WEB_SRC(object); - WebKitWebSrcPrivate* priv = src->priv; switch (propID) { - case PROP_IRADIO_MODE: { - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - priv->iradioMode = g_value_get_boolean(value); - break; - } case PROP_LOCATION: -#ifdef GST_API_VERSION_1 gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0); -#else - gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value)); -#endif + break; + case PROP_KEEP_ALIVE: + src->priv->keepAlive = g_value_get_boolean(value); + break; + case PROP_EXTRA_HEADERS: { + const GstStructure* s = gst_value_get_structure(value); + src->priv->extraHeaders.reset(s ? gst_structure_copy(s) : nullptr); + break; + } + case PROP_COMPRESS: + src->priv->compress = g_value_get_boolean(value); + break; + case PROP_METHOD: + src->priv->httpMethod.reset(g_value_dup_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec); @@ -376,25 +385,22 @@ static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value WebKitWebSrc* src = WEBKIT_WEB_SRC(object); WebKitWebSrcPrivate* priv = src->priv; - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); switch (propID) { - case PROP_IRADIO_MODE: - g_value_set_boolean(value, priv->iradioMode); - break; - case PROP_IRADIO_NAME: - g_value_set_string(value, priv->iradioName); + case PROP_LOCATION: + g_value_set_string(value, priv->uri); break; - case PROP_IRADIO_GENRE: - g_value_set_string(value, priv->iradioGenre); + case PROP_KEEP_ALIVE: + g_value_set_boolean(value, priv->keepAlive); break; - case PROP_IRADIO_URL: - g_value_set_string(value, priv->iradioUrl); + case PROP_EXTRA_HEADERS: + gst_value_set_structure(value, priv->extraHeaders.get()); break; - case PROP_IRADIO_TITLE: - g_value_set_string(value, priv->iradioTitle); + case PROP_COMPRESS: + g_value_set_boolean(value, priv->compress); break; - case PROP_LOCATION: - g_value_set_string(value, priv->uri); + case PROP_METHOD: + g_value_set_string(value, priv->httpMethod.get()); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec); @@ -402,161 +408,205 @@ static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value } } -static void removeTimeoutSources(WebKitWebSrc* src) -{ - WebKitWebSrcPrivate* priv = src->priv; - - if (priv->startID) - g_source_remove(priv->startID); - priv->startID = 0; - - if (priv->needDataID) - g_source_remove(priv->needDataID); - priv->needDataID = 0; - - if (priv->enoughDataID) - g_source_remove(priv->enoughDataID); - priv->enoughDataID = 0; - - if (priv->seekID) - g_source_remove(priv->seekID); - priv->seekID = 0; -} - -static gboolean webKitWebSrcStop(WebKitWebSrc* src) +static void webKitWebSrcStop(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; ASSERT(isMainThread()); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - bool seeking = priv->seekID; + bool wasSeeking = std::exchange(priv->isSeeking, false); - removeTimeoutSources(src); - priv->stopID = 0; + priv->notifier.cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek); if (priv->client) { delete priv->client; priv->client = 0; } + if (!priv->keepAlive) + priv->loader = nullptr; + if (priv->buffer) { -#ifdef GST_API_VERSION_1 unmapGstBuffer(priv->buffer.get()); -#endif priv->buffer.clear(); } priv->paused = FALSE; - g_free(priv->iradioName); - priv->iradioName = 0; - - g_free(priv->iradioGenre); - priv->iradioGenre = 0; - - g_free(priv->iradioUrl); - priv->iradioUrl = 0; - - g_free(priv->iradioTitle); - priv->iradioTitle = 0; - priv->offset = 0; priv->seekable = FALSE; - if (!seeking) { + if (!wasSeeking) { priv->size = 0; priv->requestedOffset = 0; - if (priv->player) { - priv->player = 0; - s_cachedResourceLoader = 0; - } + priv->player = 0; } locker.unlock(); if (priv->appsrc) { gst_app_src_set_caps(priv->appsrc, 0); - if (!seeking) + if (!wasSeeking) gst_app_src_set_size(priv->appsrc, -1); } GST_DEBUG_OBJECT(src, "Stopped request"); +} + +static bool webKitWebSrcSetExtraHeader(GQuark fieldId, const GValue* value, gpointer userData) +{ + GUniquePtr<gchar> fieldContent; + + if (G_VALUE_HOLDS_STRING(value)) + fieldContent.reset(g_value_dup_string(value)); + else { + GValue dest = G_VALUE_INIT; + + g_value_init(&dest, G_TYPE_STRING); + if (g_value_transform(value, &dest)) + fieldContent.reset(g_value_dup_string(&dest)); + } + + const gchar* fieldName = g_quark_to_string(fieldId); + if (!fieldContent.get()) { + GST_ERROR("extra-headers field '%s' contains no value or can't be converted to a string", fieldName); + return false; + } + + GST_DEBUG("Appending extra header: \"%s: %s\"", fieldName, fieldContent.get()); + ResourceRequest* request = static_cast<ResourceRequest*>(userData); + request->setHTTPHeaderField(fieldName, fieldContent.get()); + return true; +} + +static gboolean webKitWebSrcProcessExtraHeaders(GQuark fieldId, const GValue* value, gpointer userData) +{ + if (G_VALUE_TYPE(value) == GST_TYPE_ARRAY) { + unsigned size = gst_value_array_get_size(value); - return FALSE; + for (unsigned i = 0; i < size; i++) { + if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_array_get_value(value, i), userData)) + return FALSE; + } + return TRUE; + } + + if (G_VALUE_TYPE(value) == GST_TYPE_LIST) { + unsigned size = gst_value_list_get_size(value); + + for (unsigned i = 0; i < size; i++) { + if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_list_get_value(value, i), userData)) + return FALSE; + } + return TRUE; + } + + return webKitWebSrcSetExtraHeader(fieldId, value, userData); } -static gboolean webKitWebSrcStart(WebKitWebSrc* src) +static void webKitWebSrcStart(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; ASSERT(isMainThread()); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - priv->startID = 0; + priv->didPassAccessControlCheck = false; if (!priv->uri) { GST_ERROR_OBJECT(src, "No URI provided"); locker.unlock(); webKitWebSrcStop(src); - return FALSE; + return; } ASSERT(!priv->client); - KURL url = KURL(KURL(), priv->uri); + GST_DEBUG_OBJECT(src, "Fetching %s", priv->uri); + URL url = URL(URL(), priv->uri); ResourceRequest request(url); request.setAllowCookies(true); + request.setFirstPartyForCookies(url); + + priv->size = 0; if (priv->player) request.setHTTPReferrer(priv->player->referrer()); + if (priv->httpMethod.get()) + request.setHTTPMethod(priv->httpMethod.get()); + +#if USE(SOUP) + // By default, HTTP Accept-Encoding is disabled here as we don't + // want the received response to be encoded in any way as we need + // to rely on the proper size of the returned data on + // didReceiveResponse. + // If Accept-Encoding is used, the server may send the data in encoded format and + // request.expectedContentLength() will have the "wrong" size (the size of the + // compressed data), even though the data received in didReceiveData is uncompressed. + // This is however useful to enable for adaptive streaming + // scenarios, when the demuxer needs to download playlists. + if (!priv->compress) + request.setAcceptEncoding(false); +#endif + // Let Apple web servers know we want to access their nice movie trailers. if (!g_ascii_strcasecmp("movies.apple.com", url.host().utf8().data()) || !g_ascii_strcasecmp("trailers.apple.com", url.host().utf8().data())) request.setHTTPUserAgent("Quicktime/7.6.6"); if (priv->requestedOffset) { - GOwnPtr<gchar> val; - - val.set(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset)); - request.setHTTPHeaderField("Range", val.get()); + GUniquePtr<gchar> val(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset)); + request.setHTTPHeaderField(HTTPHeaderName::Range, val.get()); } priv->offset = priv->requestedOffset; - if (priv->iradioMode) - request.setHTTPHeaderField("icy-metadata", "1"); + if (!priv->keepAlive) { + GST_DEBUG_OBJECT(src, "Persistent connection support disabled"); + request.setHTTPHeaderField(HTTPHeaderName::Connection, "close"); + } - // Needed to use DLNA streaming servers - request.setHTTPHeaderField("transferMode.dlna", "Streaming"); + if (priv->extraHeaders) + gst_structure_foreach(priv->extraHeaders.get(), webKitWebSrcProcessExtraHeaders, &request); - CachedResourceLoader* loader = 0; - if (priv->player) - loader = priv->player->cachedResourceLoader(); - else - loader = s_cachedResourceLoader; + // We always request Icecast/Shoutcast metadata, just in case ... + request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1"); + + bool loadFailed = true; + if (priv->player && !priv->loader) + priv->loader = priv->player->createResourceLoader(); - if (loader) - priv->client = new CachedResourceStreamingClient(src, loader, request); + if (priv->loader) { + PlatformMediaResourceLoader::LoadOptions loadOptions = 0; + if (request.url().protocolIsBlob()) + loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData; + priv->resource = priv->loader->requestResource(request, loadOptions); + loadFailed = !priv->resource; - if (!priv->client) + if (priv->resource) + priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(src)); + } else { priv->client = new ResourceHandleStreamingClient(src, request); + loadFailed = priv->client->loadFailed(); + } - if (!priv->client || priv->client->loadFailed()) { + if (loadFailed) { GST_ERROR_OBJECT(src, "Failed to setup streaming client"); if (priv->client) { delete priv->client; - priv->client = 0; + priv->client = nullptr; } + priv->loader = nullptr; + priv->resource = nullptr; locker.unlock(); webKitWebSrcStop(src); - return FALSE; + return; } GST_DEBUG_OBJECT(src, "Started request"); - return FALSE; } static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition) @@ -584,18 +634,22 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStat return ret; } - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: + { GST_DEBUG_OBJECT(src, "READY->PAUSED"); - priv->startID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcStart, gst_object_ref(src), (GDestroyNotify) gst_object_unref); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::Start, [protector] { webKitWebSrcStart(protector.get()); }); break; + } case GST_STATE_CHANGE_PAUSED_TO_READY: + { GST_DEBUG_OBJECT(src, "PAUSED->READY"); - // cancel pending sources - removeTimeoutSources(src); - priv->stopID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcStop, gst_object_ref(src), (GDestroyNotify) gst_object_unref); + priv->notifier.cancelPendingNotifications(); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::Stop, [protector] { webKitWebSrcStop(protector.get()); }); break; + } default: break; } @@ -615,7 +669,7 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ gst_query_parse_duration(query, &format, NULL); GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format)); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); if (format == GST_FORMAT_BYTES && src->priv->size > 0) { gst_query_set_duration(query, format, src->priv->size); result = TRUE; @@ -623,11 +677,22 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ break; } case GST_QUERY_URI: { - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); gst_query_set_uri(query, src->priv->uri); result = TRUE; break; } +#if GST_CHECK_VERSION(1, 2, 0) + case GST_QUERY_SCHEDULING: { + GstSchedulingFlags flags; + int minSize, maxSize, align; + + gst_query_parse_scheduling(query, &flags, &minSize, &maxSize, &align); + gst_query_set_scheduling(query, static_cast<GstSchedulingFlags>(flags | GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED), minSize, maxSize, align); + result = TRUE; + break; + } +#endif default: { GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad))); @@ -641,17 +706,13 @@ static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQ return result; } -#ifndef GST_API_VERSION_1 -static gboolean webKitWebSrcQuery(GstPad* pad, GstQuery* query) +static bool urlHasSupportedProtocol(const URL& url) { - GRefPtr<GstElement> src = adoptGRef(gst_pad_get_parent_element(pad)); - return webKitWebSrcQueryWithParent(pad, GST_OBJECT(src.get()), query); + return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIsBlob()); } -#endif // uri handler interface -#ifdef GST_API_VERSION_1 static GstURIType webKitWebSrcUriGetType(GType) { return GST_URI_SRC; @@ -659,7 +720,7 @@ static GstURIType webKitWebSrcUriGetType(GType) const gchar* const* webKitWebSrcGetProtocols(GType) { - static const char* protocols[] = {"http", "https", 0 }; + static const char* protocols[] = {"http", "https", "blob", 0 }; return protocols; } @@ -668,7 +729,7 @@ static gchar* webKitWebSrcGetUri(GstURIHandler* handler) WebKitWebSrc* src = WEBKIT_WEB_SRC(handler); gchar* ret; - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); ret = g_strdup(src->priv->uri); return ret; } @@ -683,7 +744,7 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr return FALSE; } - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); g_free(priv->uri); priv->uri = 0; @@ -691,9 +752,8 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr if (!uri) return TRUE; - KURL url(KURL(), uri); - - if (!url.isValid() || !url.protocolIsInHTTPFamily()) { + URL url(URL(), uri); + if (!urlHasSupportedProtocol(url)) { g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri); return FALSE; } @@ -702,58 +762,6 @@ static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GEr return TRUE; } -#else -static GstURIType webKitWebSrcUriGetType(void) -{ - return GST_URI_SRC; -} - -static gchar** webKitWebSrcGetProtocols(void) -{ - static gchar* protocols[] = {(gchar*) "http", (gchar*) "https", 0 }; - return protocols; -} - -static const gchar* webKitWebSrcGetUri(GstURIHandler* handler) -{ - WebKitWebSrc* src = WEBKIT_WEB_SRC(handler); - gchar* ret; - - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - ret = g_strdup(src->priv->uri); - return ret; -} - -static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri) -{ - WebKitWebSrc* src = WEBKIT_WEB_SRC(handler); - WebKitWebSrcPrivate* priv = src->priv; - - if (GST_STATE(src) >= GST_STATE_PAUSED) { - GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED"); - return FALSE; - } - - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - - g_free(priv->uri); - priv->uri = 0; - - if (!uri) - return TRUE; - - KURL url(KURL(), uri); - - if (!url.isValid() || !url.protocolIsInHTTPFamily()) { - GST_ERROR_OBJECT(src, "Invalid URI '%s'", uri); - return FALSE; - } - - priv->uri = g_strdup(url.string().utf8().data()); - return TRUE; -} -#endif - static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer) { GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface; @@ -766,283 +774,196 @@ static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer) // appsrc callbacks -static gboolean webKitWebSrcNeedDataMainCb(WebKitWebSrc* src) +static void webKitWebSrcNeedData(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; ASSERT(isMainThread()); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - // already stopped - if (!priv->needDataID) - return FALSE; + GST_DEBUG_OBJECT(src, "Need more data"); - priv->paused = FALSE; - priv->needDataID = 0; - locker.unlock(); + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + priv->paused = FALSE; + } if (priv->client) priv->client->setDefersLoading(false); - return FALSE; + if (priv->resource) + priv->resource->setDefersLoading(false); } -static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData) -{ - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); - WebKitWebSrcPrivate* priv = src->priv; - - GST_DEBUG_OBJECT(src, "Need more data: %u", length); - - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - if (priv->needDataID || !priv->paused) { - return; - } - - priv->needDataID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); -} - -static gboolean webKitWebSrcEnoughDataMainCb(WebKitWebSrc* src) +static void webKitWebSrcEnoughData(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; ASSERT(isMainThread()); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - // already stopped - if (!priv->enoughDataID) - return FALSE; - - priv->paused = TRUE; - priv->enoughDataID = 0; - locker.unlock(); - - if (priv->client) - priv->client->setDefersLoading(true); - return FALSE; -} - -static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData) -{ - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); - WebKitWebSrcPrivate* priv = src->priv; - GST_DEBUG_OBJECT(src, "Have enough data"); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - if (priv->enoughDataID || priv->paused) { - return; + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + priv->paused = TRUE; } - priv->enoughDataID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); + if (priv->client) + priv->client->setDefersLoading(true); + if (priv->resource) + priv->resource->setDefersLoading(true); } -static gboolean webKitWebSrcSeekMainCb(WebKitWebSrc* src) +static void webKitWebSrcSeek(WebKitWebSrc* src) { - WebKitWebSrcPrivate* priv = src->priv; - ASSERT(isMainThread()); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - // already stopped - if (!priv->seekID) - return FALSE; - locker.unlock(); + GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, src->priv->requestedOffset); webKitWebSrcStop(src); webKitWebSrcStart(src); - - return FALSE; -} - -static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData) -{ - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); - WebKitWebSrcPrivate* priv = src->priv; - - GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - if (offset == priv->offset && priv->requestedOffset == priv->offset) - return TRUE; - - if (!priv->seekable) - return FALSE; - - GST_DEBUG_OBJECT(src, "Doing range-request seek"); - priv->requestedOffset = offset; - - if (priv->seekID) - g_source_remove(priv->seekID); - priv->seekID = g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc) webKitWebSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref); - return TRUE; } void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player) { ASSERT(player); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); src->priv->player = player; - s_cachedResourceLoader = player->cachedResourceLoader(); +} + +bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc* src) +{ + return src->priv->didPassAccessControlCheck; } StreamingClient::StreamingClient(WebKitWebSrc* src) - : m_src(adoptGRef(static_cast<GstElement*>(gst_object_ref(src)))) + : m_src(static_cast<GstElement*>(gst_object_ref(src))) { } StreamingClient::~StreamingClient() { + gst_object_unref(m_src); } char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); WebKitWebSrcPrivate* priv = src->priv; ASSERT(!priv->buffer); GstBuffer* buffer = gst_buffer_new_and_alloc(requestedSize); -#ifdef GST_API_VERSION_1 mapGstBuffer(buffer); -#endif - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); priv->buffer = adoptGRef(buffer); locker.unlock(); - actualSize = getGstBufferSize(buffer); + actualSize = gst_buffer_get_size(buffer); return getGstBufferDataPointer(buffer); } void StreamingClient::handleResponseReceived(const ResourceResponse& response) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); WebKitWebSrcPrivate* priv = src->priv; GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode()); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - - // If we seeked we need 206 == PARTIAL_CONTENT - if (priv->requestedOffset && response.httpStatusCode() != 206) { - locker.unlock(); - GST_ELEMENT_ERROR(src, RESOURCE, READ, (0), (0)); + if (response.httpStatusCode() >= 400) { + GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr)); gst_app_src_end_of_stream(priv->appsrc); webKitWebSrcStop(src); return; } + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + + if (priv->isSeeking) { + GST_DEBUG_OBJECT(src, "Seek in progress, ignoring response"); + return; + } + + if (priv->requestedOffset) { + // Seeking ... we expect a 206 == PARTIAL_CONTENT + if (response.httpStatusCode() == 200) { + // Range request didn't have a ranged response; resetting offset. + priv->offset = 0; + } else if (response.httpStatusCode() != 206) { + // Range request completely failed. + locker.unlock(); + GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received unexpected %d HTTP status code", response.httpStatusCode()), (nullptr)); + gst_app_src_end_of_stream(priv->appsrc); + webKitWebSrcStop(src); + return; + } + } + long long length = response.expectedContentLength(); - if (length > 0) + if (length > 0 && priv->requestedOffset && response.httpStatusCode() == 206) length += priv->requestedOffset; priv->size = length >= 0 ? length : 0; - priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField("Accept-Ranges").utf8().data()); - -#ifdef GST_API_VERSION_1 - GstTagList* tags = gst_tag_list_new_empty(); -#else - GstTagList* tags = gst_tag_list_new(); -#endif - String value = response.httpHeaderField("icy-name"); - if (!value.isEmpty()) { - g_free(priv->iradioName); - priv->iradioName = g_strdup(value.utf8().data()); - g_object_notify(G_OBJECT(src), "iradio-name"); - gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, priv->iradioName, NULL); - } - value = response.httpHeaderField("icy-genre"); - if (!value.isEmpty()) { - g_free(priv->iradioGenre); - priv->iradioGenre = g_strdup(value.utf8().data()); - g_object_notify(G_OBJECT(src), "iradio-genre"); - gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, priv->iradioGenre, NULL); - } - value = response.httpHeaderField("icy-url"); - if (!value.isEmpty()) { - g_free(priv->iradioUrl); - priv->iradioUrl = g_strdup(value.utf8().data()); - g_object_notify(G_OBJECT(src), "iradio-url"); - gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION, priv->iradioUrl, NULL); - } - value = response.httpHeaderField("icy-title"); - if (!value.isEmpty()) { - g_free(priv->iradioTitle); - priv->iradioTitle = g_strdup(value.utf8().data()); - g_object_notify(G_OBJECT(src), "iradio-title"); - gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, priv->iradioTitle, NULL); - } + priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data()); locker.unlock(); // notify size/duration if (length > 0) { gst_app_src_set_size(priv->appsrc, length); - -#ifndef GST_API_VERSION_1 - if (!priv->haveAppSrc27) { - gst_segment_set_duration(&GST_BASE_SRC(priv->appsrc)->segment, GST_FORMAT_BYTES, length); - gst_element_post_message(GST_ELEMENT(priv->appsrc), - gst_message_new_duration(GST_OBJECT(priv->appsrc), - GST_FORMAT_BYTES, length)); - } -#endif } else gst_app_src_set_size(priv->appsrc, -1); - // icecast stuff - value = response.httpHeaderField("icy-metaint"); - if (!value.isEmpty()) { - gchar* endptr = 0; - gint64 icyMetaInt = g_ascii_strtoll(value.utf8().data(), &endptr, 10); - - if (endptr && *endptr == '\0' && icyMetaInt > 0) { - GRefPtr<GstCaps> caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, (gint) icyMetaInt, NULL)); - - gst_app_src_set_caps(priv->appsrc, caps.get()); - } - } else - gst_app_src_set_caps(priv->appsrc, 0); - - // notify tags - if (gst_tag_list_is_empty(tags)) -#ifdef GST_API_VERSION_1 - gst_tag_list_unref(tags); -#else - gst_tag_list_free(tags); -#endif - else - notifyGstTagsOnPad(GST_ELEMENT(src), priv->srcpad, tags); + gst_app_src_set_caps(priv->appsrc, 0); } void StreamingClient::handleDataReceived(const char* data, int length) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); WebKitWebSrcPrivate* priv = src->priv; - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - GST_LOG_OBJECT(src, "Have %d bytes of data", priv->buffer ? getGstBufferSize(priv->buffer.get()) : length); + GST_LOG_OBJECT(src, "Have %lld bytes of data", priv->buffer ? static_cast<long long>(gst_buffer_get_size(priv->buffer.get())) : length); ASSERT(!priv->buffer || data == getGstBufferDataPointer(priv->buffer.get())); -#ifdef GST_API_VERSION_1 if (priv->buffer) unmapGstBuffer(priv->buffer.get()); -#endif - if (priv->seekID) { + if (priv->isSeeking) { GST_DEBUG_OBJECT(src, "Seek in progress, ignoring data"); priv->buffer.clear(); return; } + if (priv->offset < priv->requestedOffset) { + // Range request failed; seeking manually. + if (priv->offset + length <= priv->requestedOffset) { + // Discard all the buffers coming before the requested seek position. + priv->offset += length; + priv->buffer.clear(); + return; + } + + if (priv->offset + length > priv->requestedOffset) { + guint64 offset = priv->requestedOffset - priv->offset; + data += offset; + length -= offset; + if (priv->buffer) + gst_buffer_resize(priv->buffer.get(), offset, -1); + priv->offset = priv->requestedOffset; + } + + priv->requestedOffset = 0; + } + // Ports using the GStreamer backend but not the soup implementation of ResourceHandle // won't be using buffers provided by this client, the buffer is created here in that case. if (!priv->buffer) priv->buffer = adoptGRef(createGstBufferForData(data, length)); else - setGstBufferSize(priv->buffer.get(), length); + gst_buffer_set_size(priv->buffer.get(), static_cast<gssize>(length)); GST_BUFFER_OFFSET(priv->buffer.get()) = priv->offset; if (priv->requestedOffset == priv->offset) @@ -1059,103 +980,88 @@ void StreamingClient::handleDataReceived(const char* data, int length) locker.unlock(); GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, priv->buffer.leakRef()); -#ifdef GST_API_VERSION_1 if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS) -#else - if (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED) -#endif GST_ELEMENT_ERROR(src, CORE, FAILED, (0), (0)); } void StreamingClient::handleNotifyFinished() { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); WebKitWebSrcPrivate* priv = src->priv; GST_DEBUG_OBJECT(src, "Have EOS"); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - if (!priv->seekID) { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (!priv->isSeeking) { locker.unlock(); gst_app_src_end_of_stream(priv->appsrc); } } -CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, CachedResourceLoader* resourceLoader, const ResourceRequest& request) +CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src) : StreamingClient(src) { - CachedResourceRequest cacheRequest(request, ResourceLoaderOptions(SendCallbacks, DoNotSniffContent, DoNotBufferData, DoNotAllowStoredCredentials, DoNotAskClientForCrossOriginCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType)); - m_resource = resourceLoader->requestRawResource(cacheRequest); - if (m_resource) - m_resource->addClient(this); } CachedResourceStreamingClient::~CachedResourceStreamingClient() { - if (m_resource) { - m_resource->removeClient(this); - m_resource = 0; - } -} - -bool CachedResourceStreamingClient::loadFailed() const -{ - return !m_resource; -} - -void CachedResourceStreamingClient::setDefersLoading(bool defers) -{ - if (m_resource) - m_resource->setDefersLoading(defers); } -char* CachedResourceStreamingClient::getOrCreateReadBuffer(CachedResource*, size_t requestedSize, size_t& actualSize) +#if USE(SOUP) +char* CachedResourceStreamingClient::getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize) { return createReadBuffer(requestedSize, actualSize); } +#endif -void CachedResourceStreamingClient::responseReceived(CachedResource*, const ResourceResponse& response) +void CachedResourceStreamingClient::responseReceived(PlatformMediaResource&, const ResourceResponse& response) { + WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(m_src)->priv; + priv->didPassAccessControlCheck = priv->resource->didPassAccessControlCheck(); handleResponseReceived(response); } -void CachedResourceStreamingClient::dataReceived(CachedResource*, const char* data, int length) +void CachedResourceStreamingClient::dataReceived(PlatformMediaResource&, const char* data, int length) { handleDataReceived(data, length); } -void CachedResourceStreamingClient::notifyFinished(CachedResource* resource) +void CachedResourceStreamingClient::accessControlCheckFailed(PlatformMediaResource&, const ResourceError& error) { - if (resource->loadFailedOrCanceled()) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr)); + gst_app_src_end_of_stream(src->priv->appsrc); + webKitWebSrcStop(src); +} - if (!resource->wasCanceled()) { - const ResourceError& error = resource->resourceError(); - GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data()); - GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0)); - } - gst_app_src_end_of_stream(src->priv->appsrc); - return; +void CachedResourceStreamingClient::loadFailed(PlatformMediaResource&, const ResourceError& error) +{ + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + + if (!error.isCancellation()) { + GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data()); + GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr)); } + gst_app_src_end_of_stream(src->priv->appsrc); +} + +void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&) +{ handleNotifyFinished(); } ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, const ResourceRequest& request) : StreamingClient(src) { - NetworkingContext* context = 0; - if (s_cachedResourceLoader) - context = s_cachedResourceLoader->frame()->loader()->networkingContext(); - m_resource = ResourceHandle::create(context, request, this, false, false); + m_resource = ResourceHandle::create(0 /*context*/, request, this, false, false); } ResourceHandleStreamingClient::~ResourceHandleStreamingClient() { if (m_resource) { m_resource->cancel(); - m_resource.release(); - m_resource = 0; + m_resource = nullptr; } } @@ -1184,9 +1090,20 @@ void ResourceHandleStreamingClient::didReceiveResponse(ResourceHandle*, const Re handleResponseReceived(response); } -void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* data, int length, int) +void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int) { - handleDataReceived(data, length); + ASSERT_NOT_REACHED(); +} + +void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int /* encodedLength */) +{ + // This pattern is suggested by SharedBuffer.h. + const char* segment; + unsigned position = 0; + while (unsigned length = buffer->getSomeData(segment, position)) { + handleDataReceived(segment, length); + position += length; + } } void ResourceHandleStreamingClient::didFinishLoading(ResourceHandle*, double) @@ -1196,7 +1113,7 @@ void ResourceHandleStreamingClient::didFinishLoading(ResourceHandle*, double) void ResourceHandleStreamingClient::didFail(ResourceHandle*, const ResourceError& error) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data()); GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0)); @@ -1205,13 +1122,13 @@ void ResourceHandleStreamingClient::didFail(ResourceHandle*, const ResourceError void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); - GOwnPtr<gchar> uri; + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + GUniquePtr<gchar> uri; GST_ERROR_OBJECT(src, "Request was blocked"); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - uri.set(g_strdup(src->priv->uri)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + uri.reset(g_strdup(src->priv->uri)); locker.unlock(); GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", uri.get()), (0)); @@ -1219,13 +1136,13 @@ void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*) void ResourceHandleStreamingClient::cannotShowURL(ResourceHandle*) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get()); - GOwnPtr<gchar> uri; + WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src); + GUniquePtr<gchar> uri; GST_ERROR_OBJECT(src, "Cannot show URL"); - WebCore::GMutexLocker locker(GST_OBJECT_GET_LOCK(src)); - uri.set(g_strdup(src->priv->uri)); + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + uri.reset(g_strdup(src->priv->uri)); locker.unlock(); GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", uri.get()), (0)); |