Fix offset handling in GStreamer WebKitWebSource.
[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 "FrameLoader.h"
28 #include "GRefPtrGStreamer.h"
29 #include "GStreamerVersioning.h"
30 #include "MediaPlayer.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     priv->offset = priv->requestedOffset;
468
469     if (priv->iradioMode)
470         request.setHTTPHeaderField("icy-metadata", "1");
471
472     // Needed to use DLNA streaming servers
473     request.setHTTPHeaderField("transferMode.dlna", "Streaming");
474
475     priv->resourceHandle = ResourceHandle::create(context, request, priv->client, false, false);
476     if (!priv->resourceHandle) {
477         GST_ERROR_OBJECT(src, "Failed to create ResourceHandle");
478         return false;
479     }
480
481     GST_DEBUG_OBJECT(src, "Started request");
482
483     return true;
484 }
485
486 static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
487 {
488     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
489     WebKitWebSrc* src = WEBKIT_WEB_SRC(element);
490     WebKitWebSrcPrivate* priv = src->priv;
491
492     switch (transition) {
493     case GST_STATE_CHANGE_NULL_TO_READY:
494         if (!priv->appsrc) {
495             gst_element_post_message(element,
496                                      gst_missing_element_message_new(element, "appsrc"));
497             GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
498             return GST_STATE_CHANGE_FAILURE;
499         }
500         break;
501     default:
502         break;
503     }
504
505     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
506     if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
507         GST_DEBUG_OBJECT(src, "State change failed");
508         return ret;
509     }
510
511     switch (transition) {
512     case GST_STATE_CHANGE_READY_TO_PAUSED:
513         GST_DEBUG_OBJECT(src, "READY->PAUSED");
514         if (!webKitWebSrcStart(src))
515             ret = GST_STATE_CHANGE_FAILURE;
516         break;
517     case GST_STATE_CHANGE_PAUSED_TO_READY:
518         GST_DEBUG_OBJECT(src, "PAUSED->READY");
519         webKitWebSrcStop(src, false);
520         break;
521     default:
522         break;
523     }
524
525     return ret;
526 }
527
528 static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
529 {
530     WebKitWebSrc* webkitSrc = WEBKIT_WEB_SRC(GST_ELEMENT(parent));
531     gboolean result = FALSE;
532
533     switch (GST_QUERY_TYPE(query)) {
534     case GST_QUERY_DURATION: {
535         GstFormat format;
536
537         gst_query_parse_duration(query, &format, NULL);
538
539         GST_DEBUG_OBJECT(webkitSrc, "duration query in format %s", gst_format_get_name(format));
540         if ((format == GST_FORMAT_BYTES) && (webkitSrc->priv->size > 0)) {
541             gst_query_set_duration(query, format, webkitSrc->priv->size);
542             result = TRUE;
543         }
544         break;
545     }
546     case GST_QUERY_URI: {
547         gst_query_set_uri(query, webkitSrc->priv->uri);
548         result = TRUE;
549         break;
550     }
551     default: {
552         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
553
554         // Forward the query to the proxy target pad.
555         if (target)
556             result = gst_pad_query(target.get(), query);
557         break;
558     }
559     }
560
561     return result;
562 }
563
564 #ifndef GST_API_VERSION_1
565 static gboolean webKitWebSrcQuery(GstPad* pad, GstQuery* query)
566 {
567     GRefPtr<GstElement> src = adoptGRef(gst_pad_get_parent_element(pad));
568     return webKitWebSrcQueryWithParent(pad, GST_OBJECT(src.get()), query);
569 }
570 #endif
571
572 // uri handler interface
573
574 #ifdef GST_API_VERSION_1
575 static GstURIType webKitWebSrcUriGetType(GType)
576 {
577     return GST_URI_SRC;
578 }
579
580 const gchar* const* webKitWebSrcGetProtocols(GType)
581 {
582     static const char* protocols[] = {"http", "https", 0 };
583     return protocols;
584 }
585
586 static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
587 {
588     return g_strdup(WEBKIT_WEB_SRC(handler)->priv->uri);
589 }
590
591 static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error)
592 {
593     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
594     WebKitWebSrcPrivate* priv = src->priv;
595
596     if (GST_STATE(src) >= GST_STATE_PAUSED) {
597         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
598         return FALSE;
599     }
600
601     g_free(priv->uri);
602     priv->uri = 0;
603
604     if (!uri)
605         return TRUE;
606
607     KURL url(KURL(), uri);
608
609     if (!url.isValid() || !url.protocolIsInHTTPFamily()) {
610         g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri);
611         return FALSE;
612     }
613
614     priv->uri = g_strdup(url.string().utf8().data());
615     return TRUE;
616 }
617
618 #else
619 static GstURIType webKitWebSrcUriGetType(void)
620 {
621     return GST_URI_SRC;
622 }
623
624 static gchar** webKitWebSrcGetProtocols(void)
625 {
626     static gchar* protocols[] = {(gchar*) "http", (gchar*) "https", 0 };
627     return protocols;
628 }
629
630 static const gchar* webKitWebSrcGetUri(GstURIHandler* handler)
631 {
632     return g_strdup(WEBKIT_WEB_SRC(handler)->priv->uri);
633 }
634
635 static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri)
636 {
637     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
638     WebKitWebSrcPrivate* priv = src->priv;
639
640     if (GST_STATE(src) >= GST_STATE_PAUSED) {
641         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
642         return FALSE;
643     }
644
645     g_free(priv->uri);
646     priv->uri = 0;
647
648     if (!uri)
649         return TRUE;
650
651     KURL url(KURL(), uri);
652
653     if (!url.isValid() || !url.protocolIsInHTTPFamily()) {
654         GST_ERROR_OBJECT(src, "Invalid URI '%s'", uri);
655         return FALSE;
656     }
657
658     priv->uri = g_strdup(url.string().utf8().data());
659     return TRUE;
660 }
661 #endif
662
663 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer)
664 {
665     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
666
667     iface->get_type = webKitWebSrcUriGetType;
668     iface->get_protocols = webKitWebSrcGetProtocols;
669     iface->get_uri = webKitWebSrcGetUri;
670     iface->set_uri = webKitWebSrcSetUri;
671 }
672
673 // appsrc callbacks
674
675 static gboolean webKitWebSrcNeedDataMainCb(WebKitWebSrc* src)
676 {
677     WebKitWebSrcPrivate* priv = src->priv;
678
679     priv->resourceHandle->setDefersLoading(false);
680
681     GST_OBJECT_LOCK(src);
682     priv->paused = FALSE;
683     priv->needDataID = 0;
684     GST_OBJECT_UNLOCK(src);
685     return FALSE;
686 }
687
688 static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
689 {
690     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
691     WebKitWebSrcPrivate* priv = src->priv;
692
693     GST_DEBUG_OBJECT(src, "Need more data: %u", length);
694
695     GST_OBJECT_LOCK(src);
696     if (priv->needDataID || !priv->paused) {
697         GST_OBJECT_UNLOCK(src);
698         return;
699     }
700
701     priv->needDataID = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitWebSrcNeedDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
702     GST_OBJECT_UNLOCK(src);
703 }
704
705 static gboolean webKitWebSrcEnoughDataMainCb(WebKitWebSrc* src)
706 {
707     WebKitWebSrcPrivate* priv = src->priv;
708
709     priv->resourceHandle->setDefersLoading(true);
710
711     GST_OBJECT_LOCK(src);
712     priv->paused = TRUE;
713     priv->enoughDataID = 0;
714     GST_OBJECT_UNLOCK(src);
715
716     return FALSE;
717 }
718
719 static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData)
720 {
721     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
722     WebKitWebSrcPrivate* priv = src->priv;
723
724     GST_DEBUG_OBJECT(src, "Have enough data");
725
726     GST_OBJECT_LOCK(src);
727     if (priv->enoughDataID || priv->paused) {
728         GST_OBJECT_UNLOCK(src);
729         return;
730     }
731
732     priv->enoughDataID = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitWebSrcEnoughDataMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
733     GST_OBJECT_UNLOCK(src);
734 }
735
736 static gboolean webKitWebSrcSeekMainCb(WebKitWebSrc* src)
737 {
738     webKitWebSrcStop(src, true);
739     webKitWebSrcStart(src);
740
741     return FALSE;
742 }
743
744 static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
745 {
746     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
747     WebKitWebSrcPrivate* priv = src->priv;
748
749     GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
750     if (offset == priv->offset && priv->requestedOffset == priv->offset)
751         return TRUE;
752
753     if (!priv->seekable)
754         return FALSE;
755     if (offset > priv->size)
756         return FALSE;
757
758     GST_DEBUG_OBJECT(src, "Doing range-request seek");
759     priv->requestedOffset = offset;
760
761     GST_OBJECT_LOCK(src);
762     if (priv->seekID)
763         g_source_remove(priv->seekID);
764     priv->seekID = g_timeout_add_full(G_PRIORITY_DEFAULT, 0, (GSourceFunc) webKitWebSrcSeekMainCb, gst_object_ref(src), (GDestroyNotify) gst_object_unref);
765     GST_OBJECT_UNLOCK(src);
766     
767     return TRUE;
768 }
769
770 void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
771 {
772     WebKitWebSrcPrivate* priv = src->priv;
773     WebCore::Frame* frame = 0;
774
775     WebCore::Document* document = player->mediaPlayerClient()->mediaPlayerOwningDocument();
776     if (document)
777         frame = document->frame();
778
779     priv->frame = frame;
780     priv->player = player;
781 }
782
783 StreamingClient::StreamingClient(WebKitWebSrc* src) : m_src(src)
784 {
785
786 }
787
788 StreamingClient::~StreamingClient()
789 {
790
791 }
792
793 void StreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&)
794 {
795 }
796
797 void StreamingClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
798 {
799     WebKitWebSrcPrivate* priv = m_src->priv;
800
801     GST_DEBUG_OBJECT(m_src, "Received response: %d", response.httpStatusCode());
802
803     // If we seeked we need 206 == PARTIAL_CONTENT
804     if (priv->requestedOffset && response.httpStatusCode() != 206) {
805         GST_ELEMENT_ERROR(m_src, RESOURCE, READ, (0), (0));
806         gst_app_src_end_of_stream(priv->appsrc);
807         webKitWebSrcStop(m_src, false);
808         return;
809     }
810
811     long long length = response.expectedContentLength();
812     if (length > 0) {
813         length += priv->requestedOffset;
814         gst_app_src_set_size(priv->appsrc, length);
815
816 #ifndef GST_API_VERSION_1
817         if (!priv->haveAppSrc27) {
818             gst_segment_set_duration(&GST_BASE_SRC(priv->appsrc)->segment, GST_FORMAT_BYTES, length);
819             gst_element_post_message(GST_ELEMENT(priv->appsrc),
820                                      gst_message_new_duration(GST_OBJECT(priv->appsrc),
821                                                               GST_FORMAT_BYTES, length));
822         }
823 #endif
824     }
825
826     priv->size = length >= 0 ? length : 0;
827     priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField("Accept-Ranges").utf8().data());
828
829     // icecast stuff
830     String value = response.httpHeaderField("icy-metaint");
831     if (!value.isEmpty()) {
832         gchar* endptr = 0;
833         gint64 icyMetaInt = g_ascii_strtoll(value.utf8().data(), &endptr, 10);
834             
835         if (endptr && *endptr == '\0' && icyMetaInt > 0) {
836             GRefPtr<GstCaps> caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, (gint) icyMetaInt, NULL));
837
838             gst_app_src_set_caps(priv->appsrc, caps.get());
839         }
840     }
841
842 #ifdef GST_API_VERSION_1
843     GstTagList* tags = gst_tag_list_new_empty();
844 #else
845     GstTagList* tags = gst_tag_list_new();
846 #endif
847     value = response.httpHeaderField("icy-name");
848     if (!value.isEmpty()) {
849         g_free(priv->iradioName);
850         priv->iradioName = g_strdup(value.utf8().data());
851         g_object_notify(G_OBJECT(m_src), "iradio-name");
852         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, priv->iradioName, NULL);
853     }
854     value = response.httpHeaderField("icy-genre");
855     if (!value.isEmpty()) {
856         g_free(priv->iradioGenre);
857         priv->iradioGenre = g_strdup(value.utf8().data());
858         g_object_notify(G_OBJECT(m_src), "iradio-genre");
859         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, priv->iradioGenre, NULL);
860     }
861     value = response.httpHeaderField("icy-url");
862     if (!value.isEmpty()) {
863         g_free(priv->iradioUrl);
864         priv->iradioUrl = g_strdup(value.utf8().data());
865         g_object_notify(G_OBJECT(m_src), "iradio-url");
866         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION, priv->iradioUrl, NULL);
867     }
868     value = response.httpHeaderField("icy-title");
869     if (!value.isEmpty()) {
870         g_free(priv->iradioTitle);
871         priv->iradioTitle = g_strdup(value.utf8().data());
872         g_object_notify(G_OBJECT(m_src), "iradio-title");
873         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, priv->iradioTitle, NULL);
874     }
875
876     if (gst_tag_list_is_empty(tags))
877 #ifdef GST_API_VERSION_1
878         gst_tag_list_unref(tags);
879 #else
880         gst_tag_list_free(tags);
881 #endif
882     else
883         notifyGstTagsOnPad(GST_ELEMENT(m_src), m_src->priv->srcpad, tags);
884 }
885
886 void StreamingClient::didReceiveData(ResourceHandle* handle, const char* data, int length, int)
887 {
888     WebKitWebSrcPrivate* priv = m_src->priv;
889
890     GST_LOG_OBJECT(m_src, "Have %d bytes of data", priv->buffer ? getGstBufferSize(priv->buffer.get()) : length);
891
892     ASSERT(!priv->buffer || data == getGstBufferDataPointer(priv->buffer.get()));
893
894 #ifdef GST_API_VERSION_1
895     if (priv->buffer)
896         unmapGstBuffer(priv->buffer.get());
897 #endif
898
899     if (priv->seekID || handle != priv->resourceHandle) {
900         GST_DEBUG_OBJECT(m_src, "Seek in progress, ignoring data");
901         priv->buffer.clear();
902         return;
903     }
904
905     // Ports using the GStreamer backend but not the soup implementation of ResourceHandle
906     // won't be using buffers provided by this client, the buffer is created here in that case.
907     if (!priv->buffer)
908         priv->buffer = adoptGRef(createGstBufferForData(data, length));
909     else
910         setGstBufferSize(priv->buffer.get(), length);
911
912     GST_BUFFER_OFFSET(priv->buffer.get()) = priv->offset;
913     if (priv->requestedOffset == priv->offset)
914         priv->requestedOffset += length;
915     priv->offset += length;
916     GST_BUFFER_OFFSET_END(priv->buffer.get()) = priv->offset;
917
918     GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, priv->buffer.leakRef());
919 #ifdef GST_API_VERSION_1
920     if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
921 #else
922     if (ret != GST_FLOW_OK && ret != GST_FLOW_UNEXPECTED)
923 #endif
924         GST_ELEMENT_ERROR(m_src, CORE, FAILED, (0), (0));
925 }
926
927 char* StreamingClient::getBuffer(int requestedSize, int* actualSize)
928 {
929     WebKitWebSrcPrivate* priv = m_src->priv;
930
931     ASSERT(!priv->buffer);
932
933     GstBuffer* buffer = gst_buffer_new_and_alloc(requestedSize);
934
935 #ifdef GST_API_VERSION_1
936     mapGstBuffer(buffer);
937 #endif
938
939     priv->buffer = adoptGRef(buffer);
940
941     *actualSize = getGstBufferSize(buffer);
942     return getGstBufferDataPointer(buffer);
943 }
944
945 void StreamingClient::didFinishLoading(ResourceHandle*, double)
946 {
947     WebKitWebSrcPrivate* priv = m_src->priv;
948
949     GST_DEBUG_OBJECT(m_src, "Have EOS");
950
951     if (!priv->seekID)
952         gst_app_src_end_of_stream(m_src->priv->appsrc);
953 }
954
955 void StreamingClient::didFail(ResourceHandle*, const ResourceError& error)
956 {
957     GST_ERROR_OBJECT(m_src, "Have failure: %s", error.localizedDescription().utf8().data());
958     GST_ELEMENT_ERROR(m_src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0));
959     gst_app_src_end_of_stream(m_src->priv->appsrc);
960 }
961
962 void StreamingClient::wasBlocked(ResourceHandle*)
963 {
964     GST_ERROR_OBJECT(m_src, "Request was blocked");
965     GST_ELEMENT_ERROR(m_src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", m_src->priv->uri), (0));
966 }
967
968 void StreamingClient::cannotShowURL(ResourceHandle*)
969 {
970     GST_ERROR_OBJECT(m_src, "Cannot show URL");
971     GST_ELEMENT_ERROR(m_src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", m_src->priv->uri), (0));
972 }
973
974 #endif // USE(GSTREAMER)
975