1/*
2 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Collabora Ltd. All rights reserved.
4 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5 * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
6 * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2016, 2017 Igalia S.L
7 * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com>
8 * Copyright (C) 2015, 2016, 2017 Metrological Group B.V.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * aint with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "config.h"
27#include "MediaPlayerPrivateGStreamerMSE.h"
28
29#if ENABLE(VIDEO) && USE(GSTREAMER) && ENABLE(MEDIA_SOURCE)
30
31#include "AppendPipeline.h"
32#include "AudioTrackPrivateGStreamer.h"
33#include "GStreamerCommon.h"
34#include "GStreamerRegistryScannerMSE.h"
35#include "InbandTextTrackPrivateGStreamer.h"
36#include "MIMETypeRegistry.h"
37#include "MediaDescription.h"
38#include "MediaPlayer.h"
39#include "NotImplemented.h"
40#include "PlaybackPipeline.h"
41#include "SourceBufferPrivateGStreamer.h"
42#include "TimeRanges.h"
43#include "VideoTrackPrivateGStreamer.h"
44
45#include <gst/app/gstappsink.h>
46#include <gst/app/gstappsrc.h>
47#include <gst/gst.h>
48#include <gst/pbutils/pbutils.h>
49#include <gst/video/video.h>
50#include <wtf/Condition.h>
51#include <wtf/HashSet.h>
52#include <wtf/NeverDestroyed.h>
53#include <wtf/StringPrintStream.h>
54#include <wtf/URL.h>
55#include <wtf/text/AtomicString.h>
56#include <wtf/text/AtomicStringHash.h>
57
58static const char* dumpReadyState(WebCore::MediaPlayer::ReadyState readyState)
59{
60 switch (readyState) {
61 case WebCore::MediaPlayer::HaveNothing: return "HaveNothing";
62 case WebCore::MediaPlayer::HaveMetadata: return "HaveMetadata";
63 case WebCore::MediaPlayer::HaveCurrentData: return "HaveCurrentData";
64 case WebCore::MediaPlayer::HaveFutureData: return "HaveFutureData";
65 case WebCore::MediaPlayer::HaveEnoughData: return "HaveEnoughData";
66 default: return "(unknown)";
67 }
68}
69
70GST_DEBUG_CATEGORY(webkit_mse_debug);
71#define GST_CAT_DEFAULT webkit_mse_debug
72
73namespace WebCore {
74
75void MediaPlayerPrivateGStreamerMSE::registerMediaEngine(MediaEngineRegistrar registrar)
76{
77 initializeGStreamerAndRegisterWebKitElements();
78 GST_DEBUG_CATEGORY_INIT(webkit_mse_debug, "webkitmse", 0, "WebKit MSE media player");
79 if (isAvailable()) {
80 registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateGStreamerMSE>(player); },
81 getSupportedTypes, supportsType, nullptr, nullptr, nullptr, supportsKeySystem);
82 }
83}
84
85MediaPlayerPrivateGStreamerMSE::MediaPlayerPrivateGStreamerMSE(MediaPlayer* player)
86 : MediaPlayerPrivateGStreamer(player)
87{
88 GST_TRACE("creating the player (%p)", this);
89}
90
91MediaPlayerPrivateGStreamerMSE::~MediaPlayerPrivateGStreamerMSE()
92{
93 GST_TRACE("destroying the player (%p)", this);
94
95 // Clear the AppendPipeline map. This should cause the destruction of all the AppendPipeline's since there should
96 // be no alive references at this point.
97#ifndef NDEBUG
98 for (auto iterator : m_appendPipelinesMap)
99 ASSERT(iterator.value->hasOneRef());
100#endif
101 m_appendPipelinesMap.clear();
102
103 if (m_source) {
104 webKitMediaSrcSetMediaPlayerPrivate(WEBKIT_MEDIA_SRC(m_source.get()), nullptr);
105 g_signal_handlers_disconnect_by_data(m_source.get(), this);
106 }
107
108 if (m_playbackPipeline)
109 m_playbackPipeline->setWebKitMediaSrc(nullptr);
110}
111
112void MediaPlayerPrivateGStreamerMSE::load(const String& urlString)
113{
114 if (!urlString.startsWith("mediasource")) {
115 // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate.
116 m_networkState = MediaPlayer::FormatError;
117 m_player->networkStateChanged();
118 return;
119 }
120
121 if (!m_playbackPipeline)
122 m_playbackPipeline = PlaybackPipeline::create();
123
124 MediaPlayerPrivateGStreamer::load(urlString);
125}
126
127void MediaPlayerPrivateGStreamerMSE::load(const String& url, MediaSourcePrivateClient* mediaSource)
128{
129 m_mediaSource = mediaSource;
130 load(makeString("mediasource", url));
131}
132
133void MediaPlayerPrivateGStreamerMSE::pause()
134{
135 m_paused = true;
136 MediaPlayerPrivateGStreamer::pause();
137}
138
139MediaTime MediaPlayerPrivateGStreamerMSE::durationMediaTime() const
140{
141 if (UNLIKELY(!m_pipeline || m_errorOccured))
142 return MediaTime();
143
144 return m_mediaTimeDuration;
145}
146
147void MediaPlayerPrivateGStreamerMSE::seek(const MediaTime& time)
148{
149 if (UNLIKELY(!m_pipeline || m_errorOccured))
150 return;
151
152 GST_INFO("[Seek] seek attempt to %s secs", toString(time).utf8().data());
153
154 // Avoid useless seeking.
155 MediaTime current = currentMediaTime();
156 if (time == current) {
157 if (!m_seeking)
158 timeChanged();
159 return;
160 }
161
162 if (isLiveStream())
163 return;
164
165 if (m_seeking && m_seekIsPending) {
166 m_seekTime = time;
167 return;
168 }
169
170 GST_DEBUG("Seeking from %s to %s seconds", toString(current).utf8().data(), toString(time).utf8().data());
171
172 MediaTime previousSeekTime = m_seekTime;
173 m_seekTime = time;
174
175 if (!doSeek()) {
176 m_seekTime = previousSeekTime;
177 GST_WARNING("Seeking to %s failed", toString(time).utf8().data());
178 return;
179 }
180
181 m_isEndReached = false;
182 GST_DEBUG("m_seeking=%s, m_seekTime=%s", boolForPrinting(m_seeking), toString(m_seekTime).utf8().data());
183}
184
185void MediaPlayerPrivateGStreamerMSE::configurePlaySink()
186{
187 MediaPlayerPrivateGStreamer::configurePlaySink();
188
189 GRefPtr<GstElement> playsink = adoptGRef(gst_bin_get_by_name(GST_BIN(m_pipeline.get()), "playsink"));
190 if (playsink) {
191 // The default value (0) means "send events to all the sinks", instead
192 // of "only to the first that returns true". This is needed for MSE seek.
193 g_object_set(G_OBJECT(playsink.get()), "send-event-mode", 0, nullptr);
194 }
195}
196
197bool MediaPlayerPrivateGStreamerMSE::changePipelineState(GstState newState)
198{
199 if (seeking()) {
200 GST_DEBUG("Rejected state change to %s while seeking",
201 gst_element_state_get_name(newState));
202 return true;
203 }
204
205 return MediaPlayerPrivateGStreamer::changePipelineState(newState);
206}
207
208void MediaPlayerPrivateGStreamerMSE::notifySeekNeedsDataForTime(const MediaTime& seekTime)
209{
210 // Reenqueue samples needed to resume playback in the new position.
211 m_mediaSource->seekToTime(seekTime);
212
213 GST_DEBUG("MSE seek to %s finished", toString(seekTime).utf8().data());
214
215 if (!m_gstSeekCompleted) {
216 m_gstSeekCompleted = true;
217 maybeFinishSeek();
218 }
219}
220
221bool MediaPlayerPrivateGStreamerMSE::doSeek(const MediaTime&, float, GstSeekFlags)
222{
223 // Use doSeek() instead. If anybody is calling this version of doSeek(), something is wrong.
224 ASSERT_NOT_REACHED();
225 return false;
226}
227
228bool MediaPlayerPrivateGStreamerMSE::doSeek()
229{
230 MediaTime seekTime = m_seekTime;
231 double rate = m_player->rate();
232 GstSeekFlags seekType = static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE);
233
234 // Always move to seeking state to report correct 'currentTime' while pending for actual seek to complete.
235 m_seeking = true;
236
237 // Check if playback pipeline is ready for seek.
238 GstState state, newState;
239 GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &newState, 0);
240 if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) {
241 GST_DEBUG("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
242 webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true);
243 m_seeking = false;
244 return false;
245 }
246 if ((getStateResult == GST_STATE_CHANGE_ASYNC
247 && !(state == GST_STATE_PLAYING && newState == GST_STATE_PAUSED))
248 || state < GST_STATE_PAUSED
249 || m_isEndReached
250 || !m_gstSeekCompleted) {
251 CString reason = "Unknown reason";
252 if (getStateResult == GST_STATE_CHANGE_ASYNC) {
253 reason = makeString("In async change ",
254 gst_element_state_get_name(state), " --> ",
255 gst_element_state_get_name(newState)).utf8();
256 } else if (state < GST_STATE_PAUSED)
257 reason = "State less than PAUSED";
258 else if (m_isEndReached)
259 reason = "End reached";
260 else if (!m_gstSeekCompleted)
261 reason = "Previous seek is not finished yet";
262
263 GST_DEBUG("[Seek] Delaying the seek: %s", reason.data());
264
265 m_seekIsPending = true;
266
267 if (m_isEndReached) {
268 GST_DEBUG("[Seek] reset pipeline");
269 m_resetPipeline = true;
270 m_seeking = false;
271 if (!changePipelineState(GST_STATE_PAUSED))
272 loadingFailed(MediaPlayer::Empty);
273 else
274 m_seeking = true;
275 }
276
277 return m_seeking;
278 }
279
280 // Stop accepting new samples until actual seek is finished.
281 webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), false);
282
283 // Correct seek time if it helps to fix a small gap.
284 if (!isTimeBuffered(seekTime)) {
285 // Look if a near future time (<0.1 sec.) is buffered and change the seek target time.
286 if (m_mediaSource) {
287 const MediaTime miniGap = MediaTime(1, 10);
288 MediaTime nearest = m_mediaSource->buffered()->nearest(seekTime);
289 if (nearest.isValid() && nearest > seekTime && (nearest - seekTime) <= miniGap && isTimeBuffered(nearest + miniGap)) {
290 GST_DEBUG("[Seek] Changed the seek target time from %s to %s, a near point in the future", toString(seekTime).utf8().data(), toString(nearest).utf8().data());
291 seekTime = nearest;
292 }
293 }
294 }
295
296 // Check if MSE has samples for requested time and defer actual seek if needed.
297 if (!isTimeBuffered(seekTime)) {
298 GST_DEBUG("[Seek] Delaying the seek: MSE is not ready");
299 GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED);
300 if (setStateResult == GST_STATE_CHANGE_FAILURE) {
301 GST_DEBUG("[Seek] Cannot seek, failed to pause playback pipeline.");
302 webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true);
303 m_seeking = false;
304 return false;
305 }
306 m_readyState = MediaPlayer::HaveMetadata;
307 notifySeekNeedsDataForTime(seekTime);
308 ASSERT(!m_mseSeekCompleted);
309 return true;
310 }
311
312 // Complete previous MSE seek if needed.
313 if (!m_mseSeekCompleted) {
314 m_mediaSource->monitorSourceBuffers();
315 ASSERT(m_mseSeekCompleted);
316 // Note: seekCompleted will recursively call us.
317 return m_seeking;
318 }
319
320 GST_DEBUG("We can seek now");
321
322 MediaTime startTime = seekTime, endTime = MediaTime::invalidTime();
323
324 if (rate < 0) {
325 startTime = MediaTime::zeroTime();
326 endTime = seekTime;
327 }
328
329 if (!rate)
330 rate = 1;
331
332 GST_DEBUG("Actual seek to %s, end time: %s, rate: %f", toString(startTime).utf8().data(), toString(endTime).utf8().data(), rate);
333
334 // This will call notifySeekNeedsData() after some time to tell that the pipeline is ready for sample enqueuing.
335 webKitMediaSrcPrepareSeek(WEBKIT_MEDIA_SRC(m_source.get()), seekTime);
336
337 m_gstSeekCompleted = false;
338 if (!gst_element_seek(m_pipeline.get(), rate, GST_FORMAT_TIME, seekType, GST_SEEK_TYPE_SET, toGstClockTime(startTime), GST_SEEK_TYPE_SET, toGstClockTime(endTime))) {
339 webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true);
340 m_seeking = false;
341 m_gstSeekCompleted = true;
342 GST_DEBUG("doSeek(): gst_element_seek() failed, returning false");
343 return false;
344 }
345
346 // The samples will be enqueued in notifySeekNeedsData().
347 GST_DEBUG("doSeek(): gst_element_seek() succeeded, returning true");
348 return true;
349}
350
351void MediaPlayerPrivateGStreamerMSE::maybeFinishSeek()
352{
353 if (!m_seeking || !m_mseSeekCompleted || !m_gstSeekCompleted)
354 return;
355
356 GstState state, newState;
357 GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &newState, 0);
358
359 if (getStateResult == GST_STATE_CHANGE_ASYNC
360 && !(state == GST_STATE_PLAYING && newState == GST_STATE_PAUSED)) {
361 GST_DEBUG("[Seek] Delaying seek finish");
362 return;
363 }
364
365 if (m_seekIsPending) {
366 GST_DEBUG("[Seek] Committing pending seek to %s", toString(m_seekTime).utf8().data());
367 m_seekIsPending = false;
368 if (!doSeek()) {
369 GST_WARNING("[Seek] Seeking to %s failed", toString(m_seekTime).utf8().data());
370 m_cachedPosition = MediaTime::invalidTime();
371 }
372 return;
373 }
374
375 GST_DEBUG("[Seek] Seeked to %s", toString(m_seekTime).utf8().data());
376
377 webKitMediaSrcSetReadyForSamples(WEBKIT_MEDIA_SRC(m_source.get()), true);
378 m_seeking = false;
379 m_cachedPosition = MediaTime::invalidTime();
380 // The pipeline can still have a pending state. In this case a position query will fail.
381 // Right now we can use m_seekTime as a fallback.
382 m_canFallBackToLastFinishedSeekPosition = true;
383 timeChanged();
384}
385
386void MediaPlayerPrivateGStreamerMSE::updatePlaybackRate()
387{
388 notImplemented();
389}
390
391bool MediaPlayerPrivateGStreamerMSE::seeking() const
392{
393 return m_seeking;
394}
395
396// FIXME: MediaPlayerPrivateGStreamer manages the ReadyState on its own. We shouldn't change it manually.
397void MediaPlayerPrivateGStreamerMSE::setReadyState(MediaPlayer::ReadyState readyState)
398{
399 if (readyState == m_readyState)
400 return;
401
402 if (seeking()) {
403 GST_DEBUG("Skip ready state change(%s -> %s) due to seek\n", dumpReadyState(m_readyState), dumpReadyState(readyState));
404 return;
405 }
406
407 GST_DEBUG("Ready State Changed manually from %u to %u", m_readyState, readyState);
408 MediaPlayer::ReadyState oldReadyState = m_readyState;
409 m_readyState = readyState;
410 GST_DEBUG("m_readyState: %s -> %s", dumpReadyState(oldReadyState), dumpReadyState(m_readyState));
411
412 if (oldReadyState < MediaPlayer::HaveCurrentData && m_readyState >= MediaPlayer::HaveCurrentData) {
413 GST_DEBUG("[Seek] Reporting load state changed to trigger seek continuation");
414 loadStateChanged();
415 }
416 m_player->readyStateChanged();
417
418 GstState pipelineState;
419 GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &pipelineState, nullptr, 250 * GST_NSECOND);
420 bool isPlaying = (getStateResult == GST_STATE_CHANGE_SUCCESS && pipelineState == GST_STATE_PLAYING);
421
422 if (m_readyState == MediaPlayer::HaveMetadata && oldReadyState > MediaPlayer::HaveMetadata && isPlaying) {
423 GST_TRACE("Changing pipeline to PAUSED...");
424 bool ok = changePipelineState(GST_STATE_PAUSED);
425 GST_TRACE("Changed pipeline to PAUSED: %s", ok ? "Success" : "Error");
426 }
427}
428
429void MediaPlayerPrivateGStreamerMSE::waitForSeekCompleted()
430{
431 if (!m_seeking)
432 return;
433
434 GST_DEBUG("Waiting for MSE seek completed");
435 m_mseSeekCompleted = false;
436}
437
438void MediaPlayerPrivateGStreamerMSE::seekCompleted()
439{
440 if (m_mseSeekCompleted)
441 return;
442
443 GST_DEBUG("MSE seek completed");
444 m_mseSeekCompleted = true;
445
446 doSeek();
447
448 if (!seeking() && m_readyState >= MediaPlayer::HaveFutureData)
449 changePipelineState(GST_STATE_PLAYING);
450
451 if (!seeking())
452 m_player->timeChanged();
453}
454
455void MediaPlayerPrivateGStreamerMSE::setRate(float)
456{
457 notImplemented();
458}
459
460std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateGStreamerMSE::buffered() const
461{
462 return m_mediaSource ? m_mediaSource->buffered() : std::make_unique<PlatformTimeRanges>();
463}
464
465void MediaPlayerPrivateGStreamerMSE::sourceSetup(GstElement* sourceElement)
466{
467 m_source = sourceElement;
468
469 ASSERT(WEBKIT_IS_MEDIA_SRC(m_source.get()));
470
471 m_playbackPipeline->setWebKitMediaSrc(WEBKIT_MEDIA_SRC(m_source.get()));
472
473 MediaSourceGStreamer::open(*m_mediaSource.get(), *this);
474 g_signal_connect_swapped(m_source.get(), "video-changed", G_CALLBACK(videoChangedCallback), this);
475 g_signal_connect_swapped(m_source.get(), "audio-changed", G_CALLBACK(audioChangedCallback), this);
476 g_signal_connect_swapped(m_source.get(), "text-changed", G_CALLBACK(textChangedCallback), this);
477 webKitMediaSrcSetMediaPlayerPrivate(WEBKIT_MEDIA_SRC(m_source.get()), this);
478}
479
480void MediaPlayerPrivateGStreamerMSE::updateStates()
481{
482 if (UNLIKELY(!m_pipeline || m_errorOccured))
483 return;
484
485 MediaPlayer::NetworkState oldNetworkState = m_networkState;
486 MediaPlayer::ReadyState oldReadyState = m_readyState;
487 GstState state, pending;
488
489 GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &pending, 250 * GST_NSECOND);
490
491 bool shouldUpdatePlaybackState = false;
492 switch (getStateResult) {
493 case GST_STATE_CHANGE_SUCCESS: {
494 GST_DEBUG("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
495
496 // Do nothing if on EOS and state changed to READY to avoid recreating the player
497 // on HTMLMediaElement and properly generate the video 'ended' event.
498 if (m_isEndReached && state == GST_STATE_READY)
499 break;
500
501 m_resetPipeline = (state <= GST_STATE_READY);
502 if (m_resetPipeline)
503 m_mediaTimeDuration = MediaTime::zeroTime();
504
505 // Update ready and network states.
506 switch (state) {
507 case GST_STATE_NULL:
508 m_readyState = MediaPlayer::HaveNothing;
509 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
510 m_networkState = MediaPlayer::Empty;
511 break;
512 case GST_STATE_READY:
513 m_readyState = MediaPlayer::HaveMetadata;
514 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
515 m_networkState = MediaPlayer::Empty;
516 break;
517 case GST_STATE_PAUSED:
518 case GST_STATE_PLAYING:
519 if (seeking()) {
520 m_readyState = MediaPlayer::HaveMetadata;
521 // FIXME: Should we manage NetworkState too?
522 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
523 } else if (m_buffering) {
524 if (m_bufferingPercentage == 100) {
525 GST_DEBUG("[Buffering] Complete.");
526 m_buffering = false;
527 m_readyState = MediaPlayer::HaveEnoughData;
528 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
529 m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading;
530 } else {
531 m_readyState = MediaPlayer::HaveCurrentData;
532 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
533 m_networkState = MediaPlayer::Loading;
534 }
535 } else if (m_downloadFinished) {
536 m_readyState = MediaPlayer::HaveEnoughData;
537 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
538 m_networkState = MediaPlayer::Loaded;
539 } else {
540 m_readyState = MediaPlayer::HaveFutureData;
541 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
542 m_networkState = MediaPlayer::Loading;
543 }
544
545 if (m_eosMarked && state == GST_STATE_PLAYING)
546 m_eosPending = true;
547
548 break;
549 default:
550 ASSERT_NOT_REACHED();
551 break;
552 }
553
554 // Sync states where needed.
555 if (state == GST_STATE_PAUSED) {
556 if (!m_volumeAndMuteInitialized) {
557 notifyPlayerOfVolumeChange();
558 notifyPlayerOfMute();
559 m_volumeAndMuteInitialized = true;
560 }
561
562 if (!seeking() && !m_buffering && !m_paused && m_playbackRate) {
563 GST_DEBUG("[Buffering] Restarting playback.");
564 changePipelineState(GST_STATE_PLAYING);
565 }
566 } else if (state == GST_STATE_PLAYING) {
567 m_paused = false;
568
569 if ((m_buffering && !isLiveStream()) || !m_playbackRate) {
570 GST_DEBUG("[Buffering] Pausing stream for buffering.");
571 changePipelineState(GST_STATE_PAUSED);
572 }
573 } else
574 m_paused = true;
575
576 if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
577 shouldUpdatePlaybackState = true;
578 GST_DEBUG("Requested state change to %s was completed", gst_element_state_get_name(state));
579 }
580
581 break;
582 }
583 case GST_STATE_CHANGE_ASYNC:
584 GST_DEBUG("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
585 // Change in progress.
586 break;
587 case GST_STATE_CHANGE_FAILURE:
588 GST_WARNING("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
589 // Change failed.
590 return;
591 case GST_STATE_CHANGE_NO_PREROLL:
592 GST_DEBUG("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
593
594 // Live pipelines go in PAUSED without prerolling.
595 m_isStreaming = true;
596
597 if (state == GST_STATE_READY) {
598 m_readyState = MediaPlayer::HaveNothing;
599 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
600 } else if (state == GST_STATE_PAUSED) {
601 m_readyState = MediaPlayer::HaveEnoughData;
602 GST_DEBUG("m_readyState=%s", dumpReadyState(m_readyState));
603 m_paused = true;
604 } else if (state == GST_STATE_PLAYING)
605 m_paused = false;
606
607 if (!m_paused && m_playbackRate)
608 changePipelineState(GST_STATE_PLAYING);
609
610 m_networkState = MediaPlayer::Loading;
611 break;
612 default:
613 GST_DEBUG("Else : %d", getStateResult);
614 break;
615 }
616
617 m_requestedState = GST_STATE_VOID_PENDING;
618
619 if (shouldUpdatePlaybackState)
620 m_player->playbackStateChanged();
621
622 if (m_networkState != oldNetworkState) {
623 GST_DEBUG("Network State Changed from %u to %u", oldNetworkState, m_networkState);
624 m_player->networkStateChanged();
625 }
626 if (m_readyState != oldReadyState) {
627 GST_DEBUG("Ready State Changed from %u to %u", oldReadyState, m_readyState);
628 m_player->readyStateChanged();
629 }
630
631 if (getStateResult == GST_STATE_CHANGE_SUCCESS && state >= GST_STATE_PAUSED) {
632 updatePlaybackRate();
633 maybeFinishSeek();
634 }
635}
636void MediaPlayerPrivateGStreamerMSE::asyncStateChangeDone()
637{
638 if (UNLIKELY(!m_pipeline || m_errorOccured))
639 return;
640
641 if (m_seeking)
642 maybeFinishSeek();
643 else
644 updateStates();
645}
646
647bool MediaPlayerPrivateGStreamerMSE::isTimeBuffered(const MediaTime &time) const
648{
649 bool result = m_mediaSource && m_mediaSource->buffered()->contain(time);
650 GST_DEBUG("Time %s buffered? %s", toString(time).utf8().data(), boolForPrinting(result));
651 return result;
652}
653
654void MediaPlayerPrivateGStreamerMSE::setMediaSourceClient(Ref<MediaSourceClientGStreamerMSE> client)
655{
656 m_mediaSourceClient = client.ptr();
657}
658
659RefPtr<MediaSourceClientGStreamerMSE> MediaPlayerPrivateGStreamerMSE::mediaSourceClient()
660{
661 return m_mediaSourceClient;
662}
663
664void MediaPlayerPrivateGStreamerMSE::blockDurationChanges()
665{
666 ASSERT(isMainThread());
667 m_areDurationChangesBlocked = true;
668 m_shouldReportDurationWhenUnblocking = false;
669}
670
671void MediaPlayerPrivateGStreamerMSE::unblockDurationChanges()
672{
673 ASSERT(isMainThread());
674 if (m_shouldReportDurationWhenUnblocking) {
675 m_player->durationChanged();
676 m_playbackPipeline->notifyDurationChanged();
677 m_shouldReportDurationWhenUnblocking = false;
678 }
679
680 m_areDurationChangesBlocked = false;
681}
682
683void MediaPlayerPrivateGStreamerMSE::durationChanged()
684{
685 ASSERT(isMainThread());
686 if (!m_mediaSourceClient) {
687 GST_DEBUG("m_mediaSourceClient is null, doing nothing");
688 return;
689 }
690
691 MediaTime previousDuration = m_mediaTimeDuration;
692 m_mediaTimeDuration = m_mediaSourceClient->duration();
693
694 GST_TRACE("previous=%s, new=%s", toString(previousDuration).utf8().data(), toString(m_mediaTimeDuration).utf8().data());
695
696 // Avoid emiting durationchanged in the case where the previous duration was 0 because that case is already handled
697 // by the HTMLMediaElement.
698 if (m_mediaTimeDuration != previousDuration && m_mediaTimeDuration.isValid() && previousDuration.isValid()) {
699 if (!m_areDurationChangesBlocked) {
700 m_player->durationChanged();
701 m_playbackPipeline->notifyDurationChanged();
702 } else
703 m_shouldReportDurationWhenUnblocking = true;
704 m_mediaSource->durationChanged(m_mediaTimeDuration);
705 }
706}
707
708void MediaPlayerPrivateGStreamerMSE::trackDetected(RefPtr<AppendPipeline> appendPipeline, RefPtr<WebCore::TrackPrivateBase> newTrack, bool firstTrackDetected)
709{
710 ASSERT(appendPipeline->track() == newTrack);
711
712 GstCaps* caps = appendPipeline->appsinkCaps();
713 ASSERT(caps);
714 GST_DEBUG("track ID: %s, caps: %" GST_PTR_FORMAT, newTrack->id().string().latin1().data(), caps);
715
716 if (doCapsHaveType(caps, GST_VIDEO_CAPS_TYPE_PREFIX)) {
717 Optional<FloatSize> size = getVideoResolutionFromCaps(caps);
718 if (size.hasValue())
719 m_videoSize = size.value();
720 }
721
722 if (firstTrackDetected)
723 m_playbackPipeline->attachTrack(appendPipeline->sourceBufferPrivate(), newTrack, caps);
724 else
725 m_playbackPipeline->reattachTrack(appendPipeline->sourceBufferPrivate(), newTrack, caps);
726}
727
728void MediaPlayerPrivateGStreamerMSE::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types)
729{
730 auto& gstRegistryScanner = GStreamerRegistryScannerMSE::singleton();
731 types = gstRegistryScanner.mimeTypeSet();
732}
733
734MediaPlayer::SupportsType MediaPlayerPrivateGStreamerMSE::supportsType(const MediaEngineSupportParameters& parameters)
735{
736 MediaPlayer::SupportsType result = MediaPlayer::IsNotSupported;
737 if (!parameters.isMediaSource)
738 return result;
739
740 auto containerType = parameters.type.containerType();
741
742 // YouTube TV provides empty types for some videos and we want to be selected as best media engine for them.
743 if (containerType.isEmpty()) {
744 result = MediaPlayer::MayBeSupported;
745 GST_DEBUG("mime-type \"%s\" supported: %s", parameters.type.raw().utf8().data(), convertEnumerationToString(result).utf8().data());
746 return result;
747 }
748
749 GST_DEBUG("Checking mime-type \"%s\"", parameters.type.raw().utf8().data());
750 auto& gstRegistryScanner = GStreamerRegistryScannerMSE::singleton();
751 // Spec says we should not return "probably" if the codecs string is empty.
752 if (gstRegistryScanner.isContainerTypeSupported(containerType)) {
753 Vector<String> codecs = parameters.type.codecs();
754 result = codecs.isEmpty() ? MediaPlayer::MayBeSupported : (gstRegistryScanner.areAllCodecsSupported(codecs) ? MediaPlayer::IsSupported : MediaPlayer::IsNotSupported);
755 }
756
757 auto finalResult = extendedSupportsType(parameters, result);
758 GST_DEBUG("Supported: %s", convertEnumerationToString(finalResult).utf8().data());
759 return finalResult;
760}
761
762void MediaPlayerPrivateGStreamerMSE::markEndOfStream(MediaSourcePrivate::EndOfStreamStatus status)
763{
764 if (status != MediaSourcePrivate::EosNoError)
765 return;
766
767 GST_DEBUG("Marking end of stream");
768 m_eosMarked = true;
769 updateStates();
770}
771
772MediaTime MediaPlayerPrivateGStreamerMSE::currentMediaTime() const
773{
774 MediaTime position = MediaPlayerPrivateGStreamer::currentMediaTime();
775
776 if (m_eosPending && (paused() || (position >= durationMediaTime()))) {
777 if (m_networkState != MediaPlayer::Loaded) {
778 m_networkState = MediaPlayer::Loaded;
779 m_player->networkStateChanged();
780 }
781
782 m_eosPending = false;
783 m_isEndReached = true;
784 m_cachedPosition = m_mediaTimeDuration;
785 m_player->timeChanged();
786 }
787 return position;
788}
789
790MediaTime MediaPlayerPrivateGStreamerMSE::maxMediaTimeSeekable() const
791{
792 if (UNLIKELY(m_errorOccured))
793 return MediaTime::zeroTime();
794
795 GST_DEBUG("maxMediaTimeSeekable");
796 MediaTime result = durationMediaTime();
797 // Infinite duration means live stream.
798 if (result.isPositiveInfinite()) {
799 MediaTime maxBufferedTime = buffered()->maximumBufferedTime();
800 // Return the highest end time reported by the buffered attribute.
801 result = maxBufferedTime.isValid() ? maxBufferedTime : MediaTime::zeroTime();
802 }
803
804 return result;
805}
806
807} // namespace WebCore.
808
809#endif // USE(GSTREAMER)
810