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