Eliminate ResourceBuffer and use SharedBuffer directly instead
[WebKit-https.git] / Source / WebCore / platform / graphics / gstreamer / WebKitWebSourceGStreamer.cpp
1 /*
2  *  Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
3  *  Copyright (C) 2013 Collabora Ltd.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "WebKitWebSourceGStreamer.h"
22
23 #if ENABLE(VIDEO) && USE(GSTREAMER)
24
25 #include "GRefPtrGStreamer.h"
26 #include "GStreamerUtilities.h"
27 #include "HTTPHeaderNames.h"
28 #include "MediaPlayer.h"
29 #include "NotImplemented.h"
30 #include "PlatformMediaResourceLoader.h"
31 #include "ResourceError.h"
32 #include "ResourceHandle.h"
33 #include "ResourceHandleClient.h"
34 #include "ResourceRequest.h"
35 #include "ResourceResponse.h"
36 #include "SharedBuffer.h"
37 #include <gst/app/gstappsrc.h>
38 #include <gst/gst.h>
39 #include <gst/pbutils/missing-plugins.h>
40 #include <wtf/MainThread.h>
41 #include <wtf/Noncopyable.h>
42 #include <wtf/gobject/GMutexLocker.h>
43 #include <wtf/gobject/GRefPtr.h>
44 #include <wtf/gobject/GThreadSafeMainLoopSource.h>
45 #include <wtf/gobject/GUniquePtr.h>
46 #include <wtf/text/CString.h>
47
48 using namespace WebCore;
49
50 class StreamingClient {
51     public:
52         StreamingClient(WebKitWebSrc*);
53         virtual ~StreamingClient();
54
55     protected:
56         char* createReadBuffer(size_t requestedSize, size_t& actualSize);
57         void handleResponseReceived(const ResourceResponse&);
58         void handleDataReceived(const char*, int);
59         void handleNotifyFinished();
60
61         GstElement* m_src;
62 };
63
64 class CachedResourceStreamingClient final : public PlatformMediaResourceLoaderClient, public StreamingClient {
65     WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient);
66     public:
67         CachedResourceStreamingClient(WebKitWebSrc*);
68         virtual ~CachedResourceStreamingClient();
69
70     private:
71         // PlatformMediaResourceLoaderClient virtual methods.
72         virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override;
73         virtual void responseReceived(const ResourceResponse&) override;
74         virtual void dataReceived(const char*, int) override;
75         virtual void accessControlCheckFailed(const ResourceError&) override;
76         virtual void loadFailed(const ResourceError&) override;
77         virtual void loadFinished() override;
78 };
79
80 class ResourceHandleStreamingClient : public ResourceHandleClient, public StreamingClient {
81     WTF_MAKE_NONCOPYABLE(ResourceHandleStreamingClient); WTF_MAKE_FAST_ALLOCATED;
82     public:
83         ResourceHandleStreamingClient(WebKitWebSrc*, const ResourceRequest&);
84         virtual ~ResourceHandleStreamingClient();
85
86         // StreamingClient virtual methods.
87         bool loadFailed() const;
88         void setDefersLoading(bool);
89
90     private:
91         // ResourceHandleClient virtual methods.
92         virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize);
93         virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&);
94         virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
95         virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int);
96         virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer>, int encodedLength);
97         virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
98         virtual void didFail(ResourceHandle*, const ResourceError&);
99         virtual void wasBlocked(ResourceHandle*);
100         virtual void cannotShowURL(ResourceHandle*);
101
102         RefPtr<ResourceHandle> m_resource;
103 };
104
105 #define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate))
106 struct _WebKitWebSrcPrivate {
107     GstAppSrc* appsrc;
108     GstPad* srcpad;
109     gchar* uri;
110
111     WebCore::MediaPlayer* player;
112
113     RefPtr<PlatformMediaResourceLoader> loader;
114     ResourceHandleStreamingClient* client;
115
116     bool didPassAccessControlCheck;
117
118     guint64 offset;
119     guint64 size;
120     gboolean seekable;
121     gboolean paused;
122
123     guint64 requestedOffset;
124
125     GThreadSafeMainLoopSource startSource;
126     GThreadSafeMainLoopSource stopSource;
127     GThreadSafeMainLoopSource needDataSource;
128     GThreadSafeMainLoopSource enoughDataSource;
129     GThreadSafeMainLoopSource seekSource;
130
131     GRefPtr<GstBuffer> buffer;
132
133     // icecast stuff
134     gchar* iradioName;
135     gchar* iradioGenre;
136     gchar* iradioUrl;
137     gchar* iradioTitle;
138 };
139
140 enum {
141     PROP_IRADIO_NAME = 1,
142     PROP_IRADIO_GENRE,
143     PROP_IRADIO_URL,
144     PROP_IRADIO_TITLE,
145     PROP_LOCATION
146 };
147
148 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
149                                                                   GST_PAD_SRC,
150                                                                   GST_PAD_ALWAYS,
151                                                                   GST_STATIC_CAPS_ANY);
152
153 GST_DEBUG_CATEGORY_STATIC(webkit_web_src_debug);
154 #define GST_CAT_DEFAULT webkit_web_src_debug
155
156 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
157
158 static void webKitWebSrcDispose(GObject*);
159 static void webKitWebSrcFinalize(GObject*);
160 static void webKitWebSrcSetProperty(GObject*, guint propertyID, const GValue*, GParamSpec*);
161 static void webKitWebSrcGetProperty(GObject*, guint propertyID, GValue*, GParamSpec*);
162 static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange);
163
164 static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
165
166 static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData);
167 static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData);
168 static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData);
169
170 static GstAppSrcCallbacks appsrcCallbacks = {
171     webKitWebSrcNeedDataCb,
172     webKitWebSrcEnoughDataCb,
173     webKitWebSrcSeekDataCb,
174     { 0 }
175 };
176
177 #define webkit_web_src_parent_class parent_class
178 // We split this out into another macro to avoid a check-webkit-style error.
179 #define WEBKIT_WEB_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_web_src_debug, "webkitwebsrc", 0, "websrc element");
180 G_DEFINE_TYPE_WITH_CODE(WebKitWebSrc, webkit_web_src, GST_TYPE_BIN,
181                          G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitWebSrcUriHandlerInit);
182                          WEBKIT_WEB_SRC_CATEGORY_INIT);
183
184 static void webkit_web_src_class_init(WebKitWebSrcClass* klass)
185 {
186     GObjectClass* oklass = G_OBJECT_CLASS(klass);
187     GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
188
189     oklass->dispose = webKitWebSrcDispose;
190     oklass->finalize = webKitWebSrcFinalize;
191     oklass->set_property = webKitWebSrcSetProperty;
192     oklass->get_property = webKitWebSrcGetProperty;
193
194     gst_element_class_add_pad_template(eklass,
195                                        gst_static_pad_template_get(&srcTemplate));
196     gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris",
197                                "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
198
199     // icecast stuff
200     g_object_class_install_property(oklass,
201                                     PROP_IRADIO_NAME,
202                                     g_param_spec_string("iradio-name",
203                                                         "iradio-name",
204                                                         "Name of the stream",
205                                                         0,
206                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
207
208     g_object_class_install_property(oklass,
209                                     PROP_IRADIO_GENRE,
210                                     g_param_spec_string("iradio-genre",
211                                                         "iradio-genre",
212                                                         "Genre of the stream",
213                                                         0,
214                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
215
216     g_object_class_install_property(oklass,
217                                     PROP_IRADIO_URL,
218                                     g_param_spec_string("iradio-url",
219                                                         "iradio-url",
220                                                         "Homepage URL for radio stream",
221                                                         0,
222                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
223
224     g_object_class_install_property(oklass,
225                                     PROP_IRADIO_TITLE,
226                                     g_param_spec_string("iradio-title",
227                                                         "iradio-title",
228                                                         "Name of currently playing song",
229                                                         0,
230                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
231
232
233     /* Allows setting the uri using the 'location' property, which is used
234      * for example by gst_element_make_from_uri() */
235     g_object_class_install_property(oklass,
236                                     PROP_LOCATION,
237                                     g_param_spec_string("location",
238                                                         "location",
239                                                         "Location to read from",
240                                                         0,
241                                                         (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
242     eklass->change_state = webKitWebSrcChangeState;
243
244     g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate));
245 }
246
247 static void webkit_web_src_init(WebKitWebSrc* src)
248 {
249     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src);
250
251     src->priv = priv;
252     new (priv) WebKitWebSrcPrivate();
253
254     priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", 0));
255     if (!priv->appsrc) {
256         GST_ERROR_OBJECT(src, "Failed to create appsrc");
257         return;
258     }
259
260     gst_bin_add(GST_BIN(src), GST_ELEMENT(priv->appsrc));
261
262
263     GRefPtr<GstPad> targetPad = adoptGRef(gst_element_get_static_pad(GST_ELEMENT(priv->appsrc), "src"));
264     priv->srcpad = webkitGstGhostPadFromStaticTemplate(&srcTemplate, "src", targetPad.get());
265
266     gst_element_add_pad(GST_ELEMENT(src), priv->srcpad);
267
268     GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT);
269     gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent);
270
271     gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, 0);
272     gst_app_src_set_emit_signals(priv->appsrc, FALSE);
273     gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_SEEKABLE);
274
275     // 512k is a abitrary number but we should choose a value
276     // here to not pause/unpause the SoupMessage too often and
277     // to make sure there's always some data available for
278     // GStreamer to handle.
279     gst_app_src_set_max_bytes(priv->appsrc, 512 * 1024);
280
281     // Emit the need-data signal if the queue contains less
282     // than 20% of data. Without this the need-data signal
283     // is emitted when the queue is empty, we then dispatch
284     // the soup message unpausing to the main loop and from
285     // there unpause the soup message. This already takes
286     // quite some time and libsoup even needs some more time
287     // to actually provide data again. If we do all this
288     // already if the queue is 20% empty, it's much more
289     // likely that libsoup already provides new data before
290     // the queue is really empty.
291     // This might need tweaking for ports not using libsoup.
292     g_object_set(priv->appsrc, "min-percent", 20, NULL);
293
294     gst_app_src_set_caps(priv->appsrc, 0);
295     gst_app_src_set_size(priv->appsrc, -1);
296 }
297
298 static void webKitWebSrcDispose(GObject* object)
299 {
300     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
301     WebKitWebSrcPrivate* priv = src->priv;
302
303     priv->player = 0;
304
305     GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object));
306 }
307
308 static void webKitWebSrcFinalize(GObject* object)
309 {
310     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
311     WebKitWebSrcPrivate* priv = src->priv;
312
313     g_free(priv->uri);
314     priv->~WebKitWebSrcPrivate();
315
316     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
317 }
318
319 static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec)
320 {
321     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
322
323     switch (propID) {
324     case PROP_LOCATION:
325         gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0);
326         break;
327     default:
328         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
329         break;
330     }
331 }
332
333 static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* pspec)
334 {
335     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
336     WebKitWebSrcPrivate* priv = src->priv;
337
338     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
339     switch (propID) {
340     case PROP_IRADIO_NAME:
341         g_value_set_string(value, priv->iradioName);
342         break;
343     case PROP_IRADIO_GENRE:
344         g_value_set_string(value, priv->iradioGenre);
345         break;
346     case PROP_IRADIO_URL:
347         g_value_set_string(value, priv->iradioUrl);
348         break;
349     case PROP_IRADIO_TITLE:
350         g_value_set_string(value, priv->iradioTitle);
351         break;
352     case PROP_LOCATION:
353         g_value_set_string(value, priv->uri);
354         break;
355     default:
356         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
357         break;
358     }
359 }
360
361 static void removeTimeoutSources(WebKitWebSrc* src)
362 {
363     WebKitWebSrcPrivate* priv = src->priv;
364
365     priv->startSource.cancel();
366     priv->needDataSource.cancel();
367     priv->enoughDataSource.cancel();
368     priv->seekSource.cancel();
369 }
370
371 static void webKitWebSrcStop(WebKitWebSrc* src)
372 {
373     WebKitWebSrcPrivate* priv = src->priv;
374
375     ASSERT(isMainThread());
376
377     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
378
379     bool seeking = priv->seekSource.isActive();
380
381     removeTimeoutSources(src);
382
383     if (priv->client) {
384         delete priv->client;
385         priv->client = 0;
386     }
387
388     priv->loader = nullptr;
389
390     if (priv->buffer) {
391         unmapGstBuffer(priv->buffer.get());
392         priv->buffer.clear();
393     }
394
395     priv->paused = FALSE;
396
397     g_free(priv->iradioName);
398     priv->iradioName = 0;
399
400     g_free(priv->iradioGenre);
401     priv->iradioGenre = 0;
402
403     g_free(priv->iradioUrl);
404     priv->iradioUrl = 0;
405
406     g_free(priv->iradioTitle);
407     priv->iradioTitle = 0;
408
409     priv->offset = 0;
410     priv->seekable = FALSE;
411
412     if (!seeking) {
413         priv->size = 0;
414         priv->requestedOffset = 0;
415         priv->player = 0;
416     }
417
418     locker.unlock();
419
420     if (priv->appsrc) {
421         gst_app_src_set_caps(priv->appsrc, 0);
422         if (!seeking)
423             gst_app_src_set_size(priv->appsrc, -1);
424     }
425
426     GST_DEBUG_OBJECT(src, "Stopped request");
427 }
428
429 static void webKitWebSrcStart(WebKitWebSrc* src)
430 {
431     WebKitWebSrcPrivate* priv = src->priv;
432
433     ASSERT(isMainThread());
434
435     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
436
437     priv->didPassAccessControlCheck = false;
438
439     if (!priv->uri) {
440         GST_ERROR_OBJECT(src, "No URI provided");
441         locker.unlock();
442         webKitWebSrcStop(src);
443         return;
444     }
445
446     ASSERT(!priv->client);
447     ASSERT(!priv->loader);
448
449     URL url = URL(URL(), priv->uri);
450
451     ResourceRequest request(url);
452     request.setAllowCookies(true);
453     request.setFirstPartyForCookies(url);
454
455     if (priv->player)
456         request.setHTTPReferrer(priv->player->referrer());
457
458 #if USE(SOUP)
459     // Let's disable HTTP Accept-Encoding here as we don't want the received response to be
460     // encoded in any way as we need to rely on the proper size of the returned data on
461     // didReceiveResponse.
462     // If Accept-Encoding is used, the server may send the data in encoded format and
463     // request.expectedContentLength() will have the "wrong" size (the size of the
464     // compressed data), even though the data received in didReceiveData is uncompressed.
465     request.setAcceptEncoding(false);
466 #endif
467
468     // Let Apple web servers know we want to access their nice movie trailers.
469     if (!g_ascii_strcasecmp("movies.apple.com", url.host().utf8().data())
470         || !g_ascii_strcasecmp("trailers.apple.com", url.host().utf8().data()))
471         request.setHTTPUserAgent("Quicktime/7.6.6");
472
473     if (priv->requestedOffset) {
474         GUniquePtr<gchar> val(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset));
475         request.setHTTPHeaderField(HTTPHeaderName::Range, val.get());
476     }
477     priv->offset = priv->requestedOffset;
478
479     // We always request Icecast/Shoutcast metadata, just in case ...
480     request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1");
481
482     bool loadFailed = true;
483     if (priv->player) {
484         priv->loader = priv->player->createResourceLoader(std::make_unique<CachedResourceStreamingClient>(src));
485         if (priv->loader) {
486             PlatformMediaResourceLoader::LoadOptions loadOptions = 0;
487             if (request.url().protocolIs("blob"))
488                 loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData;
489             loadFailed = !priv->loader->start(request, loadOptions);
490         }
491     }
492
493     if (!priv->loader) {
494         priv->client = new ResourceHandleStreamingClient(src, request);
495         loadFailed = priv->client->loadFailed();
496     }
497
498     if (loadFailed) {
499         GST_ERROR_OBJECT(src, "Failed to setup streaming client");
500         if (priv->client) {
501             delete priv->client;
502             priv->client = nullptr;
503         }
504         priv->loader = nullptr;
505         locker.unlock();
506         webKitWebSrcStop(src);
507         return;
508     }
509     GST_DEBUG_OBJECT(src, "Started request");
510 }
511
512 static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
513 {
514     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
515     WebKitWebSrc* src = WEBKIT_WEB_SRC(element);
516     WebKitWebSrcPrivate* priv = src->priv;
517
518     switch (transition) {
519     case GST_STATE_CHANGE_NULL_TO_READY:
520         if (!priv->appsrc) {
521             gst_element_post_message(element,
522                                      gst_missing_element_message_new(element, "appsrc"));
523             GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
524             return GST_STATE_CHANGE_FAILURE;
525         }
526         break;
527     default:
528         break;
529     }
530
531     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
532     if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
533         GST_DEBUG_OBJECT(src, "State change failed");
534         return ret;
535     }
536
537     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
538     switch (transition) {
539     case GST_STATE_CHANGE_READY_TO_PAUSED:
540         GST_DEBUG_OBJECT(src, "READY->PAUSED");
541         gst_object_ref(src);
542         priv->startSource.schedule("[WebKit] webKitWebSrcStart", std::function<void()>(std::bind(webKitWebSrcStart, src)), G_PRIORITY_DEFAULT,
543             [src] { gst_object_unref(src); });
544         break;
545     case GST_STATE_CHANGE_PAUSED_TO_READY:
546         GST_DEBUG_OBJECT(src, "PAUSED->READY");
547         // cancel pending sources
548         removeTimeoutSources(src);
549         gst_object_ref(src);
550         priv->stopSource.schedule("[WebKit] webKitWebSrcStop", std::function<void()>(std::bind(webKitWebSrcStop, src)), G_PRIORITY_DEFAULT,
551             [src] { gst_object_unref(src); });
552         break;
553     default:
554         break;
555     }
556
557     return ret;
558 }
559
560 static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
561 {
562     WebKitWebSrc* src = WEBKIT_WEB_SRC(GST_ELEMENT(parent));
563     gboolean result = FALSE;
564
565     switch (GST_QUERY_TYPE(query)) {
566     case GST_QUERY_DURATION: {
567         GstFormat format;
568
569         gst_query_parse_duration(query, &format, NULL);
570
571         GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format));
572         GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
573         if (format == GST_FORMAT_BYTES && src->priv->size > 0) {
574             gst_query_set_duration(query, format, src->priv->size);
575             result = TRUE;
576         }
577         break;
578     }
579     case GST_QUERY_URI: {
580         GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
581         gst_query_set_uri(query, src->priv->uri);
582         result = TRUE;
583         break;
584     }
585     default: {
586         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
587
588         // Forward the query to the proxy target pad.
589         if (target)
590             result = gst_pad_query(target.get(), query);
591         break;
592     }
593     }
594
595     return result;
596 }
597
598 static bool urlHasSupportedProtocol(const URL& url)
599 {
600     return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIs("blob"));
601 }
602
603 // uri handler interface
604
605 static GstURIType webKitWebSrcUriGetType(GType)
606 {
607     return GST_URI_SRC;
608 }
609
610 const gchar* const* webKitWebSrcGetProtocols(GType)
611 {
612     static const char* protocols[] = {"http", "https", "blob", 0 };
613     return protocols;
614 }
615
616 static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
617 {
618     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
619     gchar* ret;
620
621     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
622     ret = g_strdup(src->priv->uri);
623     return ret;
624 }
625
626 static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error)
627 {
628     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
629     WebKitWebSrcPrivate* priv = src->priv;
630
631     if (GST_STATE(src) >= GST_STATE_PAUSED) {
632         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
633         return FALSE;
634     }
635
636     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
637
638     g_free(priv->uri);
639     priv->uri = 0;
640
641     if (!uri)
642         return TRUE;
643
644     URL url(URL(), uri);
645     if (!urlHasSupportedProtocol(url)) {
646         g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri);
647         return FALSE;
648     }
649
650     priv->uri = g_strdup(url.string().utf8().data());
651     return TRUE;
652 }
653
654 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer)
655 {
656     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
657
658     iface->get_type = webKitWebSrcUriGetType;
659     iface->get_protocols = webKitWebSrcGetProtocols;
660     iface->get_uri = webKitWebSrcGetUri;
661     iface->set_uri = webKitWebSrcSetUri;
662 }
663
664 // appsrc callbacks
665
666 static void webKitWebSrcNeedDataMainCb(WebKitWebSrc* src)
667 {
668     WebKitWebSrcPrivate* priv = src->priv;
669
670     ASSERT(isMainThread());
671
672     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
673     priv->paused = FALSE;
674     locker.unlock();
675
676     if (priv->client)
677         priv->client->setDefersLoading(false);
678     if (priv->loader)
679         priv->loader->setDefersLoading(false);
680 }
681
682 static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
683 {
684     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
685     WebKitWebSrcPrivate* priv = src->priv;
686
687     GST_DEBUG_OBJECT(src, "Need more data: %u", length);
688
689     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
690     if (priv->needDataSource.isScheduled() || !priv->paused)
691         return;
692
693     gst_object_ref(src);
694     priv->needDataSource.schedule("[WebKit] webKitWebSrcNeedDataMainCb", std::function<void()>(std::bind(webKitWebSrcNeedDataMainCb, src)), G_PRIORITY_DEFAULT,
695         [src] { gst_object_unref(src); });
696 }
697
698 static void webKitWebSrcEnoughDataMainCb(WebKitWebSrc* src)
699 {
700     WebKitWebSrcPrivate* priv = src->priv;
701
702     ASSERT(isMainThread());
703
704     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
705     priv->paused = TRUE;
706     locker.unlock();
707
708     if (priv->client)
709         priv->client->setDefersLoading(true);
710     if (priv->loader)
711         priv->loader->setDefersLoading(true);
712 }
713
714 static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData)
715 {
716     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
717     WebKitWebSrcPrivate* priv = src->priv;
718
719     GST_DEBUG_OBJECT(src, "Have enough data");
720
721     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
722     if (priv->enoughDataSource.isScheduled() || priv->paused)
723         return;
724
725     gst_object_ref(src);
726     priv->enoughDataSource.schedule("[WebKit] webKitWebSrcEnoughDataMainCb", std::function<void()>(std::bind(webKitWebSrcEnoughDataMainCb, src)), G_PRIORITY_DEFAULT,
727         [src] { gst_object_unref(src); });
728 }
729
730 static void webKitWebSrcSeekMainCb(WebKitWebSrc* src)
731 {
732     ASSERT(isMainThread());
733
734     webKitWebSrcStop(src);
735     webKitWebSrcStart(src);
736 }
737
738 static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
739 {
740     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
741     WebKitWebSrcPrivate* priv = src->priv;
742
743     GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
744     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
745     if (offset == priv->offset && priv->requestedOffset == priv->offset)
746         return TRUE;
747
748     if (!priv->seekable)
749         return FALSE;
750
751     GST_DEBUG_OBJECT(src, "Doing range-request seek");
752     priv->requestedOffset = offset;
753
754     gst_object_ref(src);
755     priv->seekSource.schedule("[WebKit] webKitWebSrcSeekMainCb", std::function<void()>(std::bind(webKitWebSrcSeekMainCb, src)), G_PRIORITY_DEFAULT,
756         [src] { gst_object_unref(src); });
757     return TRUE;
758 }
759
760 void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
761 {
762     ASSERT(player);
763     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
764     src->priv->player = player;
765 }
766
767 bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc* src)
768 {
769     return src->priv->didPassAccessControlCheck;
770 }
771
772 StreamingClient::StreamingClient(WebKitWebSrc* src)
773     : m_src(static_cast<GstElement*>(gst_object_ref(src)))
774 {
775 }
776
777 StreamingClient::~StreamingClient()
778 {
779     gst_object_unref(m_src);
780 }
781
782 char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize)
783 {
784     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
785     WebKitWebSrcPrivate* priv = src->priv;
786
787     ASSERT(!priv->buffer);
788
789     GstBuffer* buffer = gst_buffer_new_and_alloc(requestedSize);
790
791     mapGstBuffer(buffer);
792
793     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
794     priv->buffer = adoptGRef(buffer);
795     locker.unlock();
796
797     actualSize = gst_buffer_get_size(buffer);
798     return getGstBufferDataPointer(buffer);
799 }
800
801 void StreamingClient::handleResponseReceived(const ResourceResponse& response)
802 {
803     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
804     WebKitWebSrcPrivate* priv = src->priv;
805
806     GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode());
807
808     if (response.httpStatusCode() >= 400) {
809         GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr));
810         gst_app_src_end_of_stream(priv->appsrc);
811         webKitWebSrcStop(src);
812         return;
813     }
814
815     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
816
817     if (priv->seekSource.isActive()) {
818         GST_DEBUG_OBJECT(src, "Seek in progress, ignoring response");
819         return;
820     }
821
822     if (priv->requestedOffset) {
823         // Seeking ... we expect a 206 == PARTIAL_CONTENT
824         if (response.httpStatusCode() == 200) {
825             // Range request didn't have a ranged response; resetting offset.
826             priv->offset = 0;
827         } else if (response.httpStatusCode() != 206) {
828             // Range request completely failed.
829             locker.unlock();
830             GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received unexpected %d HTTP status code", response.httpStatusCode()), (nullptr));
831             gst_app_src_end_of_stream(priv->appsrc);
832             webKitWebSrcStop(src);
833             return;
834         }
835     }
836
837     long long length = response.expectedContentLength();
838     if (length > 0 && priv->requestedOffset && response.httpStatusCode() == 206)
839         length += priv->requestedOffset;
840
841     priv->size = length >= 0 ? length : 0;
842     priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data());
843
844     // Wait until we unlock to send notifications
845     g_object_freeze_notify(G_OBJECT(src));
846
847     GstTagList* tags = gst_tag_list_new_empty();
848     String value = response.httpHeaderField(HTTPHeaderName::IcyName);
849     if (!value.isEmpty()) {
850         g_free(priv->iradioName);
851         priv->iradioName = g_strdup(value.utf8().data());
852         g_object_notify(G_OBJECT(src), "iradio-name");
853         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, priv->iradioName, NULL);
854     }
855     value = response.httpHeaderField(HTTPHeaderName::IcyGenre);
856     if (!value.isEmpty()) {
857         g_free(priv->iradioGenre);
858         priv->iradioGenre = g_strdup(value.utf8().data());
859         g_object_notify(G_OBJECT(src), "iradio-genre");
860         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, priv->iradioGenre, NULL);
861     }
862     value = response.httpHeaderField(HTTPHeaderName::IcyURL);
863     if (!value.isEmpty()) {
864         g_free(priv->iradioUrl);
865         priv->iradioUrl = g_strdup(value.utf8().data());
866         g_object_notify(G_OBJECT(src), "iradio-url");
867         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION, priv->iradioUrl, NULL);
868     }
869     value = response.httpHeaderField(HTTPHeaderName::IcyTitle);
870     if (!value.isEmpty()) {
871         g_free(priv->iradioTitle);
872         priv->iradioTitle = g_strdup(value.utf8().data());
873         g_object_notify(G_OBJECT(src), "iradio-title");
874         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, priv->iradioTitle, NULL);
875     }
876
877     locker.unlock();
878     g_object_thaw_notify(G_OBJECT(src));
879
880     // notify size/duration
881     if (length > 0) {
882         gst_app_src_set_size(priv->appsrc, length);
883     } else
884         gst_app_src_set_size(priv->appsrc, -1);
885
886     // icecast stuff
887     value = response.httpHeaderField(HTTPHeaderName::IcyMetaInt);
888     if (!value.isEmpty()) {
889         gchar* endptr = 0;
890         gint64 icyMetaInt = g_ascii_strtoll(value.utf8().data(), &endptr, 10);
891
892         if (endptr && *endptr == '\0' && icyMetaInt > 0) {
893             GRefPtr<GstCaps> caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, (gint) icyMetaInt, NULL));
894
895             gst_app_src_set_caps(priv->appsrc, caps.get());
896         }
897     } else
898         gst_app_src_set_caps(priv->appsrc, 0);
899
900     // notify tags
901     if (gst_tag_list_is_empty(tags))
902         gst_tag_list_unref(tags);
903     else
904         gst_pad_push_event(priv->srcpad, gst_event_new_tag(tags));
905 }
906
907 void StreamingClient::handleDataReceived(const char* data, int length)
908 {
909     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
910     WebKitWebSrcPrivate* priv = src->priv;
911
912     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
913
914     GST_LOG_OBJECT(src, "Have %lld bytes of data", priv->buffer ? static_cast<long long>(gst_buffer_get_size(priv->buffer.get())) : length);
915
916     ASSERT(!priv->buffer || data == getGstBufferDataPointer(priv->buffer.get()));
917
918     if (priv->buffer)
919         unmapGstBuffer(priv->buffer.get());
920
921     if (priv->seekSource.isActive()) {
922         GST_DEBUG_OBJECT(src, "Seek in progress, ignoring data");
923         priv->buffer.clear();
924         return;
925     }
926
927     if (priv->offset < priv->requestedOffset) {
928         // Range request failed; seeking manually.
929         if (priv->offset + length <= priv->requestedOffset) {
930             // Discard all the buffers coming before the requested seek position.
931             priv->offset += length;
932             priv->buffer.clear();
933             return;
934         }
935
936         if (priv->offset + length > priv->requestedOffset) {
937             guint64 offset = priv->requestedOffset - priv->offset;
938             data += offset;
939             length -= offset;
940             if (priv->buffer)
941                 gst_buffer_resize(priv->buffer.get(), offset, -1);
942             priv->offset = priv->requestedOffset;
943         }
944
945         priv->requestedOffset = 0;
946     }
947
948     // Ports using the GStreamer backend but not the soup implementation of ResourceHandle
949     // won't be using buffers provided by this client, the buffer is created here in that case.
950     if (!priv->buffer)
951         priv->buffer = adoptGRef(createGstBufferForData(data, length));
952     else
953         gst_buffer_set_size(priv->buffer.get(), static_cast<gssize>(length));
954
955     GST_BUFFER_OFFSET(priv->buffer.get()) = priv->offset;
956     if (priv->requestedOffset == priv->offset)
957         priv->requestedOffset += length;
958     priv->offset += length;
959     // priv->size == 0 if received length on didReceiveResponse < 0.
960     if (priv->size > 0 && priv->offset > priv->size) {
961         GST_DEBUG_OBJECT(src, "Updating internal size from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT, priv->size, priv->offset);
962         gst_app_src_set_size(priv->appsrc, priv->offset);
963         priv->size = priv->offset;
964     }
965     GST_BUFFER_OFFSET_END(priv->buffer.get()) = priv->offset;
966
967     locker.unlock();
968
969     GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, priv->buffer.leakRef());
970     if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
971         GST_ELEMENT_ERROR(src, CORE, FAILED, (0), (0));
972 }
973
974 void StreamingClient::handleNotifyFinished()
975 {
976     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
977     WebKitWebSrcPrivate* priv = src->priv;
978
979     GST_DEBUG_OBJECT(src, "Have EOS");
980
981     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
982     if (!priv->seekSource.isActive()) {
983         locker.unlock();
984         gst_app_src_end_of_stream(priv->appsrc);
985     }
986 }
987
988 CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src)
989     : StreamingClient(src)
990 {
991 }
992
993 CachedResourceStreamingClient::~CachedResourceStreamingClient()
994 {
995 }
996
997 char* CachedResourceStreamingClient::getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize)
998 {
999     return createReadBuffer(requestedSize, actualSize);
1000 }
1001
1002 void CachedResourceStreamingClient::responseReceived(const ResourceResponse& response)
1003 {
1004     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(m_src)->priv;
1005     priv->didPassAccessControlCheck = priv->loader->didPassAccessControlCheck();
1006     handleResponseReceived(response);
1007 }
1008
1009 void CachedResourceStreamingClient::dataReceived(const char* data, int length)
1010 {
1011     handleDataReceived(data, length);
1012 }
1013
1014 void CachedResourceStreamingClient::accessControlCheckFailed(const ResourceError& error)
1015 {
1016     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1017     GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr));
1018     gst_app_src_end_of_stream(src->priv->appsrc);
1019     webKitWebSrcStop(src);
1020 }
1021
1022 void CachedResourceStreamingClient::loadFailed(const ResourceError& error)
1023 {
1024     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1025
1026     if (!error.isCancellation()) {
1027         GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
1028         GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr));
1029     }
1030
1031     gst_app_src_end_of_stream(src->priv->appsrc);
1032 }
1033
1034 void CachedResourceStreamingClient::loadFinished()
1035 {
1036     handleNotifyFinished();
1037 }
1038
1039 ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, const ResourceRequest& request)
1040     : StreamingClient(src)
1041 {
1042     m_resource = ResourceHandle::create(0 /*context*/, request, this, false, false);
1043 }
1044
1045 ResourceHandleStreamingClient::~ResourceHandleStreamingClient()
1046 {
1047     if (m_resource) {
1048         m_resource->cancel();
1049         m_resource.release();
1050         m_resource = 0;
1051     }
1052 }
1053
1054 bool ResourceHandleStreamingClient::loadFailed() const
1055 {
1056     return !m_resource;
1057 }
1058
1059 void ResourceHandleStreamingClient::setDefersLoading(bool defers)
1060 {
1061     if (m_resource)
1062         m_resource->setDefersLoading(defers);
1063 }
1064
1065 char* ResourceHandleStreamingClient::getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize)
1066 {
1067     return createReadBuffer(requestedSize, actualSize);
1068 }
1069
1070 void ResourceHandleStreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&)
1071 {
1072 }
1073
1074 void ResourceHandleStreamingClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
1075 {
1076     handleResponseReceived(response);
1077 }
1078
1079 void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int)
1080 {
1081     ASSERT_NOT_REACHED();
1082 }
1083
1084 void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int /* encodedLength */)
1085 {
1086     // This pattern is suggested by SharedBuffer.h.
1087     const char* segment;
1088     unsigned position = 0;
1089     while (unsigned length = buffer->getSomeData(segment, position)) {
1090         handleDataReceived(segment, length);
1091         position += length;
1092     }
1093 }
1094
1095 void ResourceHandleStreamingClient::didFinishLoading(ResourceHandle*, double)
1096 {
1097     handleNotifyFinished();
1098 }
1099
1100 void ResourceHandleStreamingClient::didFail(ResourceHandle*, const ResourceError& error)
1101 {
1102     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1103
1104     GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
1105     GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0));
1106     gst_app_src_end_of_stream(src->priv->appsrc);
1107 }
1108
1109 void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*)
1110 {
1111     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1112     GUniquePtr<gchar> uri;
1113
1114     GST_ERROR_OBJECT(src, "Request was blocked");
1115
1116     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
1117     uri.reset(g_strdup(src->priv->uri));
1118     locker.unlock();
1119
1120     GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", uri.get()), (0));
1121 }
1122
1123 void ResourceHandleStreamingClient::cannotShowURL(ResourceHandle*)
1124 {
1125     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1126     GUniquePtr<gchar> uri;
1127
1128     GST_ERROR_OBJECT(src, "Cannot show URL");
1129
1130     GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
1131     uri.reset(g_strdup(src->priv->uri));
1132     locker.unlock();
1133
1134     GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", uri.get()), (0));
1135 }
1136
1137 #endif // USE(GSTREAMER)
1138