1/*
2 *  Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
3 *  Copyright (C) 2013 Collabora Ltd.
4 *  Copyright (C) 2013 Orange
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20
21#include "config.h"
22#include "WebKitMediaSourceGStreamer.h"
23
24#if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
25
26#include "GRefPtrGStreamer.h"
27#include "GStreamerUtilities.h"
28#include "NotImplemented.h"
29#include "TimeRanges.h"
30#include <gst/app/gstappsrc.h>
31#include <gst/gst.h>
32#include <gst/pbutils/missing-plugins.h>
33#include <wtf/gobject/GMainLoopSource.h>
34#include <wtf/gobject/GUniquePtr.h>
35#include <wtf/text/CString.h>
36
37typedef struct _Source {
38    GstElement* appsrc;
39    guint sourceid;        /* To control the GSource */
40    GstPad* srcpad;
41    gboolean padAdded;
42
43    guint64 offset;
44    guint64 size;
45    gboolean paused;
46
47    GMainLoopSource start;
48    GMainLoopSource stop;
49    GMainLoopSource needData;
50    GMainLoopSource enoughData;
51    GMainLoopSource seek;
52
53    guint64 requestedOffset;
54} Source;
55
56
57#define WEBKIT_MEDIA_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_SRC, WebKitMediaSrcPrivate))
58
59struct _WebKitMediaSrcPrivate {
60    gchar* uri;
61    Source sourceVideo;
62    Source sourceAudio;
63    WebCore::MediaPlayer* player;
64    GstElement* playbin;
65    gint64 duration;
66    gboolean seekable;
67    gboolean noMorePad;
68    // TRUE if appsrc's version is >= 0.10.27, see
69    // https://bugzilla.gnome.org/show_bug.cgi?id=609423
70    gboolean haveAppSrc27;
71    guint nbSource;
72};
73
74enum {
75    PropLocation = 1,
76    ProLast
77};
78
79static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC, GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY);
80
81GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug);
82#define GST_CAT_DEFAULT webkit_media_src_debug
83
84static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
85static void webKitMediaSrcFinalize(GObject*);
86static void webKitMediaSrcSetProperty(GObject*, guint propertyId, const GValue*, GParamSpec*);
87static void webKitMediaSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*);
88static GstStateChangeReturn webKitMediaSrcChangeState(GstElement*, GstStateChange);
89static gboolean webKitMediaSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
90
91static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint, gpointer);
92static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer);
93static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64, gpointer);
94static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint, gpointer);
95static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer);
96static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64, gpointer);
97static GstAppSrcCallbacks appsrcCallbacksVideo = {
98    webKitMediaVideoSrcNeedDataCb,
99    webKitMediaVideoSrcEnoughDataCb,
100    webKitMediaVideoSrcSeekDataCb,
101    { 0 }
102};
103static GstAppSrcCallbacks appsrcCallbacksAudio = {
104    webKitMediaAudioSrcNeedDataCb,
105    webKitMediaAudioSrcEnoughDataCb,
106    webKitMediaAudioSrcSeekDataCb,
107    { 0 }
108};
109#define webkit_media_src_parent_class parent_class
110// We split this out into another macro to avoid a check-webkit-style error.
111#define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element");
112G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN,
113    G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit);
114    WEBKIT_MEDIA_SRC_CATEGORY_INIT);
115
116static void webkit_media_src_class_init(WebKitMediaSrcClass* klass)
117{
118    GObjectClass* oklass = G_OBJECT_CLASS(klass);
119    GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
120
121    oklass->finalize = webKitMediaSrcFinalize;
122    oklass->set_property = webKitMediaSrcSetProperty;
123    oklass->get_property = webKitMediaSrcGetProperty;
124
125    gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate));
126
127    gst_element_class_set_metadata(eklass, "WebKit Media source element", "Source", "Handles Blob uris", "Stephane Jadaud <sjadaud@sii.fr>");
128
129    /* Allows setting the uri using the 'location' property, which is used
130     * for example by gst_element_make_from_uri() */
131    g_object_class_install_property(oklass,
132        PropLocation,
133        g_param_spec_string("location", "location", "Location to read from", 0,
134        (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
135
136    eklass->change_state = webKitMediaSrcChangeState;
137
138    g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate));
139}
140
141static void webKitMediaSrcAddSrc(WebKitMediaSrc* src, GstElement* element)
142{
143    GstPad* ghostPad;
144    WebKitMediaSrcPrivate* priv = src->priv;
145
146    if (!gst_bin_add(GST_BIN(src), element)) {
147        GST_DEBUG_OBJECT(src, "Src element not added");
148        return;
149    }
150    GRefPtr<GstPad> targetsrc = adoptGRef(gst_element_get_static_pad(element, "src"));
151    if (!targetsrc) {
152        GST_DEBUG_OBJECT(src, "Pad not found");
153        return;
154    }
155
156    gst_element_sync_state_with_parent(element);
157    GUniquePtr<gchar> name(g_strdup_printf("src_%u", priv->nbSource));
158    ghostPad = WebCore::webkitGstGhostPadFromStaticTemplate(&srcTemplate, name.get(), targetsrc.get());
159    gst_pad_set_active(ghostPad, TRUE);
160
161    priv->nbSource++;
162
163    if (priv->sourceVideo.appsrc == element)
164        priv->sourceVideo.srcpad = ghostPad;
165    else if (priv->sourceAudio.appsrc == element)
166        priv->sourceAudio.srcpad = ghostPad;
167
168    GST_OBJECT_FLAG_SET(ghostPad, GST_PAD_FLAG_NEED_PARENT);
169    gst_pad_set_query_function(ghostPad, webKitMediaSrcQueryWithParent);
170}
171
172static void webkit_media_src_init(WebKitMediaSrc* src)
173{
174    WebKitMediaSrcPrivate* priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(src);
175    src->priv = priv;
176    new (priv) WebKitMediaSrcPrivate();
177
178    priv->sourceVideo.appsrc = gst_element_factory_make("appsrc", "videoappsrc");
179    gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceVideo.appsrc), &appsrcCallbacksVideo, src, 0);
180    webKitMediaSrcAddSrc(src, priv->sourceVideo.appsrc);
181
182    priv->sourceAudio.appsrc = gst_element_factory_make("appsrc", "audioappsrc");
183    gst_app_src_set_callbacks(GST_APP_SRC(priv->sourceAudio.appsrc), &appsrcCallbacksAudio, src, 0);
184    webKitMediaSrcAddSrc(src, priv->sourceAudio.appsrc);
185}
186
187static void webKitMediaSrcFinalize(GObject* object)
188{
189    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
190    WebKitMediaSrcPrivate* priv = src->priv;
191
192    g_free(priv->uri);
193    priv->~WebKitMediaSrcPrivate();
194
195    GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
196}
197
198static void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
199{
200    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
201    switch (propId) {
202    case PropLocation:
203        gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0);
204        break;
205    default:
206        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
207        break;
208    }
209}
210
211static void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
212{
213    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
214    WebKitMediaSrcPrivate* priv = src->priv;
215
216    GST_OBJECT_LOCK(src);
217    switch (propId) {
218    case PropLocation:
219        g_value_set_string(value, priv->uri);
220        break;
221    default:
222        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
223        break;
224    }
225    GST_OBJECT_UNLOCK(src);
226}
227
228// must be called on main thread and with object unlocked
229static void webKitMediaVideoSrcStop(WebKitMediaSrc* src)
230{
231    WebKitMediaSrcPrivate* priv = src->priv;
232    gboolean seeking;
233
234    GST_OBJECT_LOCK(src);
235
236    seeking = priv->sourceVideo.seek.isActive();
237
238    priv->sourceVideo.start.cancel();
239
240    priv->player = 0;
241    priv->playbin = 0;
242
243    priv->sourceVideo.needData.cancel();
244    priv->sourceVideo.enoughData.cancel();
245    priv->sourceVideo.seek.cancel();
246
247    priv->sourceVideo.paused = FALSE;
248    priv->sourceVideo.offset = 0;
249    priv->seekable = FALSE;
250
251    priv->duration = 0;
252    priv->nbSource = 0;
253
254    GST_OBJECT_UNLOCK(src);
255
256    if (priv->sourceVideo.appsrc) {
257        gst_app_src_set_caps(GST_APP_SRC(priv->sourceVideo.appsrc), 0);
258        if (!seeking)
259            gst_app_src_set_size(GST_APP_SRC(priv->sourceVideo.appsrc), -1);
260    }
261
262    GST_DEBUG_OBJECT(src, "Stopped request");
263}
264
265static void webKitMediaAudioSrcStop(WebKitMediaSrc* src)
266{
267    WebKitMediaSrcPrivate* priv = src->priv;
268    gboolean seeking;
269
270    GST_OBJECT_LOCK(src);
271
272    seeking = priv->sourceAudio.seek.isActive();
273
274    priv->sourceAudio.start.cancel();
275
276    priv->player = 0;
277
278    priv->playbin = 0;
279
280    priv->sourceAudio.needData.cancel();
281    priv->sourceAudio.enoughData.cancel();
282    priv->sourceAudio.seek.cancel();
283
284    priv->sourceAudio.paused = FALSE;
285
286    priv->sourceAudio.offset = 0;
287
288    priv->seekable = FALSE;
289
290    priv->duration = 0;
291    priv->nbSource = 0;
292
293    GST_OBJECT_UNLOCK(src);
294
295    if (priv->sourceAudio.appsrc) {
296        gst_app_src_set_caps(GST_APP_SRC(priv->sourceAudio.appsrc), 0);
297        if (!seeking)
298            gst_app_src_set_size(GST_APP_SRC(priv->sourceAudio.appsrc), -1);
299    }
300
301    GST_DEBUG_OBJECT(src, "Stopped request");
302}
303
304// must be called on main thread and with object unlocked
305static void webKitMediaVideoSrcStart(WebKitMediaSrc* src)
306{
307    WebKitMediaSrcPrivate* priv = src->priv;
308
309    GST_OBJECT_LOCK(src);
310    if (!priv->uri) {
311        GST_ERROR_OBJECT(src, "No URI provided");
312        GST_OBJECT_UNLOCK(src);
313        webKitMediaVideoSrcStop(src);
314        return;
315    }
316
317    GST_OBJECT_UNLOCK(src);
318    GST_DEBUG_OBJECT(src, "Started request");
319}
320
321// must be called on main thread and with object unlocked
322static void webKitMediaAudioSrcStart(WebKitMediaSrc* src)
323{
324    WebKitMediaSrcPrivate* priv = src->priv;
325
326    GST_OBJECT_LOCK(src);
327    if (!priv->uri) {
328        GST_ERROR_OBJECT(src, "No URI provided");
329        GST_OBJECT_UNLOCK(src);
330        webKitMediaAudioSrcStop(src);
331        return;
332    }
333
334    GST_OBJECT_UNLOCK(src);
335    GST_DEBUG_OBJECT(src, "Started request");
336}
337
338static GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition)
339{
340    GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
341    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(element);
342    WebKitMediaSrcPrivate* priv = src->priv;
343
344    switch (transition) {
345    case GST_STATE_CHANGE_NULL_TO_READY:
346        if (!priv->sourceVideo.appsrc && !priv->sourceAudio.appsrc) {
347            gst_element_post_message(element,
348                gst_missing_element_message_new(element, "appsrc"));
349            GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
350            return GST_STATE_CHANGE_FAILURE;
351        }
352        break;
353    default:
354        break;
355    }
356
357    ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
358    if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
359        GST_DEBUG_OBJECT(src, "State change failed");
360        return ret;
361    }
362
363    switch (transition) {
364    case GST_STATE_CHANGE_READY_TO_PAUSED:
365        GST_DEBUG_OBJECT(src, "READY->PAUSED");
366        GST_OBJECT_LOCK(src);
367
368        gst_object_ref(src);
369        priv->sourceVideo.start.schedule("[WebKit] webKitMediaVideoSrcStart", std::function<void()>(std::bind(webKitMediaVideoSrcStart, src)), G_PRIORITY_DEFAULT,
370            [src] { gst_object_unref(src); });
371
372        gst_object_ref(src);
373        priv->sourceAudio.start.schedule("[WebKit] webKitMediaAudioSrcStart", std::function<void()>(std::bind(webKitMediaAudioSrcStart, src)), G_PRIORITY_DEFAULT,
374            [src] { gst_object_unref(src); });
375
376        GST_OBJECT_UNLOCK(src);
377        break;
378    case GST_STATE_CHANGE_PAUSED_TO_READY:
379        GST_DEBUG_OBJECT(src, "PAUSED->READY");
380        GST_OBJECT_LOCK(src);
381
382        gst_object_ref(src);
383        priv->sourceVideo.stop.schedule("[WebKit] webKitMediaVideoSrcStop", std::function<void()>(std::bind(webKitMediaVideoSrcStop, src)), G_PRIORITY_DEFAULT,
384            [src] { gst_object_unref(src); });
385
386        gst_object_ref(src);
387        priv->sourceAudio.stop.schedule("[WebKit] webKitMediaAudioSrcStop", std::function<void()>(std::bind(webKitMediaAudioSrcStop, src)), G_PRIORITY_DEFAULT,
388            [src] { gst_object_unref(src); });
389
390        GST_OBJECT_UNLOCK(src);
391        break;
392    default:
393        break;
394    }
395
396    return ret;
397}
398
399static gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
400{
401    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent));
402    gboolean result = FALSE;
403
404    switch (GST_QUERY_TYPE(query)) {
405    case GST_QUERY_DURATION: {
406        GstFormat format;
407        gst_query_parse_duration(query, &format, NULL);
408
409        GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format));
410        GST_OBJECT_LOCK(src);
411        if ((format == GST_FORMAT_TIME) && (src->priv->duration > 0)) {
412            gst_query_set_duration(query, format, src->priv->duration);
413            result = TRUE;
414        }
415        GST_OBJECT_UNLOCK(src);
416        break;
417    }
418    case GST_QUERY_URI: {
419        GST_OBJECT_LOCK(src);
420        gst_query_set_uri(query, src->priv->uri);
421        GST_OBJECT_UNLOCK(src);
422        result = TRUE;
423        break;
424    }
425    default: {
426        GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
427        // Forward the query to the proxy target pad.
428        if (target)
429            result = gst_pad_query(target.get(), query);
430        break;
431    }
432    }
433
434    return result;
435}
436
437// uri handler interface
438static GstURIType webKitMediaSrcUriGetType(GType)
439{
440    return GST_URI_SRC;
441}
442
443const gchar* const* webKitMediaSrcGetProtocols(GType)
444{
445    static const char* protocols[] = {"mediasourceblob", 0 };
446    return protocols;
447}
448
449static gchar* webKitMediaSrcGetUri(GstURIHandler* handler)
450{
451    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler);
452    gchar* ret;
453
454    GST_OBJECT_LOCK(src);
455    ret = g_strdup(src->priv->uri);
456    GST_OBJECT_UNLOCK(src);
457    return ret;
458}
459
460static gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError**)
461{
462    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler);
463    WebKitMediaSrcPrivate* priv = src->priv;
464    if (GST_STATE(src) >= GST_STATE_PAUSED) {
465        GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
466        return FALSE;
467    }
468
469    GST_OBJECT_LOCK(src);
470    g_free(priv->uri);
471    priv->uri = 0;
472    if (!uri) {
473        GST_OBJECT_UNLOCK(src);
474        return TRUE;
475    }
476
477    WebCore::URL url(WebCore::URL(), uri);
478
479    priv->uri = g_strdup(url.string().utf8().data());
480    GST_OBJECT_UNLOCK(src);
481    return TRUE;
482}
483
484static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer)
485{
486    GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
487
488    iface->get_type = webKitMediaSrcUriGetType;
489    iface->get_protocols = webKitMediaSrcGetProtocols;
490    iface->get_uri = webKitMediaSrcGetUri;
491    iface->set_uri = webKitMediaSrcSetUri;
492}
493
494// appsrc callbacks
495static void webKitMediaVideoSrcNeedDataMainCb(WebKitMediaSrc* src)
496{
497    WebKitMediaSrcPrivate* priv = src->priv;
498
499    GST_OBJECT_LOCK(src);
500    priv->sourceVideo.paused = FALSE;
501    GST_OBJECT_UNLOCK(src);
502}
503
504static void webKitMediaAudioSrcNeedDataMainCb(WebKitMediaSrc* src)
505{
506    WebKitMediaSrcPrivate* priv = src->priv;
507
508    GST_OBJECT_LOCK(src);
509    priv->sourceAudio.paused = FALSE;
510    GST_OBJECT_UNLOCK(src);
511}
512
513static void webKitMediaVideoSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
514{
515    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
516    WebKitMediaSrcPrivate* priv = src->priv;
517
518    GST_DEBUG_OBJECT(src, "Need more data: %u", length);
519
520    GST_OBJECT_LOCK(src);
521    if (priv->sourceVideo.needData.isScheduled() || !priv->sourceVideo.paused) {
522        GST_OBJECT_UNLOCK(src);
523        return;
524    }
525
526    gst_object_ref(src);
527    priv->sourceVideo.needData.schedule("[WebKit] webKitMediaVideoSrcNeedDataMainCb", std::function<void()>(std::bind(webKitMediaVideoSrcNeedDataMainCb, src)), G_PRIORITY_DEFAULT,
528        [src] { gst_object_unref(src); });
529    GST_OBJECT_UNLOCK(src);
530}
531
532static void webKitMediaAudioSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
533{
534    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
535    WebKitMediaSrcPrivate* priv = src->priv;
536
537    GST_DEBUG_OBJECT(src, "Need more data: %u", length);
538
539    GST_OBJECT_LOCK(src);
540    if (priv->sourceAudio.needData.isScheduled() || !priv->sourceAudio.paused) {
541        GST_OBJECT_UNLOCK(src);
542        return;
543    }
544
545    gst_object_ref(src);
546    priv->sourceAudio.needData.schedule("[WebKit] webKitMediaAudioSrcNeedDataMainCb", std::function<void()>(std::bind(webKitMediaAudioSrcNeedDataMainCb, src)), G_PRIORITY_DEFAULT,
547        [src] { gst_object_unref(src); });
548    GST_OBJECT_UNLOCK(src);
549}
550
551static void webKitMediaVideoSrcEnoughDataMainCb(WebKitMediaSrc* src)
552{
553    WebKitMediaSrcPrivate* priv = src->priv;
554
555    GST_OBJECT_LOCK(src);
556    priv->sourceVideo.paused = TRUE;
557    GST_OBJECT_UNLOCK(src);
558}
559
560static void webKitMediaAudioSrcEnoughDataMainCb(WebKitMediaSrc* src)
561{
562    WebKitMediaSrcPrivate* priv = src->priv;
563
564    GST_OBJECT_LOCK(src);
565    priv->sourceAudio.paused = TRUE;
566    GST_OBJECT_UNLOCK(src);
567}
568
569static void webKitMediaVideoSrcEnoughDataCb(GstAppSrc*, gpointer userData)
570{
571    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
572    WebKitMediaSrcPrivate* priv = src->priv;
573
574    GST_DEBUG_OBJECT(src, "Have enough data");
575
576    GST_OBJECT_LOCK(src);
577    if (priv->sourceVideo.enoughData.isScheduled() || priv->sourceVideo.paused) {
578        GST_OBJECT_UNLOCK(src);
579        return;
580    }
581
582    gst_object_ref(src);
583    priv->sourceVideo.enoughData.schedule("[WebKit] webKitMediaVideoSrcEnoughDataMainCb", std::function<void()>(std::bind(webKitMediaVideoSrcEnoughDataMainCb, src)), G_PRIORITY_DEFAULT,
584        [src] { gst_object_unref(src); });
585
586    GST_OBJECT_UNLOCK(src);
587}
588
589static void webKitMediaAudioSrcEnoughDataCb(GstAppSrc*, gpointer userData)
590{
591    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
592    WebKitMediaSrcPrivate* priv = src->priv;
593
594    GST_DEBUG_OBJECT(src, "Have enough data");
595
596    GST_OBJECT_LOCK(src);
597    if (priv->sourceAudio.enoughData.isScheduled() || priv->sourceAudio.paused) {
598        GST_OBJECT_UNLOCK(src);
599        return;
600    }
601
602    gst_object_ref(src);
603    priv->sourceAudio.enoughData.schedule("[WebKit] webKitMediaAudioSrcEnoughDataMainCb", std::function<void()>(std::bind(webKitMediaAudioSrcEnoughDataMainCb, src)), G_PRIORITY_DEFAULT,
604        [src] { gst_object_unref(src); });
605
606    GST_OBJECT_UNLOCK(src);
607}
608
609static void webKitMediaVideoSrcSeekMainCb(WebKitMediaSrc*)
610{
611    notImplemented();
612}
613
614
615static void webKitMediaAudioSrcSeekMainCb(WebKitMediaSrc*)
616{
617    notImplemented();
618}
619
620static gboolean webKitMediaVideoSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
621{
622    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
623    WebKitMediaSrcPrivate* priv = src->priv;
624
625    GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
626    GST_OBJECT_LOCK(src);
627    if (offset == priv->sourceVideo.offset && priv->sourceVideo.requestedOffset == priv->sourceVideo.offset) {
628        GST_OBJECT_UNLOCK(src);
629        return TRUE;
630    }
631
632    if (!priv->seekable) {
633        GST_OBJECT_UNLOCK(src);
634        return FALSE;
635    }
636    if (offset > priv->sourceVideo.size) {
637        GST_OBJECT_UNLOCK(src);
638        return FALSE;
639    }
640
641    GST_DEBUG_OBJECT(src, "Doing range-request seek");
642    priv->sourceVideo.requestedOffset = offset;
643
644    gst_object_ref(src);
645    priv->sourceVideo.seek.schedule("[WebKit] webKitMediaVideoSrcSeekMainCb", std::function<void()>(std::bind(webKitMediaVideoSrcSeekMainCb, src)), G_PRIORITY_DEFAULT,
646        [src] { gst_object_unref(src); });
647
648    GST_OBJECT_UNLOCK(src);
649
650    return TRUE;
651}
652
653static gboolean webKitMediaAudioSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
654{
655    WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(userData);
656    WebKitMediaSrcPrivate* priv = src->priv;
657
658    GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
659    GST_OBJECT_LOCK(src);
660    if (offset == priv->sourceAudio.offset && priv->sourceAudio.requestedOffset == priv->sourceAudio.offset) {
661        GST_OBJECT_UNLOCK(src);
662        return TRUE;
663    }
664
665    if (!priv->seekable) {
666        GST_OBJECT_UNLOCK(src);
667        return FALSE;
668    }
669    if (offset > priv->sourceAudio.size) {
670        GST_OBJECT_UNLOCK(src);
671        return FALSE;
672    }
673
674    GST_DEBUG_OBJECT(src, "Doing range-request seek");
675    priv->sourceAudio.requestedOffset = offset;
676
677    gst_object_ref(src);
678    priv->sourceAudio.seek.schedule("[WebKit] webKitMediaAudioSrcSeekMainCb", std::function<void()>(std::bind(webKitMediaAudioSrcSeekMainCb, src)), G_PRIORITY_DEFAULT,
679        [src] { gst_object_unref(src); });
680
681    GST_OBJECT_UNLOCK(src);
682
683    return TRUE;
684}
685
686void webKitMediaSrcSetMediaPlayer(WebKitMediaSrc* src, WebCore::MediaPlayer* player)
687{
688    WebKitMediaSrcPrivate* priv = src->priv;
689    priv->player = player;
690}
691
692void webKitMediaSrcSetPlayBin(WebKitMediaSrc* src, GstElement* playBin)
693{
694    WebKitMediaSrcPrivate* priv = src->priv;
695    priv->playbin = playBin;
696}
697
698MediaSourceClientGstreamer::MediaSourceClientGstreamer(WebKitMediaSrc* src)
699    : m_src(static_cast<WebKitMediaSrc*>(gst_object_ref(src)))
700{
701}
702
703MediaSourceClientGstreamer::~MediaSourceClientGstreamer()
704{
705    gst_object_unref(m_src);
706}
707
708void MediaSourceClientGstreamer::didReceiveDuration(double duration)
709{
710    WebKitMediaSrcPrivate* priv = m_src->priv;
711    GST_DEBUG_OBJECT(m_src, "Received duration: %lf", duration);
712
713    GST_OBJECT_LOCK(m_src);
714    priv->duration = duration >= 0.0 ? static_cast<gint64>(duration*GST_SECOND) : 0;
715    GST_OBJECT_UNLOCK(m_src);
716}
717
718void MediaSourceClientGstreamer::didReceiveData(const char* data, int length, String type)
719{
720    WebKitMediaSrcPrivate* priv = m_src->priv;
721    GstFlowReturn ret = GST_FLOW_OK;
722    GstBuffer * buffer;
723
724    if (type.startsWith("video")) {
725        if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == TRUE) {
726            gst_element_no_more_pads(GST_ELEMENT(m_src));
727            priv->noMorePad = TRUE;
728        }
729        if (priv->noMorePad == FALSE && priv->sourceVideo.padAdded == FALSE) {
730            gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceVideo.srcpad);
731            priv->sourceVideo.padAdded = TRUE;
732        }
733        GST_OBJECT_LOCK(m_src);
734        buffer = WebCore::createGstBufferForData(data, length);
735        GST_OBJECT_UNLOCK(m_src);
736
737        ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceVideo.appsrc), buffer);
738    } else if (type.startsWith("audio")) {
739        if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == TRUE) {
740            gst_element_no_more_pads(GST_ELEMENT(m_src));
741            priv->noMorePad = TRUE;
742        }
743        if (priv->noMorePad == FALSE && priv->sourceAudio.padAdded == FALSE) {
744            gst_element_add_pad(GST_ELEMENT(m_src), priv->sourceAudio.srcpad);
745            priv->sourceAudio.padAdded = TRUE;
746        }
747        GST_OBJECT_LOCK(m_src);
748        buffer = WebCore::createGstBufferForData(data, length);
749        GST_OBJECT_UNLOCK(m_src);
750
751        ret = gst_app_src_push_buffer(GST_APP_SRC(priv->sourceAudio.appsrc), buffer);
752    }
753
754    if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
755        GST_ELEMENT_ERROR(m_src, CORE, FAILED, (0), (0));
756}
757
758void MediaSourceClientGstreamer::didFinishLoading(double)
759{
760    WebKitMediaSrcPrivate* priv = m_src->priv;
761
762    GST_DEBUG_OBJECT(m_src, "Have EOS");
763
764    GST_OBJECT_LOCK(m_src);
765    if (!priv->sourceVideo.seek.isActive()) {
766        GST_OBJECT_UNLOCK(m_src);
767        gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceVideo.appsrc));
768    } else
769        GST_OBJECT_UNLOCK(m_src);
770
771    GST_OBJECT_LOCK(m_src);
772    if (!priv->sourceAudio.seek.isActive()) {
773        GST_OBJECT_UNLOCK(m_src);
774        gst_app_src_end_of_stream(GST_APP_SRC(priv->sourceAudio.appsrc));
775    } else
776        GST_OBJECT_UNLOCK(m_src);
777}
778
779void MediaSourceClientGstreamer::didFail()
780{
781    gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceVideo.appsrc));
782    gst_app_src_end_of_stream(GST_APP_SRC(m_src->priv->sourceAudio.appsrc));
783}
784
785#endif // USE(GSTREAMER)
786
787