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