[GStreamer][MSE] WebKitMediaSourceGStreamer refactoring
[WebKit-https.git] / Source / WebCore / platform / graphics / gstreamer / mse / 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, 2015 Sebastian Dröge <sebastian@centricular.com>
6  *  Copyright (C) 2015, 2016 Metrological Group B.V.
7  *  Copyright (C) 2015, 2016 Igalia, S.L
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23
24 #include "config.h"
25 #include "WebKitMediaSourceGStreamer.h"
26
27 #include "PlaybackPipeline.h"
28
29 #if ENABLE(VIDEO) && ENABLE(MEDIA_SOURCE) && USE(GSTREAMER)
30
31 #include "AudioTrackPrivateGStreamer.h"
32 #include "GStreamerUtilities.h"
33 #include "MediaDescription.h"
34 #include "MediaPlayerPrivateGStreamerMSE.h"
35 #include "MediaSample.h"
36 #include "MediaSourceGStreamer.h"
37 #include "NotImplemented.h"
38 #include "SourceBufferPrivateGStreamer.h"
39 #include "TimeRanges.h"
40 #include "VideoTrackPrivateGStreamer.h"
41 #include "WebKitMediaSourceGStreamerPrivate.h"
42
43 #include <gst/app/app.h>
44 #include <gst/app/gstappsrc.h>
45 #include <gst/gst.h>
46 #include <gst/pbutils/missing-plugins.h>
47 #include <gst/pbutils/pbutils.h>
48 #include <gst/video/video.h>
49 #include <wtf/Condition.h>
50 #include <wtf/MainThread.h>
51 #include <wtf/glib/GMutexLocker.h>
52 #include <wtf/glib/GUniquePtr.h>
53 #include <wtf/text/CString.h>
54
55 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC,
56     GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY);
57
58 static void enabledAppsrcNeedData(GstAppSrc*, guint, gpointer);
59 static void enabledAppsrcEnoughData(GstAppSrc*, gpointer);
60 static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer);
61
62 static void disabledAppsrcNeedData(GstAppSrc*, guint, gpointer) { };
63 static void disabledAppsrcEnoughData(GstAppSrc*, gpointer) { };
64 static gboolean disabledAppsrcSeekData(GstAppSrc*, guint64, gpointer)
65 {
66     return FALSE;
67 };
68
69 GstAppSrcCallbacks enabledAppsrcCallbacks = {
70     enabledAppsrcNeedData,
71     enabledAppsrcEnoughData,
72     enabledAppsrcSeekData,
73     { 0 }
74 };
75
76 GstAppSrcCallbacks disabledAppsrcCallbacks = {
77     disabledAppsrcNeedData,
78     disabledAppsrcEnoughData,
79     disabledAppsrcSeekData,
80     { 0 }
81 };
82
83 static Stream* getStreamByAppsrc(WebKitMediaSrc*, GstElement*);
84
85 static void enabledAppsrcNeedData(GstAppSrc* appsrc, guint, gpointer userData)
86 {
87     WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
88     ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
89
90     GST_OBJECT_LOCK(webKitMediaSrc);
91     OnSeekDataAction appsrcSeekDataNextAction = webKitMediaSrc->priv->appsrcSeekDataNextAction;
92     Stream* appsrcStream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
93     bool allAppsrcNeedDataAfterSeek = false;
94
95     if (webKitMediaSrc->priv->appsrcSeekDataCount > 0) {
96         if (appsrcStream && !appsrcStream->appsrcNeedDataFlag) {
97             ++webKitMediaSrc->priv->appsrcNeedDataCount;
98             appsrcStream->appsrcNeedDataFlag = true;
99         }
100         int numAppsrcs = webKitMediaSrc->priv->streams.size();
101         if (webKitMediaSrc->priv->appsrcSeekDataCount == numAppsrcs && webKitMediaSrc->priv->appsrcNeedDataCount == numAppsrcs) {
102             GST_DEBUG("All needDatas completed");
103             allAppsrcNeedDataAfterSeek = true;
104             webKitMediaSrc->priv->appsrcSeekDataCount = 0;
105             webKitMediaSrc->priv->appsrcNeedDataCount = 0;
106             webKitMediaSrc->priv->appsrcSeekDataNextAction = Nothing;
107
108             for (Stream* stream : webKitMediaSrc->priv->streams)
109                 stream->appsrcNeedDataFlag = false;
110         }
111     }
112     GST_OBJECT_UNLOCK(webKitMediaSrc);
113
114     if (allAppsrcNeedDataAfterSeek) {
115         GST_DEBUG("All expected appsrcSeekData() and appsrcNeedData() calls performed. Running next action (%d)", static_cast<int>(appsrcSeekDataNextAction));
116
117         switch (appsrcSeekDataNextAction) {
118         case MediaSourceSeekToTime: {
119             GstStructure* structure = gst_structure_new_empty("seek-needs-data");
120             GstMessage* message = gst_message_new_application(GST_OBJECT(appsrc), structure);
121             gst_bus_post(webKitMediaSrc->priv->bus.get(), message);
122             GST_TRACE("seek-needs-data message posted to the bus");
123             break;
124         }
125         case Nothing:
126             break;
127         }
128     } else if (appsrcSeekDataNextAction == Nothing) {
129         LockHolder locker(webKitMediaSrc->priv->streamLock);
130
131         GST_OBJECT_LOCK(webKitMediaSrc);
132
133         // Search again for the Stream, just in case it was removed between the previous lock and this one.
134         appsrcStream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
135
136         if (appsrcStream && appsrcStream->type != WebCore::Invalid) {
137             GstStructure* structure = gst_structure_new("ready-for-more-samples", "appsrc-stream", G_TYPE_POINTER, appsrcStream, nullptr);
138             GstMessage* message = gst_message_new_application(GST_OBJECT(appsrc), structure);
139             gst_bus_post(webKitMediaSrc->priv->bus.get(), message);
140             GST_TRACE("ready-for-more-samples message posted to the bus");
141         }
142
143         GST_OBJECT_UNLOCK(webKitMediaSrc);
144     }
145 }
146
147 static void enabledAppsrcEnoughData(GstAppSrc *appsrc, gpointer userData)
148 {
149     // No need to lock on webKitMediaSrc, we're on the main thread and nobody is going to remove the stream in the meantime.
150     ASSERT(WTF::isMainThread());
151
152     WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
153     ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
154     Stream* stream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
155
156     // This callback might have been scheduled from a child thread before the stream was removed.
157     // Then, the removal code might have run, and later this callback.
158     // This check solves the race condition.
159     if (!stream || stream->type == WebCore::Invalid)
160         return;
161
162     stream->sourceBuffer->setReadyForMoreSamples(false);
163 }
164
165 static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer userData)
166 {
167     ASSERT(WTF::isMainThread());
168
169     WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
170
171     ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
172
173     GST_OBJECT_LOCK(webKitMediaSrc);
174     webKitMediaSrc->priv->appsrcSeekDataCount++;
175     GST_OBJECT_UNLOCK(webKitMediaSrc);
176
177     return TRUE;
178 }
179
180 static Stream* getStreamByAppsrc(WebKitMediaSrc* source, GstElement* appsrc)
181 {
182     for (Stream* stream : source->priv->streams) {
183         if (stream->appsrc == appsrc)
184             return stream;
185     }
186     return nullptr;
187 }
188
189 GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug);
190 #define GST_CAT_DEFAULT webkit_media_src_debug
191
192 #define webkit_media_src_parent_class parent_class
193 #define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element");
194 G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN,
195     G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit);
196     WEBKIT_MEDIA_SRC_CATEGORY_INIT);
197
198 guint webKitMediaSrcSignals[LAST_SIGNAL] = { 0 };
199
200 static void webkit_media_src_class_init(WebKitMediaSrcClass* klass)
201 {
202     GObjectClass* oklass = G_OBJECT_CLASS(klass);
203     GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
204
205     oklass->finalize = webKitMediaSrcFinalize;
206     oklass->set_property = webKitMediaSrcSetProperty;
207     oklass->get_property = webKitMediaSrcGetProperty;
208
209     gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate));
210
211     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>, Enrique Ocaña González <eocanha@igalia.com>");
212
213     // Allows setting the uri using the 'location' property, which is used for example by gst_element_make_from_uri().
214     g_object_class_install_property(oklass,
215         PROP_LOCATION,
216         g_param_spec_string("location", "location", "Location to read from", nullptr,
217         GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
218     g_object_class_install_property(oklass,
219         PROP_N_AUDIO,
220         g_param_spec_int("n-audio", "Number Audio", "Total number of audio streams",
221         0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
222     g_object_class_install_property(oklass,
223         PROP_N_VIDEO,
224         g_param_spec_int("n-video", "Number Video", "Total number of video streams",
225         0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
226     g_object_class_install_property(oklass,
227         PROP_N_TEXT,
228         g_param_spec_int("n-text", "Number Text", "Total number of text streams",
229         0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
230
231     webKitMediaSrcSignals[SIGNAL_VIDEO_CHANGED] =
232         g_signal_new("video-changed", G_TYPE_FROM_CLASS(oklass),
233         G_SIGNAL_RUN_LAST,
234         G_STRUCT_OFFSET(WebKitMediaSrcClass, videoChanged), nullptr, nullptr,
235         g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
236     webKitMediaSrcSignals[SIGNAL_AUDIO_CHANGED] =
237         g_signal_new("audio-changed", G_TYPE_FROM_CLASS(oklass),
238         G_SIGNAL_RUN_LAST,
239         G_STRUCT_OFFSET(WebKitMediaSrcClass, audioChanged), nullptr, nullptr,
240         g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
241     webKitMediaSrcSignals[SIGNAL_TEXT_CHANGED] =
242         g_signal_new("text-changed", G_TYPE_FROM_CLASS(oklass),
243         G_SIGNAL_RUN_LAST,
244         G_STRUCT_OFFSET(WebKitMediaSrcClass, textChanged), nullptr, nullptr,
245         g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
246
247     eklass->change_state = webKitMediaSrcChangeState;
248
249     g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate));
250 }
251
252 static void webkit_media_src_init(WebKitMediaSrc* source)
253 {
254     source->priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(source);
255     new (source->priv) WebKitMediaSrcPrivate();
256     source->priv->seekTime = MediaTime::invalidTime();
257     source->priv->appsrcSeekDataCount = 0;
258     source->priv->appsrcNeedDataCount = 0;
259     source->priv->appsrcSeekDataNextAction = Nothing;
260
261     // No need to reset Stream.appsrcNeedDataFlag because there are no Streams at this point yet.
262 }
263
264 void webKitMediaSrcFinalize(GObject* object)
265 {
266     ASSERT(WTF::isMainThread());
267
268     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
269     WebKitMediaSrcPrivate* priv = source->priv;
270
271     Deque<Stream*> oldStreams;
272     source->priv->streams.swap(oldStreams);
273
274     for (Stream* stream : oldStreams)
275         webKitMediaSrcFreeStream(source, stream);
276
277     priv->seekTime = MediaTime::invalidTime();
278
279     if (priv->mediaPlayerPrivate)
280         webKitMediaSrcSetMediaPlayerPrivate(source, nullptr);
281
282     // We used a placement new for construction, the destructor won't be called automatically.
283     priv->~_WebKitMediaSrcPrivate();
284
285     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
286 }
287
288 void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
289 {
290     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
291
292     switch (propId) {
293     case PROP_LOCATION:
294         gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(source), g_value_get_string(value), nullptr);
295         break;
296     default:
297         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
298         break;
299     }
300 }
301
302 void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
303 {
304     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
305     WebKitMediaSrcPrivate* priv = source->priv;
306
307     GST_OBJECT_LOCK(source);
308     switch (propId) {
309     case PROP_LOCATION:
310         g_value_set_string(value, priv->location.get());
311         break;
312     case PROP_N_AUDIO:
313         g_value_set_int(value, priv->numberOfAudioStreams);
314         break;
315     case PROP_N_VIDEO:
316         g_value_set_int(value, priv->numberOfVideoStreams);
317         break;
318     case PROP_N_TEXT:
319         g_value_set_int(value, priv->numberOfTextStreams);
320         break;
321     default:
322         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
323         break;
324     }
325     GST_OBJECT_UNLOCK(source);
326 }
327
328 void webKitMediaSrcDoAsyncStart(WebKitMediaSrc* source)
329 {
330     source->priv->asyncStart = true;
331     GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(source),
332         gst_message_new_async_start(GST_OBJECT(source)));
333 }
334
335 void webKitMediaSrcDoAsyncDone(WebKitMediaSrc* source)
336 {
337     WebKitMediaSrcPrivate* priv = source->priv;
338     if (priv->asyncStart) {
339         GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(source),
340             gst_message_new_async_done(GST_OBJECT(source), GST_CLOCK_TIME_NONE));
341         priv->asyncStart = false;
342     }
343 }
344
345 GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition)
346 {
347     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(element);
348     WebKitMediaSrcPrivate* priv = source->priv;
349
350     switch (transition) {
351     case GST_STATE_CHANGE_READY_TO_PAUSED:
352         priv->allTracksConfigured = false;
353         webKitMediaSrcDoAsyncStart(source);
354         break;
355     default:
356         break;
357     }
358
359     GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
360     if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE)) {
361         GST_WARNING_OBJECT(source, "State change failed");
362         webKitMediaSrcDoAsyncDone(source);
363         return result;
364     }
365
366     switch (transition) {
367     case GST_STATE_CHANGE_READY_TO_PAUSED:
368         result = GST_STATE_CHANGE_ASYNC;
369         break;
370     case GST_STATE_CHANGE_PAUSED_TO_READY:
371         webKitMediaSrcDoAsyncDone(source);
372         priv->allTracksConfigured = false;
373         break;
374     default:
375         break;
376     }
377
378     return result;
379 }
380
381 gint64 webKitMediaSrcGetSize(WebKitMediaSrc* webKitMediaSrc)
382 {
383     gint64 duration = 0;
384     for (Stream* stream : webKitMediaSrc->priv->streams)
385         duration = std::max<gint64>(duration, gst_app_src_get_size(GST_APP_SRC(stream->appsrc)));
386     return duration;
387 }
388
389 gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
390 {
391     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent));
392     gboolean result = FALSE;
393
394     switch (GST_QUERY_TYPE(query)) {
395     case GST_QUERY_DURATION: {
396         GstFormat format;
397         gst_query_parse_duration(query, &format, nullptr);
398
399         GST_DEBUG_OBJECT(source, "duration query in format %s", gst_format_get_name(format));
400         GST_OBJECT_LOCK(source);
401         switch (format) {
402         case GST_FORMAT_TIME: {
403             if (source->priv && source->priv->mediaPlayerPrivate) {
404                 float duration = source->priv->mediaPlayerPrivate->durationMediaTime().toFloat();
405                 if (duration > 0) {
406                     gst_query_set_duration(query, format, WebCore::toGstClockTime(duration));
407                     GST_DEBUG_OBJECT(source, "Answering: duration=%" GST_TIME_FORMAT, GST_TIME_ARGS(WebCore::toGstClockTime(duration)));
408                     result = TRUE;
409                 }
410             }
411             break;
412         }
413         case GST_FORMAT_BYTES: {
414             if (source->priv) {
415                 gint64 duration = webKitMediaSrcGetSize(source);
416                 if (duration) {
417                     gst_query_set_duration(query, format, duration);
418                     GST_DEBUG_OBJECT(source, "size: %" G_GINT64_FORMAT, duration);
419                     result = TRUE;
420                 }
421             }
422             break;
423         }
424         default:
425             break;
426         }
427
428         GST_OBJECT_UNLOCK(source);
429         break;
430     }
431     case GST_QUERY_URI:
432         if (source) {
433             GST_OBJECT_LOCK(source);
434             if (source->priv)
435                 gst_query_set_uri(query, source->priv->location.get());
436             GST_OBJECT_UNLOCK(source);
437         }
438         result = TRUE;
439         break;
440     default: {
441         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
442         // Forward the query to the proxy target pad.
443         if (target)
444             result = gst_pad_query(target.get(), query);
445         break;
446     }
447     }
448
449     return result;
450 }
451
452 void webKitMediaSrcUpdatePresentationSize(GstCaps* caps, Stream* stream)
453 {
454     GstStructure* structure = gst_caps_get_structure(caps, 0);
455     const gchar* structureName = gst_structure_get_name(structure);
456     GstVideoInfo info;
457
458     GST_OBJECT_LOCK(stream->parent);
459     if (g_str_has_prefix(structureName, "video/") && gst_video_info_from_caps(&info, caps)) {
460         float width, height;
461
462         // FIXME: Correct?.
463         width = info.width;
464         height = info.height * ((float) info.par_d / (float) info.par_n);
465         stream->presentationSize = WebCore::FloatSize(width, height);
466     } else
467         stream->presentationSize = WebCore::FloatSize();
468
469     gst_caps_ref(caps);
470     stream->caps = adoptGRef(caps);
471     GST_OBJECT_UNLOCK(stream->parent);
472 }
473
474 void webKitMediaSrcLinkStreamToSrcPad(GstPad* sourcePad, Stream* stream)
475 {
476     unsigned padId = static_cast<unsigned>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sourcePad), "padId")));
477     GST_DEBUG_OBJECT(stream->parent, "linking stream to src pad (id: %u)", padId);
478
479     GUniquePtr<gchar> padName(g_strdup_printf("src_%u", padId));
480     GstPad* ghostpad = WebCore::webkitGstGhostPadFromStaticTemplate(&srcTemplate, padName.get(), sourcePad);
481
482     gst_pad_set_query_function(ghostpad, webKitMediaSrcQueryWithParent);
483
484     gst_pad_set_active(ghostpad, TRUE);
485     gst_element_add_pad(GST_ELEMENT(stream->parent), ghostpad);
486
487     if (stream->decodebinSinkPad) {
488         GST_DEBUG_OBJECT(stream->parent, "A decodebin was previously used for this source, trying to reuse it.");
489         // FIXME: error checking here. Not sure what to do if linking
490         // fails though, because decodebin is out of this source
491         // element's scope, in theory.
492         gst_pad_link(ghostpad, stream->decodebinSinkPad);
493     }
494 }
495
496 void webKitMediaSrcLinkParser(GstPad* sourcePad, GstCaps* caps, Stream* stream)
497 {
498     ASSERT(caps && stream->parent);
499     if (!caps || !stream->parent) {
500         GST_ERROR("Unable to link parser");
501         return;
502     }
503
504     webKitMediaSrcUpdatePresentationSize(caps, stream);
505
506     // FIXME: drop webKitMediaSrcLinkStreamToSrcPad() and move its code here.
507     if (!gst_pad_is_linked(sourcePad)) {
508         GST_DEBUG_OBJECT(stream->parent, "pad not linked yet");
509         webKitMediaSrcLinkStreamToSrcPad(sourcePad, stream);
510     }
511
512     webKitMediaSrcCheckAllTracksConfigured(stream->parent);
513 }
514
515 void webKitMediaSrcFreeStream(WebKitMediaSrc* source, Stream* stream)
516 {
517     if (stream->appsrc) {
518         // Don't trigger callbacks from this appsrc to avoid using the stream anymore.
519         gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &disabledAppsrcCallbacks, nullptr, nullptr);
520         gst_app_src_end_of_stream(GST_APP_SRC(stream->appsrc));
521     }
522
523     if (stream->type != WebCore::Invalid) {
524         GST_DEBUG("Freeing track-related info on stream %p", stream);
525
526         LockHolder locker(source->priv->streamLock);
527
528         if (stream->caps)
529             stream->caps = nullptr;
530
531         if (stream->audioTrack)
532             stream->audioTrack = nullptr;
533         if (stream->videoTrack)
534             stream->videoTrack = nullptr;
535
536         int signal = -1;
537         switch (stream->type) {
538         case WebCore::Audio:
539             signal = SIGNAL_AUDIO_CHANGED;
540             break;
541         case WebCore::Video:
542             signal = SIGNAL_VIDEO_CHANGED;
543             break;
544         case WebCore::Text:
545             signal = SIGNAL_TEXT_CHANGED;
546             break;
547         default:
548             break;
549         }
550         stream->type = WebCore::Invalid;
551
552         if (signal != -1)
553             g_signal_emit(G_OBJECT(source), webKitMediaSrcSignals[signal], 0, nullptr);
554
555         source->priv->streamCondition.notifyOne();
556     }
557
558     GST_DEBUG("Releasing stream: %p", stream);
559     delete stream;
560 }
561
562 void webKitMediaSrcCheckAllTracksConfigured(WebKitMediaSrc* webKitMediaSrc)
563 {
564     bool allTracksConfigured = false;
565
566     GST_OBJECT_LOCK(webKitMediaSrc);
567     if (!webKitMediaSrc->priv->allTracksConfigured) {
568         allTracksConfigured = true;
569         for (Stream* stream : webKitMediaSrc->priv->streams) {
570             if (stream->type == WebCore::Invalid) {
571                 allTracksConfigured = false;
572                 break;
573             }
574         }
575         if (allTracksConfigured)
576             webKitMediaSrc->priv->allTracksConfigured = true;
577     }
578     GST_OBJECT_UNLOCK(webKitMediaSrc);
579
580     if (allTracksConfigured) {
581         GST_DEBUG("All tracks attached. Completing async state change operation.");
582         gst_element_no_more_pads(GST_ELEMENT(webKitMediaSrc));
583         webKitMediaSrcDoAsyncDone(webKitMediaSrc);
584     }
585 }
586
587 // Uri handler interface.
588 GstURIType webKitMediaSrcUriGetType(GType)
589 {
590     return GST_URI_SRC;
591 }
592
593 const gchar* const* webKitMediaSrcGetProtocols(GType)
594 {
595     static const char* protocols[] = {"mediasourceblob", 0 };
596     return protocols;
597 }
598
599 gchar* webKitMediaSrcGetUri(GstURIHandler* handler)
600 {
601     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(handler);
602     gchar* result;
603
604     GST_OBJECT_LOCK(source);
605     result = g_strdup(source->priv->location.get());
606     GST_OBJECT_UNLOCK(source);
607     return result;
608 }
609
610 gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError**)
611 {
612     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(handler);
613
614     if (GST_STATE(source) >= GST_STATE_PAUSED) {
615         GST_ERROR_OBJECT(source, "URI can only be set in states < PAUSED");
616         return FALSE;
617     }
618
619     GST_OBJECT_LOCK(source);
620     WebKitMediaSrcPrivate* priv = source->priv;
621     priv->location = nullptr;
622     if (!uri) {
623         GST_OBJECT_UNLOCK(source);
624         return TRUE;
625     }
626
627     WebCore::URL url(WebCore::URL(), uri);
628
629     priv->location = GUniquePtr<gchar>(g_strdup(url.string().utf8().data()));
630     GST_OBJECT_UNLOCK(source);
631     return TRUE;
632 }
633
634 void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer)
635 {
636     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
637
638     iface->get_type = webKitMediaSrcUriGetType;
639     iface->get_protocols = webKitMediaSrcGetProtocols;
640     iface->get_uri = webKitMediaSrcGetUri;
641     iface->set_uri = webKitMediaSrcSetUri;
642 }
643
644 static void seekNeedsDataMainThread(WebKitMediaSrc* source)
645 {
646     GST_DEBUG("Buffering needed before seek");
647
648     ASSERT(WTF::isMainThread());
649
650     GST_OBJECT_LOCK(source);
651     MediaTime seekTime = source->priv->seekTime;
652     WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate = source->priv->mediaPlayerPrivate;
653
654     if (!mediaPlayerPrivate) {
655         GST_OBJECT_UNLOCK(source);
656         return;
657     }
658
659     for (Stream* stream : source->priv->streams) {
660         if (stream->type != WebCore::Invalid)
661             stream->sourceBuffer->setReadyForMoreSamples(true);
662     }
663     GST_OBJECT_UNLOCK(source);
664     mediaPlayerPrivate->notifySeekNeedsDataForTime(seekTime);
665 }
666
667 static void notifyReadyForMoreSamplesMainThread(WebKitMediaSrc* source, Stream* appsrcStream)
668 {
669     GST_OBJECT_LOCK(source);
670
671     auto it = std::find(source->priv->streams.begin(), source->priv->streams.end(), appsrcStream);
672     if (it == source->priv->streams.end()) {
673         GST_OBJECT_UNLOCK(source);
674         return;
675     }
676
677     WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate = source->priv->mediaPlayerPrivate;
678     if (mediaPlayerPrivate && !mediaPlayerPrivate->seeking())
679         appsrcStream->sourceBuffer->notifyReadyForMoreSamples();
680
681     GST_OBJECT_UNLOCK(source);
682 }
683
684 static void applicationMessageCallback(GstBus*, GstMessage* message, WebKitMediaSrc* source)
685 {
686     ASSERT(WTF::isMainThread());
687     ASSERT(GST_MESSAGE_TYPE(message) == GST_MESSAGE_APPLICATION);
688
689     const GstStructure* structure = gst_message_get_structure(message);
690
691     if (gst_structure_has_name(structure, "seek-needs-data")) {
692         seekNeedsDataMainThread(source);
693         return;
694     }
695
696     if (gst_structure_has_name(structure, "ready-for-more-samples")) {
697         Stream* appsrcStream = nullptr;
698         gst_structure_get(structure, "appsrc-stream", G_TYPE_POINTER, &appsrcStream, nullptr);
699         ASSERT(appsrcStream);
700
701         notifyReadyForMoreSamplesMainThread(source, appsrcStream);
702         return;
703     }
704
705     ASSERT_NOT_REACHED();
706 }
707
708 void webKitMediaSrcSetMediaPlayerPrivate(WebKitMediaSrc* source, WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate)
709 {
710     GST_OBJECT_LOCK(source);
711     if (source->priv->mediaPlayerPrivate && source->priv->mediaPlayerPrivate != mediaPlayerPrivate && source->priv->bus)
712         g_signal_handlers_disconnect_by_func(source->priv->bus.get(), gpointer(applicationMessageCallback), source);
713
714     // Set to nullptr on MediaPlayerPrivateGStreamer destruction, never a dangling pointer.
715     source->priv->mediaPlayerPrivate = mediaPlayerPrivate;
716     source->priv->bus = mediaPlayerPrivate ? adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(mediaPlayerPrivate->pipeline()))) : nullptr;
717     if (source->priv->bus) {
718         // MediaPlayerPrivateGStreamer has called gst_bus_add_signal_watch() at this point, so we can subscribe.
719         g_signal_connect(source->priv->bus.get(), "message::application", G_CALLBACK(applicationMessageCallback), source);
720     }
721     GST_OBJECT_UNLOCK(source);
722 }
723
724 void webKitMediaSrcSetReadyForSamples(WebKitMediaSrc* source, bool isReady)
725 {
726     if (source) {
727         GST_OBJECT_LOCK(source);
728         for (Stream* stream : source->priv->streams)
729             stream->sourceBuffer->setReadyForMoreSamples(isReady);
730         GST_OBJECT_UNLOCK(source);
731     }
732 }
733
734 void webKitMediaSrcPrepareSeek(WebKitMediaSrc* source, const MediaTime& time)
735 {
736     GST_OBJECT_LOCK(source);
737     source->priv->seekTime = time;
738     source->priv->appsrcSeekDataCount = 0;
739     source->priv->appsrcNeedDataCount = 0;
740
741     for (Stream* stream : source->priv->streams) {
742         stream->appsrcNeedDataFlag = false;
743         // Don't allow samples away from the seekTime to be enqueued.
744         stream->lastEnqueuedTime = time;
745     }
746
747     // The pending action will be performed in enabledAppsrcSeekData().
748     source->priv->appsrcSeekDataNextAction = MediaSourceSeekToTime;
749     GST_OBJECT_UNLOCK(source);
750 }
751
752 namespace WTF {
753 template <> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc* ptr)
754 {
755     ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr)));
756     return GRefPtr<WebKitMediaSrc>(ptr, GRefPtrAdopt);
757 }
758
759 template <> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
760 {
761     if (ptr)
762         gst_object_ref_sink(GST_OBJECT(ptr));
763
764     return ptr;
765 }
766
767 template <> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
768 {
769     if (ptr)
770         gst_object_unref(ptr);
771 }
772 };
773
774 #endif // USE(GSTREAMER)
775