31250ea3172984076308a97433ba322ba685f916
[WebKit-https.git] / Source / WebCore / platform / graphics / gstreamer / WebKitMediaSourceGStreamer.cpp
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  *  Copyright (C) 2014 Sebastian Dröge <sebastian@centricular.com>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "config.h"
23 #include "WebKitMediaSourceGStreamer.h"
24
25 #if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
26
27 #include "GStreamerUtilities.h"
28 #include "NotImplemented.h"
29 #include "TimeRanges.h"
30
31 #include <gst/app/gstappsrc.h>
32 #include <gst/gst.h>
33 #include <gst/pbutils/missing-plugins.h>
34 #include <wtf/glib/GUniquePtr.h>
35 #include <wtf/text/CString.h>
36
37 typedef struct _Source Source;
38 struct _Source {
39     GstElement* src;
40     // Just for identification
41     WebCore::SourceBufferPrivate* sourceBuffer;
42 };
43
44 struct _WebKitMediaSrcPrivate {
45     GList* sources;
46     gchar* location;
47     GstClockTime duration;
48     bool haveAppsrc;
49     bool asyncStart;
50     bool noMorePads;
51 };
52
53 enum {
54     Prop0,
55     PropLocation
56 };
57
58 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC,
59     GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY);
60
61 #define WEBKIT_MEDIA_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_SRC, WebKitMediaSrcPrivate))
62
63 GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug);
64 #define GST_CAT_DEFAULT webkit_media_src_debug
65
66 static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
67 static void webKitMediaSrcFinalize(GObject*);
68 static void webKitMediaSrcSetProperty(GObject*, guint propertyId, const GValue*, GParamSpec*);
69 static void webKitMediaSrcGetProperty(GObject*, guint propertyId, GValue*, GParamSpec*);
70 static GstStateChangeReturn webKitMediaSrcChangeState(GstElement*, GstStateChange);
71 static gboolean webKitMediaSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
72
73 #define webkit_media_src_parent_class parent_class
74 // We split this out into another macro to avoid a check-webkit-style error.
75 #define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element");
76 G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN,
77     G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit);
78     WEBKIT_MEDIA_SRC_CATEGORY_INIT);
79
80 static void webkit_media_src_class_init(WebKitMediaSrcClass* klass)
81 {
82     GObjectClass* oklass = G_OBJECT_CLASS(klass);
83     GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
84
85     oklass->finalize = webKitMediaSrcFinalize;
86     oklass->set_property = webKitMediaSrcSetProperty;
87     oklass->get_property = webKitMediaSrcGetProperty;
88
89     gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate));
90
91     gst_element_class_set_static_metadata(eklass, "WebKit Media source element", "Source", "Handles Blob uris", "Stephane Jadaud <sjadaud@sii.fr>, Sebastian Dröge <sebastian@centricular.com>");
92
93     /* Allows setting the uri using the 'location' property, which is used
94      * for example by gst_element_make_from_uri() */
95     g_object_class_install_property(oklass,
96         PropLocation,
97         g_param_spec_string("location", "location", "Location to read from", 0,
98         (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
99
100     eklass->change_state = webKitMediaSrcChangeState;
101
102     g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate));
103 }
104
105 static void webkit_media_src_init(WebKitMediaSrc* src)
106 {
107     src->priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(src);
108 }
109
110 static void webKitMediaSrcFinalize(GObject* object)
111 {
112     WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
113     WebKitMediaSrcPrivate* priv = src->priv;
114
115     // TODO: Free sources
116     g_free(priv->location);
117
118     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
119 }
120
121 static void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
122 {
123     WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
124
125     switch (propId) {
126     case PropLocation:
127         gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0);
128         break;
129     default:
130         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
131         break;
132     }
133 }
134
135 static void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
136 {
137     WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(object);
138     WebKitMediaSrcPrivate* priv = src->priv;
139
140     GST_OBJECT_LOCK(src);
141     switch (propId) {
142     case PropLocation:
143         g_value_set_string(value, priv->location);
144         break;
145     default:
146         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
147         break;
148     }
149     GST_OBJECT_UNLOCK(src);
150 }
151
152 static void webKitMediaSrcDoAsyncStart(WebKitMediaSrc* src)
153 {
154     WebKitMediaSrcPrivate* priv = src->priv;
155     priv->asyncStart = true;
156     GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(src),
157         gst_message_new_async_start(GST_OBJECT(src)));
158 }
159
160 static void webKitMediaSrcDoAsyncDone(WebKitMediaSrc* src)
161 {
162     WebKitMediaSrcPrivate* priv = src->priv;
163     if (priv->asyncStart) {
164         GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(src),
165             gst_message_new_async_done(GST_OBJECT(src), GST_CLOCK_TIME_NONE));
166         priv->asyncStart = false;
167     }
168 }
169
170 static GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition)
171 {
172     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
173     WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(element);
174     WebKitMediaSrcPrivate* priv = src->priv;
175
176     switch (transition) {
177     case GST_STATE_CHANGE_READY_TO_PAUSED:
178         priv->noMorePads = false;
179         webKitMediaSrcDoAsyncStart(src);
180         break;
181     default:
182         break;
183     }
184
185     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
186     if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
187         GST_DEBUG_OBJECT(src, "State change failed");
188         webKitMediaSrcDoAsyncDone(src);
189         return ret;
190     }
191
192     switch (transition) {
193     case GST_STATE_CHANGE_READY_TO_PAUSED:
194         ret = GST_STATE_CHANGE_ASYNC;
195         break;
196     case GST_STATE_CHANGE_PAUSED_TO_READY:
197         webKitMediaSrcDoAsyncDone(src);
198         priv->noMorePads = false;
199         break;
200     default:
201         break;
202     }
203
204     return ret;
205 }
206
207 static gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
208 {
209     WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent));
210     gboolean result = FALSE;
211
212     switch (GST_QUERY_TYPE(query)) {
213     case GST_QUERY_DURATION: {
214         GstFormat format;
215         gst_query_parse_duration(query, &format, NULL);
216
217         GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format));
218         GST_OBJECT_LOCK(src);
219         if ((format == GST_FORMAT_TIME) && (src->priv->duration > 0)) {
220             gst_query_set_duration(query, format, src->priv->duration);
221             result = TRUE;
222         }
223         GST_OBJECT_UNLOCK(src);
224         break;
225     }
226     case GST_QUERY_URI: {
227         GST_OBJECT_LOCK(src);
228         gst_query_set_uri(query, src->priv->location);
229         GST_OBJECT_UNLOCK(src);
230         result = TRUE;
231         break;
232     }
233     default: {
234         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
235         // Forward the query to the proxy target pad.
236         if (target)
237             result = gst_pad_query(target.get(), query);
238         break;
239     }
240     }
241
242     return result;
243 }
244
245 // uri handler interface
246 static GstURIType webKitMediaSrcUriGetType(GType)
247 {
248     return GST_URI_SRC;
249 }
250
251 const gchar* const* webKitMediaSrcGetProtocols(GType)
252 {
253     static const char* protocols[] = {"mediasourceblob", 0 };
254     return protocols;
255 }
256
257 static gchar* webKitMediaSrcGetUri(GstURIHandler* handler)
258 {
259     WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler);
260     gchar* ret;
261
262     GST_OBJECT_LOCK(src);
263     ret = g_strdup(src->priv->location);
264     GST_OBJECT_UNLOCK(src);
265     return ret;
266 }
267
268 static gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError**)
269 {
270     WebKitMediaSrc* src = WEBKIT_MEDIA_SRC(handler);
271     WebKitMediaSrcPrivate* priv = src->priv;
272
273     if (GST_STATE(src) >= GST_STATE_PAUSED) {
274         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
275         return FALSE;
276     }
277
278     GST_OBJECT_LOCK(src);
279     g_free(priv->location);
280     priv->location = 0;
281     if (!uri) {
282         GST_OBJECT_UNLOCK(src);
283         return TRUE;
284     }
285
286     WebCore::URL url(WebCore::URL(), uri);
287
288     priv->location = g_strdup(url.string().utf8().data());
289     GST_OBJECT_UNLOCK(src);
290     return TRUE;
291 }
292 static void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer)
293 {
294     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
295
296     iface->get_type = webKitMediaSrcUriGetType;
297     iface->get_protocols = webKitMediaSrcGetProtocols;
298     iface->get_uri = webKitMediaSrcGetUri;
299     iface->set_uri = webKitMediaSrcSetUri;
300 }
301
302 namespace WebCore {
303 MediaSourceClientGStreamer::MediaSourceClientGStreamer(WebKitMediaSrc* src)
304     : m_src(adoptGRef(static_cast<WebKitMediaSrc*>(gst_object_ref(src))))
305 {
306 }
307
308 MediaSourceClientGStreamer::~MediaSourceClientGStreamer()
309 {
310 }
311
312 MediaSourcePrivate::AddStatus MediaSourceClientGStreamer::addSourceBuffer(PassRefPtr<SourceBufferPrivate> sourceBufferPrivate, const ContentType&)
313 {
314     WebKitMediaSrcPrivate* priv = m_src->priv;
315
316     if (priv->noMorePads) {
317         GST_ERROR_OBJECT(m_src.get(), "Adding new source buffers after first data not supported yet");
318         return MediaSourcePrivate::NotSupported;
319     }
320
321     GST_DEBUG_OBJECT(m_src.get(), "State %d", static_cast<int>(GST_STATE(m_src.get())));
322
323     GST_OBJECT_LOCK(m_src.get());
324
325     Source* source = g_new0(Source, 1);
326     guint numberOfSources = g_list_length(priv->sources);
327     GUniquePtr<gchar> srcName(g_strdup_printf("src%u", numberOfSources));
328
329     source->src = gst_element_factory_make("appsrc", srcName.get());
330     source->sourceBuffer = sourceBufferPrivate.get();
331
332     GUniquePtr<gchar> padName(g_strdup_printf("src_%u", numberOfSources));
333     priv->sources = g_list_prepend(priv->sources, source);
334     GST_OBJECT_UNLOCK(m_src.get());
335
336     priv->haveAppsrc = source->src;
337
338     gst_bin_add(GST_BIN(m_src.get()), source->src);
339     GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(source->src, "src"));
340     GstPad* ghostPad = gst_ghost_pad_new_from_template(padName.get(), pad.get(), gst_static_pad_template_get(&srcTemplate));
341     gst_pad_set_query_function(ghostPad, webKitMediaSrcQueryWithParent);
342     gst_pad_set_active(ghostPad, TRUE);
343     gst_element_add_pad(GST_ELEMENT(m_src.get()), ghostPad);
344
345     gst_element_sync_state_with_parent(source->src);
346
347     return MediaSourcePrivate::Ok;
348 }
349
350 void MediaSourceClientGStreamer::durationChanged(const MediaTime& duration)
351 {
352     WebKitMediaSrcPrivate* priv = m_src->priv;
353     GstClockTime gstDuration = gst_util_uint64_scale(duration.timeValue(), GST_SECOND, duration.timeScale());
354
355     GST_DEBUG_OBJECT(m_src.get(), "Received duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(gstDuration));
356
357     GST_OBJECT_LOCK(m_src.get());
358     priv->duration = gstDuration;
359     GST_OBJECT_UNLOCK(m_src.get());
360     gst_element_post_message(GST_ELEMENT(m_src.get()), gst_message_new_duration_changed(GST_OBJECT(m_src.get())));
361 }
362
363 SourceBufferPrivateClient::AppendResult MediaSourceClientGStreamer::append(PassRefPtr<SourceBufferPrivate> sourceBufferPrivate, const unsigned char* data, unsigned length)
364 {
365     WebKitMediaSrcPrivate* priv = m_src->priv;
366     GstFlowReturn ret = GST_FLOW_OK;
367     GstBuffer* buffer;
368     Source* source = 0;
369
370     if (!priv->noMorePads) {
371         priv->noMorePads = true;
372         gst_element_no_more_pads(GST_ELEMENT(m_src.get()));
373         webKitMediaSrcDoAsyncDone(m_src.get());
374     }
375
376     for (GList* iter = priv->sources; iter; iter = iter->next) {
377         Source* tmp = static_cast<Source*>(iter->data);
378         if (tmp->sourceBuffer == sourceBufferPrivate.get()) {
379             source = tmp;
380             break;
381         }
382     }
383
384     if (!source || !source->src)
385         return SourceBufferPrivateClient::ReadStreamFailed;
386
387     buffer = gst_buffer_new_and_alloc(length);
388     gst_buffer_fill(buffer, 0, data, length);
389
390     ret = gst_app_src_push_buffer(GST_APP_SRC(source->src), buffer);
391     GST_DEBUG_OBJECT(m_src.get(), "push buffer %d\n", static_cast<int>(ret));
392
393     if (ret == GST_FLOW_OK)
394         return SourceBufferPrivateClient::AppendSucceeded;
395
396     return SourceBufferPrivateClient::ReadStreamFailed;
397 }
398
399 void MediaSourceClientGStreamer::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus)
400 {
401     WebKitMediaSrcPrivate* priv = m_src->priv;
402
403     GST_DEBUG_OBJECT(m_src.get(), "Have EOS");
404
405     if (!priv->noMorePads) {
406         priv->noMorePads = true;
407         gst_element_no_more_pads(GST_ELEMENT(m_src.get()));
408         webKitMediaSrcDoAsyncDone(m_src.get());
409     }
410
411     for (GList* iter = priv->sources; iter; iter = iter->next) {
412         Source* source = static_cast<Source*>(iter->data);
413         if (source->src)
414             gst_app_src_end_of_stream(GST_APP_SRC(source->src));
415     }
416 }
417
418 void MediaSourceClientGStreamer::removedFromMediaSource(PassRefPtr<SourceBufferPrivate> sourceBufferPrivate)
419 {
420     WebKitMediaSrcPrivate* priv = m_src->priv;
421     Source* source = 0;
422
423     for (GList* iter = priv->sources; iter; iter = iter->next) {
424         Source* tmp = static_cast<Source*>(iter->data);
425         if (tmp->sourceBuffer == sourceBufferPrivate.get()) {
426             source = tmp;
427             break;
428         }
429     }
430
431     ASSERT(source && source->src);
432
433     gst_app_src_end_of_stream(GST_APP_SRC(source->src));
434 }
435
436 };
437
438 namespace WTF {
439 template <> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc* ptr)
440 {
441     ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr)));
442     return GRefPtr<WebKitMediaSrc>(ptr, GRefPtrAdopt);
443 }
444
445 template <> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
446 {
447     if (ptr)
448         gst_object_ref_sink(GST_OBJECT(ptr));
449
450     return ptr;
451 }
452
453 template <> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
454 {
455     if (ptr)
456         gst_object_unref(ptr);
457 }
458 };
459
460 #endif // USE(GSTREAMER)
461