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
50GST_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
56static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src_%u", GST_PAD_SRC,
57 GST_PAD_SOMETIMES, GST_STATIC_CAPS_ANY);
58
59static void enabledAppsrcNeedData(GstAppSrc*, guint, gpointer);
60static void enabledAppsrcEnoughData(GstAppSrc*, gpointer);
61static gboolean enabledAppsrcSeekData(GstAppSrc*, guint64, gpointer);
62
63static void disabledAppsrcNeedData(GstAppSrc*, guint, gpointer) { };
64static void disabledAppsrcEnoughData(GstAppSrc*, gpointer) { };
65static gboolean disabledAppsrcSeekData(GstAppSrc*, guint64, gpointer)
66{
67 return FALSE;
68};
69
70GstAppSrcCallbacks enabledAppsrcCallbacks = {
71 enabledAppsrcNeedData,
72 enabledAppsrcEnoughData,
73 enabledAppsrcSeekData,
74 { 0 }
75};
76
77GstAppSrcCallbacks disabledAppsrcCallbacks = {
78 disabledAppsrcNeedData,
79 disabledAppsrcEnoughData,
80 disabledAppsrcSeekData,
81 { 0 }
82};
83
84static Stream* getStreamByAppsrc(WebKitMediaSrc*, GstElement*);
85static void seekNeedsDataMainThread(WebKitMediaSrc*);
86static void notifyReadyForMoreSamplesMainThread(WebKitMediaSrc*, Stream*);
87
88static 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
146static 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
164static 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
179static 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
188G_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
192guint webKitMediaSrcSignals[LAST_SIGNAL] = { 0 };
193
194static 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
246static 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
253static 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
267void 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
293void 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
307void 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
333void 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
340void 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
350GstStateChangeReturn 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
386gint64 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
394gboolean 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
457void webKitMediaSrcUpdatePresentationSize(GstCaps* caps, Stream* stream)
458{
459 GST_OBJECT_LOCK(stream->parent);
460 if (WebCore::doCapsHaveType(caps, GST_VIDEO_CAPS_TYPE_PREFIX)) {
461 Optional<WebCore::FloatSize> size = WebCore::getVideoResolutionFromCaps(caps);
462 if (size.hasValue())
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
474void 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
491void 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
510void 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
573void 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.
599GstURIType webKitMediaSrcUriGetType(GType)
600{
601 return GST_URI_SRC;
602}
603
604const gchar* const* webKitMediaSrcGetProtocols(GType)
605{
606 static const char* protocols[] = {"mediasourceblob", nullptr };
607 return protocols;
608}
609
610gchar* 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
621gboolean 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
645void 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
655static 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
678static 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
695void 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
704void 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
714void 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
732namespace WTF {
733template <> GRefPtr<WebKitMediaSrc> adoptGRef(WebKitMediaSrc* ptr)
734{
735 ASSERT(!ptr || !g_object_is_floating(G_OBJECT(ptr)));
736 return GRefPtr<WebKitMediaSrc>(ptr, GRefPtrAdopt);
737}
738
739template <> WebKitMediaSrc* refGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
740{
741 if (ptr)
742 gst_object_ref_sink(GST_OBJECT(ptr));
743
744 return ptr;
745}
746
747template <> void derefGPtr<WebKitMediaSrc>(WebKitMediaSrc* ptr)
748{
749 if (ptr)
750 gst_object_unref(ptr);
751}
752};
753
754#endif // USE(GSTREAMER)
755
756