f7656760d6fc94f4a10c4c5f473ccfff8ad84c5e
[WebKit-https.git] / Source / WebCore / platform / graphics / gstreamer / MediaPlayerPrivateGStreamer.cpp
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, 2015, 2016 Igalia S.L
7  * Copyright (C) 2014 Cable Television Laboratories, Inc.
8  * Copyright (C) 2015, 2016 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 "MediaPlayerPrivateGStreamer.h"
28
29 #if ENABLE(VIDEO) && USE(GSTREAMER)
30
31 #include "FileSystem.h"
32 #include "GStreamerUtilities.h"
33 #include "URL.h"
34 #include "MIMETypeRegistry.h"
35 #include "MediaPlayer.h"
36 #include "MediaPlayerRequestInstallMissingPluginsCallback.h"
37 #include "NotImplemented.h"
38 #include "SecurityOrigin.h"
39 #include "TimeRanges.h"
40 #include "WebKitWebSourceGStreamer.h"
41 #include <glib.h>
42 #include <gst/gst.h>
43 #include <gst/pbutils/missing-plugins.h>
44 #include <limits>
45 #include <wtf/HexNumber.h>
46 #include <wtf/MediaTime.h>
47 #include <wtf/NeverDestroyed.h>
48 #include <wtf/glib/GUniquePtr.h>
49 #include <wtf/text/CString.h>
50
51 #if ENABLE(VIDEO_TRACK)
52 #include "AudioTrackPrivateGStreamer.h"
53 #include "InbandMetadataTextTrackPrivateGStreamer.h"
54 #include "InbandTextTrackPrivateGStreamer.h"
55 #include "TextCombinerGStreamer.h"
56 #include "TextSinkGStreamer.h"
57 #include "VideoTrackPrivateGStreamer.h"
58 #endif
59
60 #if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
61 #define GST_USE_UNSTABLE_API
62 #include <gst/mpegts/mpegts.h>
63 #undef GST_USE_UNSTABLE_API
64 #endif
65 #include <gst/audio/streamvolume.h>
66
67 #if ENABLE(MEDIA_SOURCE)
68 #include "MediaSource.h"
69 #include "WebKitMediaSourceGStreamer.h"
70 #endif
71
72 #if ENABLE(WEB_AUDIO)
73 #include "AudioSourceProviderGStreamer.h"
74 #endif
75
76 GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
77 #define GST_CAT_DEFAULT webkit_media_player_debug
78
79 using namespace std;
80
81 namespace WebCore {
82
83 static void busMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
84 {
85     player->handleMessage(message);
86 }
87
88 void MediaPlayerPrivateGStreamer::setAudioStreamPropertiesCallback(MediaPlayerPrivateGStreamer* player, GObject* object)
89 {
90     player->setAudioStreamProperties(object);
91 }
92
93 void MediaPlayerPrivateGStreamer::setAudioStreamProperties(GObject* object)
94 {
95     if (g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
96         return;
97
98     const char* role = m_player->client().mediaPlayerIsVideo() ? "video" : "music";
99     GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, NULL);
100     g_object_set(object, "stream-properties", structure, NULL);
101     gst_structure_free(structure);
102     GUniquePtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(object)));
103     GST_DEBUG("Set media.role as %s at %s", role, elementName.get());
104 }
105
106 void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar)
107 {
108     if (isAvailable())
109         registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateGStreamer>(player); },
110             getSupportedTypes, supportsType, 0, 0, 0, supportsKeySystem);
111 }
112
113 bool initializeGStreamerAndRegisterWebKitElements()
114 {
115     if (!initializeGStreamer())
116         return false;
117
118     registerWebKitGStreamerElements();
119
120     GRefPtr<GstElementFactory> srcFactory = adoptGRef(gst_element_factory_find("webkitwebsrc"));
121     if (!srcFactory) {
122         GST_DEBUG_CATEGORY_INIT(webkit_media_player_debug, "webkitmediaplayer", 0, "WebKit media player");
123         gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
124     }
125
126     return true;
127 }
128
129 bool MediaPlayerPrivateGStreamer::isAvailable()
130 {
131     if (!initializeGStreamerAndRegisterWebKitElements())
132         return false;
133
134     GRefPtr<GstElementFactory> factory = adoptGRef(gst_element_factory_find("playbin"));
135     return factory;
136 }
137
138 MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
139     : MediaPlayerPrivateGStreamerBase(player)
140     , m_buffering(false)
141     , m_bufferingPercentage(0)
142     , m_canFallBackToLastFinishedSeekPosition(false)
143     , m_changingRate(false)
144     , m_downloadFinished(false)
145     , m_errorOccured(false)
146     , m_isEndReached(false)
147     , m_isStreaming(false)
148     , m_durationAtEOS(0)
149     , m_paused(true)
150     , m_playbackRate(1)
151     , m_requestedState(GST_STATE_VOID_PENDING)
152     , m_resetPipeline(false)
153     , m_seeking(false)
154     , m_seekIsPending(false)
155     , m_seekTime(0)
156     , m_source(0)
157     , m_volumeAndMuteInitialized(false)
158     , m_weakPtrFactory(this)
159     , m_mediaLocations(0)
160     , m_mediaLocationCurrentIndex(0)
161     , m_playbackRatePause(false)
162     , m_timeOfOverlappingSeek(-1)
163     , m_lastPlaybackRate(1)
164     , m_fillTimer(*this, &MediaPlayerPrivateGStreamer::fillTimerFired)
165     , m_maxTimeLoaded(0)
166     , m_preload(player->preload())
167     , m_delayingLoad(false)
168     , m_maxTimeLoadedAtLastDidLoadingProgress(0)
169     , m_hasVideo(false)
170     , m_hasAudio(false)
171     , m_readyTimerHandler(RunLoop::main(), this, &MediaPlayerPrivateGStreamer::readyTimerFired)
172     , m_totalBytes(0)
173     , m_preservesPitch(false)
174 {
175 #if USE(GLIB)
176     m_readyTimerHandler.setPriority(G_PRIORITY_DEFAULT_IDLE);
177 #endif
178 }
179
180 MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
181 {
182 #if ENABLE(VIDEO_TRACK)
183     for (size_t i = 0; i < m_audioTracks.size(); ++i)
184         m_audioTracks[i]->disconnect();
185
186     for (size_t i = 0; i < m_textTracks.size(); ++i)
187         m_textTracks[i]->disconnect();
188
189     for (size_t i = 0; i < m_videoTracks.size(); ++i)
190         m_videoTracks[i]->disconnect();
191 #endif
192     if (m_fillTimer.isActive())
193         m_fillTimer.stop();
194
195     if (m_mediaLocations) {
196         gst_structure_free(m_mediaLocations);
197         m_mediaLocations = 0;
198     }
199
200     if (WEBKIT_IS_WEB_SRC(m_source.get()) && GST_OBJECT_PARENT(m_source.get()))
201         g_signal_handlers_disconnect_by_func(GST_ELEMENT_PARENT(m_source.get()), reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), this);
202
203     if (m_autoAudioSink)
204         g_signal_handlers_disconnect_by_func(G_OBJECT(m_autoAudioSink.get()),
205             reinterpret_cast<gpointer>(setAudioStreamPropertiesCallback), this);
206
207     m_readyTimerHandler.stop();
208     if (m_missingPluginsCallback) {
209         m_missingPluginsCallback->invalidate();
210         m_missingPluginsCallback = nullptr;
211     }
212
213     if (m_videoSink) {
214         GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_videoSink.get(), "sink"));
215         g_signal_handlers_disconnect_matched(videoSinkPad.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
216     }
217
218     if (m_pipeline) {
219         GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
220         ASSERT(bus);
221         g_signal_handlers_disconnect_by_func(bus.get(), gpointer(busMessageCallback), this);
222         gst_bus_remove_signal_watch(bus.get());
223         gst_bus_set_sync_handler(bus.get(), nullptr, nullptr, nullptr);
224         g_signal_handlers_disconnect_matched(m_pipeline.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
225     }
226 }
227
228 void MediaPlayerPrivateGStreamer::load(const String& urlString)
229 {
230     if (!initializeGStreamerAndRegisterWebKitElements())
231         return;
232
233     URL url(URL(), urlString);
234     if (url.isBlankURL())
235         return;
236
237     // Clean out everything after file:// url path.
238     String cleanURL(urlString);
239     if (url.isLocalFile())
240         cleanURL = cleanURL.substring(0, url.pathEnd());
241
242     if (!m_pipeline)
243         createGSTPlayBin();
244
245     if (m_fillTimer.isActive())
246         m_fillTimer.stop();
247
248     ASSERT(m_pipeline);
249
250     m_url = URL(URL(), cleanURL);
251     g_object_set(m_pipeline.get(), "uri", cleanURL.utf8().data(), nullptr);
252
253     GST_INFO("Load %s", cleanURL.utf8().data());
254
255     if (m_preload == MediaPlayer::None) {
256         GST_DEBUG("Delaying load.");
257         m_delayingLoad = true;
258     }
259
260     // Reset network and ready states. Those will be set properly once
261     // the pipeline pre-rolled.
262     m_networkState = MediaPlayer::Loading;
263     m_player->networkStateChanged();
264     m_readyState = MediaPlayer::HaveNothing;
265     m_player->readyStateChanged();
266     m_volumeAndMuteInitialized = false;
267     m_durationAtEOS = 0;
268
269     if (!m_delayingLoad)
270         commitLoad();
271 }
272
273 #if ENABLE(MEDIA_SOURCE)
274 void MediaPlayerPrivateGStreamer::load(const String&, MediaSourcePrivateClient*)
275 {
276     // Properly fail so the global MediaPlayer tries to fallback to the next MediaPlayerPrivate.
277     m_networkState = MediaPlayer::FormatError;
278     m_player->networkStateChanged();
279 }
280 #endif
281
282 #if ENABLE(MEDIA_STREAM)
283 void MediaPlayerPrivateGStreamer::load(MediaStreamPrivate&)
284 {
285     notImplemented();
286 }
287 #endif
288
289 void MediaPlayerPrivateGStreamer::commitLoad()
290 {
291     ASSERT(!m_delayingLoad);
292     GST_DEBUG("Committing load.");
293
294     // GStreamer needs to have the pipeline set to a paused state to
295     // start providing anything useful.
296     changePipelineState(GST_STATE_PAUSED);
297
298     setDownloadBuffering();
299     updateStates();
300 }
301
302 double MediaPlayerPrivateGStreamer::playbackPosition() const
303 {
304     if (m_isEndReached) {
305         // Position queries on a null pipeline return 0. If we're at
306         // the end of the stream the pipeline is null but we want to
307         // report either the seek time or the duration because this is
308         // what the Media element spec expects us to do.
309         if (m_seeking)
310             return m_seekTime;
311
312         MediaTime mediaDuration = durationMediaTime();
313         if (mediaDuration)
314             return mediaDuration.toDouble();
315         return 0;
316     }
317
318     // Position is only available if no async state change is going on and the state is either paused or playing.
319     gint64 position = GST_CLOCK_TIME_NONE;
320     GstQuery* query= gst_query_new_position(GST_FORMAT_TIME);
321     if (gst_element_query(m_pipeline.get(), query))
322         gst_query_parse_position(query, 0, &position);
323     gst_query_unref(query);
324
325     GST_DEBUG("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
326
327     double result = 0.0f;
328     if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE) {
329         GTimeVal timeValue;
330         GST_TIME_TO_TIMEVAL(position, timeValue);
331         result = static_cast<double>(timeValue.tv_sec + (timeValue.tv_usec / 1000000.0));
332     } else if (m_canFallBackToLastFinishedSeekPosition)
333         result = m_seekTime;
334
335     return result;
336 }
337
338 void MediaPlayerPrivateGStreamer::readyTimerFired()
339 {
340     changePipelineState(GST_STATE_NULL);
341 }
342
343 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
344 {
345     ASSERT(m_pipeline);
346
347     GstState currentState;
348     GstState pending;
349
350     gst_element_get_state(m_pipeline.get(), &currentState, &pending, 0);
351     if (currentState == newState || pending == newState) {
352         GST_DEBUG("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState),
353             gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
354         return true;
355     }
356
357     GST_DEBUG("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState),
358         gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
359
360     GstStateChangeReturn setStateResult = gst_element_set_state(m_pipeline.get(), newState);
361     GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
362     if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE) {
363         return false;
364     }
365
366     // Create a timer when entering the READY state so that we can free resources
367     // if we stay for too long on READY.
368     // Also lets remove the timer if we request a state change for any state other than READY.
369     // See also https://bugs.webkit.org/show_bug.cgi?id=117354
370     if (newState == GST_STATE_READY && !m_readyTimerHandler.isActive()) {
371         // Max interval in seconds to stay in the READY state on manual
372         // state change requests.
373         static const double readyStateTimerDelay = 60;
374         m_readyTimerHandler.startOneShot(readyStateTimerDelay);
375     } else if (newState != GST_STATE_READY)
376         m_readyTimerHandler.stop();
377
378     return true;
379 }
380
381 void MediaPlayerPrivateGStreamer::prepareToPlay()
382 {
383     m_preload = MediaPlayer::Auto;
384     if (m_delayingLoad) {
385         m_delayingLoad = false;
386         commitLoad();
387     }
388 }
389
390 void MediaPlayerPrivateGStreamer::play()
391 {
392     if (!m_playbackRate) {
393         m_playbackRatePause = true;
394         return;
395     }
396
397     if (changePipelineState(GST_STATE_PLAYING)) {
398         m_isEndReached = false;
399         m_delayingLoad = false;
400         m_preload = MediaPlayer::Auto;
401         setDownloadBuffering();
402         GST_DEBUG("Play");
403     } else {
404         loadingFailed(MediaPlayer::Empty);
405     }
406 }
407
408 void MediaPlayerPrivateGStreamer::pause()
409 {
410     m_playbackRatePause = false;
411     GstState currentState, pendingState;
412     gst_element_get_state(m_pipeline.get(), &currentState, &pendingState, 0);
413     if (currentState < GST_STATE_PAUSED && pendingState <= GST_STATE_PAUSED)
414         return;
415
416     if (changePipelineState(GST_STATE_PAUSED))
417         GST_INFO("Pause");
418     else
419         loadingFailed(MediaPlayer::Empty);
420 }
421
422 MediaTime MediaPlayerPrivateGStreamer::durationMediaTime() const
423 {
424     if (!m_pipeline)
425         return { };
426
427     if (m_errorOccured)
428         return { };
429
430     if (m_durationAtEOS)
431         return MediaTime::createWithDouble(m_durationAtEOS);
432
433     // The duration query would fail on a not-prerolled pipeline.
434     if (GST_STATE(m_pipeline.get()) < GST_STATE_PAUSED)
435         return { };
436
437     GstFormat timeFormat = GST_FORMAT_TIME;
438     gint64 timeLength = 0;
439
440     bool failure = !gst_element_query_duration(m_pipeline.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
441     if (failure) {
442         GST_DEBUG("Time duration query failed for %s", m_url.string().utf8().data());
443         return MediaTime::positiveInfiniteTime();
444     }
445
446     GST_DEBUG("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
447
448     return MediaTime::createWithDouble(static_cast<double>(timeLength) / GST_SECOND);
449     // FIXME: handle 3.14.9.5 properly
450 }
451
452 MediaTime MediaPlayerPrivateGStreamer::currentMediaTime() const
453 {
454     if (!m_pipeline)
455         return { };
456
457     if (m_errorOccured)
458         return { };
459
460     if (m_seeking)
461         return MediaTime::createWithFloat(m_seekTime);
462
463     // Workaround for
464     // https://bugzilla.gnome.org/show_bug.cgi?id=639941 In GStreamer
465     // 0.10.35 basesink reports wrong duration in case of EOS and
466     // negative playback rate. There's no upstream accepted patch for
467     // this bug yet, hence this temporary workaround.
468     if (m_isEndReached && m_playbackRate < 0)
469         return { };
470
471     return MediaTime::createWithDouble(playbackPosition());
472 }
473
474 void MediaPlayerPrivateGStreamer::seek(float time)
475 {
476     if (!m_pipeline)
477         return;
478
479     if (m_errorOccured)
480         return;
481
482     GST_INFO("[Seek] seek attempt to %f secs", time);
483
484     // Avoid useless seeking.
485     if (MediaTime::createWithFloat(time) == currentMediaTime())
486         return;
487
488     if (isLiveStream())
489         return;
490
491     GstClockTime clockTime = toGstClockTime(time);
492     GST_INFO("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time);
493
494     if (m_seeking) {
495         m_timeOfOverlappingSeek = time;
496         if (m_seekIsPending) {
497             m_seekTime = time;
498             return;
499         }
500     }
501
502     GstState state;
503     GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
504     if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) {
505         GST_DEBUG("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
506         return;
507     }
508     if (getStateResult == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED || m_isEndReached) {
509         m_seekIsPending = true;
510         if (m_isEndReached) {
511             GST_DEBUG("[Seek] reset pipeline");
512             m_resetPipeline = true;
513             if (!changePipelineState(GST_STATE_PAUSED))
514                 loadingFailed(MediaPlayer::Empty);
515         }
516     } else {
517         // We can seek now.
518         if (!doSeek(clockTime, m_player->rate(), static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE))) {
519             GST_DEBUG("[Seek] seeking to %f failed", time);
520             return;
521         }
522     }
523
524     m_seeking = true;
525     m_seekTime = time;
526     m_isEndReached = false;
527 }
528
529 bool MediaPlayerPrivateGStreamer::doSeek(gint64 position, float rate, GstSeekFlags seekType)
530 {
531     gint64 startTime, endTime;
532
533     // TODO: Should do more than that, need to notify the media source
534     // and probably flush the pipeline at least.
535     if (isMediaSource())
536         return true;
537
538     if (rate > 0) {
539         startTime = position;
540         endTime = GST_CLOCK_TIME_NONE;
541     } else {
542         startTime = 0;
543         // If we are at beginning of media, start from the end to
544         // avoid immediate EOS.
545         if (position < 0)
546             endTime = static_cast<gint64>(durationMediaTime().toDouble() * GST_SECOND);
547         else
548             endTime = position;
549     }
550
551     if (!rate)
552         rate = 1.0;
553
554     return gst_element_seek(m_pipeline.get(), rate, GST_FORMAT_TIME, seekType,
555         GST_SEEK_TYPE_SET, startTime, GST_SEEK_TYPE_SET, endTime);
556 }
557
558 void MediaPlayerPrivateGStreamer::updatePlaybackRate()
559 {
560     if (!m_changingRate)
561         return;
562
563     float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND);
564     bool mute = false;
565
566     GST_INFO("Set Rate to %f", m_playbackRate);
567
568     if (m_playbackRate > 0) {
569         // Mute the sound if the playback rate is too extreme and
570         // audio pitch is not adjusted.
571         mute = (!m_preservesPitch && (m_playbackRate < 0.8 || m_playbackRate > 2));
572     } else {
573         if (currentPosition == 0.0f)
574             currentPosition = -1.0f;
575         mute = true;
576     }
577
578     GST_INFO("Need to mute audio?: %d", (int) mute);
579     if (doSeek(currentPosition, m_playbackRate, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH))) {
580         g_object_set(m_pipeline.get(), "mute", mute, nullptr);
581         m_lastPlaybackRate = m_playbackRate;
582     } else {
583         m_playbackRate = m_lastPlaybackRate;
584         GST_ERROR("Set rate to %f failed", m_playbackRate);
585     }
586
587     if (m_playbackRatePause) {
588         GstState state;
589         GstState pending;
590
591         gst_element_get_state(m_pipeline.get(), &state, &pending, 0);
592         if (state != GST_STATE_PLAYING && pending != GST_STATE_PLAYING)
593             changePipelineState(GST_STATE_PLAYING);
594         m_playbackRatePause = false;
595     }
596
597     m_changingRate = false;
598     m_player->rateChanged();
599 }
600
601 bool MediaPlayerPrivateGStreamer::paused() const
602 {
603     if (m_isEndReached) {
604         GST_DEBUG("Ignoring pause at EOS");
605         return true;
606     }
607
608     if (m_playbackRatePause)
609         return false;
610
611     GstState state;
612     gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
613     return state <= GST_STATE_PAUSED;
614 }
615
616 bool MediaPlayerPrivateGStreamer::seeking() const
617 {
618     return m_seeking;
619 }
620
621 void MediaPlayerPrivateGStreamer::videoChangedCallback(MediaPlayerPrivateGStreamer* player)
622 {
623     player->m_notifier.notify(MainThreadNotification::VideoChanged, [player] { player->notifyPlayerOfVideo(); });
624 }
625
626 void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
627 {
628     if (UNLIKELY(!m_pipeline || !m_source))
629         return;
630
631     gint numTracks = 0;
632     bool useMediaSource = isMediaSource();
633     GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get();
634     g_object_get(element, "n-video", &numTracks, nullptr);
635
636     m_hasVideo = numTracks > 0;
637     if (m_hasVideo)
638         m_player->sizeChanged();
639
640     if (useMediaSource) {
641         GST_DEBUG("Tracks managed by source element. Bailing out now.");
642         m_player->client().mediaPlayerEngineUpdated(m_player);
643         return;
644     }
645
646 #if ENABLE(VIDEO_TRACK)
647     for (gint i = 0; i < numTracks; ++i) {
648         GRefPtr<GstPad> pad;
649         g_signal_emit_by_name(m_pipeline.get(), "get-video-pad", i, &pad.outPtr(), nullptr);
650         ASSERT(pad);
651
652         if (i < static_cast<gint>(m_videoTracks.size())) {
653             RefPtr<VideoTrackPrivateGStreamer> existingTrack = m_videoTracks[i];
654             existingTrack->setIndex(i);
655             if (existingTrack->pad() == pad)
656                 continue;
657         }
658
659         RefPtr<VideoTrackPrivateGStreamer> track = VideoTrackPrivateGStreamer::create(m_pipeline, i, pad);
660         m_videoTracks.append(track);
661         m_player->addVideoTrack(*track);
662     }
663
664     while (static_cast<gint>(m_videoTracks.size()) > numTracks) {
665         RefPtr<VideoTrackPrivateGStreamer> track = m_videoTracks.last();
666         track->disconnect();
667         m_videoTracks.removeLast();
668         m_player->removeVideoTrack(*track);
669     }
670 #endif
671
672     m_player->client().mediaPlayerEngineUpdated(m_player);
673 }
674
675 void MediaPlayerPrivateGStreamer::videoSinkCapsChangedCallback(MediaPlayerPrivateGStreamer* player)
676 {
677     player->m_notifier.notify(MainThreadNotification::VideoCapsChanged, [player] { player->notifyPlayerOfVideoCaps(); });
678 }
679
680 void MediaPlayerPrivateGStreamer::notifyPlayerOfVideoCaps()
681 {
682     m_videoSize = IntSize();
683     m_player->client().mediaPlayerEngineUpdated(m_player);
684 }
685
686 void MediaPlayerPrivateGStreamer::audioChangedCallback(MediaPlayerPrivateGStreamer* player)
687 {
688     player->m_notifier.notify(MainThreadNotification::AudioChanged, [player] { player->notifyPlayerOfAudio(); });
689 }
690
691 void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
692 {
693     if (UNLIKELY(!m_pipeline || !m_source))
694         return;
695
696     gint numTracks = 0;
697     bool useMediaSource = isMediaSource();
698     GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get();
699     g_object_get(element, "n-audio", &numTracks, nullptr);
700
701     m_hasAudio = numTracks > 0;
702
703     if (useMediaSource) {
704         GST_DEBUG("Tracks managed by source element. Bailing out now.");
705         m_player->client().mediaPlayerEngineUpdated(m_player);
706         return;
707     }
708
709 #if ENABLE(VIDEO_TRACK)
710     for (gint i = 0; i < numTracks; ++i) {
711         GRefPtr<GstPad> pad;
712         g_signal_emit_by_name(m_pipeline.get(), "get-audio-pad", i, &pad.outPtr(), nullptr);
713         ASSERT(pad);
714
715         if (i < static_cast<gint>(m_audioTracks.size())) {
716             RefPtr<AudioTrackPrivateGStreamer> existingTrack = m_audioTracks[i];
717             existingTrack->setIndex(i);
718             if (existingTrack->pad() == pad)
719                 continue;
720         }
721
722         RefPtr<AudioTrackPrivateGStreamer> track = AudioTrackPrivateGStreamer::create(m_pipeline, i, pad);
723         m_audioTracks.insert(i, track);
724         m_player->addAudioTrack(*track);
725     }
726
727     while (static_cast<gint>(m_audioTracks.size()) > numTracks) {
728         RefPtr<AudioTrackPrivateGStreamer> track = m_audioTracks.last();
729         track->disconnect();
730         m_audioTracks.removeLast();
731         m_player->removeAudioTrack(*track);
732     }
733 #endif
734
735     m_player->client().mediaPlayerEngineUpdated(m_player);
736 }
737
738 #if ENABLE(VIDEO_TRACK)
739 void MediaPlayerPrivateGStreamer::textChangedCallback(MediaPlayerPrivateGStreamer* player)
740 {
741     player->m_notifier.notify(MainThreadNotification::TextChanged, [player] { player->notifyPlayerOfText(); });
742 }
743
744 void MediaPlayerPrivateGStreamer::notifyPlayerOfText()
745 {
746     if (UNLIKELY(!m_pipeline || !m_source))
747         return;
748
749     gint numTracks = 0;
750     bool useMediaSource = isMediaSource();
751     GstElement* element = useMediaSource ? m_source.get() : m_pipeline.get();
752     g_object_get(element, "n-text", &numTracks, nullptr);
753
754     if (useMediaSource) {
755         GST_DEBUG("Tracks managed by source element. Bailing out now.");
756         return;
757     }
758
759     for (gint i = 0; i < numTracks; ++i) {
760         GRefPtr<GstPad> pad;
761         g_signal_emit_by_name(m_pipeline.get(), "get-text-pad", i, &pad.outPtr(), nullptr);
762         ASSERT(pad);
763
764         if (i < static_cast<gint>(m_textTracks.size())) {
765             RefPtr<InbandTextTrackPrivateGStreamer> existingTrack = m_textTracks[i];
766             existingTrack->setIndex(i);
767             if (existingTrack->pad() == pad)
768                 continue;
769         }
770
771         RefPtr<InbandTextTrackPrivateGStreamer> track = InbandTextTrackPrivateGStreamer::create(i, pad);
772         m_textTracks.insert(i, track);
773         m_player->addTextTrack(*track);
774     }
775
776     while (static_cast<gint>(m_textTracks.size()) > numTracks) {
777         RefPtr<InbandTextTrackPrivateGStreamer> track = m_textTracks.last();
778         track->disconnect();
779         m_textTracks.removeLast();
780         m_player->removeTextTrack(*track);
781     }
782 }
783
784 GstFlowReturn MediaPlayerPrivateGStreamer::newTextSampleCallback(MediaPlayerPrivateGStreamer* player)
785 {
786     player->newTextSample();
787     return GST_FLOW_OK;
788 }
789
790 void MediaPlayerPrivateGStreamer::newTextSample()
791 {
792     if (!m_textAppSink)
793         return;
794
795     GRefPtr<GstEvent> streamStartEvent = adoptGRef(
796         gst_pad_get_sticky_event(m_textAppSinkPad.get(), GST_EVENT_STREAM_START, 0));
797
798     GRefPtr<GstSample> sample;
799     g_signal_emit_by_name(m_textAppSink.get(), "pull-sample", &sample.outPtr(), NULL);
800     ASSERT(sample);
801
802     if (streamStartEvent) {
803         bool found = FALSE;
804         const gchar* id;
805         gst_event_parse_stream_start(streamStartEvent.get(), &id);
806         for (size_t i = 0; i < m_textTracks.size(); ++i) {
807             RefPtr<InbandTextTrackPrivateGStreamer> track = m_textTracks[i];
808             if (track->streamId() == id) {
809                 track->handleSample(sample);
810                 found = true;
811                 break;
812             }
813         }
814         if (!found)
815             GST_WARNING("Got sample with unknown stream ID.");
816     } else
817         GST_WARNING("Unable to handle sample with no stream start event.");
818 }
819 #endif
820
821 void MediaPlayerPrivateGStreamer::setRate(float rate)
822 {
823     // Higher rate causes crash.
824     rate = clampTo(rate, -20.0, 20.0);
825
826     // Avoid useless playback rate update.
827     if (m_playbackRate == rate) {
828         // and make sure that upper layers were notified if rate was set
829
830         if (!m_changingRate && m_player->rate() != m_playbackRate)
831             m_player->rateChanged();
832         return;
833     }
834
835     if (isLiveStream()) {
836         // notify upper layers that we cannot handle passed rate.
837         m_changingRate = false;
838         m_player->rateChanged();
839         return;
840     }
841
842     GstState state;
843     GstState pending;
844
845     m_playbackRate = rate;
846     m_changingRate = true;
847
848     gst_element_get_state(m_pipeline.get(), &state, &pending, 0);
849
850     if (!rate) {
851         m_changingRate = false;
852         m_playbackRatePause = true;
853         if (state != GST_STATE_PAUSED && pending != GST_STATE_PAUSED)
854             changePipelineState(GST_STATE_PAUSED);
855         return;
856     }
857
858     if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
859         || (pending == GST_STATE_PAUSED))
860         return;
861
862     updatePlaybackRate();
863 }
864
865 double MediaPlayerPrivateGStreamer::rate() const
866 {
867     return m_playbackRate;
868 }
869
870 void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch)
871 {
872     m_preservesPitch = preservesPitch;
873 }
874
875 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateGStreamer::buffered() const
876 {
877     auto timeRanges = std::make_unique<PlatformTimeRanges>();
878     if (m_errorOccured || isLiveStream())
879         return timeRanges;
880
881     float mediaDuration(durationMediaTime().toDouble());
882     if (!mediaDuration || std::isinf(mediaDuration))
883         return timeRanges;
884
885     GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
886
887     if (!gst_element_query(m_pipeline.get(), query)) {
888         gst_query_unref(query);
889         return timeRanges;
890     }
891
892     guint numBufferingRanges = gst_query_get_n_buffering_ranges(query);
893     for (guint index = 0; index < numBufferingRanges; index++) {
894         gint64 rangeStart = 0, rangeStop = 0;
895         if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop))
896             timeRanges->add(MediaTime::createWithDouble((rangeStart * mediaDuration) / GST_FORMAT_PERCENT_MAX),
897                 MediaTime::createWithDouble((rangeStop * mediaDuration) / GST_FORMAT_PERCENT_MAX));
898     }
899
900     // Fallback to the more general maxTimeLoaded() if no range has
901     // been found.
902     if (!timeRanges->length())
903         if (float loaded = maxTimeLoaded())
904             timeRanges->add(MediaTime::zeroTime(), MediaTime::createWithDouble(loaded));
905
906     gst_query_unref(query);
907
908     return timeRanges;
909 }
910
911 void MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
912 {
913     GUniqueOutPtr<GError> err;
914     GUniqueOutPtr<gchar> debug;
915     MediaPlayer::NetworkState error;
916     bool issueError = true;
917     bool attemptNextLocation = false;
918     const GstStructure* structure = gst_message_get_structure(message);
919     GstState requestedState, currentState;
920
921     m_canFallBackToLastFinishedSeekPosition = false;
922
923     if (structure) {
924         const gchar* messageTypeName = gst_structure_get_name(structure);
925
926         // Redirect messages are sent from elements, like qtdemux, to
927         // notify of the new location(s) of the media.
928         if (!g_strcmp0(messageTypeName, "redirect")) {
929             mediaLocationChanged(message);
930             return;
931         }
932     }
933
934     // We ignore state changes from internal elements. They are forwarded to playbin2 anyway.
935     bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_pipeline.get());
936
937     GST_DEBUG("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message));
938     switch (GST_MESSAGE_TYPE(message)) {
939     case GST_MESSAGE_ERROR:
940         if (m_resetPipeline)
941             break;
942         if (m_missingPluginsCallback)
943             break;
944         gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
945         GST_ERROR("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
946
947         GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error");
948
949         error = MediaPlayer::Empty;
950         if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
951             || err->code == GST_STREAM_ERROR_WRONG_TYPE
952             || err->code == GST_STREAM_ERROR_FAILED
953             || err->code == GST_CORE_ERROR_MISSING_PLUGIN
954             || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
955             error = MediaPlayer::FormatError;
956         else if (err->domain == GST_STREAM_ERROR) {
957             // Let the mediaPlayerClient handle the stream error, in
958             // this case the HTMLMediaElement will emit a stalled
959             // event.
960             if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
961                 GST_ERROR("Decode error, let the Media element emit a stalled event.");
962                 break;
963             }
964             error = MediaPlayer::DecodeError;
965             attemptNextLocation = true;
966         } else if (err->domain == GST_RESOURCE_ERROR)
967             error = MediaPlayer::NetworkError;
968
969         if (attemptNextLocation)
970             issueError = !loadNextLocation();
971         if (issueError)
972             loadingFailed(error);
973         break;
974     case GST_MESSAGE_EOS:
975         didEnd();
976         break;
977     case GST_MESSAGE_ASYNC_DONE:
978         if (!messageSourceIsPlaybin || m_delayingLoad)
979             break;
980         asyncStateChangeDone();
981         break;
982     case GST_MESSAGE_STATE_CHANGED: {
983         if (!messageSourceIsPlaybin || m_delayingLoad)
984             break;
985         updateStates();
986
987         // Construct a filename for the graphviz dot file output.
988         GstState newState;
989         gst_message_parse_state_changed(message, &currentState, &newState, 0);
990         CString dotFileName = String::format("webkit-video.%s_%s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState)).utf8();
991         GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
992
993         break;
994     }
995     case GST_MESSAGE_BUFFERING:
996         processBufferingStats(message);
997         break;
998     case GST_MESSAGE_DURATION_CHANGED:
999         // Duration in MSE is managed by MediaSource, SourceBuffer and AppendPipeline.
1000         if (messageSourceIsPlaybin && !isMediaSource())
1001             durationChanged();
1002         break;
1003     case GST_MESSAGE_REQUEST_STATE:
1004         gst_message_parse_request_state(message, &requestedState);
1005         gst_element_get_state(m_pipeline.get(), &currentState, nullptr, 250 * GST_NSECOND);
1006         if (requestedState < currentState) {
1007             GUniquePtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(message)));
1008             GST_INFO("Element %s requested state change to %s", elementName.get(),
1009                 gst_element_state_get_name(requestedState));
1010             m_requestedState = requestedState;
1011             if (!changePipelineState(requestedState))
1012                 loadingFailed(MediaPlayer::Empty);
1013         }
1014         break;
1015     case GST_MESSAGE_CLOCK_LOST:
1016         // This can only happen in PLAYING state and we should just
1017         // get a new clock by moving back to PAUSED and then to
1018         // PLAYING again.
1019         // This can happen if the stream that ends in a sink that
1020         // provides the current clock disappears, for example if
1021         // the audio sink provides the clock and the audio stream
1022         // is disabled. It also happens relatively often with
1023         // HTTP adaptive streams when switching between different
1024         // variants of a stream.
1025         gst_element_set_state(m_pipeline.get(), GST_STATE_PAUSED);
1026         gst_element_set_state(m_pipeline.get(), GST_STATE_PLAYING);
1027         break;
1028     case GST_MESSAGE_LATENCY:
1029         // Recalculate the latency, we don't need any special handling
1030         // here other than the GStreamer default.
1031         // This can happen if the latency of live elements changes, or
1032         // for one reason or another a new live element is added or
1033         // removed from the pipeline.
1034         gst_bin_recalculate_latency(GST_BIN(m_pipeline.get()));
1035         break;
1036     case GST_MESSAGE_ELEMENT:
1037         if (gst_is_missing_plugin_message(message)) {
1038             if (gst_install_plugins_supported()) {
1039                 m_missingPluginsCallback = MediaPlayerRequestInstallMissingPluginsCallback::create([this](uint32_t result) {
1040                     m_missingPluginsCallback = nullptr;
1041                     if (result != GST_INSTALL_PLUGINS_SUCCESS)
1042                         return;
1043
1044                     changePipelineState(GST_STATE_READY);
1045                     changePipelineState(GST_STATE_PAUSED);
1046                 });
1047                 GUniquePtr<char> detail(gst_missing_plugin_message_get_installer_detail(message));
1048                 GUniquePtr<char> description(gst_missing_plugin_message_get_description(message));
1049                 m_player->client().requestInstallMissingPlugins(String::fromUTF8(detail.get()), String::fromUTF8(description.get()), *m_missingPluginsCallback);
1050             }
1051         }
1052 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
1053         else if (gst_structure_has_name(structure, "drm-key-needed")) {
1054             GST_DEBUG("drm-key-needed message from %s", GST_MESSAGE_SRC_NAME(message));
1055             GRefPtr<GstEvent> event;
1056             gst_structure_get(structure, "event", GST_TYPE_EVENT, &event.outPtr(), nullptr);
1057             handleProtectionEvent(event.get());
1058         }
1059 #endif
1060 #if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
1061         else {
1062             GstMpegtsSection* section = gst_message_parse_mpegts_section(message);
1063             if (section) {
1064                 processMpegTsSection(section);
1065                 gst_mpegts_section_unref(section);
1066             }
1067         }
1068 #endif
1069         break;
1070 #if ENABLE(VIDEO_TRACK)
1071     case GST_MESSAGE_TOC:
1072         processTableOfContents(message);
1073         break;
1074 #endif
1075     case GST_MESSAGE_TAG: {
1076         GstTagList* tags = nullptr;
1077         GUniqueOutPtr<gchar> tag;
1078         gst_message_parse_tag(message, &tags);
1079         if (gst_tag_list_get_string(tags, GST_TAG_IMAGE_ORIENTATION, &tag.outPtr())) {
1080             if (!g_strcmp0(tag.get(), "rotate-90"))
1081                 setVideoSourceOrientation(ImageOrientation(OriginRightTop));
1082             else if (!g_strcmp0(tag.get(), "rotate-180"))
1083                 setVideoSourceOrientation(ImageOrientation(OriginBottomRight));
1084             else if (!g_strcmp0(tag.get(), "rotate-270"))
1085                 setVideoSourceOrientation(ImageOrientation(OriginLeftBottom));
1086         }
1087         gst_tag_list_unref(tags);
1088         break;
1089     }
1090     default:
1091         GST_DEBUG("Unhandled GStreamer message type: %s",
1092                     GST_MESSAGE_TYPE_NAME(message));
1093         break;
1094     }
1095     return;
1096 }
1097
1098 void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
1099 {
1100     m_buffering = true;
1101     gst_message_parse_buffering(message, &m_bufferingPercentage);
1102
1103     GST_DEBUG("[Buffering] Buffering: %d%%.", m_bufferingPercentage);
1104
1105     updateStates();
1106 }
1107
1108 #if ENABLE(VIDEO_TRACK) && USE(GSTREAMER_MPEGTS)
1109 void MediaPlayerPrivateGStreamer::processMpegTsSection(GstMpegtsSection* section)
1110 {
1111     ASSERT(section);
1112
1113     if (section->section_type == GST_MPEGTS_SECTION_PMT) {
1114         const GstMpegtsPMT* pmt = gst_mpegts_section_get_pmt(section);
1115         m_metadataTracks.clear();
1116         for (guint i = 0; i < pmt->streams->len; ++i) {
1117             const GstMpegtsPMTStream* stream = static_cast<const GstMpegtsPMTStream*>(g_ptr_array_index(pmt->streams, i));
1118             if (stream->stream_type == 0x05 || stream->stream_type >= 0x80) {
1119                 AtomicString pid = String::number(stream->pid);
1120                 RefPtr<InbandMetadataTextTrackPrivateGStreamer> track = InbandMetadataTextTrackPrivateGStreamer::create(
1121                     InbandTextTrackPrivate::Metadata, InbandTextTrackPrivate::Data, pid);
1122
1123                 // 4.7.10.12.2 Sourcing in-band text tracks
1124                 // If the new text track's kind is metadata, then set the text track in-band metadata track dispatch
1125                 // type as follows, based on the type of the media resource:
1126                 // Let stream type be the value of the "stream_type" field describing the text track's type in the
1127                 // file's program map section, interpreted as an 8-bit unsigned integer. Let length be the value of
1128                 // the "ES_info_length" field for the track in the same part of the program map section, interpreted
1129                 // as an integer as defined by the MPEG-2 specification. Let descriptor bytes be the length bytes
1130                 // following the "ES_info_length" field. The text track in-band metadata track dispatch type must be
1131                 // set to the concatenation of the stream type byte and the zero or more descriptor bytes bytes,
1132                 // expressed in hexadecimal using uppercase ASCII hex digits.
1133                 String inbandMetadataTrackDispatchType;
1134                 appendUnsignedAsHexFixedSize(stream->stream_type, inbandMetadataTrackDispatchType, 2);
1135                 for (guint j = 0; j < stream->descriptors->len; ++j) {
1136                     const GstMpegtsDescriptor* descriptor = static_cast<const GstMpegtsDescriptor*>(g_ptr_array_index(stream->descriptors, j));
1137                     for (guint k = 0; k < descriptor->length; ++k)
1138                         appendByteAsHex(descriptor->data[k], inbandMetadataTrackDispatchType);
1139                 }
1140                 track->setInBandMetadataTrackDispatchType(inbandMetadataTrackDispatchType);
1141
1142                 m_metadataTracks.add(pid, track);
1143                 m_player->addTextTrack(*track);
1144             }
1145         }
1146     } else {
1147         AtomicString pid = String::number(section->pid);
1148         RefPtr<InbandMetadataTextTrackPrivateGStreamer> track = m_metadataTracks.get(pid);
1149         if (!track)
1150             return;
1151
1152         GRefPtr<GBytes> data = gst_mpegts_section_get_data(section);
1153         gsize size;
1154         const void* bytes = g_bytes_get_data(data.get(), &size);
1155
1156         track->addDataCue(currentMediaTime(), currentMediaTime(), bytes, size);
1157     }
1158 }
1159 #endif
1160
1161 #if ENABLE(VIDEO_TRACK)
1162 void MediaPlayerPrivateGStreamer::processTableOfContents(GstMessage* message)
1163 {
1164     if (m_chaptersTrack)
1165         m_player->removeTextTrack(*m_chaptersTrack);
1166
1167     m_chaptersTrack = InbandMetadataTextTrackPrivateGStreamer::create(InbandTextTrackPrivate::Chapters, InbandTextTrackPrivate::Generic);
1168     m_player->addTextTrack(*m_chaptersTrack);
1169
1170     GRefPtr<GstToc> toc;
1171     gboolean updated;
1172     gst_message_parse_toc(message, &toc.outPtr(), &updated);
1173     ASSERT(toc);
1174
1175     for (GList* i = gst_toc_get_entries(toc.get()); i; i = i->next)
1176         processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data), 0);
1177 }
1178
1179 void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry, GstTocEntry* parent)
1180 {
1181     UNUSED_PARAM(parent);
1182     ASSERT(entry);
1183
1184     RefPtr<GenericCueData> cue = GenericCueData::create();
1185
1186     gint64 start = -1, stop = -1;
1187     gst_toc_entry_get_start_stop_times(entry, &start, &stop);
1188     if (start != -1)
1189         cue->setStartTime(MediaTime(start, GST_SECOND));
1190     if (stop != -1)
1191         cue->setEndTime(MediaTime(stop, GST_SECOND));
1192
1193     GstTagList* tags = gst_toc_entry_get_tags(entry);
1194     if (tags) {
1195         gchar* title =  0;
1196         gst_tag_list_get_string(tags, GST_TAG_TITLE, &title);
1197         if (title) {
1198             cue->setContent(title);
1199             g_free(title);
1200         }
1201     }
1202
1203     m_chaptersTrack->addGenericCue(cue.release());
1204
1205     for (GList* i = gst_toc_entry_get_sub_entries(entry); i; i = i->next)
1206         processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data), entry);
1207 }
1208 #endif
1209
1210 void MediaPlayerPrivateGStreamer::fillTimerFired()
1211 {
1212     GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
1213
1214     if (!gst_element_query(m_pipeline.get(), query)) {
1215         gst_query_unref(query);
1216         return;
1217     }
1218
1219     gint64 start, stop;
1220     gdouble fillStatus = 100.0;
1221
1222     gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
1223     gst_query_unref(query);
1224
1225     if (stop != -1)
1226         fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
1227
1228     GST_DEBUG("[Buffering] Download buffer filled up to %f%%", fillStatus);
1229
1230     float mediaDuration = durationMediaTime().toDouble();
1231
1232     // Update maxTimeLoaded only if the media duration is
1233     // available. Otherwise we can't compute it.
1234     if (mediaDuration) {
1235         if (fillStatus == 100.0)
1236             m_maxTimeLoaded = mediaDuration;
1237         else
1238             m_maxTimeLoaded = static_cast<float>((fillStatus * mediaDuration) / 100.0);
1239         GST_DEBUG("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
1240     }
1241
1242     m_downloadFinished = fillStatus == 100.0;
1243     if (!m_downloadFinished) {
1244         updateStates();
1245         return;
1246     }
1247
1248     // Media is now fully loaded. It will play even if network
1249     // connection is cut. Buffering is done, remove the fill source
1250     // from the main loop.
1251     m_fillTimer.stop();
1252     updateStates();
1253 }
1254
1255 float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
1256 {
1257     if (m_errorOccured)
1258         return 0.0f;
1259
1260     float mediaDuration = durationMediaTime().toDouble();
1261     GST_DEBUG("maxTimeSeekable, duration: %f", mediaDuration);
1262     // infinite duration means live stream
1263     if (std::isinf(mediaDuration))
1264         return 0.0f;
1265
1266     return mediaDuration;
1267 }
1268
1269 float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
1270 {
1271     if (m_errorOccured)
1272         return 0.0f;
1273
1274     float loaded = m_maxTimeLoaded;
1275     if (m_isEndReached)
1276         loaded = durationMediaTime().toDouble();
1277     GST_DEBUG("maxTimeLoaded: %f", loaded);
1278     return loaded;
1279 }
1280
1281 bool MediaPlayerPrivateGStreamer::didLoadingProgress() const
1282 {
1283     if (UNLIKELY(!m_pipeline || !durationMediaTime() || (!isMediaSource() && !totalBytes())))
1284         return false;
1285     float currentMaxTimeLoaded = maxTimeLoaded();
1286     bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
1287     m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
1288     GST_DEBUG("didLoadingProgress: %d", didLoadingProgress);
1289     return didLoadingProgress;
1290 }
1291
1292 unsigned long long MediaPlayerPrivateGStreamer::totalBytes() const
1293 {
1294     if (m_errorOccured)
1295         return 0;
1296
1297     if (m_totalBytes)
1298         return m_totalBytes;
1299
1300     if (!m_source)
1301         return 0;
1302
1303     GstFormat fmt = GST_FORMAT_BYTES;
1304     gint64 length = 0;
1305     if (gst_element_query_duration(m_source.get(), fmt, &length)) {
1306         GST_INFO("totalBytes %" G_GINT64_FORMAT, length);
1307         m_totalBytes = static_cast<unsigned long long>(length);
1308         m_isStreaming = !length;
1309         return m_totalBytes;
1310     }
1311
1312     // Fall back to querying the source pads manually.
1313     // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749
1314     GstIterator* iter = gst_element_iterate_src_pads(m_source.get());
1315     bool done = false;
1316     while (!done) {
1317         GValue item = G_VALUE_INIT;
1318         switch (gst_iterator_next(iter, &item)) {
1319         case GST_ITERATOR_OK: {
1320             GstPad* pad = static_cast<GstPad*>(g_value_get_object(&item));
1321             gint64 padLength = 0;
1322             if (gst_pad_query_duration(pad, fmt, &padLength) && padLength > length)
1323                 length = padLength;
1324             break;
1325         }
1326         case GST_ITERATOR_RESYNC:
1327             gst_iterator_resync(iter);
1328             break;
1329         case GST_ITERATOR_ERROR:
1330             FALLTHROUGH;
1331         case GST_ITERATOR_DONE:
1332             done = true;
1333             break;
1334         }
1335
1336         g_value_unset(&item);
1337     }
1338
1339     gst_iterator_free(iter);
1340
1341     GST_INFO("totalBytes %" G_GINT64_FORMAT, length);
1342     m_totalBytes = static_cast<unsigned long long>(length);
1343     m_isStreaming = !length;
1344     return m_totalBytes;
1345 }
1346
1347 void MediaPlayerPrivateGStreamer::sourceChangedCallback(MediaPlayerPrivateGStreamer* player)
1348 {
1349     player->sourceChanged();
1350 }
1351
1352 void MediaPlayerPrivateGStreamer::uriDecodeBinElementAddedCallback(GstBin* bin, GstElement* element, MediaPlayerPrivateGStreamer* player)
1353 {
1354     if (g_strcmp0(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(G_OBJECT(element))), "GstDownloadBuffer"))
1355         return;
1356
1357     player->m_downloadBuffer = element;
1358     g_signal_handlers_disconnect_by_func(bin, reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), player);
1359     g_signal_connect_swapped(element, "notify::temp-location", G_CALLBACK(downloadBufferFileCreatedCallback), player);
1360
1361     GUniqueOutPtr<char> oldDownloadTemplate;
1362     g_object_get(element, "temp-template", &oldDownloadTemplate.outPtr(), nullptr);
1363
1364     GUniquePtr<char> newDownloadTemplate(g_build_filename(G_DIR_SEPARATOR_S, "var", "tmp", "WebKit-Media-XXXXXX", nullptr));
1365     g_object_set(element, "temp-template", newDownloadTemplate.get(), nullptr);
1366     GST_TRACE("Reconfigured file download template from '%s' to '%s'", oldDownloadTemplate.get(), newDownloadTemplate.get());
1367
1368     player->purgeOldDownloadFiles(oldDownloadTemplate.get());
1369 }
1370
1371 void MediaPlayerPrivateGStreamer::downloadBufferFileCreatedCallback(MediaPlayerPrivateGStreamer* player)
1372 {
1373     ASSERT(player->m_downloadBuffer);
1374
1375     g_signal_handlers_disconnect_by_func(player->m_downloadBuffer.get(), reinterpret_cast<gpointer>(downloadBufferFileCreatedCallback), player);
1376
1377     GUniqueOutPtr<char> downloadFile;
1378     g_object_get(player->m_downloadBuffer.get(), "temp-location", &downloadFile.outPtr(), nullptr);
1379     player->m_downloadBuffer = nullptr;
1380
1381     if (UNLIKELY(!deleteFile(downloadFile.get()))) {
1382         GST_WARNING("Couldn't unlink media temporary file %s after creation", downloadFile.get());
1383         return;
1384     }
1385
1386     GST_TRACE("Unlinked media temporary file %s after creation", downloadFile.get());
1387 }
1388
1389 void MediaPlayerPrivateGStreamer::purgeOldDownloadFiles(const char* downloadFileTemplate)
1390 {
1391     if (!downloadFileTemplate)
1392         return;
1393
1394     GUniquePtr<char> templatePath(g_path_get_dirname(downloadFileTemplate));
1395     GUniquePtr<char> templateFile(g_path_get_basename(downloadFileTemplate));
1396     String templatePattern = String(templateFile.get()).replace("X", "?");
1397
1398     for (auto& filePath : listDirectory(templatePath.get(), templatePattern)) {
1399         if (UNLIKELY(!deleteFile(filePath))) {
1400             GST_WARNING("Couldn't unlink legacy media temporary file: %s", filePath.utf8().data());
1401             continue;
1402         }
1403
1404         GST_TRACE("Unlinked legacy media temporary file: %s", filePath.utf8().data());
1405     }
1406 }
1407
1408 void MediaPlayerPrivateGStreamer::sourceChanged()
1409 {
1410     if (WEBKIT_IS_WEB_SRC(m_source.get()) && GST_OBJECT_PARENT(m_source.get()))
1411         g_signal_handlers_disconnect_by_func(GST_ELEMENT_PARENT(m_source.get()), reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), this);
1412
1413     m_source.clear();
1414     g_object_get(m_pipeline.get(), "source", &m_source.outPtr(), nullptr);
1415
1416     if (WEBKIT_IS_WEB_SRC(m_source.get())) {
1417         webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player);
1418         g_signal_connect(GST_ELEMENT_PARENT(m_source.get()), "element-added", G_CALLBACK(uriDecodeBinElementAddedCallback), this);
1419     }
1420 }
1421
1422 bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const
1423 {
1424     if (!m_source)
1425         return false;
1426
1427     if (!WEBKIT_IS_WEB_SRC(m_source.get()))
1428         return true;
1429
1430     GUniqueOutPtr<char> originalURI, resolvedURI;
1431     g_object_get(m_source.get(), "location", &originalURI.outPtr(), "resolved-location", &resolvedURI.outPtr(), nullptr);
1432     if (!originalURI || !resolvedURI)
1433         return false;
1434     if (!g_strcmp0(originalURI.get(), resolvedURI.get()))
1435         return true;
1436
1437     Ref<SecurityOrigin> resolvedOrigin(SecurityOrigin::createFromString(String::fromUTF8(resolvedURI.get())));
1438     Ref<SecurityOrigin> requestedOrigin(SecurityOrigin::createFromString(String::fromUTF8(originalURI.get())));
1439     return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get());
1440 }
1441
1442 void MediaPlayerPrivateGStreamer::cancelLoad()
1443 {
1444     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
1445         return;
1446
1447     if (m_pipeline)
1448         changePipelineState(GST_STATE_READY);
1449 }
1450
1451 void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
1452 {
1453     if (!m_pipeline || m_errorOccured)
1454         return;
1455
1456     if (m_seeking) {
1457         if (m_seekIsPending)
1458             updateStates();
1459         else {
1460             GST_DEBUG("[Seek] seeked to %f", m_seekTime);
1461             m_seeking = false;
1462             if (m_timeOfOverlappingSeek != m_seekTime && m_timeOfOverlappingSeek != -1) {
1463                 seek(m_timeOfOverlappingSeek);
1464                 m_timeOfOverlappingSeek = -1;
1465                 return;
1466             }
1467             m_timeOfOverlappingSeek = -1;
1468
1469             // The pipeline can still have a pending state. In this case a position query will fail.
1470             // Right now we can use m_seekTime as a fallback.
1471             m_canFallBackToLastFinishedSeekPosition = true;
1472             timeChanged();
1473         }
1474     } else
1475         updateStates();
1476 }
1477
1478 void MediaPlayerPrivateGStreamer::updateStates()
1479 {
1480     if (!m_pipeline)
1481         return;
1482
1483     if (m_errorOccured)
1484         return;
1485
1486     MediaPlayer::NetworkState oldNetworkState = m_networkState;
1487     MediaPlayer::ReadyState oldReadyState = m_readyState;
1488     GstState state;
1489     GstState pending;
1490
1491     GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &pending, 250 * GST_NSECOND);
1492
1493     bool shouldUpdatePlaybackState = false;
1494     switch (getStateResult) {
1495     case GST_STATE_CHANGE_SUCCESS: {
1496         GST_DEBUG("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1497
1498         // Do nothing if on EOS and state changed to READY to avoid recreating the player
1499         // on HTMLMediaElement and properly generate the video 'ended' event.
1500         if (m_isEndReached && state == GST_STATE_READY)
1501             break;
1502
1503         m_resetPipeline = state <= GST_STATE_READY;
1504
1505         bool didBuffering = m_buffering;
1506
1507         // Update ready and network states.
1508         switch (state) {
1509         case GST_STATE_NULL:
1510             m_readyState = MediaPlayer::HaveNothing;
1511             m_networkState = MediaPlayer::Empty;
1512             break;
1513         case GST_STATE_READY:
1514             m_readyState = MediaPlayer::HaveMetadata;
1515             m_networkState = MediaPlayer::Empty;
1516             break;
1517         case GST_STATE_PAUSED:
1518         case GST_STATE_PLAYING:
1519             if (m_buffering) {
1520                 if (m_bufferingPercentage == 100) {
1521                     GST_DEBUG("[Buffering] Complete.");
1522                     m_buffering = false;
1523                     m_readyState = MediaPlayer::HaveEnoughData;
1524                     m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading;
1525                 } else {
1526                     m_readyState = MediaPlayer::HaveCurrentData;
1527                     m_networkState = MediaPlayer::Loading;
1528                 }
1529             } else if (m_downloadFinished) {
1530                 m_readyState = MediaPlayer::HaveEnoughData;
1531                 m_networkState = MediaPlayer::Loaded;
1532             } else {
1533                 m_readyState = MediaPlayer::HaveFutureData;
1534                 m_networkState = MediaPlayer::Loading;
1535             }
1536
1537             break;
1538         default:
1539             ASSERT_NOT_REACHED();
1540             break;
1541         }
1542
1543         // Sync states where needed.
1544         if (state == GST_STATE_PAUSED) {
1545             if (!m_volumeAndMuteInitialized) {
1546                 notifyPlayerOfVolumeChange();
1547                 notifyPlayerOfMute();
1548                 m_volumeAndMuteInitialized = true;
1549             }
1550
1551             if (didBuffering && !m_buffering && !m_paused && m_playbackRate) {
1552                 GST_DEBUG("[Buffering] Restarting playback.");
1553                 changePipelineState(GST_STATE_PLAYING);
1554             }
1555         } else if (state == GST_STATE_PLAYING) {
1556             m_paused = false;
1557
1558             if ((m_buffering && !isLiveStream()) || !m_playbackRate) {
1559                 GST_DEBUG("[Buffering] Pausing stream for buffering.");
1560                 changePipelineState(GST_STATE_PAUSED);
1561             }
1562         } else
1563             m_paused = true;
1564
1565         if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
1566             shouldUpdatePlaybackState = true;
1567             GST_DEBUG("Requested state change to %s was completed", gst_element_state_get_name(state));
1568         }
1569
1570         break;
1571     }
1572     case GST_STATE_CHANGE_ASYNC:
1573         GST_DEBUG("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1574         // Change in progress.
1575         break;
1576     case GST_STATE_CHANGE_FAILURE:
1577         GST_DEBUG("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1578         // Change failed
1579         return;
1580     case GST_STATE_CHANGE_NO_PREROLL:
1581         GST_DEBUG("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1582
1583         // Live pipelines go in PAUSED without prerolling.
1584         m_isStreaming = true;
1585         setDownloadBuffering();
1586
1587         if (state == GST_STATE_READY)
1588             m_readyState = MediaPlayer::HaveNothing;
1589         else if (state == GST_STATE_PAUSED) {
1590             m_readyState = MediaPlayer::HaveEnoughData;
1591             m_paused = true;
1592         } else if (state == GST_STATE_PLAYING)
1593             m_paused = false;
1594
1595         if (!m_paused && m_playbackRate)
1596             changePipelineState(GST_STATE_PLAYING);
1597
1598         m_networkState = MediaPlayer::Loading;
1599         break;
1600     default:
1601         GST_DEBUG("Else : %d", getStateResult);
1602         break;
1603     }
1604
1605     m_requestedState = GST_STATE_VOID_PENDING;
1606
1607     if (shouldUpdatePlaybackState)
1608         m_player->playbackStateChanged();
1609
1610     if (m_networkState != oldNetworkState) {
1611         GST_DEBUG("Network State Changed from %u to %u", oldNetworkState, m_networkState);
1612         m_player->networkStateChanged();
1613     }
1614     if (m_readyState != oldReadyState) {
1615         GST_DEBUG("Ready State Changed from %u to %u", oldReadyState, m_readyState);
1616         m_player->readyStateChanged();
1617     }
1618
1619     if (getStateResult == GST_STATE_CHANGE_SUCCESS && state >= GST_STATE_PAUSED) {
1620         updatePlaybackRate();
1621         if (m_seekIsPending) {
1622             GST_DEBUG("[Seek] committing pending seek to %f", m_seekTime);
1623             m_seekIsPending = false;
1624             m_seeking = doSeek(toGstClockTime(m_seekTime), m_player->rate(), static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE));
1625             if (!m_seeking)
1626                 GST_DEBUG("[Seek] seeking to %f failed", m_seekTime);
1627         }
1628     }
1629 }
1630
1631 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
1632 {
1633     if (m_mediaLocations)
1634         gst_structure_free(m_mediaLocations);
1635
1636     const GstStructure* structure = gst_message_get_structure(message);
1637     if (structure) {
1638         // This structure can contain:
1639         // - both a new-location string and embedded locations structure
1640         // - or only a new-location string.
1641         m_mediaLocations = gst_structure_copy(structure);
1642         const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1643
1644         if (locations)
1645             m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1;
1646
1647         loadNextLocation();
1648     }
1649 }
1650
1651 bool MediaPlayerPrivateGStreamer::loadNextLocation()
1652 {
1653     if (!m_mediaLocations)
1654         return false;
1655
1656     const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1657     const gchar* newLocation = 0;
1658
1659     if (!locations) {
1660         // Fallback on new-location string.
1661         newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
1662         if (!newLocation)
1663             return false;
1664     }
1665
1666     if (!newLocation) {
1667         if (m_mediaLocationCurrentIndex < 0) {
1668             m_mediaLocations = 0;
1669             return false;
1670         }
1671
1672         const GValue* location = gst_value_list_get_value(locations,
1673                                                           m_mediaLocationCurrentIndex);
1674         const GstStructure* structure = gst_value_get_structure(location);
1675
1676         if (!structure) {
1677             m_mediaLocationCurrentIndex--;
1678             return false;
1679         }
1680
1681         newLocation = gst_structure_get_string(structure, "new-location");
1682     }
1683
1684     if (newLocation) {
1685         // Found a candidate. new-location is not always an absolute url
1686         // though. We need to take the base of the current url and
1687         // append the value of new-location to it.
1688         URL baseUrl = gst_uri_is_valid(newLocation) ? URL() : m_url;
1689         URL newUrl = URL(baseUrl, newLocation);
1690
1691         RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(m_url);
1692         if (securityOrigin->canRequest(newUrl)) {
1693             GST_INFO("New media url: %s", newUrl.string().utf8().data());
1694
1695             // Reset player states.
1696             m_networkState = MediaPlayer::Loading;
1697             m_player->networkStateChanged();
1698             m_readyState = MediaPlayer::HaveNothing;
1699             m_player->readyStateChanged();
1700
1701             // Reset pipeline state.
1702             m_resetPipeline = true;
1703             changePipelineState(GST_STATE_READY);
1704
1705             GstState state;
1706             gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
1707             if (state <= GST_STATE_READY) {
1708                 // Set the new uri and start playing.
1709                 g_object_set(m_pipeline.get(), "uri", newUrl.string().utf8().data(), nullptr);
1710                 m_url = newUrl;
1711                 changePipelineState(GST_STATE_PLAYING);
1712                 return true;
1713             }
1714         } else
1715             GST_INFO("Not allowed to load new media location: %s", newUrl.string().utf8().data());
1716     }
1717     m_mediaLocationCurrentIndex--;
1718     return false;
1719 }
1720
1721 void MediaPlayerPrivateGStreamer::loadStateChanged()
1722 {
1723     updateStates();
1724 }
1725
1726 void MediaPlayerPrivateGStreamer::timeChanged()
1727 {
1728     updateStates();
1729     m_player->timeChanged();
1730 }
1731
1732 void MediaPlayerPrivateGStreamer::didEnd()
1733 {
1734     // Synchronize position and duration values to not confuse the
1735     // HTMLMediaElement. In some cases like reverse playback the
1736     // position is not always reported as 0 for instance.
1737     MediaTime now = currentMediaTime();
1738     if (now > MediaTime { } && now <= durationMediaTime())
1739         m_player->durationChanged();
1740
1741     m_isEndReached = true;
1742     timeChanged();
1743
1744     if (!m_player->client().mediaPlayerIsLooping()) {
1745         m_paused = true;
1746         m_durationAtEOS = durationMediaTime().toDouble();
1747         changePipelineState(GST_STATE_READY);
1748         m_downloadFinished = false;
1749     }
1750 }
1751
1752 void MediaPlayerPrivateGStreamer::durationChanged()
1753 {
1754     float previousDuration = durationMediaTime().toDouble();
1755
1756     // Avoid emiting durationchanged in the case where the previous
1757     // duration was 0 because that case is already handled by the
1758     // HTMLMediaElement.
1759     if (previousDuration && durationMediaTime().toDouble() != previousDuration)
1760         m_player->durationChanged();
1761 }
1762
1763 void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
1764 {
1765     m_errorOccured = true;
1766     if (m_networkState != error) {
1767         m_networkState = error;
1768         m_player->networkStateChanged();
1769     }
1770     if (m_readyState != MediaPlayer::HaveNothing) {
1771         m_readyState = MediaPlayer::HaveNothing;
1772         m_player->readyStateChanged();
1773     }
1774
1775     // Loading failed, remove ready timer.
1776     m_readyTimerHandler.stop();
1777 }
1778
1779 static HashSet<String, ASCIICaseInsensitiveHash>& mimeTypeSet()
1780 {
1781     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> mimeTypes = []()
1782     {
1783         initializeGStreamerAndRegisterWebKitElements();
1784         HashSet<String, ASCIICaseInsensitiveHash> set;
1785
1786         GList* audioDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
1787         GList* videoDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, GST_RANK_MARGINAL);
1788         GList* demuxerFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, GST_RANK_MARGINAL);
1789
1790         enum ElementType {
1791             AudioDecoder = 0,
1792             VideoDecoder,
1793             Demuxer
1794         };
1795         struct GstCapsWebKitMapping {
1796             ElementType elementType;
1797             const char* capsString;
1798             Vector<AtomicString> webkitMimeTypes;
1799         };
1800
1801         Vector<GstCapsWebKitMapping> mapping = {
1802             {AudioDecoder, "audio/midi", {"audio/midi", "audio/riff-midi"}},
1803             {AudioDecoder, "audio/x-sbc", { }},
1804             {AudioDecoder, "audio/x-sid", { }},
1805             {AudioDecoder, "audio/x-flac", {"audio/x-flac", "audio/flac"}},
1806             {AudioDecoder, "audio/x-wav", {"audio/x-wav", "audio/wav"}},
1807             {AudioDecoder, "audio/x-wavpack", {"audio/x-wavpack"}},
1808             {AudioDecoder, "audio/x-speex", {"audio/speex", "audio/x-speex"}},
1809             {AudioDecoder, "audio/x-ac3", { }},
1810             {AudioDecoder, "audio/x-eac3", {"audio/x-ac3"}},
1811             {AudioDecoder, "audio/x-dts", { }},
1812             {VideoDecoder, "video/x-h264, profile=(string)high", {"video/mp4", "video/x-m4v"}},
1813             {VideoDecoder, "video/x-msvideocodec", {"video/x-msvideo"}},
1814             {VideoDecoder, "video/x-h263", { }},
1815             {VideoDecoder, "video/mpegts", { }},
1816             {VideoDecoder, "video/mpeg, mpegversion=(int){1,2}, systemstream=(boolean)false", {"video/mpeg"}},
1817             {VideoDecoder, "video/x-dirac", { }},
1818             {VideoDecoder, "video/x-flash-video", {"video/flv", "video/x-flv"}},
1819             {Demuxer, "video/quicktime", { }},
1820             {Demuxer, "video/quicktime, variant=(string)3gpp", {"video/3gpp"}},
1821             {Demuxer, "application/x-3gp", { }},
1822             {Demuxer, "video/x-ms-asf", { }},
1823             {Demuxer, "audio/x-aiff", { }},
1824             {Demuxer, "application/x-pn-realaudio", { }},
1825             {Demuxer, "application/vnd.rn-realmedia", { }},
1826             {Demuxer, "audio/x-wav", {"audio/x-wav", "audio/wav"}},
1827             {Demuxer, "application/x-hls", {"application/vnd.apple.mpegurl", "application/x-mpegurl"}}
1828         };
1829
1830         for (auto& current : mapping) {
1831             GList* factories = demuxerFactories;
1832             if (current.elementType == AudioDecoder)
1833                 factories = audioDecoderFactories;
1834             else if (current.elementType == VideoDecoder)
1835                 factories = videoDecoderFactories;
1836
1837             if (gstRegistryHasElementForMediaType(factories, current.capsString)) {
1838                 if (!current.webkitMimeTypes.isEmpty()) {
1839                     for (const auto& mimeType : current.webkitMimeTypes)
1840                         set.add(mimeType);
1841                 } else
1842                     set.add(AtomicString(current.capsString));
1843             }
1844         }
1845
1846         bool opusSupported = false;
1847         if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/x-opus")) {
1848             opusSupported = true;
1849             set.add(AtomicString("audio/opus"));
1850         }
1851
1852         bool vorbisSupported = false;
1853         if (gstRegistryHasElementForMediaType(demuxerFactories, "application/ogg")) {
1854             set.add(AtomicString("application/ogg"));
1855
1856             vorbisSupported = gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/x-vorbis");
1857             if (vorbisSupported) {
1858                 set.add(AtomicString("audio/ogg"));
1859                 set.add(AtomicString("audio/x-vorbis+ogg"));
1860             }
1861
1862             if (gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-theora"))
1863                 set.add(AtomicString("video/ogg"));
1864         }
1865
1866         bool audioMpegSupported = false;
1867         if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/mpeg, mpegversion=(int)1, layer=(int)[1, 3]")) {
1868             audioMpegSupported = true;
1869             set.add(AtomicString("audio/mp1"));
1870             set.add(AtomicString("audio/mp3"));
1871             set.add(AtomicString("audio/x-mp3"));
1872         }
1873
1874         if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/mpeg, mpegversion=(int){2, 4}")) {
1875             audioMpegSupported = true;
1876             set.add(AtomicString("audio/aac"));
1877             set.add(AtomicString("audio/mp2"));
1878             set.add(AtomicString("audio/mp4"));
1879             set.add(AtomicString("audio/x-m4a"));
1880         }
1881
1882         if (audioMpegSupported) {
1883             set.add(AtomicString("audio/mpeg"));
1884             set.add(AtomicString("audio/x-mpeg"));
1885         }
1886
1887         if (gstRegistryHasElementForMediaType(demuxerFactories, "video/x-matroska")) {
1888             set.add(AtomicString("video/x-matroska"));
1889
1890             if (gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp8")
1891                 || gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp9")
1892                 || gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp10"))
1893                 set.add(AtomicString("video/webm"));
1894
1895             if (vorbisSupported || opusSupported)
1896                 set.add(AtomicString("audio/webm"));
1897         }
1898
1899         gst_plugin_feature_list_free(audioDecoderFactories);
1900         gst_plugin_feature_list_free(videoDecoderFactories);
1901         gst_plugin_feature_list_free(demuxerFactories);
1902         return set;
1903     }();
1904     return mimeTypes;
1905 }
1906
1907 void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types)
1908 {
1909     types = mimeTypeSet();
1910 }
1911
1912 MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const MediaEngineSupportParameters& parameters)
1913 {
1914     MediaPlayer::SupportsType result = MediaPlayer::IsNotSupported;
1915 #if ENABLE(MEDIA_SOURCE)
1916     // MediaPlayerPrivateGStreamerMSE is in charge of mediasource playback, not us.
1917     if (parameters.isMediaSource)
1918         return result;
1919 #endif
1920
1921     // MediaStream playback is handled by the OpenWebRTC player.
1922     if (parameters.isMediaStream)
1923         return result;
1924
1925     if (parameters.type.isNull() || parameters.type.isEmpty())
1926         return result;
1927
1928     // spec says we should not return "probably" if the codecs string is empty
1929     if (mimeTypeSet().contains(parameters.type))
1930         result = parameters.codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
1931
1932     return extendedSupportsType(parameters, result);
1933 }
1934
1935 void MediaPlayerPrivateGStreamer::setDownloadBuffering()
1936 {
1937     if (!m_pipeline)
1938         return;
1939
1940     unsigned flags;
1941     g_object_get(m_pipeline.get(), "flags", &flags, nullptr);
1942
1943     unsigned flagDownload = getGstPlayFlag("download");
1944
1945     // We don't want to stop downloading if we already started it.
1946     if (flags & flagDownload && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline)
1947         return;
1948
1949     bool shouldDownload = !isLiveStream() && m_preload == MediaPlayer::Auto;
1950     if (shouldDownload) {
1951         GST_DEBUG("Enabling on-disk buffering");
1952         g_object_set(m_pipeline.get(), "flags", flags | flagDownload, nullptr);
1953         m_fillTimer.startRepeating(0.2);
1954     } else {
1955         GST_DEBUG("Disabling on-disk buffering");
1956         g_object_set(m_pipeline.get(), "flags", flags & ~flagDownload, nullptr);
1957         m_fillTimer.stop();
1958     }
1959 }
1960
1961 void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
1962 {
1963     if (preload == MediaPlayer::Auto && isLiveStream())
1964         return;
1965
1966     m_preload = preload;
1967     setDownloadBuffering();
1968
1969     if (m_delayingLoad && m_preload != MediaPlayer::None) {
1970         m_delayingLoad = false;
1971         commitLoad();
1972     }
1973 }
1974
1975 GstElement* MediaPlayerPrivateGStreamer::createAudioSink()
1976 {
1977     m_autoAudioSink = gst_element_factory_make("autoaudiosink", 0);
1978     if (!m_autoAudioSink) {
1979         GST_WARNING("GStreamer's autoaudiosink not found. Please check your gst-plugins-good installation");
1980         return nullptr;
1981     }
1982
1983     g_signal_connect_swapped(m_autoAudioSink.get(), "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this);
1984
1985     GstElement* audioSinkBin;
1986
1987     if (webkitGstCheckVersion(1, 4, 2)) {
1988 #if ENABLE(WEB_AUDIO)
1989         audioSinkBin = gst_bin_new("audio-sink");
1990         ensureAudioSourceProvider();
1991         m_audioSourceProvider->configureAudioBin(audioSinkBin, nullptr);
1992         return audioSinkBin;
1993 #else
1994         return m_autoAudioSink.get();
1995 #endif
1996     }
1997
1998     // Construct audio sink only if pitch preserving is enabled.
1999     // If GStreamer 1.4.2 is used the audio-filter playbin property is used instead.
2000     if (m_preservesPitch) {
2001         GstElement* scale = gst_element_factory_make("scaletempo", nullptr);
2002         if (!scale) {
2003             GST_WARNING("Failed to create scaletempo");
2004             return m_autoAudioSink.get();
2005         }
2006
2007         audioSinkBin = gst_bin_new("audio-sink");
2008         gst_bin_add(GST_BIN(audioSinkBin), scale);
2009         GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
2010         gst_element_add_pad(audioSinkBin, gst_ghost_pad_new("sink", pad.get()));
2011
2012 #if ENABLE(WEB_AUDIO)
2013         ensureAudioSourceProvider();
2014         m_audioSourceProvider->configureAudioBin(audioSinkBin, scale);
2015 #else
2016         GstElement* convert = gst_element_factory_make("audioconvert", nullptr);
2017         GstElement* resample = gst_element_factory_make("audioresample", nullptr);
2018
2019         gst_bin_add_many(GST_BIN(audioSinkBin), convert, resample, m_autoAudioSink.get(), nullptr);
2020
2021         if (!gst_element_link_many(scale, convert, resample, m_autoAudioSink.get(), nullptr)) {
2022             GST_WARNING("Failed to link audio sink elements");
2023             gst_object_unref(audioSinkBin);
2024             return m_autoAudioSink.get();
2025         }
2026 #endif
2027         return audioSinkBin;
2028     }
2029
2030 #if ENABLE(WEB_AUDIO)
2031     audioSinkBin = gst_bin_new("audio-sink");
2032     ensureAudioSourceProvider();
2033     m_audioSourceProvider->configureAudioBin(audioSinkBin, nullptr);
2034     return audioSinkBin;
2035 #endif
2036     ASSERT_NOT_REACHED();
2037     return nullptr;
2038 }
2039
2040 GstElement* MediaPlayerPrivateGStreamer::audioSink() const
2041 {
2042     GstElement* sink;
2043     g_object_get(m_pipeline.get(), "audio-sink", &sink, nullptr);
2044     return sink;
2045 }
2046
2047 #if ENABLE(WEB_AUDIO)
2048 void MediaPlayerPrivateGStreamer::ensureAudioSourceProvider()
2049 {
2050     if (!m_audioSourceProvider)
2051         m_audioSourceProvider = std::make_unique<AudioSourceProviderGStreamer>();
2052 }
2053
2054 AudioSourceProvider* MediaPlayerPrivateGStreamer::audioSourceProvider()
2055 {
2056     ensureAudioSourceProvider();
2057     return m_audioSourceProvider.get();
2058 }
2059 #endif
2060
2061 void MediaPlayerPrivateGStreamer::createGSTPlayBin()
2062 {
2063     ASSERT(!m_pipeline);
2064
2065     // gst_element_factory_make() returns a floating reference so
2066     // we should not adopt.
2067     setPipeline(gst_element_factory_make("playbin", "play"));
2068     setStreamVolumeElement(GST_STREAM_VOLUME(m_pipeline.get()));
2069
2070     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
2071     gst_bus_set_sync_handler(bus.get(), [](GstBus*, GstMessage* message, gpointer userData) {
2072         auto& player = *static_cast<MediaPlayerPrivateGStreamer*>(userData);
2073
2074         if (player.handleSyncMessage(message)) {
2075             gst_message_unref(message);
2076             return GST_BUS_DROP;
2077         }
2078
2079         return GST_BUS_PASS;
2080     }, this, nullptr);
2081
2082     // Let also other listeners subscribe to (application) messages in this bus.
2083     gst_bus_add_signal_watch(bus.get());
2084     g_signal_connect(bus.get(), "message", G_CALLBACK(busMessageCallback), this);
2085
2086     g_object_set(m_pipeline.get(), "mute", m_player->muted(), nullptr);
2087
2088     g_signal_connect_swapped(m_pipeline.get(), "notify::source", G_CALLBACK(sourceChangedCallback), this);
2089     g_signal_connect_swapped(m_pipeline.get(), "video-changed", G_CALLBACK(videoChangedCallback), this);
2090     g_signal_connect_swapped(m_pipeline.get(), "audio-changed", G_CALLBACK(audioChangedCallback), this);
2091 #if ENABLE(VIDEO_TRACK)
2092     g_signal_connect_swapped(m_pipeline.get(), "text-changed", G_CALLBACK(textChangedCallback), this);
2093
2094     GstElement* textCombiner = webkitTextCombinerNew();
2095     ASSERT(textCombiner);
2096     g_object_set(m_pipeline.get(), "text-stream-combiner", textCombiner, nullptr);
2097
2098     m_textAppSink = webkitTextSinkNew();
2099     ASSERT(m_textAppSink);
2100
2101     m_textAppSinkPad = adoptGRef(gst_element_get_static_pad(m_textAppSink.get(), "sink"));
2102     ASSERT(m_textAppSinkPad);
2103
2104     g_object_set(m_textAppSink.get(), "emit-signals", true, "enable-last-sample", false, "caps", gst_caps_new_empty_simple("text/vtt"), nullptr);
2105     g_signal_connect_swapped(m_textAppSink.get(), "new-sample", G_CALLBACK(newTextSampleCallback), this);
2106
2107     g_object_set(m_pipeline.get(), "text-sink", m_textAppSink.get(), nullptr);
2108 #endif
2109
2110     g_object_set(m_pipeline.get(), "video-sink", createVideoSink(), "audio-sink", createAudioSink(), nullptr);
2111
2112     configurePlaySink();
2113
2114     // On 1.4.2 and newer we use the audio-filter property instead.
2115     // See https://bugzilla.gnome.org/show_bug.cgi?id=735748 for
2116     // the reason for using >= 1.4.2 instead of >= 1.4.0.
2117     if (m_preservesPitch && webkitGstCheckVersion(1, 4, 2)) {
2118         GstElement* scale = gst_element_factory_make("scaletempo", 0);
2119
2120         if (!scale)
2121             GST_WARNING("Failed to create scaletempo");
2122         else
2123             g_object_set(m_pipeline.get(), "audio-filter", scale, nullptr);
2124     }
2125
2126     if (!m_renderingCanBeAccelerated) {
2127         // If not using accelerated compositing, let GStreamer handle
2128         // the image-orientation tag.
2129         GstElement* videoFlip = gst_element_factory_make("videoflip", nullptr);
2130         g_object_set(videoFlip, "method", 8, nullptr);
2131         g_object_set(m_pipeline.get(), "video-filter", videoFlip, nullptr);
2132     }
2133
2134     GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_videoSink.get(), "sink"));
2135     if (videoSinkPad)
2136         g_signal_connect_swapped(videoSinkPad.get(), "notify::caps", G_CALLBACK(videoSinkCapsChangedCallback), this);
2137 }
2138
2139 void MediaPlayerPrivateGStreamer::simulateAudioInterruption()
2140 {
2141     GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_pipeline.get()), GST_STATE_PAUSED);
2142     gst_element_post_message(m_pipeline.get(), message);
2143 }
2144
2145 bool MediaPlayerPrivateGStreamer::didPassCORSAccessCheck() const
2146 {
2147     if (WEBKIT_IS_WEB_SRC(m_source.get()))
2148         return webKitSrcPassedCORSAccessCheck(WEBKIT_WEB_SRC(m_source.get()));
2149     return false;
2150 }
2151
2152 bool MediaPlayerPrivateGStreamer::canSaveMediaData() const
2153 {
2154     if (isLiveStream())
2155         return false;
2156
2157     if (m_url.isLocalFile())
2158         return true;
2159
2160     if (m_url.protocolIsInHTTPFamily())
2161         return true;
2162
2163     return false;
2164 }
2165
2166 bool MediaPlayerPrivateGStreamer::handleSyncMessage(GstMessage* message)
2167 {
2168     return MediaPlayerPrivateGStreamerBase::handleSyncMessage(message);
2169 }
2170
2171 }
2172
2173 #endif // USE(GSTREAMER)