[GStreamer] Adopt nullptr
[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, nullptr);
100     g_object_set(object, "stream-properties", structure, nullptr);
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, nullptr, nullptr, nullptr, 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(nullptr, "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(nullptr)
157     , m_volumeAndMuteInitialized(false)
158     , m_weakPtrFactory(this)
159     , m_mediaLocations(nullptr)
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 = nullptr;
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(), nullptr);
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, nullptr);
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));
1177 }
1178
1179 void MediaPlayerPrivateGStreamer::processTableOfContentsEntry(GstTocEntry* entry)
1180 {
1181     ASSERT(entry);
1182
1183     RefPtr<GenericCueData> cue = GenericCueData::create();
1184
1185     gint64 start = -1, stop = -1;
1186     gst_toc_entry_get_start_stop_times(entry, &start, &stop);
1187     if (start != -1)
1188         cue->setStartTime(MediaTime(start, GST_SECOND));
1189     if (stop != -1)
1190         cue->setEndTime(MediaTime(stop, GST_SECOND));
1191
1192     GstTagList* tags = gst_toc_entry_get_tags(entry);
1193     if (tags) {
1194         gchar* title =  nullptr;
1195         gst_tag_list_get_string(tags, GST_TAG_TITLE, &title);
1196         if (title) {
1197             cue->setContent(title);
1198             g_free(title);
1199         }
1200     }
1201
1202     m_chaptersTrack->addGenericCue(cue.release());
1203
1204     for (GList* i = gst_toc_entry_get_sub_entries(entry); i; i = i->next)
1205         processTableOfContentsEntry(static_cast<GstTocEntry*>(i->data));
1206 }
1207 #endif
1208
1209 void MediaPlayerPrivateGStreamer::fillTimerFired()
1210 {
1211     GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
1212
1213     if (!gst_element_query(m_pipeline.get(), query)) {
1214         gst_query_unref(query);
1215         return;
1216     }
1217
1218     gint64 start, stop;
1219     gdouble fillStatus = 100.0;
1220
1221     gst_query_parse_buffering_range(query, nullptr, &start, &stop, nullptr);
1222     gst_query_unref(query);
1223
1224     if (stop != -1)
1225         fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
1226
1227     GST_DEBUG("[Buffering] Download buffer filled up to %f%%", fillStatus);
1228
1229     float mediaDuration = durationMediaTime().toDouble();
1230
1231     // Update maxTimeLoaded only if the media duration is
1232     // available. Otherwise we can't compute it.
1233     if (mediaDuration) {
1234         if (fillStatus == 100.0)
1235             m_maxTimeLoaded = mediaDuration;
1236         else
1237             m_maxTimeLoaded = static_cast<float>((fillStatus * mediaDuration) / 100.0);
1238         GST_DEBUG("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
1239     }
1240
1241     m_downloadFinished = fillStatus == 100.0;
1242     if (!m_downloadFinished) {
1243         updateStates();
1244         return;
1245     }
1246
1247     // Media is now fully loaded. It will play even if network
1248     // connection is cut. Buffering is done, remove the fill source
1249     // from the main loop.
1250     m_fillTimer.stop();
1251     updateStates();
1252 }
1253
1254 float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
1255 {
1256     if (m_errorOccured)
1257         return 0.0f;
1258
1259     float mediaDuration = durationMediaTime().toDouble();
1260     GST_DEBUG("maxTimeSeekable, duration: %f", mediaDuration);
1261     // infinite duration means live stream
1262     if (std::isinf(mediaDuration))
1263         return 0.0f;
1264
1265     return mediaDuration;
1266 }
1267
1268 float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
1269 {
1270     if (m_errorOccured)
1271         return 0.0f;
1272
1273     float loaded = m_maxTimeLoaded;
1274     if (m_isEndReached)
1275         loaded = durationMediaTime().toDouble();
1276     GST_DEBUG("maxTimeLoaded: %f", loaded);
1277     return loaded;
1278 }
1279
1280 bool MediaPlayerPrivateGStreamer::didLoadingProgress() const
1281 {
1282     if (UNLIKELY(!m_pipeline || !durationMediaTime() || (!isMediaSource() && !totalBytes())))
1283         return false;
1284     float currentMaxTimeLoaded = maxTimeLoaded();
1285     bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
1286     m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
1287     GST_DEBUG("didLoadingProgress: %d", didLoadingProgress);
1288     return didLoadingProgress;
1289 }
1290
1291 unsigned long long MediaPlayerPrivateGStreamer::totalBytes() const
1292 {
1293     if (m_errorOccured)
1294         return 0;
1295
1296     if (m_totalBytes)
1297         return m_totalBytes;
1298
1299     if (!m_source)
1300         return 0;
1301
1302     GstFormat fmt = GST_FORMAT_BYTES;
1303     gint64 length = 0;
1304     if (gst_element_query_duration(m_source.get(), fmt, &length)) {
1305         GST_INFO("totalBytes %" G_GINT64_FORMAT, length);
1306         m_totalBytes = static_cast<unsigned long long>(length);
1307         m_isStreaming = !length;
1308         return m_totalBytes;
1309     }
1310
1311     // Fall back to querying the source pads manually.
1312     // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749
1313     GstIterator* iter = gst_element_iterate_src_pads(m_source.get());
1314     bool done = false;
1315     while (!done) {
1316         GValue item = G_VALUE_INIT;
1317         switch (gst_iterator_next(iter, &item)) {
1318         case GST_ITERATOR_OK: {
1319             GstPad* pad = static_cast<GstPad*>(g_value_get_object(&item));
1320             gint64 padLength = 0;
1321             if (gst_pad_query_duration(pad, fmt, &padLength) && padLength > length)
1322                 length = padLength;
1323             break;
1324         }
1325         case GST_ITERATOR_RESYNC:
1326             gst_iterator_resync(iter);
1327             break;
1328         case GST_ITERATOR_ERROR:
1329             FALLTHROUGH;
1330         case GST_ITERATOR_DONE:
1331             done = true;
1332             break;
1333         }
1334
1335         g_value_unset(&item);
1336     }
1337
1338     gst_iterator_free(iter);
1339
1340     GST_INFO("totalBytes %" G_GINT64_FORMAT, length);
1341     m_totalBytes = static_cast<unsigned long long>(length);
1342     m_isStreaming = !length;
1343     return m_totalBytes;
1344 }
1345
1346 void MediaPlayerPrivateGStreamer::sourceChangedCallback(MediaPlayerPrivateGStreamer* player)
1347 {
1348     player->sourceChanged();
1349 }
1350
1351 void MediaPlayerPrivateGStreamer::uriDecodeBinElementAddedCallback(GstBin* bin, GstElement* element, MediaPlayerPrivateGStreamer* player)
1352 {
1353     if (g_strcmp0(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(G_OBJECT(element))), "GstDownloadBuffer"))
1354         return;
1355
1356     player->m_downloadBuffer = element;
1357     g_signal_handlers_disconnect_by_func(bin, reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), player);
1358     g_signal_connect_swapped(element, "notify::temp-location", G_CALLBACK(downloadBufferFileCreatedCallback), player);
1359
1360     GUniqueOutPtr<char> oldDownloadTemplate;
1361     g_object_get(element, "temp-template", &oldDownloadTemplate.outPtr(), nullptr);
1362
1363     GUniquePtr<char> newDownloadTemplate(g_build_filename(G_DIR_SEPARATOR_S, "var", "tmp", "WebKit-Media-XXXXXX", nullptr));
1364     g_object_set(element, "temp-template", newDownloadTemplate.get(), nullptr);
1365     GST_TRACE("Reconfigured file download template from '%s' to '%s'", oldDownloadTemplate.get(), newDownloadTemplate.get());
1366
1367     player->purgeOldDownloadFiles(oldDownloadTemplate.get());
1368 }
1369
1370 void MediaPlayerPrivateGStreamer::downloadBufferFileCreatedCallback(MediaPlayerPrivateGStreamer* player)
1371 {
1372     ASSERT(player->m_downloadBuffer);
1373
1374     g_signal_handlers_disconnect_by_func(player->m_downloadBuffer.get(), reinterpret_cast<gpointer>(downloadBufferFileCreatedCallback), player);
1375
1376     GUniqueOutPtr<char> downloadFile;
1377     g_object_get(player->m_downloadBuffer.get(), "temp-location", &downloadFile.outPtr(), nullptr);
1378     player->m_downloadBuffer = nullptr;
1379
1380     if (UNLIKELY(!deleteFile(downloadFile.get()))) {
1381         GST_WARNING("Couldn't unlink media temporary file %s after creation", downloadFile.get());
1382         return;
1383     }
1384
1385     GST_TRACE("Unlinked media temporary file %s after creation", downloadFile.get());
1386 }
1387
1388 void MediaPlayerPrivateGStreamer::purgeOldDownloadFiles(const char* downloadFileTemplate)
1389 {
1390     if (!downloadFileTemplate)
1391         return;
1392
1393     GUniquePtr<char> templatePath(g_path_get_dirname(downloadFileTemplate));
1394     GUniquePtr<char> templateFile(g_path_get_basename(downloadFileTemplate));
1395     String templatePattern = String(templateFile.get()).replace("X", "?");
1396
1397     for (auto& filePath : listDirectory(templatePath.get(), templatePattern)) {
1398         if (UNLIKELY(!deleteFile(filePath))) {
1399             GST_WARNING("Couldn't unlink legacy media temporary file: %s", filePath.utf8().data());
1400             continue;
1401         }
1402
1403         GST_TRACE("Unlinked legacy media temporary file: %s", filePath.utf8().data());
1404     }
1405 }
1406
1407 void MediaPlayerPrivateGStreamer::sourceChanged()
1408 {
1409     if (WEBKIT_IS_WEB_SRC(m_source.get()) && GST_OBJECT_PARENT(m_source.get()))
1410         g_signal_handlers_disconnect_by_func(GST_ELEMENT_PARENT(m_source.get()), reinterpret_cast<gpointer>(uriDecodeBinElementAddedCallback), this);
1411
1412     m_source.clear();
1413     g_object_get(m_pipeline.get(), "source", &m_source.outPtr(), nullptr);
1414
1415     if (WEBKIT_IS_WEB_SRC(m_source.get())) {
1416         webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player);
1417         g_signal_connect(GST_ELEMENT_PARENT(m_source.get()), "element-added", G_CALLBACK(uriDecodeBinElementAddedCallback), this);
1418     }
1419 }
1420
1421 bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const
1422 {
1423     if (!m_source)
1424         return false;
1425
1426     if (!WEBKIT_IS_WEB_SRC(m_source.get()))
1427         return true;
1428
1429     GUniqueOutPtr<char> originalURI, resolvedURI;
1430     g_object_get(m_source.get(), "location", &originalURI.outPtr(), "resolved-location", &resolvedURI.outPtr(), nullptr);
1431     if (!originalURI || !resolvedURI)
1432         return false;
1433     if (!g_strcmp0(originalURI.get(), resolvedURI.get()))
1434         return true;
1435
1436     Ref<SecurityOrigin> resolvedOrigin(SecurityOrigin::createFromString(String::fromUTF8(resolvedURI.get())));
1437     Ref<SecurityOrigin> requestedOrigin(SecurityOrigin::createFromString(String::fromUTF8(originalURI.get())));
1438     return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get());
1439 }
1440
1441 void MediaPlayerPrivateGStreamer::cancelLoad()
1442 {
1443     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
1444         return;
1445
1446     if (m_pipeline)
1447         changePipelineState(GST_STATE_READY);
1448 }
1449
1450 void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
1451 {
1452     if (!m_pipeline || m_errorOccured)
1453         return;
1454
1455     if (m_seeking) {
1456         if (m_seekIsPending)
1457             updateStates();
1458         else {
1459             GST_DEBUG("[Seek] seeked to %f", m_seekTime);
1460             m_seeking = false;
1461             if (m_timeOfOverlappingSeek != m_seekTime && m_timeOfOverlappingSeek != -1) {
1462                 seek(m_timeOfOverlappingSeek);
1463                 m_timeOfOverlappingSeek = -1;
1464                 return;
1465             }
1466             m_timeOfOverlappingSeek = -1;
1467
1468             // The pipeline can still have a pending state. In this case a position query will fail.
1469             // Right now we can use m_seekTime as a fallback.
1470             m_canFallBackToLastFinishedSeekPosition = true;
1471             timeChanged();
1472         }
1473     } else
1474         updateStates();
1475 }
1476
1477 void MediaPlayerPrivateGStreamer::updateStates()
1478 {
1479     if (!m_pipeline)
1480         return;
1481
1482     if (m_errorOccured)
1483         return;
1484
1485     MediaPlayer::NetworkState oldNetworkState = m_networkState;
1486     MediaPlayer::ReadyState oldReadyState = m_readyState;
1487     GstState state;
1488     GstState pending;
1489
1490     GstStateChangeReturn getStateResult = gst_element_get_state(m_pipeline.get(), &state, &pending, 250 * GST_NSECOND);
1491
1492     bool shouldUpdatePlaybackState = false;
1493     switch (getStateResult) {
1494     case GST_STATE_CHANGE_SUCCESS: {
1495         GST_DEBUG("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1496
1497         // Do nothing if on EOS and state changed to READY to avoid recreating the player
1498         // on HTMLMediaElement and properly generate the video 'ended' event.
1499         if (m_isEndReached && state == GST_STATE_READY)
1500             break;
1501
1502         m_resetPipeline = state <= GST_STATE_READY;
1503
1504         bool didBuffering = m_buffering;
1505
1506         // Update ready and network states.
1507         switch (state) {
1508         case GST_STATE_NULL:
1509             m_readyState = MediaPlayer::HaveNothing;
1510             m_networkState = MediaPlayer::Empty;
1511             break;
1512         case GST_STATE_READY:
1513             m_readyState = MediaPlayer::HaveMetadata;
1514             m_networkState = MediaPlayer::Empty;
1515             break;
1516         case GST_STATE_PAUSED:
1517         case GST_STATE_PLAYING:
1518             if (m_buffering) {
1519                 if (m_bufferingPercentage == 100) {
1520                     GST_DEBUG("[Buffering] Complete.");
1521                     m_buffering = false;
1522                     m_readyState = MediaPlayer::HaveEnoughData;
1523                     m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading;
1524                 } else {
1525                     m_readyState = MediaPlayer::HaveCurrentData;
1526                     m_networkState = MediaPlayer::Loading;
1527                 }
1528             } else if (m_downloadFinished) {
1529                 m_readyState = MediaPlayer::HaveEnoughData;
1530                 m_networkState = MediaPlayer::Loaded;
1531             } else {
1532                 m_readyState = MediaPlayer::HaveFutureData;
1533                 m_networkState = MediaPlayer::Loading;
1534             }
1535
1536             break;
1537         default:
1538             ASSERT_NOT_REACHED();
1539             break;
1540         }
1541
1542         // Sync states where needed.
1543         if (state == GST_STATE_PAUSED) {
1544             if (!m_volumeAndMuteInitialized) {
1545                 notifyPlayerOfVolumeChange();
1546                 notifyPlayerOfMute();
1547                 m_volumeAndMuteInitialized = true;
1548             }
1549
1550             if (didBuffering && !m_buffering && !m_paused && m_playbackRate) {
1551                 GST_DEBUG("[Buffering] Restarting playback.");
1552                 changePipelineState(GST_STATE_PLAYING);
1553             }
1554         } else if (state == GST_STATE_PLAYING) {
1555             m_paused = false;
1556
1557             if ((m_buffering && !isLiveStream()) || !m_playbackRate) {
1558                 GST_DEBUG("[Buffering] Pausing stream for buffering.");
1559                 changePipelineState(GST_STATE_PAUSED);
1560             }
1561         } else
1562             m_paused = true;
1563
1564         if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
1565             shouldUpdatePlaybackState = true;
1566             GST_DEBUG("Requested state change to %s was completed", gst_element_state_get_name(state));
1567         }
1568
1569         break;
1570     }
1571     case GST_STATE_CHANGE_ASYNC:
1572         GST_DEBUG("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1573         // Change in progress.
1574         break;
1575     case GST_STATE_CHANGE_FAILURE:
1576         GST_DEBUG("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1577         // Change failed
1578         return;
1579     case GST_STATE_CHANGE_NO_PREROLL:
1580         GST_DEBUG("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1581
1582         // Live pipelines go in PAUSED without prerolling.
1583         m_isStreaming = true;
1584         setDownloadBuffering();
1585
1586         if (state == GST_STATE_READY)
1587             m_readyState = MediaPlayer::HaveNothing;
1588         else if (state == GST_STATE_PAUSED) {
1589             m_readyState = MediaPlayer::HaveEnoughData;
1590             m_paused = true;
1591         } else if (state == GST_STATE_PLAYING)
1592             m_paused = false;
1593
1594         if (!m_paused && m_playbackRate)
1595             changePipelineState(GST_STATE_PLAYING);
1596
1597         m_networkState = MediaPlayer::Loading;
1598         break;
1599     default:
1600         GST_DEBUG("Else : %d", getStateResult);
1601         break;
1602     }
1603
1604     m_requestedState = GST_STATE_VOID_PENDING;
1605
1606     if (shouldUpdatePlaybackState)
1607         m_player->playbackStateChanged();
1608
1609     if (m_networkState != oldNetworkState) {
1610         GST_DEBUG("Network State Changed from %u to %u", oldNetworkState, m_networkState);
1611         m_player->networkStateChanged();
1612     }
1613     if (m_readyState != oldReadyState) {
1614         GST_DEBUG("Ready State Changed from %u to %u", oldReadyState, m_readyState);
1615         m_player->readyStateChanged();
1616     }
1617
1618     if (getStateResult == GST_STATE_CHANGE_SUCCESS && state >= GST_STATE_PAUSED) {
1619         updatePlaybackRate();
1620         if (m_seekIsPending) {
1621             GST_DEBUG("[Seek] committing pending seek to %f", m_seekTime);
1622             m_seekIsPending = false;
1623             m_seeking = doSeek(toGstClockTime(m_seekTime), m_player->rate(), static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE));
1624             if (!m_seeking)
1625                 GST_DEBUG("[Seek] seeking to %f failed", m_seekTime);
1626         }
1627     }
1628 }
1629
1630 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
1631 {
1632     if (m_mediaLocations)
1633         gst_structure_free(m_mediaLocations);
1634
1635     const GstStructure* structure = gst_message_get_structure(message);
1636     if (structure) {
1637         // This structure can contain:
1638         // - both a new-location string and embedded locations structure
1639         // - or only a new-location string.
1640         m_mediaLocations = gst_structure_copy(structure);
1641         const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1642
1643         if (locations)
1644             m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1;
1645
1646         loadNextLocation();
1647     }
1648 }
1649
1650 bool MediaPlayerPrivateGStreamer::loadNextLocation()
1651 {
1652     if (!m_mediaLocations)
1653         return false;
1654
1655     const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1656     const gchar* newLocation = nullptr;
1657
1658     if (!locations) {
1659         // Fallback on new-location string.
1660         newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
1661         if (!newLocation)
1662             return false;
1663     }
1664
1665     if (!newLocation) {
1666         if (m_mediaLocationCurrentIndex < 0) {
1667             m_mediaLocations = nullptr;
1668             return false;
1669         }
1670
1671         const GValue* location = gst_value_list_get_value(locations,
1672                                                           m_mediaLocationCurrentIndex);
1673         const GstStructure* structure = gst_value_get_structure(location);
1674
1675         if (!structure) {
1676             m_mediaLocationCurrentIndex--;
1677             return false;
1678         }
1679
1680         newLocation = gst_structure_get_string(structure, "new-location");
1681     }
1682
1683     if (newLocation) {
1684         // Found a candidate. new-location is not always an absolute url
1685         // though. We need to take the base of the current url and
1686         // append the value of new-location to it.
1687         URL baseUrl = gst_uri_is_valid(newLocation) ? URL() : m_url;
1688         URL newUrl = URL(baseUrl, newLocation);
1689
1690         RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(m_url);
1691         if (securityOrigin->canRequest(newUrl)) {
1692             GST_INFO("New media url: %s", newUrl.string().utf8().data());
1693
1694             // Reset player states.
1695             m_networkState = MediaPlayer::Loading;
1696             m_player->networkStateChanged();
1697             m_readyState = MediaPlayer::HaveNothing;
1698             m_player->readyStateChanged();
1699
1700             // Reset pipeline state.
1701             m_resetPipeline = true;
1702             changePipelineState(GST_STATE_READY);
1703
1704             GstState state;
1705             gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
1706             if (state <= GST_STATE_READY) {
1707                 // Set the new uri and start playing.
1708                 g_object_set(m_pipeline.get(), "uri", newUrl.string().utf8().data(), nullptr);
1709                 m_url = newUrl;
1710                 changePipelineState(GST_STATE_PLAYING);
1711                 return true;
1712             }
1713         } else
1714             GST_INFO("Not allowed to load new media location: %s", newUrl.string().utf8().data());
1715     }
1716     m_mediaLocationCurrentIndex--;
1717     return false;
1718 }
1719
1720 void MediaPlayerPrivateGStreamer::loadStateChanged()
1721 {
1722     updateStates();
1723 }
1724
1725 void MediaPlayerPrivateGStreamer::timeChanged()
1726 {
1727     updateStates();
1728     m_player->timeChanged();
1729 }
1730
1731 void MediaPlayerPrivateGStreamer::didEnd()
1732 {
1733     // Synchronize position and duration values to not confuse the
1734     // HTMLMediaElement. In some cases like reverse playback the
1735     // position is not always reported as 0 for instance.
1736     MediaTime now = currentMediaTime();
1737     if (now > MediaTime { } && now <= durationMediaTime())
1738         m_player->durationChanged();
1739
1740     m_isEndReached = true;
1741     timeChanged();
1742
1743     if (!m_player->client().mediaPlayerIsLooping()) {
1744         m_paused = true;
1745         m_durationAtEOS = durationMediaTime().toDouble();
1746         changePipelineState(GST_STATE_READY);
1747         m_downloadFinished = false;
1748     }
1749 }
1750
1751 void MediaPlayerPrivateGStreamer::durationChanged()
1752 {
1753     float previousDuration = durationMediaTime().toDouble();
1754
1755     // Avoid emiting durationchanged in the case where the previous
1756     // duration was 0 because that case is already handled by the
1757     // HTMLMediaElement.
1758     if (previousDuration && durationMediaTime().toDouble() != previousDuration)
1759         m_player->durationChanged();
1760 }
1761
1762 void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
1763 {
1764     m_errorOccured = true;
1765     if (m_networkState != error) {
1766         m_networkState = error;
1767         m_player->networkStateChanged();
1768     }
1769     if (m_readyState != MediaPlayer::HaveNothing) {
1770         m_readyState = MediaPlayer::HaveNothing;
1771         m_player->readyStateChanged();
1772     }
1773
1774     // Loading failed, remove ready timer.
1775     m_readyTimerHandler.stop();
1776 }
1777
1778 static HashSet<String, ASCIICaseInsensitiveHash>& mimeTypeSet()
1779 {
1780     static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> mimeTypes = []()
1781     {
1782         initializeGStreamerAndRegisterWebKitElements();
1783         HashSet<String, ASCIICaseInsensitiveHash> set;
1784
1785         GList* audioDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
1786         GList* videoDecoderFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DECODER | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO, GST_RANK_MARGINAL);
1787         GList* demuxerFactories = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, GST_RANK_MARGINAL);
1788
1789         enum ElementType {
1790             AudioDecoder = 0,
1791             VideoDecoder,
1792             Demuxer
1793         };
1794         struct GstCapsWebKitMapping {
1795             ElementType elementType;
1796             const char* capsString;
1797             Vector<AtomicString> webkitMimeTypes;
1798         };
1799
1800         Vector<GstCapsWebKitMapping> mapping = {
1801             {AudioDecoder, "audio/midi", {"audio/midi", "audio/riff-midi"}},
1802             {AudioDecoder, "audio/x-sbc", { }},
1803             {AudioDecoder, "audio/x-sid", { }},
1804             {AudioDecoder, "audio/x-flac", {"audio/x-flac", "audio/flac"}},
1805             {AudioDecoder, "audio/x-wav", {"audio/x-wav", "audio/wav"}},
1806             {AudioDecoder, "audio/x-wavpack", {"audio/x-wavpack"}},
1807             {AudioDecoder, "audio/x-speex", {"audio/speex", "audio/x-speex"}},
1808             {AudioDecoder, "audio/x-ac3", { }},
1809             {AudioDecoder, "audio/x-eac3", {"audio/x-ac3"}},
1810             {AudioDecoder, "audio/x-dts", { }},
1811             {VideoDecoder, "video/x-h264, profile=(string)high", {"video/mp4", "video/x-m4v"}},
1812             {VideoDecoder, "video/x-msvideocodec", {"video/x-msvideo"}},
1813             {VideoDecoder, "video/x-h263", { }},
1814             {VideoDecoder, "video/mpegts", { }},
1815             {VideoDecoder, "video/mpeg, mpegversion=(int){1,2}, systemstream=(boolean)false", {"video/mpeg"}},
1816             {VideoDecoder, "video/x-dirac", { }},
1817             {VideoDecoder, "video/x-flash-video", {"video/flv", "video/x-flv"}},
1818             {Demuxer, "video/quicktime", { }},
1819             {Demuxer, "video/quicktime, variant=(string)3gpp", {"video/3gpp"}},
1820             {Demuxer, "application/x-3gp", { }},
1821             {Demuxer, "video/x-ms-asf", { }},
1822             {Demuxer, "audio/x-aiff", { }},
1823             {Demuxer, "application/x-pn-realaudio", { }},
1824             {Demuxer, "application/vnd.rn-realmedia", { }},
1825             {Demuxer, "audio/x-wav", {"audio/x-wav", "audio/wav"}},
1826             {Demuxer, "application/x-hls", {"application/vnd.apple.mpegurl", "application/x-mpegurl"}}
1827         };
1828
1829         for (auto& current : mapping) {
1830             GList* factories = demuxerFactories;
1831             if (current.elementType == AudioDecoder)
1832                 factories = audioDecoderFactories;
1833             else if (current.elementType == VideoDecoder)
1834                 factories = videoDecoderFactories;
1835
1836             if (gstRegistryHasElementForMediaType(factories, current.capsString)) {
1837                 if (!current.webkitMimeTypes.isEmpty()) {
1838                     for (const auto& mimeType : current.webkitMimeTypes)
1839                         set.add(mimeType);
1840                 } else
1841                     set.add(AtomicString(current.capsString));
1842             }
1843         }
1844
1845         bool opusSupported = false;
1846         if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/x-opus")) {
1847             opusSupported = true;
1848             set.add(AtomicString("audio/opus"));
1849         }
1850
1851         bool vorbisSupported = false;
1852         if (gstRegistryHasElementForMediaType(demuxerFactories, "application/ogg")) {
1853             set.add(AtomicString("application/ogg"));
1854
1855             vorbisSupported = gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/x-vorbis");
1856             if (vorbisSupported) {
1857                 set.add(AtomicString("audio/ogg"));
1858                 set.add(AtomicString("audio/x-vorbis+ogg"));
1859             }
1860
1861             if (gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-theora"))
1862                 set.add(AtomicString("video/ogg"));
1863         }
1864
1865         bool audioMpegSupported = false;
1866         if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/mpeg, mpegversion=(int)1, layer=(int)[1, 3]")) {
1867             audioMpegSupported = true;
1868             set.add(AtomicString("audio/mp1"));
1869             set.add(AtomicString("audio/mp3"));
1870             set.add(AtomicString("audio/x-mp3"));
1871         }
1872
1873         if (gstRegistryHasElementForMediaType(audioDecoderFactories, "audio/mpeg, mpegversion=(int){2, 4}")) {
1874             audioMpegSupported = true;
1875             set.add(AtomicString("audio/aac"));
1876             set.add(AtomicString("audio/mp2"));
1877             set.add(AtomicString("audio/mp4"));
1878             set.add(AtomicString("audio/x-m4a"));
1879         }
1880
1881         if (audioMpegSupported) {
1882             set.add(AtomicString("audio/mpeg"));
1883             set.add(AtomicString("audio/x-mpeg"));
1884         }
1885
1886         if (gstRegistryHasElementForMediaType(demuxerFactories, "video/x-matroska")) {
1887             set.add(AtomicString("video/x-matroska"));
1888
1889             if (gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp8")
1890                 || gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp9")
1891                 || gstRegistryHasElementForMediaType(videoDecoderFactories, "video/x-vp10"))
1892                 set.add(AtomicString("video/webm"));
1893
1894             if (vorbisSupported || opusSupported)
1895                 set.add(AtomicString("audio/webm"));
1896         }
1897
1898         gst_plugin_feature_list_free(audioDecoderFactories);
1899         gst_plugin_feature_list_free(videoDecoderFactories);
1900         gst_plugin_feature_list_free(demuxerFactories);
1901         return set;
1902     }();
1903     return mimeTypes;
1904 }
1905
1906 void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& types)
1907 {
1908     types = mimeTypeSet();
1909 }
1910
1911 MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const MediaEngineSupportParameters& parameters)
1912 {
1913     MediaPlayer::SupportsType result = MediaPlayer::IsNotSupported;
1914 #if ENABLE(MEDIA_SOURCE)
1915     // MediaPlayerPrivateGStreamerMSE is in charge of mediasource playback, not us.
1916     if (parameters.isMediaSource)
1917         return result;
1918 #endif
1919
1920     // MediaStream playback is handled by the OpenWebRTC player.
1921     if (parameters.isMediaStream)
1922         return result;
1923
1924     if (parameters.type.isNull() || parameters.type.isEmpty())
1925         return result;
1926
1927     // spec says we should not return "probably" if the codecs string is empty
1928     if (mimeTypeSet().contains(parameters.type))
1929         result = parameters.codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
1930
1931     return extendedSupportsType(parameters, result);
1932 }
1933
1934 void MediaPlayerPrivateGStreamer::setDownloadBuffering()
1935 {
1936     if (!m_pipeline)
1937         return;
1938
1939     unsigned flags;
1940     g_object_get(m_pipeline.get(), "flags", &flags, nullptr);
1941
1942     unsigned flagDownload = getGstPlayFlag("download");
1943
1944     // We don't want to stop downloading if we already started it.
1945     if (flags & flagDownload && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline)
1946         return;
1947
1948     bool shouldDownload = !isLiveStream() && m_preload == MediaPlayer::Auto;
1949     if (shouldDownload) {
1950         GST_DEBUG("Enabling on-disk buffering");
1951         g_object_set(m_pipeline.get(), "flags", flags | flagDownload, nullptr);
1952         m_fillTimer.startRepeating(0.2);
1953     } else {
1954         GST_DEBUG("Disabling on-disk buffering");
1955         g_object_set(m_pipeline.get(), "flags", flags & ~flagDownload, nullptr);
1956         m_fillTimer.stop();
1957     }
1958 }
1959
1960 void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
1961 {
1962     if (preload == MediaPlayer::Auto && isLiveStream())
1963         return;
1964
1965     m_preload = preload;
1966     setDownloadBuffering();
1967
1968     if (m_delayingLoad && m_preload != MediaPlayer::None) {
1969         m_delayingLoad = false;
1970         commitLoad();
1971     }
1972 }
1973
1974 GstElement* MediaPlayerPrivateGStreamer::createAudioSink()
1975 {
1976     m_autoAudioSink = gst_element_factory_make("autoaudiosink", nullptr);
1977     if (!m_autoAudioSink) {
1978         GST_WARNING("GStreamer's autoaudiosink not found. Please check your gst-plugins-good installation");
1979         return nullptr;
1980     }
1981
1982     g_signal_connect_swapped(m_autoAudioSink.get(), "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this);
1983
1984     GstElement* audioSinkBin;
1985
1986     if (webkitGstCheckVersion(1, 4, 2)) {
1987 #if ENABLE(WEB_AUDIO)
1988         audioSinkBin = gst_bin_new("audio-sink");
1989         ensureAudioSourceProvider();
1990         m_audioSourceProvider->configureAudioBin(audioSinkBin, nullptr);
1991         return audioSinkBin;
1992 #else
1993         return m_autoAudioSink.get();
1994 #endif
1995     }
1996
1997     // Construct audio sink only if pitch preserving is enabled.
1998     // If GStreamer 1.4.2 is used the audio-filter playbin property is used instead.
1999     if (m_preservesPitch) {
2000         GstElement* scale = gst_element_factory_make("scaletempo", nullptr);
2001         if (!scale) {
2002             GST_WARNING("Failed to create scaletempo");
2003             return m_autoAudioSink.get();
2004         }
2005
2006         audioSinkBin = gst_bin_new("audio-sink");
2007         gst_bin_add(GST_BIN(audioSinkBin), scale);
2008         GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
2009         gst_element_add_pad(audioSinkBin, gst_ghost_pad_new("sink", pad.get()));
2010
2011 #if ENABLE(WEB_AUDIO)
2012         ensureAudioSourceProvider();
2013         m_audioSourceProvider->configureAudioBin(audioSinkBin, scale);
2014 #else
2015         GstElement* convert = gst_element_factory_make("audioconvert", nullptr);
2016         GstElement* resample = gst_element_factory_make("audioresample", nullptr);
2017
2018         gst_bin_add_many(GST_BIN(audioSinkBin), convert, resample, m_autoAudioSink.get(), nullptr);
2019
2020         if (!gst_element_link_many(scale, convert, resample, m_autoAudioSink.get(), nullptr)) {
2021             GST_WARNING("Failed to link audio sink elements");
2022             gst_object_unref(audioSinkBin);
2023             return m_autoAudioSink.get();
2024         }
2025 #endif
2026         return audioSinkBin;
2027     }
2028
2029 #if ENABLE(WEB_AUDIO)
2030     audioSinkBin = gst_bin_new("audio-sink");
2031     ensureAudioSourceProvider();
2032     m_audioSourceProvider->configureAudioBin(audioSinkBin, nullptr);
2033     return audioSinkBin;
2034 #endif
2035     ASSERT_NOT_REACHED();
2036     return nullptr;
2037 }
2038
2039 GstElement* MediaPlayerPrivateGStreamer::audioSink() const
2040 {
2041     GstElement* sink;
2042     g_object_get(m_pipeline.get(), "audio-sink", &sink, nullptr);
2043     return sink;
2044 }
2045
2046 #if ENABLE(WEB_AUDIO)
2047 void MediaPlayerPrivateGStreamer::ensureAudioSourceProvider()
2048 {
2049     if (!m_audioSourceProvider)
2050         m_audioSourceProvider = std::make_unique<AudioSourceProviderGStreamer>();
2051 }
2052
2053 AudioSourceProvider* MediaPlayerPrivateGStreamer::audioSourceProvider()
2054 {
2055     ensureAudioSourceProvider();
2056     return m_audioSourceProvider.get();
2057 }
2058 #endif
2059
2060 void MediaPlayerPrivateGStreamer::createGSTPlayBin()
2061 {
2062     ASSERT(!m_pipeline);
2063
2064     // gst_element_factory_make() returns a floating reference so
2065     // we should not adopt.
2066     setPipeline(gst_element_factory_make("playbin", "play"));
2067     setStreamVolumeElement(GST_STREAM_VOLUME(m_pipeline.get()));
2068
2069     GRefPtr<GstBus> bus = adoptGRef(gst_pipeline_get_bus(GST_PIPELINE(m_pipeline.get())));
2070     gst_bus_set_sync_handler(bus.get(), [](GstBus*, GstMessage* message, gpointer userData) {
2071         auto& player = *static_cast<MediaPlayerPrivateGStreamer*>(userData);
2072
2073         if (player.handleSyncMessage(message)) {
2074             gst_message_unref(message);
2075             return GST_BUS_DROP;
2076         }
2077
2078         return GST_BUS_PASS;
2079     }, this, nullptr);
2080
2081     // Let also other listeners subscribe to (application) messages in this bus.
2082     gst_bus_add_signal_watch(bus.get());
2083     g_signal_connect(bus.get(), "message", G_CALLBACK(busMessageCallback), this);
2084
2085     g_object_set(m_pipeline.get(), "mute", m_player->muted(), nullptr);
2086
2087     g_signal_connect_swapped(m_pipeline.get(), "notify::source", G_CALLBACK(sourceChangedCallback), this);
2088     g_signal_connect_swapped(m_pipeline.get(), "video-changed", G_CALLBACK(videoChangedCallback), this);
2089     g_signal_connect_swapped(m_pipeline.get(), "audio-changed", G_CALLBACK(audioChangedCallback), this);
2090 #if ENABLE(VIDEO_TRACK)
2091     g_signal_connect_swapped(m_pipeline.get(), "text-changed", G_CALLBACK(textChangedCallback), this);
2092
2093     GstElement* textCombiner = webkitTextCombinerNew();
2094     ASSERT(textCombiner);
2095     g_object_set(m_pipeline.get(), "text-stream-combiner", textCombiner, nullptr);
2096
2097     m_textAppSink = webkitTextSinkNew();
2098     ASSERT(m_textAppSink);
2099
2100     m_textAppSinkPad = adoptGRef(gst_element_get_static_pad(m_textAppSink.get(), "sink"));
2101     ASSERT(m_textAppSinkPad);
2102
2103     g_object_set(m_textAppSink.get(), "emit-signals", true, "enable-last-sample", false, "caps", gst_caps_new_empty_simple("text/vtt"), nullptr);
2104     g_signal_connect_swapped(m_textAppSink.get(), "new-sample", G_CALLBACK(newTextSampleCallback), this);
2105
2106     g_object_set(m_pipeline.get(), "text-sink", m_textAppSink.get(), nullptr);
2107 #endif
2108
2109     g_object_set(m_pipeline.get(), "video-sink", createVideoSink(), "audio-sink", createAudioSink(), nullptr);
2110
2111     configurePlaySink();
2112
2113     // On 1.4.2 and newer we use the audio-filter property instead.
2114     // See https://bugzilla.gnome.org/show_bug.cgi?id=735748 for
2115     // the reason for using >= 1.4.2 instead of >= 1.4.0.
2116     if (m_preservesPitch && webkitGstCheckVersion(1, 4, 2)) {
2117         GstElement* scale = gst_element_factory_make("scaletempo", nullptr);
2118
2119         if (!scale)
2120             GST_WARNING("Failed to create scaletempo");
2121         else
2122             g_object_set(m_pipeline.get(), "audio-filter", scale, nullptr);
2123     }
2124
2125     if (!m_renderingCanBeAccelerated) {
2126         // If not using accelerated compositing, let GStreamer handle
2127         // the image-orientation tag.
2128         GstElement* videoFlip = gst_element_factory_make("videoflip", nullptr);
2129         g_object_set(videoFlip, "method", 8, nullptr);
2130         g_object_set(m_pipeline.get(), "video-filter", videoFlip, nullptr);
2131     }
2132
2133     GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_videoSink.get(), "sink"));
2134     if (videoSinkPad)
2135         g_signal_connect_swapped(videoSinkPad.get(), "notify::caps", G_CALLBACK(videoSinkCapsChangedCallback), this);
2136 }
2137
2138 void MediaPlayerPrivateGStreamer::simulateAudioInterruption()
2139 {
2140     GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_pipeline.get()), GST_STATE_PAUSED);
2141     gst_element_post_message(m_pipeline.get(), message);
2142 }
2143
2144 bool MediaPlayerPrivateGStreamer::didPassCORSAccessCheck() const
2145 {
2146     if (WEBKIT_IS_WEB_SRC(m_source.get()))
2147         return webKitSrcPassedCORSAccessCheck(WEBKIT_WEB_SRC(m_source.get()));
2148     return false;
2149 }
2150
2151 bool MediaPlayerPrivateGStreamer::canSaveMediaData() const
2152 {
2153     if (isLiveStream())
2154         return false;
2155
2156     if (m_url.isLocalFile())
2157         return true;
2158
2159     if (m_url.protocolIsInHTTPFamily())
2160         return true;
2161
2162     return false;
2163 }
2164
2165 bool MediaPlayerPrivateGStreamer::handleSyncMessage(GstMessage* message)
2166 {
2167     return MediaPlayerPrivateGStreamerBase::handleSyncMessage(message);
2168 }
2169
2170 }
2171
2172 #endif // USE(GSTREAMER)