Move URL from WebCore to WTF
[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 "GStreamerCommon.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/pbutils/pbutils.h>
44 #include <gst/video/video.h>
45 #include <wtf/Condition.h>
46 #include <wtf/MainThread.h>
47 #include <wtf/RefPtr.h>
48 #include <wtf/text/CString.h>
49
50 GST_DEBUG_CATEGORY_STATIC(webkit_media_src_debug);
51 #define GST_CAT_DEFAULT webkit_media_src_debug
52
53 #define webkit_media_src_parent_class parent_class
54 #define WEBKIT_MEDIA_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_media_src_debug, "webkitmediasrc", 0, "websrc element");
55
56 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC,
57     GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY);
58
59 static void enabledAppsrcNeedData(GstAppSrc*, guint, gpointer);
60 static void enabledAppsrcEnoughData(GstAppSrc*, gpointer);
61 static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer);
62
63 static void disabledAppsrcNeedData(GstAppSrc*, guint, gpointer) { };
64 static void disabledAppsrcEnoughData(GstAppSrc*, gpointer) { };
65 static gboolean disabledAppsrcSeekData(GstAppSrc*, guint64, gpointer)
66 {
67     return FALSE;
68 };
69
70 GstAppSrcCallbacks enabledAppsrcCallbacks = {
71     enabledAppsrcNeedData,
72     enabledAppsrcEnoughData,
73     enabledAppsrcSeekData,
74     { 0 }
75 };
76
77 GstAppSrcCallbacks disabledAppsrcCallbacks = {
78     disabledAppsrcNeedData,
79     disabledAppsrcEnoughData,
80     disabledAppsrcSeekData,
81     { 0 }
82 };
83
84 static Stream* getStreamByAppsrc(WebKitMediaSrc*, GstElement*);
85 static void seekNeedsDataMainThread(WebKitMediaSrc*);
86 static void notifyReadyForMoreSamplesMainThread(WebKitMediaSrc*, Stream*);
87
88 static void enabledAppsrcNeedData(GstAppSrc* appsrc, guint, gpointer userData)
89 {
90     WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
91     ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
92
93     GST_OBJECT_LOCK(webKitMediaSrc);
94     OnSeekDataAction appsrcSeekDataNextAction = webKitMediaSrc->priv->appsrcSeekDataNextAction;
95     Stream* appsrcStream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
96     bool allAppsrcNeedDataAfterSeek = false;
97
98     if (webKitMediaSrc->priv->appsrcSeekDataCount > 0) {
99         if (appsrcStream && !appsrcStream->appsrcNeedDataFlag) {
100             ++webKitMediaSrc->priv->appsrcNeedDataCount;
101             appsrcStream->appsrcNeedDataFlag = true;
102         }
103         int numAppsrcs = webKitMediaSrc->priv->streams.size();
104         if (webKitMediaSrc->priv->appsrcSeekDataCount == numAppsrcs && webKitMediaSrc->priv->appsrcNeedDataCount == numAppsrcs) {
105             GST_DEBUG("All needDatas completed");
106             allAppsrcNeedDataAfterSeek = true;
107             webKitMediaSrc->priv->appsrcSeekDataCount = 0;
108             webKitMediaSrc->priv->appsrcNeedDataCount = 0;
109             webKitMediaSrc->priv->appsrcSeekDataNextAction = Nothing;
110
111             for (Stream* stream : webKitMediaSrc->priv->streams)
112                 stream->appsrcNeedDataFlag = false;
113         }
114     }
115     GST_OBJECT_UNLOCK(webKitMediaSrc);
116
117     if (allAppsrcNeedDataAfterSeek) {
118         GST_DEBUG("All expected appsrcSeekData() and appsrcNeedData() calls performed. Running next action (%d)", static_cast<int>(appsrcSeekDataNextAction));
119
120         switch (appsrcSeekDataNextAction) {
121         case MediaSourceSeekToTime:
122             webKitMediaSrc->priv->notifier->notify(WebKitMediaSrcMainThreadNotification::SeekNeedsData, [webKitMediaSrc] {
123                 seekNeedsDataMainThread(webKitMediaSrc);
124             });
125             break;
126         case Nothing:
127             break;
128         }
129     } else if (appsrcSeekDataNextAction == Nothing) {
130         LockHolder locker(webKitMediaSrc->priv->streamLock);
131
132         GST_OBJECT_LOCK(webKitMediaSrc);
133
134         // Search again for the Stream, just in case it was removed between the previous lock and this one.
135         appsrcStream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
136
137         if (appsrcStream && appsrcStream->type != WebCore::Invalid)
138             webKitMediaSrc->priv->notifier->notify(WebKitMediaSrcMainThreadNotification::ReadyForMoreSamples, [webKitMediaSrc, appsrcStream] {
139                 notifyReadyForMoreSamplesMainThread(webKitMediaSrc, appsrcStream);
140             });
141
142         GST_OBJECT_UNLOCK(webKitMediaSrc);
143     }
144 }
145
146 static void enabledAppsrcEnoughData(GstAppSrc *appsrc, gpointer userData)
147 {
148     // No need to lock on webKitMediaSrc, we're on the main thread and nobody is going to remove the stream in the meantime.
149     ASSERT(WTF::isMainThread());
150
151     WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
152     ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
153     Stream* stream = getStreamByAppsrc(webKitMediaSrc, GST_ELEMENT(appsrc));
154
155     // This callback might have been scheduled from a child thread before the stream was removed.
156     // Then, the removal code might have run, and later this callback.
157     // This check solves the race condition.
158     if (!stream || stream->type == WebCore::Invalid)
159         return;
160
161     stream->sourceBuffer->setReadyForMoreSamples(false);
162 }
163
164 static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer userData)
165 {
166     ASSERT(WTF::isMainThread());
167
168     WebKitMediaSrc* webKitMediaSrc = static_cast<WebKitMediaSrc*>(userData);
169
170     ASSERT(WEBKIT_IS_MEDIA_SRC(webKitMediaSrc));
171
172     GST_OBJECT_LOCK(webKitMediaSrc);
173     webKitMediaSrc->priv->appsrcSeekDataCount++;
174     GST_OBJECT_UNLOCK(webKitMediaSrc);
175
176     return TRUE;
177 }
178
179 static Stream* getStreamByAppsrc(WebKitMediaSrc* source, GstElement* appsrc)
180 {
181     for (Stream* stream : source->priv->streams) {
182         if (stream->appsrc == appsrc)
183             return stream;
184     }
185     return nullptr;
186 }
187
188 G_DEFINE_TYPE_WITH_CODE(WebKitMediaSrc, webkit_media_src, GST_TYPE_BIN,
189     G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitMediaSrcUriHandlerInit);
190     WEBKIT_MEDIA_SRC_CATEGORY_INIT);
191
192 guint webKitMediaSrcSignals[LAST_SIGNAL] = { 0 };
193
194 static void webkit_media_src_class_init(WebKitMediaSrcClass* klass)
195 {
196     GObjectClass* oklass = G_OBJECT_CLASS(klass);
197     GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
198
199     oklass->finalize = webKitMediaSrcFinalize;
200     oklass->set_property = webKitMediaSrcSetProperty;
201     oklass->get_property = webKitMediaSrcGetProperty;
202
203     gst_element_class_add_pad_template(eklass, gst_static_pad_template_get(&srcTemplate));
204
205     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>");
206
207     // Allows setting the uri using the 'location' property, which is used for example by gst_element_make_from_uri().
208     g_object_class_install_property(oklass,
209         PROP_LOCATION,
210         g_param_spec_string("location", "location", "Location to read from", nullptr,
211         GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
212     g_object_class_install_property(oklass,
213         PROP_N_AUDIO,
214         g_param_spec_int("n-audio", "Number Audio", "Total number of audio streams",
215         0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
216     g_object_class_install_property(oklass,
217         PROP_N_VIDEO,
218         g_param_spec_int("n-video", "Number Video", "Total number of video streams",
219         0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
220     g_object_class_install_property(oklass,
221         PROP_N_TEXT,
222         g_param_spec_int("n-text", "Number Text", "Total number of text streams",
223         0, G_MAXINT, 0, GParamFlags(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
224
225     webKitMediaSrcSignals[SIGNAL_VIDEO_CHANGED] =
226         g_signal_new("video-changed", G_TYPE_FROM_CLASS(oklass),
227         G_SIGNAL_RUN_LAST,
228         G_STRUCT_OFFSET(WebKitMediaSrcClass, videoChanged), nullptr, nullptr,
229         g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
230     webKitMediaSrcSignals[SIGNAL_AUDIO_CHANGED] =
231         g_signal_new("audio-changed", G_TYPE_FROM_CLASS(oklass),
232         G_SIGNAL_RUN_LAST,
233         G_STRUCT_OFFSET(WebKitMediaSrcClass, audioChanged), nullptr, nullptr,
234         g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
235     webKitMediaSrcSignals[SIGNAL_TEXT_CHANGED] =
236         g_signal_new("text-changed", G_TYPE_FROM_CLASS(oklass),
237         G_SIGNAL_RUN_LAST,
238         G_STRUCT_OFFSET(WebKitMediaSrcClass, textChanged), nullptr, nullptr,
239         g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE);
240
241     eklass->change_state = webKitMediaSrcChangeState;
242
243     g_type_class_add_private(klass, sizeof(WebKitMediaSrcPrivate));
244 }
245
246 static GstFlowReturn webkitMediaSrcChain(GstPad* pad, GstObject* parent, GstBuffer* buffer)
247 {
248     GRefPtr<WebKitMediaSrc> self = adoptGRef(WEBKIT_MEDIA_SRC(gst_object_get_parent(parent)));
249
250     return gst_flow_combiner_update_pad_flow(self->priv->flowCombiner.get(), pad, gst_proxy_pad_chain_default(pad, GST_OBJECT(self.get()), buffer));
251 }
252
253 static void webkit_media_src_init(WebKitMediaSrc* source)
254 {
255     source->priv = WEBKIT_MEDIA_SRC_GET_PRIVATE(source);
256     new (source->priv) WebKitMediaSrcPrivate();
257     source->priv->seekTime = MediaTime::invalidTime();
258     source->priv->appsrcSeekDataCount = 0;
259     source->priv->appsrcNeedDataCount = 0;
260     source->priv->appsrcSeekDataNextAction = Nothing;
261     source->priv->flowCombiner = GUniquePtr<GstFlowCombiner>(gst_flow_combiner_new());
262     source->priv->notifier = WebCore::MainThreadNotifier<WebKitMediaSrcMainThreadNotification>::create();
263
264     // No need to reset Stream.appsrcNeedDataFlag because there are no Streams at this point yet.
265 }
266
267 void webKitMediaSrcFinalize(GObject* object)
268 {
269     ASSERT(WTF::isMainThread());
270
271     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
272     WebKitMediaSrcPrivate* priv = source->priv;
273
274     Vector<Stream*> oldStreams;
275     source->priv->streams.swap(oldStreams);
276
277     for (Stream* stream : oldStreams)
278         webKitMediaSrcFreeStream(source, stream);
279
280     priv->seekTime = MediaTime::invalidTime();
281
282     source->priv->notifier->invalidate();
283
284     if (priv->mediaPlayerPrivate)
285         webKitMediaSrcSetMediaPlayerPrivate(source, nullptr);
286
287     // We used a placement new for construction, the destructor won't be called automatically.
288     priv->~_WebKitMediaSrcPrivate();
289
290     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
291 }
292
293 void webKitMediaSrcSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
294 {
295     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
296
297     switch (propId) {
298     case PROP_LOCATION:
299         gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(source), g_value_get_string(value), nullptr);
300         break;
301     default:
302         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
303         break;
304     }
305 }
306
307 void webKitMediaSrcGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
308 {
309     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(object);
310     WebKitMediaSrcPrivate* priv = source->priv;
311
312     GST_OBJECT_LOCK(source);
313     switch (propId) {
314     case PROP_LOCATION:
315         g_value_set_string(value, priv->location.get());
316         break;
317     case PROP_N_AUDIO:
318         g_value_set_int(value, priv->numberOfAudioStreams);
319         break;
320     case PROP_N_VIDEO:
321         g_value_set_int(value, priv->numberOfVideoStreams);
322         break;
323     case PROP_N_TEXT:
324         g_value_set_int(value, priv->numberOfTextStreams);
325         break;
326     default:
327         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
328         break;
329     }
330     GST_OBJECT_UNLOCK(source);
331 }
332
333 void webKitMediaSrcDoAsyncStart(WebKitMediaSrc* source)
334 {
335     source->priv->asyncStart = true;
336     GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(source),
337         gst_message_new_async_start(GST_OBJECT(source)));
338 }
339
340 void webKitMediaSrcDoAsyncDone(WebKitMediaSrc* source)
341 {
342     WebKitMediaSrcPrivate* priv = source->priv;
343     if (priv->asyncStart) {
344         GST_BIN_CLASS(parent_class)->handle_message(GST_BIN(source),
345             gst_message_new_async_done(GST_OBJECT(source), GST_CLOCK_TIME_NONE));
346         priv->asyncStart = false;
347     }
348 }
349
350 GstStateChangeReturn webKitMediaSrcChangeState(GstElement* element, GstStateChange transition)
351 {
352     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(element);
353     WebKitMediaSrcPrivate* priv = source->priv;
354
355     switch (transition) {
356     case GST_STATE_CHANGE_READY_TO_PAUSED:
357         priv->allTracksConfigured = false;
358         webKitMediaSrcDoAsyncStart(source);
359         break;
360     default:
361         break;
362     }
363
364     GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
365     if (G_UNLIKELY(result == GST_STATE_CHANGE_FAILURE)) {
366         GST_WARNING_OBJECT(source, "State change failed");
367         webKitMediaSrcDoAsyncDone(source);
368         return result;
369     }
370
371     switch (transition) {
372     case GST_STATE_CHANGE_READY_TO_PAUSED:
373         result = GST_STATE_CHANGE_ASYNC;
374         break;
375     case GST_STATE_CHANGE_PAUSED_TO_READY:
376         webKitMediaSrcDoAsyncDone(source);
377         priv->allTracksConfigured = false;
378         break;
379     default:
380         break;
381     }
382
383     return result;
384 }
385
386 gint64 webKitMediaSrcGetSize(WebKitMediaSrc* webKitMediaSrc)
387 {
388     gint64 duration = 0;
389     for (Stream* stream : webKitMediaSrc->priv->streams)
390         duration = std::max<gint64>(duration, gst_app_src_get_size(GST_APP_SRC(stream->appsrc)));
391     return duration;
392 }
393
394 gboolean webKitMediaSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
395 {
396     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(GST_ELEMENT(parent));
397     gboolean result = FALSE;
398
399     switch (GST_QUERY_TYPE(query)) {
400     case GST_QUERY_DURATION: {
401         GstFormat format;
402         gst_query_parse_duration(query, &format, nullptr);
403
404         GST_DEBUG_OBJECT(source, "duration query in format %s", gst_format_get_name(format));
405         GST_OBJECT_LOCK(source);
406         switch (format) {
407         case GST_FORMAT_TIME: {
408             if (source->priv && source->priv->mediaPlayerPrivate) {
409                 MediaTime duration = source->priv->mediaPlayerPrivate->durationMediaTime();
410                 if (duration > MediaTime::zeroTime()) {
411                     gst_query_set_duration(query, format, WebCore::toGstClockTime(duration));
412                     GST_DEBUG_OBJECT(source, "Answering: duration=%" GST_TIME_FORMAT, GST_TIME_ARGS(WebCore::toGstClockTime(duration)));
413                     result = TRUE;
414                 }
415             }
416             break;
417         }
418         case GST_FORMAT_BYTES: {
419             if (source->priv) {
420                 gint64 duration = webKitMediaSrcGetSize(source);
421                 if (duration) {
422                     gst_query_set_duration(query, format, duration);
423                     GST_DEBUG_OBJECT(source, "size: %" G_GINT64_FORMAT, duration);
424                     result = TRUE;
425                 }
426             }
427             break;
428         }
429         default:
430             break;
431         }
432
433         GST_OBJECT_UNLOCK(source);
434         break;
435     }
436     case GST_QUERY_URI:
437         if (source) {
438             GST_OBJECT_LOCK(source);
439             if (source->priv)
440                 gst_query_set_uri(query, source->priv->location.get());
441             GST_OBJECT_UNLOCK(source);
442         }
443         result = TRUE;
444         break;
445     default: {
446         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
447         // Forward the query to the proxy target pad.
448         if (target)
449             result = gst_pad_query(target.get(), query);
450         break;
451     }
452     }
453
454     return result;
455 }
456
457 void webKitMediaSrcUpdatePresentationSize(GstCaps* caps, Stream* stream)
458 {
459     GST_OBJECT_LOCK(stream->parent);
460     if (WebCore::doCapsHaveType(caps, GST_VIDEO_CAPS_TYPE_PREFIX)) {
461         std::optional<WebCore::FloatSize> size = WebCore::getVideoResolutionFromCaps(caps);
462         if (size.has_value())
463             stream->presentationSize = size.value();
464         else
465             stream->presentationSize = WebCore::FloatSize();
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     auto proxypad = adoptGRef(GST_PAD(gst_proxy_pad_get_internal(GST_PROXY_PAD(ghostpad))));
483     gst_flow_combiner_add_pad(stream->parent->priv->flowCombiner.get(), proxypad.get());
484     gst_pad_set_chain_function(proxypad.get(), static_cast<GstPadChainFunction>(webkitMediaSrcChain));
485     gst_pad_set_query_function(ghostpad, webKitMediaSrcQueryWithParent);
486
487     gst_pad_set_active(ghostpad, TRUE);
488     gst_element_add_pad(GST_ELEMENT(stream->parent), ghostpad);
489 }
490
491 void webKitMediaSrcLinkSourcePad(GstPad* sourcePad, GstCaps* caps, Stream* stream)
492 {
493     ASSERT(caps && stream->parent);
494     if (!caps || !stream->parent) {
495         GST_ERROR("Unable to link parser");
496         return;
497     }
498
499     webKitMediaSrcUpdatePresentationSize(caps, stream);
500
501     // FIXME: drop webKitMediaSrcLinkStreamToSrcPad() and move its code here.
502     if (!gst_pad_is_linked(sourcePad)) {
503         GST_DEBUG_OBJECT(stream->parent, "pad not linked yet");
504         webKitMediaSrcLinkStreamToSrcPad(sourcePad, stream);
505     }
506
507     webKitMediaSrcCheckAllTracksConfigured(stream->parent);
508 }
509
510 void webKitMediaSrcFreeStream(WebKitMediaSrc* source, Stream* stream)
511 {
512     if (GST_IS_APP_SRC(stream->appsrc)) {
513         // Don't trigger callbacks from this appsrc to avoid using the stream anymore.
514         gst_app_src_set_callbacks(GST_APP_SRC(stream->appsrc), &disabledAppsrcCallbacks, nullptr, nullptr);
515         gst_app_src_end_of_stream(GST_APP_SRC(stream->appsrc));
516     }
517
518     GST_OBJECT_LOCK(source);
519     switch (stream->type) {
520     case WebCore::Audio:
521         source->priv->numberOfAudioStreams--;
522         break;
523     case WebCore::Video:
524         source->priv->numberOfVideoStreams--;
525         break;
526     case WebCore::Text:
527         source->priv->numberOfTextStreams--;
528         break;
529     default:
530         break;
531     }
532     GST_OBJECT_UNLOCK(source);
533
534     if (stream->type != WebCore::Invalid) {
535         GST_DEBUG("Freeing track-related info on stream %p", stream);
536
537         LockHolder locker(source->priv->streamLock);
538
539         if (stream->caps)
540             stream->caps = nullptr;
541
542         if (stream->audioTrack)
543             stream->audioTrack = nullptr;
544         if (stream->videoTrack)
545             stream->videoTrack = nullptr;
546
547         int signal = -1;
548         switch (stream->type) {
549         case WebCore::Audio:
550             signal = SIGNAL_AUDIO_CHANGED;
551             break;
552         case WebCore::Video:
553             signal = SIGNAL_VIDEO_CHANGED;
554             break;
555         case WebCore::Text:
556             signal = SIGNAL_TEXT_CHANGED;
557             break;
558         default:
559             break;
560         }
561         stream->type = WebCore::Invalid;
562
563         if (signal != -1)
564             g_signal_emit(G_OBJECT(source), webKitMediaSrcSignals[signal], 0, nullptr);
565
566         source->priv->streamCondition.notifyOne();
567     }
568
569     GST_DEBUG("Releasing stream: %p", stream);
570     delete stream;
571 }
572
573 void webKitMediaSrcCheckAllTracksConfigured(WebKitMediaSrc* webKitMediaSrc)
574 {
575     bool allTracksConfigured = false;
576
577     GST_OBJECT_LOCK(webKitMediaSrc);
578     if (!webKitMediaSrc->priv->allTracksConfigured) {
579         allTracksConfigured = true;
580         for (Stream* stream : webKitMediaSrc->priv->streams) {
581             if (stream->type == WebCore::Invalid) {
582                 allTracksConfigured = false;
583                 break;
584             }
585         }
586         if (allTracksConfigured)
587             webKitMediaSrc->priv->allTracksConfigured = true;
588     }
589     GST_OBJECT_UNLOCK(webKitMediaSrc);
590
591     if (allTracksConfigured) {
592         GST_DEBUG("All tracks attached. Completing async state change operation.");
593         gst_element_no_more_pads(GST_ELEMENT(webKitMediaSrc));
594         webKitMediaSrcDoAsyncDone(webKitMediaSrc);
595     }
596 }
597
598 // Uri handler interface.
599 GstURIType webKitMediaSrcUriGetType(GType)
600 {
601     return GST_URI_SRC;
602 }
603
604 const gchar* const* webKitMediaSrcGetProtocols(GType)
605 {
606     static const char* protocols[] = {"mediasourceblob", nullptr };
607     return protocols;
608 }
609
610 gchar* webKitMediaSrcGetUri(GstURIHandler* handler)
611 {
612     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(handler);
613     gchar* result;
614
615     GST_OBJECT_LOCK(source);
616     result = g_strdup(source->priv->location.get());
617     GST_OBJECT_UNLOCK(source);
618     return result;
619 }
620
621 gboolean webKitMediaSrcSetUri(GstURIHandler* handler, const gchar* uri, GError**)
622 {
623     WebKitMediaSrc* source = WEBKIT_MEDIA_SRC(handler);
624
625     if (GST_STATE(source) >= GST_STATE_PAUSED) {
626         GST_ERROR_OBJECT(source, "URI can only be set in states < PAUSED");
627         return FALSE;
628     }
629
630     GST_OBJECT_LOCK(source);
631     WebKitMediaSrcPrivate* priv = source->priv;
632     priv->location = nullptr;
633     if (!uri) {
634         GST_OBJECT_UNLOCK(source);
635         return TRUE;
636     }
637
638     URL url(URL(), uri);
639
640     priv->location = GUniquePtr<gchar>(g_strdup(url.string().utf8().data()));
641     GST_OBJECT_UNLOCK(source);
642     return TRUE;
643 }
644
645 void webKitMediaSrcUriHandlerInit(gpointer gIface, gpointer)
646 {
647     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
648
649     iface->get_type = webKitMediaSrcUriGetType;
650     iface->get_protocols = webKitMediaSrcGetProtocols;
651     iface->get_uri = webKitMediaSrcGetUri;
652     iface->set_uri = webKitMediaSrcSetUri;
653 }
654
655 static void seekNeedsDataMainThread(WebKitMediaSrc* source)
656 {
657     GST_DEBUG("Buffering needed before seek");
658
659     ASSERT(WTF::isMainThread());
660
661     GST_OBJECT_LOCK(source);
662     MediaTime seekTime = source->priv->seekTime;
663     WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate = source->priv->mediaPlayerPrivate;
664
665     if (!mediaPlayerPrivate) {
666         GST_OBJECT_UNLOCK(source);
667         return;
668     }
669
670     for (Stream* stream : source->priv->streams) {
671         if (stream->type != WebCore::Invalid)
672             stream->sourceBuffer->setReadyForMoreSamples(true);
673     }
674     GST_OBJECT_UNLOCK(source);
675     mediaPlayerPrivate->notifySeekNeedsDataForTime(seekTime);
676 }
677
678 static void notifyReadyForMoreSamplesMainThread(WebKitMediaSrc* source, Stream* appsrcStream)
679 {
680     GST_OBJECT_LOCK(source);
681
682     auto it = std::find(source->priv->streams.begin(), source->priv->streams.end(), appsrcStream);
683     if (it == source->priv->streams.end()) {
684         GST_OBJECT_UNLOCK(source);
685         return;
686     }
687
688     WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate = source->priv->mediaPlayerPrivate;
689     if (mediaPlayerPrivate && !mediaPlayerPrivate->seeking())
690         appsrcStream->sourceBuffer->notifyReadyForMoreSamples();
691
692     GST_OBJECT_UNLOCK(source);
693 }
694
695 void webKitMediaSrcSetMediaPlayerPrivate(WebKitMediaSrc* source, WebCore::MediaPlayerPrivateGStreamerMSE* mediaPlayerPrivate)
696 {
697     GST_OBJECT_LOCK(source);
698
699     // Set to nullptr on MediaPlayerPrivateGStreamer destruction, never a dangling pointer.
700     source->priv->mediaPlayerPrivate = mediaPlayerPrivate;
701     GST_OBJECT_UNLOCK(source);
702 }
703
704 void webKitMediaSrcSetReadyForSamples(WebKitMediaSrc* source, bool isReady)
705 {
706     if (source) {
707         GST_OBJECT_LOCK(source);
708         for (Stream* stream : source->priv->streams)
709             stream->sourceBuffer->setReadyForMoreSamples(isReady);
710         GST_OBJECT_UNLOCK(source);
711     }
712 }
713
714 void webKitMediaSrcPrepareSeek(WebKitMediaSrc* source, const MediaTime& time)
715 {
716     GST_OBJECT_LOCK(source);
717     source->priv->seekTime = time;
718     source->priv->appsrcSeekDataCount = 0;
719     source->priv->appsrcNeedDataCount = 0;
720
721     for (Stream* stream : source->priv->streams) {
722         stream->appsrcNeedDataFlag = false;
723         // Don't allow samples away from the seekTime to be enqueued.
724         stream->lastEnqueuedTime = time;
725     }
726
727     // The pending action will be performed in enabledAppsrcSeekData().
728     source->priv->appsrcSeekDataNextAction = MediaSourceSeekToTime;
729     GST_OBJECT_UNLOCK(source);
730 }
731
732 namespace WTF {
733 template <> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc* ptr)
734 {
735     ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr)));
736     return GRefPtr<WebKitMediaSrc>(ptr, GRefPtrAdopt);
737 }
738
739 template <> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
740 {
741     if (ptr)
742         gst_object_ref_sink(GST_OBJECT(ptr));
743
744     return ptr;
745 }
746
747 template <> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
748 {
749     if (ptr)
750         gst_object_unref(ptr);
751 }
752 };
753
754 #endif // USE(GSTREAMER)
755