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