[v8] isolate parameter added to all v8::peristent calls
[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 "Document.h"
26 #include "Frame.h"
27 #include "GRefPtrGStreamer.h"
28 #include "GStreamerVersioning.h"
29 #include "MediaPlayer.h"
30 #include "MediaPlayerPrivateGStreamer.h"
31 #include "NetworkingContext.h"
32 #include "NotImplemented.h"
33 #include "ResourceHandleClient.h"
34 #include "ResourceHandleInternal.h"
35 #include "ResourceRequest.h"
36 #include "ResourceResponse.h"
37 #include <gst/app/gstappsrc.h>
38 #include <gst/gst.h>
39 #include <gst/pbutils/missing-plugins.h>
40 #include <wtf/Noncopyable.h>
41 #include <wtf/gobject/GOwnPtr.h>
42 #include <wtf/gobject/GRefPtr.h>
43 #include <wtf/text/CString.h>
44
45 using namespace WebCore;
46
47 class StreamingClient : public ResourceHandleClient {
48     WTF_MAKE_NONCOPYABLE(StreamingClient); WTF_MAKE_FAST_ALLOCATED;
49     public:
50         StreamingClient(WebKitWebSrc*);
51         virtual ~StreamingClient();
52
53         virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&);
54         virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
55
56         virtual char* getBuffer(int, int*);
57
58         virtual void didReceiveData(ResourceHandle*, const char*, int, int);
59         virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
60         virtual void didFail(ResourceHandle*, const ResourceError&);
61         virtual void wasBlocked(ResourceHandle*);
62         virtual void cannotShowURL(ResourceHandle*);
63
64     private:
65         WebKitWebSrc* m_src;
66 };
67
68 #define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate))
69 struct _WebKitWebSrcPrivate {
70     GstAppSrc* appsrc;
71     GstPad* srcpad;
72     gchar* uri;
73
74     RefPtr<WebCore::Frame> frame;
75     WebCore::MediaPlayer* player;
76
77     StreamingClient* client;
78     RefPtr<ResourceHandle> resourceHandle;
79
80     guint64 offset;
81     guint64 size;
82     gboolean seekable;
83     gboolean paused;
84
85     guint64 requestedOffset;
86
87     guint needDataID;
88     guint enoughDataID;
89     guint seekID;
90
91     GRefPtr<GstBuffer> buffer;
92
93     // icecast stuff
94     gboolean iradioMode;
95     gchar* iradioName;
96     gchar* iradioGenre;
97     gchar* iradioUrl;
98     gchar* iradioTitle;
99
100     // TRUE if appsrc's version is >= 0.10.27, see
101     // https://bugzilla.gnome.org/show_bug.cgi?id=609423
102     gboolean haveAppSrc27;
103 };
104
105 enum {
106     PROP_IRADIO_MODE = 1,
107     PROP_IRADIO_NAME,
108     PROP_IRADIO_GENRE,
109     PROP_IRADIO_URL,
110     PROP_IRADIO_TITLE,
111     PROP_LOCATION
112 };
113
114 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
115                                                                   GST_PAD_SRC,
116                                                                   GST_PAD_ALWAYS,
117                                                                   GST_STATIC_CAPS_ANY);
118
119 GST_DEBUG_CATEGORY_STATIC(webkit_web_src_debug);
120 #define GST_CAT_DEFAULT webkit_web_src_debug
121
122 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
123
124 static void webKitWebSrcDispose(GObject*);
125 static void webKitWebSrcFinalize(GObject*);
126 static void webKitWebSrcSetProperty(GObject*, guint propertyID, const GValue*, GParamSpec*);
127 static void webKitWebSrcGetProperty(GObject*, guint propertyID, GValue*, GParamSpec*);
128 static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange);
129
130 static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
131 #ifndef GST_API_VERSION_1
132 static gboolean webKitWebSrcQuery(GstPad*, GstQuery*);
133 #endif
134
135 static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData);
136 static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData);
137 static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData);
138
139 static void webKitWebSrcStop(WebKitWebSrc*, bool);
140
141 static GstAppSrcCallbacks appsrcCallbacks = {
142     webKitWebSrcNeedDataCb,
143     webKitWebSrcEnoughDataCb,
144     webKitWebSrcSeekDataCb,
145     { 0 }
146 };
147
148 #define webkit_web_src_parent_class parent_class
149 // We split this out into another macro to avoid a check-webkit-style error.
150 #define WEBKIT_WEB_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_web_src_debug, "webkitwebsrc", 0, "websrc element");
151 G_DEFINE_TYPE_WITH_CODE(WebKitWebSrc, webkit_web_src, GST_TYPE_BIN,
152                          G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitWebSrcUriHandlerInit);
153                          WEBKIT_WEB_SRC_CATEGORY_INIT);
154
155 static void webkit_web_src_class_init(WebKitWebSrcClass* klass)
156 {
157     GObjectClass* oklass = G_OBJECT_CLASS(klass);
158     GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
159
160     oklass->dispose = webKitWebSrcDispose;
161     oklass->finalize = webKitWebSrcFinalize;
162     oklass->set_property = webKitWebSrcSetProperty;
163     oklass->get_property = webKitWebSrcGetProperty;
164
165     gst_element_class_add_pad_template(eklass,
166                                        gst_static_pad_template_get(&srcTemplate));
167     setGstElementClassMetadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris",
168                                "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
169
170     // icecast stuff
171     g_object_class_install_property(oklass,
172                                     PROP_IRADIO_MODE,
173                                     g_param_spec_boolean("iradio-mode",
174                                                          "iradio-mode",
175                                                          "Enable internet radio mode (extraction of shoutcast/icecast metadata)",
176                                                          FALSE,
177                                                          (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
178
179     g_object_class_install_property(oklass,
180                                     PROP_IRADIO_NAME,
181                                     g_param_spec_string("iradio-name",
182                                                         "iradio-name",
183                                                         "Name of the stream",
184                                                         0,
185                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
186
187     g_object_class_install_property(oklass,
188                                     PROP_IRADIO_GENRE,
189                                     g_param_spec_string("iradio-genre",
190                                                         "iradio-genre",
191                                                         "Genre of the stream",
192                                                         0,
193                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
194                                                         
195     g_object_class_install_property(oklass,
196                                     PROP_IRADIO_URL,
197                                     g_param_spec_string("iradio-url",
198                                                         "iradio-url",
199                                                         "Homepage URL for radio stream",
200                                                         0,
201                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
202
203     g_object_class_install_property(oklass,
204                                     PROP_IRADIO_TITLE,
205                                     g_param_spec_string("iradio-title",
206                                                         "iradio-title",
207                                                         "Name of currently playing song",
208                                                         0,
209                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
210
211
212     /* Allows setting the uri using the 'location' property, which is used
213      * for example by gst_element_make_from_uri() */
214     g_object_class_install_property(oklass,
215                                     PROP_LOCATION,
216                                     g_param_spec_string("location",
217                                                         "location",
218                                                         "Location to read from",
219                                                         0,
220                                                         (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
221     eklass->change_state = webKitWebSrcChangeState;
222
223     g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate));
224 }
225
226 static void webkit_web_src_init(WebKitWebSrc* src)
227 {
228     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src);
229
230     src->priv = priv;
231
232     priv->client = new StreamingClient(src);
233
234     priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", 0));
235     if (!priv->appsrc) {
236         GST_ERROR_OBJECT(src, "Failed to create appsrc");
237         return;
238     }
239
240     GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(priv->appsrc)->elementfactory);
241     priv->haveAppSrc27 = gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 27);
242
243     gst_bin_add(GST_BIN(src), GST_ELEMENT(priv->appsrc));
244
245
246     GRefPtr<GstPad> targetPad = adoptGRef(gst_element_get_static_pad(GST_ELEMENT(priv->appsrc), "src"));
247     priv->srcpad = webkitGstGhostPadFromStaticTemplate(&srcTemplate, "src", targetPad.get());
248
249     gst_element_add_pad(GST_ELEMENT(src), priv->srcpad);
250
251 #ifdef GST_API_VERSION_1
252     GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT);
253     gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent);
254 #else
255     gst_pad_set_query_function(priv->srcpad, webKitWebSrcQuery);
256 #endif
257
258     gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, 0);
259     gst_app_src_set_emit_signals(priv->appsrc, FALSE);
260     gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_SEEKABLE);
261
262     // 512k is a abitrary number but we should choose a value
263     // here to not pause/unpause the SoupMessage too often and
264     // to make sure there's always some data available for
265     // GStreamer to handle.
266     gst_app_src_set_max_bytes(priv->appsrc, 512 * 1024);
267
268     // Emit the need-data signal if the queue contains less
269     // than 20% of data. Without this the need-data signal
270     // is emitted when the queue is empty, we then dispatch
271     // the soup message unpausing to the main loop and from
272     // there unpause the soup message. This already takes
273     // quite some time and libsoup even needs some more time
274     // to actually provide data again. If we do all this
275     // already if the queue is 20% empty, it's much more
276     // likely that libsoup already provides new data before
277     // the queue is really empty.
278     // This might need tweaking for ports not using libsoup.
279     if (priv->haveAppSrc27)
280         g_object_set(priv->appsrc, "min-percent", 20, NULL);
281
282     webKitWebSrcStop(src, false);
283 }
284
285 static void webKitWebSrcDispose(GObject* object)
286 {
287     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
288     WebKitWebSrcPrivate* priv = src->priv;
289
290     if (priv->buffer) {
291 #ifdef GST_API_VERSION_1
292         unmapGstBuffer(priv->buffer.get());
293 #endif
294         priv->buffer.clear();
295     }
296
297     GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object));
298 }
299
300 static void webKitWebSrcFinalize(GObject* object)
301 {
302     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
303     WebKitWebSrcPrivate* priv = src->priv;
304
305     delete priv->client;
306
307     g_free(priv->uri);
308
309     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
310 }
311
312 static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec)
313 {
314     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
315     WebKitWebSrcPrivate* priv = src->priv;
316
317     switch (propID) {
318     case PROP_IRADIO_MODE:
319         priv->iradioMode = g_value_get_boolean(value);
320         break;
321     case PROP_LOCATION:
322 #ifdef GST_API_VERSION_1
323         gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0);
324 #else
325         gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value));
326 #endif
327         break;
328     default:
329         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
330         break;
331     }
332 }
333
334 static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* pspec)
335 {
336     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
337     WebKitWebSrcPrivate* priv = src->priv;
338
339     switch (propID) {
340     case PROP_IRADIO_MODE:
341         g_value_set_boolean(value, priv->iradioMode);
342         break;
343     case PROP_IRADIO_NAME:
344         g_value_set_string(value, priv->iradioName);
345         break;
346     case PROP_IRADIO_GENRE:
347         g_value_set_string(value, priv->iradioGenre);
348         break;
349     case PROP_IRADIO_URL:
350         g_value_set_string(value, priv->iradioUrl);
351         break;
352     case PROP_IRADIO_TITLE:
353         g_value_set_string(value, priv->iradioTitle);
354         break;
355     case PROP_LOCATION:
356         g_value_set_string(value, priv->uri);
357         break;
358     default:
359         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
360         break;
361     }
362 }
363
364
365 static void webKitWebSrcStop(WebKitWebSrc* src, bool seeking)
366 {
367     WebKitWebSrcPrivate* priv = src->priv;
368
369     if (priv->resourceHandle) {
370         priv->resourceHandle->cancel();
371         priv->resourceHandle.release();
372     }
373     priv->resourceHandle = 0;
374
375     if (priv->frame && !seeking)
376         priv->frame.clear();
377
378     priv->player = 0;
379
380     if (priv->buffer) {
381 #ifdef GST_API_VERSION_1
382         unmapGstBuffer(priv->buffer.get());
383 #endif
384         priv->buffer.clear();
385     }
386
387     GST_OBJECT_LOCK(src);
388     if (priv->needDataID)
389         g_source_remove(priv->needDataID);
390     priv->needDataID = 0;
391
392     if (priv->enoughDataID)
393         g_source_remove(priv->enoughDataID);
394     priv->enoughDataID = 0;
395
396     if (priv->seekID)
397         g_source_remove(priv->seekID);
398     priv->seekID = 0;
399
400     priv->paused = FALSE;
401     GST_OBJECT_UNLOCK(src);
402
403     g_free(priv->iradioName);
404     priv->iradioName = 0;
405
406     g_free(priv->iradioGenre);
407     priv->iradioGenre = 0;
408
409     g_free(priv->iradioUrl);
410     priv->iradioUrl = 0;
411
412     g_free(priv->iradioTitle);
413     priv->iradioTitle = 0;
414
415     if (priv->appsrc) {
416         gst_app_src_set_caps(priv->appsrc, 0);
417         if (!seeking)
418             gst_app_src_set_size(priv->appsrc, -1);
419     }
420
421     priv->offset = 0;
422     priv->seekable = FALSE;
423
424     if (!seeking) {
425         priv->size = 0;
426         priv->requestedOffset = 0;
427     }
428
429     GST_DEBUG_OBJECT(src, "Stopped request");
430 }
431
432 static bool webKitWebSrcStart(WebKitWebSrc* src)
433 {
434     WebKitWebSrcPrivate* priv = src->priv;
435
436     if (!priv->uri) {
437         GST_ERROR_OBJECT(src, "No URI provided");
438         return false;
439     }
440     
441     KURL url = KURL(KURL(), priv->uri);
442
443     ResourceRequest request(url);
444     request.setAllowCookies(true);
445
446     NetworkingContext* context = 0;
447     FrameLoader* loader = priv->frame ? priv->frame->loader() : 0;
448     if (loader) {
449         loader->addExtraFieldsToSubresourceRequest(request);
450         context = loader->networkingContext();
451     }
452
453     if (priv->player)
454         request.setHTTPReferrer(priv->player->referrer());
455
456     // Let Apple web servers know we want to access their nice movie trailers.
457     if (!g_ascii_strcasecmp("movies.apple.com", url.host().utf8().data())
458         || !g_ascii_strcasecmp("trailers.apple.com", url.host().utf8().data()))
459         request.setHTTPUserAgent("Quicktime/7.6.6");
460
461     if (priv->requestedOffset) {
462         GOwnPtr<gchar> val;
463
464         val.set(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset));
465         request.setHTTPHeaderField("Range", val.get());
466     }
467
468     if (priv->iradioMode)
469         request.setHTTPHeaderField("icy-metadata", "1");
470
471     // Needed to use DLNA streaming servers
472     request.setHTTPHeaderField("transferMode.dlna", "Streaming");
473
474     priv->resourceHandle = ResourceHandle::create(context, request, priv->client, false, false);
475     if (!priv->resourceHandle) {
476         GST_ERROR_OBJECT(src, "Failed to create ResourceHandle");
477         return false;
478     }
479
480     GST_DEBUG_OBJECT(src, "Started request");
481
482     return true;
483 }
484
485 static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
486 {
487     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
488     WebKitWebSrc* src = WEBKIT_WEB_SRC(element);
489     WebKitWebSrcPrivate* priv = src->priv;
490
491     switch (transition) {
492     case GST_STATE_CHANGE_NULL_TO_READY:
493         if (!priv->appsrc) {
494             gst_element_post_message(element,
495                                      gst_missing_element_message_new(element, "appsrc"));
496             GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
497             return GST_STATE_CHANGE_FAILURE;
498         }
499         break;
500     default:
501         break;
502     }
503
504     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
505     if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
506         GST_DEBUG_OBJECT(src, "State change failed");
507         return ret;
508     }
509
510     switch (transition) {
511     case GST_STATE_CHANGE_READY_TO_PAUSED:
512         GST_DEBUG_OBJECT(src, "READY->PAUSED");
513         if (!webKitWebSrcStart(src))
514             ret = GST_STATE_CHANGE_FAILURE;
515         break;
516     case GST_STATE_CHANGE_PAUSED_TO_READY:
517         GST_DEBUG_OBJECT(src, "PAUSED->READY");
518         webKitWebSrcStop(src, false);
519         break;
520     default:
521         break;
522     }
523
524     return ret;
525 }
526
527 static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
528 {
529     WebKitWebSrc* webkitSrc = WEBKIT_WEB_SRC(GST_ELEMENT(parent));
530     gboolean result = FALSE;
531
532     switch (GST_QUERY_TYPE(query)) {
533     case GST_QUERY_DURATION: {
534         GstFormat format;
535
536         gst_query_parse_duration(query, &format, NULL);
537
538         GST_DEBUG_OBJECT(webkitSrc, "duration query in format %s", gst_format_get_name(format));
539         if ((format == GST_FORMAT_BYTES) && (webkitSrc->priv->size > 0)) {
540             gst_query_set_duration(query, format, webkitSrc->priv->size);
541             result = TRUE;
542         }
543         break;
544     }
545     case GST_QUERY_URI: {
546         gst_query_set_uri(query, webkitSrc->priv->uri);
547         result = TRUE;
548         break;
549     }
550     default: {
551         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
552
553         // Forward the query to the proxy target pad.
554         if (target)
555             result = gst_pad_query(target.get(), query);
556         break;
557     }
558     }
559
560     return result;
561 }
562
563 #ifndef GST_API_VERSION_1
564 static gboolean webKitWebSrcQuery(GstPad* pad, GstQuery* query)
565 {
566     GRefPtr<GstElement> src = adoptGRef(gst_pad_get_parent_element(pad));
567     return webKitWebSrcQueryWithParent(pad, GST_OBJECT(src.get()), query);
568 }
569 #endif
570
571 // uri handler interface
572
573 #ifdef GST_API_VERSION_1
574 static GstURIType webKitWebSrcUriGetType(GType)
575 {
576     return GST_URI_SRC;
577 }
578
579 const gchar* const* webKitWebSrcGetProtocols(GType)
580 {
581     static const char* const protocols[] = {"webkit+http", "webkit+https", 0 };
582     return protocols;
583 }
584
585 static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
586 {
587     return g_strdup(WEBKIT_WEB_SRC(handler)->priv->uri);
588 }
589
590 static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error)
591 {
592     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
593     WebKitWebSrcPrivate* priv = src->priv;
594
595     if (GST_STATE(src) >= GST_STATE_PAUSED) {
596         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
597         return FALSE;
598     }
599
600     g_free(priv->uri);
601     priv->uri = 0;
602
603     if (!uri)
604         return TRUE;
605
606     KURL url = WebCore::MediaPlayerPrivateGStreamer::convertPlaybinURL(uri);
607
608     if (!url.isValid() || !url.protocolIsInHTTPFamily()) {
609         g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri);
610         return FALSE;
611     }
612
613     priv->uri = g_strdup(url.string().utf8().data());
614     return TRUE;
615 }
616
617 #else
618 static GstURIType webKitWebSrcUriGetType(void)
619 {
620     return GST_URI_SRC;
621 }
622
623 static gchar** webKitWebSrcGetProtocols(void)
624 {
625     static gchar* protocols[] = {(gchar*) "webkit+http", (gchar*) "webkit+https", 0 };
626     return protocols;
627 }
628
629 static const gchar* webKitWebSrcGetUri(GstURIHandler* handler)
630 {
631     return g_strdup(WEBKIT_WEB_SRC(handler)->priv->uri);
632 }
633
634 static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri)
635 {
636     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
637     WebKitWebSrcPrivate* priv = src->priv;
638
639     if (GST_STATE(src) >= GST_STATE_PAUSED) {
640         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
641         return FALSE;
642     }
643
644     g_free(priv->uri);
645     priv->uri = 0;
646
647     if (!uri)
648         return TRUE;
649
650     KURL url = WebCore::MediaPlayerPrivateGStreamer::convertPlaybinURL(uri);
651
652     if (!url.isValid() || !url.protocolIsInHTTPFamily()) {
653         GST_ERROR_OBJECT(src, "Invalid URI '%s'", uri);
654         return FALSE;
655     }
656
657     priv->uri = g_strdup(url.string().utf8().data());
658     return TRUE;
659 }
660 #endif
661
662 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer)
663 {
664     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
665
666     iface->get_type = webKitWebSrcUriGetType;
667     iface->get_protocols = webKitWebSrcGetProtocols;
668     iface->get_uri = webKitWebSrcGetUri;
669     iface->set_uri = webKitWebSrcSetUri;
670 }
671
672 // appsrc callbacks
673
674 static gboolean webKitWebSrcNeedDataMainCb(WebKitWebSrc* src)
675 {
676     WebKitWebSrcPrivate* priv = src->priv;
677
678     priv->resourceHandle->setDefersLoading(false);
679
680     GST_OBJECT_LOCK(src);
681     priv->paused = FALSE;
682     priv->needDataID = 0;
683     GST_OBJECT_UNLOCK(src);
684     return 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     GST_OBJECT_LOCK(src);
695     if (priv->needDataID || !priv->paused) {
696         GST_OBJECT_UNLOCK(src);
697         return;
698     }
699
700     priv->needDataID = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitWebSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
701     GST_OBJECT_UNLOCK(src);
702 }
703
704 static gboolean webKitWebSrcEnoughDataMainCb(WebKitWebSrc* src)
705 {
706     WebKitWebSrcPrivate* priv = src->priv;
707
708     priv->resourceHandle->setDefersLoading(true);
709
710     GST_OBJECT_LOCK(src);
711     priv->paused = TRUE;
712     priv->enoughDataID = 0;
713     GST_OBJECT_UNLOCK(src);
714
715     return FALSE;
716 }
717
718 static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData)
719 {
720     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
721     WebKitWebSrcPrivate* priv = src->priv;
722
723     GST_DEBUG_OBJECT(src, "Have enough data");
724
725     GST_OBJECT_LOCK(src);
726     if (priv->enoughDataID || priv->paused) {
727         GST_OBJECT_UNLOCK(src);
728         return;
729     }
730
731     priv->enoughDataID = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitWebSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
732     GST_OBJECT_UNLOCK(src);
733 }
734
735 static gboolean webKitWebSrcSeekMainCb(WebKitWebSrc* src)
736 {
737     webKitWebSrcStop(src, true);
738     webKitWebSrcStart(src);
739
740     return FALSE;
741 }
742
743 static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
744 {
745     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
746     WebKitWebSrcPrivate* priv = src->priv;
747
748     GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
749     if (offset == priv->offset)
750         return TRUE;
751
752     if (!priv->seekable)
753         return FALSE;
754     if (offset > priv->size)
755         return FALSE;
756
757     GST_DEBUG_OBJECT(src, "Doing range-request seek");
758     priv->requestedOffset = offset;
759
760     GST_OBJECT_LOCK(src);
761     if (priv->seekID)
762         g_source_remove(priv->seekID);
763     priv->seekID = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitWebSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
764     GST_OBJECT_UNLOCK(src);
765     
766     return TRUE;
767 }
768
769 void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
770 {
771     WebKitWebSrcPrivate* priv = src->priv;
772     WebCore::Frame* frame = 0;
773
774     WebCore::Document* document = player->mediaPlayerClient()->mediaPlayerOwningDocument();
775     if (document)
776         frame = document->frame();
777
778     priv->frame = frame;
779     priv->player = player;
780 }
781
782 StreamingClient::StreamingClient(WebKitWebSrc* src) : m_src(src)
783 {
784
785 }
786
787 StreamingClient::~StreamingClient()
788 {
789
790 }
791
792 void StreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&)
793 {
794 }
795
796 void StreamingClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
797 {
798     WebKitWebSrcPrivate* priv = m_src->priv;
799
800     GST_DEBUG_OBJECT(m_src, "Received response: %d", response.httpStatusCode());
801
802     // If we seeked we need 206 == PARTIAL_CONTENT
803     if (priv->requestedOffset && response.httpStatusCode() != 206) {
804         GST_ELEMENT_ERROR(m_src, RESOURCE, READ, (0), (0));
805         gst_app_src_end_of_stream(priv->appsrc);
806         webKitWebSrcStop(m_src, false);
807         return;
808     }
809
810     long long length = response.expectedContentLength();
811     if (length > 0) {
812         length += priv->requestedOffset;
813         gst_app_src_set_size(priv->appsrc, length);
814
815 #ifndef GST_API_VERSION_1
816         if (!priv->haveAppSrc27) {
817             gst_segment_set_duration(&GST_BASE_SRC(priv->appsrc)->segment, GST_FORMAT_BYTES, length);
818             gst_element_post_message(GST_ELEMENT(priv->appsrc),
819                                      gst_message_new_duration(GST_OBJECT(priv->appsrc),
820                                                               GST_FORMAT_BYTES, length));
821         }
822 #endif
823     }
824
825     priv->size = length >= 0 ? length : 0;
826     priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField("Accept-Ranges").utf8().data());
827
828     // icecast stuff
829     String value = response.httpHeaderField("icy-metaint");
830     if (!value.isEmpty()) {
831         gchar* endptr = 0;
832         gint64 icyMetaInt = g_ascii_strtoll(value.utf8().data(), &endptr, 10);
833             
834         if (endptr && *endptr == '\0' && icyMetaInt > 0) {
835             GRefPtr<GstCaps> caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, (gint) icyMetaInt, NULL));
836
837             gst_app_src_set_caps(priv->appsrc, caps.get());
838         }
839     }
840
841 #ifdef GST_API_VERSION_1
842     GstTagList* tags = gst_tag_list_new_empty();
843 #else
844     GstTagList* tags = gst_tag_list_new();
845 #endif
846     value = response.httpHeaderField("icy-name");
847     if (!value.isEmpty()) {
848         g_free(priv->iradioName);
849         priv->iradioName = g_strdup(value.utf8().data());
850         g_object_notify(G_OBJECT(m_src), "iradio-name");
851         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, priv->iradioName, NULL);
852     }
853     value = response.httpHeaderField("icy-genre");
854     if (!value.isEmpty()) {
855         g_free(priv->iradioGenre);
856         priv->iradioGenre = g_strdup(value.utf8().data());
857         g_object_notify(G_OBJECT(m_src), "iradio-genre");
858         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, priv->iradioGenre, NULL);
859     }
860     value = response.httpHeaderField("icy-url");
861     if (!value.isEmpty()) {
862         g_free(priv->iradioUrl);
863         priv->iradioUrl = g_strdup(value.utf8().data());
864         g_object_notify(G_OBJECT(m_src), "iradio-url");
865         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION, priv->iradioUrl, NULL);
866     }
867     value = response.httpHeaderField("icy-title");
868     if (!value.isEmpty()) {
869         g_free(priv->iradioTitle);
870         priv->iradioTitle = g_strdup(value.utf8().data());
871         g_object_notify(G_OBJECT(m_src), "iradio-title");
872         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, priv->iradioTitle, NULL);
873     }
874
875     if (gst_tag_list_is_empty(tags))
876 #ifdef GST_API_VERSION_1
877         gst_tag_list_unref(tags);
878 #else
879         gst_tag_list_free(tags);
880 #endif
881     else
882         notifyGstTagsOnPad(GST_ELEMENT(m_src), m_src->priv->srcpad, tags);
883 }
884
885 void StreamingClient::didReceiveData(ResourceHandle* handle, const char* data, int length, int)
886 {
887     WebKitWebSrcPrivate* priv = m_src->priv;
888
889     GST_LOG_OBJECT(m_src, "Have %d bytes of data", priv->buffer ? getGstBufferSize(priv->buffer.get()) : length);
890
891     ASSERT(!priv->buffer || data == getGstBufferDataPointer(priv->buffer.get()));
892
893 #ifdef GST_API_VERSION_1
894     if (priv->buffer)
895         unmapGstBuffer(priv->buffer.get());
896 #endif
897
898     if (priv->seekID || handle != priv->resourceHandle) {
899         GST_DEBUG_OBJECT(m_src, "Seek in progress, ignoring data");
900         priv->buffer.clear();
901         return;
902     }
903
904     // Ports using the GStreamer backend but not the soup implementation of ResourceHandle
905     // won't be using buffers provided by this client, the buffer is created here in that case.
906     if (!priv->buffer)
907         priv->buffer = adoptGRef(createGstBufferForData(data, length));
908     else
909         setGstBufferSize(priv->buffer.get(), length);
910
911     GST_BUFFER_OFFSET(priv->buffer.get()) = priv->offset;
912     priv->offset += length;
913     GST_BUFFER_OFFSET_END(priv->buffer.get()) = priv->offset;
914
915     GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, priv->buffer.leakRef());
916 #ifdef GST_API_VERSION_1
917     if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
918 #else
919     if (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED)
920 #endif
921         GST_ELEMENT_ERROR(m_src, CORE, FAILED, (0), (0));
922 }
923
924 char* StreamingClient::getBuffer(int requestedSize, int* actualSize)
925 {
926     WebKitWebSrcPrivate* priv = m_src->priv;
927
928     ASSERT(!priv->buffer);
929
930     GstBuffer* buffer = gst_buffer_new_and_alloc(requestedSize);
931
932 #ifdef GST_API_VERSION_1
933     mapGstBuffer(buffer);
934 #endif
935
936     priv->buffer = adoptGRef(buffer);
937
938     *actualSize = getGstBufferSize(buffer);
939     return getGstBufferDataPointer(buffer);
940 }
941
942 void StreamingClient::didFinishLoading(ResourceHandle*, double)
943 {
944     WebKitWebSrcPrivate* priv = m_src->priv;
945
946     GST_DEBUG_OBJECT(m_src, "Have EOS");
947
948     if (!priv->seekID)
949         gst_app_src_end_of_stream(m_src->priv->appsrc);
950 }
951
952 void StreamingClient::didFail(ResourceHandle*, const ResourceError& error)
953 {
954     GST_ERROR_OBJECT(m_src, "Have failure: %s", error.localizedDescription().utf8().data());
955     GST_ELEMENT_ERROR(m_src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0));
956     gst_app_src_end_of_stream(m_src->priv->appsrc);
957 }
958
959 void StreamingClient::wasBlocked(ResourceHandle*)
960 {
961     GST_ERROR_OBJECT(m_src, "Request was blocked");
962     GST_ELEMENT_ERROR(m_src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", m_src->priv->uri), (0));
963 }
964
965 void StreamingClient::cannotShowURL(ResourceHandle*)
966 {
967     GST_ERROR_OBJECT(m_src, "Cannot show URL");
968     GST_ELEMENT_ERROR(m_src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", m_src->priv->uri), (0));
969 }
970
971 #endif // USE(GSTREAMER)
972