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 Igalia S.L
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * aint with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
25 #include "MediaPlayerPrivateGStreamer.h"
27 #if ENABLE(VIDEO) && USE(GSTREAMER)
29 #include "GStreamerUtilities.h"
30 #include "GStreamerVersioning.h"
33 #include "MIMETypeRegistry.h"
34 #include "MediaPlayer.h"
35 #include "NotImplemented.h"
36 #include "SecurityOrigin.h"
37 #include "TimeRanges.h"
38 #include "WebKitWebSourceGStreamer.h"
40 #include <gst/pbutils/missing-plugins.h>
42 #include <wtf/gobject/GOwnPtr.h>
43 #include <wtf/text/CString.h>
45 #if ENABLE(VIDEO_TRACK) && defined(GST_API_VERSION_1)
46 #include "InbandTextTrackPrivateGStreamer.h"
47 #include "TextCombinerGStreamer.h"
48 #include "TextSinkGStreamer.h"
51 #ifdef GST_API_VERSION_1
52 #include <gst/audio/streamvolume.h>
54 #include <gst/interfaces/streamvolume.h>
57 // GstPlayFlags flags from playbin2. It is the policy of GStreamer to
58 // not publicly expose element-specific enums. That's why this
59 // GstPlayFlags enum has been copied here.
61 GST_PLAY_FLAG_VIDEO = 0x00000001,
62 GST_PLAY_FLAG_AUDIO = 0x00000002,
63 GST_PLAY_FLAG_TEXT = 0x00000004,
64 GST_PLAY_FLAG_VIS = 0x00000008,
65 GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010,
66 GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020,
67 GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040,
68 GST_PLAY_FLAG_DOWNLOAD = 0x00000080,
69 GST_PLAY_FLAG_BUFFERING = 0x000000100
72 // gPercentMax is used when parsing buffering ranges with
73 // gst_query_parse_nth_buffering_range as there was a bug in GStreamer
74 // 0.10 that was using 100 instead of GST_FORMAT_PERCENT_MAX. This was
75 // corrected in 1.0. gst_query_parse_buffering_range worked as
76 // expected with GST_FORMAT_PERCENT_MAX in both cases.
77 #ifdef GST_API_VERSION_1
78 static const char* gPlaybinName = "playbin";
79 static const gint64 gPercentMax = GST_FORMAT_PERCENT_MAX;
81 static const char* gPlaybinName = "playbin2";
82 static const gint64 gPercentMax = 100;
84 // Max interval in seconds to stay in the READY state on manual
85 // state change requests.
86 static const guint gReadyStateTimerInterval = 60;
88 GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
89 #define GST_CAT_DEFAULT webkit_media_player_debug
95 static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
97 return player->handleMessage(message);
100 static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
102 player->sourceChanged();
105 static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
107 player->videoChanged();
110 static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
112 player->videoChanged();
115 static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
117 player->audioChanged();
120 static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
122 // This is the callback of the timeout source created in ::audioChanged.
123 player->notifyPlayerOfAudio();
127 #ifdef GST_API_VERSION_1
128 static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, gchar*,
129 MediaPlayerPrivateGStreamer* player)
131 static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, MediaPlayerPrivateGStreamer* player)
134 player->setAudioStreamProperties(object);
137 static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
139 // This is the callback of the timeout source created in ::videoChanged.
140 player->notifyPlayerOfVideo();
144 #if ENABLE(VIDEO_TRACK) && defined(GST_API_VERSION_1)
145 static void mediaPlayerPrivateTextChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
147 player->textChanged();
150 static gboolean mediaPlayerPrivateTextChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
152 // This is the callback of the timeout source created in ::textChanged.
153 player->notifyPlayerOfText();
157 static GstFlowReturn mediaPlayerPrivateNewTextSampleCallback(GObject*, MediaPlayerPrivateGStreamer* player)
159 player->newTextSample();
164 static gboolean mediaPlayerPrivateReadyStateTimeoutCallback(MediaPlayerPrivateGStreamer* player)
166 // This is the callback of the timeout source created in ::changePipelineState.
167 // Reset pipeline if we are sitting on READY state when timeout is reached
168 player->changePipelineState(GST_STATE_NULL);
172 static void mediaPlayerPrivatePluginInstallerResultFunction(GstInstallPluginsReturn result, gpointer userData)
174 MediaPlayerPrivateGStreamer* player = reinterpret_cast<MediaPlayerPrivateGStreamer*>(userData);
175 player->handlePluginInstallerResult(result);
178 static GstClockTime toGstClockTime(float time)
180 // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to
181 // round the microseconds so no floating point precision is lost and we can perform an accurate seek.
183 float microSeconds = modf(time, &seconds) * 1000000;
185 timeValue.tv_sec = static_cast<glong>(seconds);
186 timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
187 return GST_TIMEVAL_TO_TIME(timeValue);
190 void MediaPlayerPrivateGStreamer::setAudioStreamProperties(GObject* object)
192 if (g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
195 const char* role = m_player->mediaPlayerClient() && m_player->mediaPlayerClient()->mediaPlayerIsVideo()
197 GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, NULL);
198 g_object_set(object, "stream-properties", structure, NULL);
199 gst_structure_free(structure);
200 GOwnPtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(object)));
201 LOG_MEDIA_MESSAGE("Set media.role as %s at %s", role, elementName.get());
204 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player)
206 return adoptPtr(new MediaPlayerPrivateGStreamer(player));
209 void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar)
212 registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
215 bool initializeGStreamerAndRegisterWebKitElements()
217 if (!initializeGStreamer())
220 GRefPtr<GstElementFactory> srcFactory = gst_element_factory_find("webkitwebsrc");
222 GST_DEBUG_CATEGORY_INIT(webkit_media_player_debug, "webkitmediaplayer", 0, "WebKit media player");
223 return gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
229 bool MediaPlayerPrivateGStreamer::isAvailable()
231 if (!initializeGStreamerAndRegisterWebKitElements())
234 GRefPtr<GstElementFactory> factory = gst_element_factory_find(gPlaybinName);
238 MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
239 : MediaPlayerPrivateGStreamerBase(player)
242 , m_changingRate(false)
243 , m_endTime(numeric_limits<float>::infinity())
244 , m_isEndReached(false)
245 , m_isStreaming(false)
246 , m_mediaLocations(0)
247 , m_mediaLocationCurrentIndex(0)
248 , m_resetPipeline(false)
251 , m_seekIsPending(false)
252 , m_timeOfOverlappingSeek(-1)
255 , m_errorOccured(false)
257 , m_downloadFinished(false)
258 , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired)
260 , m_bufferingPercentage(0)
261 , m_preload(player->preload())
262 , m_delayingLoad(false)
263 , m_mediaDurationKnown(true)
264 , m_maxTimeLoadedAtLastDidLoadingProgress(0)
265 , m_volumeAndMuteInitialized(false)
268 , m_audioTimerHandler(0)
269 , m_textTimerHandler(0)
270 , m_videoTimerHandler(0)
271 , m_readyTimerHandler(0)
272 , m_webkitAudioSink(0)
274 , m_preservesPitch(false)
275 , m_requestedState(GST_STATE_VOID_PENDING)
276 , m_missingPlugins(false)
280 MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
282 #if ENABLE(VIDEO_TRACK) && defined(GST_API_VERSION_1)
283 for (size_t i = 0; i < m_textTracks.size(); ++i)
284 m_textTracks[i]->disconnect();
286 if (m_fillTimer.isActive())
289 if (m_mediaLocations) {
290 gst_structure_free(m_mediaLocations);
291 m_mediaLocations = 0;
295 g_signal_handlers_disconnect_by_func(G_OBJECT(m_autoAudioSink.get()),
296 reinterpret_cast<gpointer>(setAudioStreamPropertiesCallback), this);
298 if (m_readyTimerHandler)
299 g_source_remove(m_readyTimerHandler);
302 GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
304 g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateMessageCallback), this);
305 gst_bus_remove_signal_watch(bus.get());
307 g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateSourceChangedCallback), this);
308 g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoChangedCallback), this);
309 g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateAudioChangedCallback), this);
310 #if ENABLE(VIDEO_TRACK) && defined(GST_API_VERSION_1)
311 g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateNewTextSampleCallback), this);
312 g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateTextChangedCallback), this);
315 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
319 GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink"));
320 g_signal_handlers_disconnect_by_func(videoSinkPad.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
322 if (m_videoTimerHandler)
323 g_source_remove(m_videoTimerHandler);
325 if (m_audioTimerHandler)
326 g_source_remove(m_audioTimerHandler);
328 if (m_textTimerHandler)
329 g_source_remove(m_textTimerHandler);
332 void MediaPlayerPrivateGStreamer::load(const String& url)
334 if (!initializeGStreamerAndRegisterWebKitElements())
337 KURL kurl(KURL(), url);
338 String cleanUrl(url);
340 // Clean out everything after file:// url path.
341 if (kurl.isLocalFile())
342 cleanUrl = cleanUrl.substring(0, kurl.pathEnd());
349 m_url = KURL(KURL(), cleanUrl);
350 g_object_set(m_playBin.get(), "uri", cleanUrl.utf8().data(), NULL);
352 INFO_MEDIA_MESSAGE("Load %s", cleanUrl.utf8().data());
354 if (m_preload == MediaPlayer::None) {
355 LOG_MEDIA_MESSAGE("Delaying load.");
356 m_delayingLoad = true;
359 // Reset network and ready states. Those will be set properly once
360 // the pipeline pre-rolled.
361 m_networkState = MediaPlayer::Loading;
362 m_player->networkStateChanged();
363 m_readyState = MediaPlayer::HaveNothing;
364 m_player->readyStateChanged();
365 m_volumeAndMuteInitialized = false;
371 #if ENABLE(MEDIA_SOURCE)
372 void MediaPlayerPrivateGStreamer::load(const String& url, PassRefPtr<MediaSource>)
378 void MediaPlayerPrivateGStreamer::commitLoad()
380 ASSERT(!m_delayingLoad);
381 LOG_MEDIA_MESSAGE("Committing load.");
383 // GStreamer needs to have the pipeline set to a paused state to
384 // start providing anything useful.
385 changePipelineState(GST_STATE_PAUSED);
387 setDownloadBuffering();
391 float MediaPlayerPrivateGStreamer::playbackPosition() const
393 if (m_isEndReached) {
394 // Position queries on a null pipeline return 0. If we're at
395 // the end of the stream the pipeline is null but we want to
396 // report either the seek time or the duration because this is
397 // what the Media element spec expects us to do.
401 return m_mediaDuration;
405 // Position is only available if no async state change is going on and the state is either paused or playing.
406 gint64 position = GST_CLOCK_TIME_NONE;
407 GstQuery* query= gst_query_new_position(GST_FORMAT_TIME);
408 if (gst_element_query(m_playBin.get(), query))
409 gst_query_parse_position(query, 0, &position);
412 if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE)
413 result = static_cast<double>(position) / GST_SECOND;
414 else if (m_canFallBackToLastFinishedSeekPositon)
417 LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
419 gst_query_unref(query);
424 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
428 GstState currentState;
431 gst_element_get_state(m_playBin.get(), ¤tState, &pending, 0);
432 if (currentState == newState || pending == newState) {
433 LOG_MEDIA_MESSAGE("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState),
434 gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
438 LOG_MEDIA_MESSAGE("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState),
439 gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
441 GstStateChangeReturn setStateResult = gst_element_set_state(m_playBin.get(), newState);
442 GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
443 if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE) {
447 // Create a timer when entering the READY state so that we can free resources
448 // if we stay for too long on READY.
449 // Also lets remove the timer if we request a state change for any state other than READY.
450 // See also https://bugs.webkit.org/show_bug.cgi?id=117354
451 if (newState == GST_STATE_READY && !m_readyTimerHandler) {
452 m_readyTimerHandler = g_timeout_add_seconds(gReadyStateTimerInterval, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateReadyStateTimeoutCallback), this);
453 } else if (newState != GST_STATE_READY && m_readyTimerHandler) {
454 g_source_remove(m_readyTimerHandler);
455 m_readyTimerHandler = 0;
461 void MediaPlayerPrivateGStreamer::prepareToPlay()
463 m_preload = MediaPlayer::Auto;
464 if (m_delayingLoad) {
465 m_delayingLoad = false;
470 void MediaPlayerPrivateGStreamer::play()
472 if (changePipelineState(GST_STATE_PLAYING)) {
473 m_isEndReached = false;
474 m_delayingLoad = false;
475 m_preload = MediaPlayer::Auto;
476 setDownloadBuffering();
477 LOG_MEDIA_MESSAGE("Play");
479 loadingFailed(MediaPlayer::Empty);
483 void MediaPlayerPrivateGStreamer::pause()
485 GstState currentState, pendingState;
486 gst_element_get_state(m_playBin.get(), ¤tState, &pendingState, 0);
487 if (currentState < GST_STATE_PAUSED && pendingState <= GST_STATE_PAUSED)
490 if (changePipelineState(GST_STATE_PAUSED))
491 INFO_MEDIA_MESSAGE("Pause");
493 loadingFailed(MediaPlayer::Empty);
496 float MediaPlayerPrivateGStreamer::duration() const
504 // Media duration query failed already, don't attempt new useless queries.
505 if (!m_mediaDurationKnown)
506 return numeric_limits<float>::infinity();
509 return m_mediaDuration;
511 GstFormat timeFormat = GST_FORMAT_TIME;
512 gint64 timeLength = 0;
514 #ifdef GST_API_VERSION_1
515 bool failure = !gst_element_query_duration(m_playBin.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
517 bool failure = !gst_element_query_duration(m_playBin.get(), &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
520 LOG_MEDIA_MESSAGE("Time duration query failed for %s", m_url.string().utf8().data());
521 return numeric_limits<float>::infinity();
524 LOG_MEDIA_MESSAGE("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
526 m_mediaDuration = static_cast<double>(timeLength) / GST_SECOND;
527 return m_mediaDuration;
528 // FIXME: handle 3.14.9.5 properly
531 float MediaPlayerPrivateGStreamer::currentTime() const
543 // https://bugzilla.gnome.org/show_bug.cgi?id=639941 In GStreamer
544 // 0.10.35 basesink reports wrong duration in case of EOS and
545 // negative playback rate. There's no upstream accepted patch for
546 // this bug yet, hence this temporary workaround.
547 if (m_isEndReached && m_playbackRate < 0)
550 return playbackPosition();
553 void MediaPlayerPrivateGStreamer::seek(float time)
561 INFO_MEDIA_MESSAGE("[Seek] seek attempt to %f secs", time);
563 // Avoid useless seeking.
564 if (time == currentTime())
570 GstClockTime clockTime = toGstClockTime(time);
571 INFO_MEDIA_MESSAGE("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time);
574 m_timeOfOverlappingSeek = time;
575 if (m_seekIsPending) {
582 GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, 0, 0);
583 if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) {
584 LOG_MEDIA_MESSAGE("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
587 if (getStateResult == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED || m_isEndReached) {
588 m_seekIsPending = true;
589 if (m_isEndReached) {
590 LOG_MEDIA_MESSAGE("[Seek] reset pipeline");
591 m_resetPipeline = true;
592 if (!changePipelineState(GST_STATE_PAUSED))
593 loadingFailed(MediaPlayer::Empty);
597 if (!gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
598 GST_SEEK_TYPE_SET, clockTime, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
599 LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", time);
606 m_isEndReached = false;
609 bool MediaPlayerPrivateGStreamer::paused() const
611 if (m_isEndReached) {
612 LOG_MEDIA_MESSAGE("Ignoring pause at EOS");
617 gst_element_get_state(m_playBin.get(), &state, 0, 0);
618 return state == GST_STATE_PAUSED;
621 bool MediaPlayerPrivateGStreamer::seeking() const
626 void MediaPlayerPrivateGStreamer::videoChanged()
628 if (m_videoTimerHandler)
629 g_source_remove(m_videoTimerHandler);
630 m_videoTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this);
633 void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
635 m_videoTimerHandler = 0;
637 gint videoTracks = 0;
639 g_object_get(m_playBin.get(), "n-video", &videoTracks, NULL);
641 m_hasVideo = videoTracks > 0;
643 m_videoSize = IntSize();
645 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
648 void MediaPlayerPrivateGStreamer::audioChanged()
650 if (m_audioTimerHandler)
651 g_source_remove(m_audioTimerHandler);
652 m_audioTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this);
655 void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
657 m_audioTimerHandler = 0;
659 gint audioTracks = 0;
661 g_object_get(m_playBin.get(), "n-audio", &audioTracks, NULL);
662 m_hasAudio = audioTracks > 0;
663 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
666 #if ENABLE(VIDEO_TRACK) && defined(GST_API_VERSION_1)
667 void MediaPlayerPrivateGStreamer::textChanged()
669 if (m_textTimerHandler)
670 g_source_remove(m_textTimerHandler);
671 m_textTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateTextChangeTimeoutCallback), this);
674 void MediaPlayerPrivateGStreamer::notifyPlayerOfText()
676 m_textTimerHandler = 0;
680 g_object_get(m_playBin.get(), "n-text", &numTracks, NULL);
682 for (gint i = 0; i < numTracks; ++i) {
684 g_signal_emit_by_name(m_playBin.get(), "get-text-pad", i, &pad, NULL);
687 if (i < static_cast<gint>(m_textTracks.size())) {
688 RefPtr<InbandTextTrackPrivateGStreamer> existingTrack = m_textTracks[i];
689 existingTrack->setIndex(i);
690 if (existingTrack->pad() == pad) {
691 gst_object_unref(pad);
696 RefPtr<InbandTextTrackPrivateGStreamer> track = InbandTextTrackPrivateGStreamer::create(i, adoptGRef(pad));
697 m_textTracks.insert(i, track);
698 m_player->addTextTrack(track.release());
701 while (static_cast<gint>(m_textTracks.size()) > numTracks) {
702 RefPtr<InbandTextTrackPrivateGStreamer> track = m_textTracks.last();
704 m_textTracks.removeLast();
705 m_player->removeTextTrack(track.release());
709 void MediaPlayerPrivateGStreamer::newTextSample()
714 GRefPtr<GstEvent> streamStartEvent = adoptGRef(
715 gst_pad_get_sticky_event(m_textAppSinkPad.get(), GST_EVENT_STREAM_START, 0));
718 g_signal_emit_by_name(m_textAppSink.get(), "pull-sample", &sample, NULL);
721 if (streamStartEvent) {
724 gst_event_parse_stream_start(streamStartEvent.get(), &id);
725 for (size_t i = 0; i < m_textTracks.size(); ++i) {
726 RefPtr<InbandTextTrackPrivateGStreamer> track = m_textTracks[i];
727 if (track->streamId() == id) {
728 track->handleSample(sample);
734 WARN_MEDIA_MESSAGE("Got sample with unknown stream ID.");
736 WARN_MEDIA_MESSAGE("Unable to handle sample with no stream start event.");
737 gst_sample_unref(sample);
741 void MediaPlayerPrivateGStreamer::setRate(float rate)
743 // Avoid useless playback rate update.
744 if (m_playbackRate == rate)
750 gst_element_get_state(m_playBin.get(), &state, &pending, 0);
751 if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
752 || (pending == GST_STATE_PAUSED))
758 m_playbackRate = rate;
759 m_changingRate = true;
762 changePipelineState(GST_STATE_PAUSED);
766 float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND);
767 GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
771 INFO_MEDIA_MESSAGE("Set Rate to %f", rate);
773 // Mute the sound if the playback rate is too extreme and
774 // audio pitch is not adjusted.
775 mute = (!m_preservesPitch && (rate < 0.8 || rate > 2));
776 start = currentPosition;
777 end = GST_CLOCK_TIME_NONE;
782 // If we are at beginning of media, start from the end to
783 // avoid immediate EOS.
784 if (currentPosition <= 0)
785 end = static_cast<gint64>(duration() * GST_SECOND);
787 end = currentPosition;
790 INFO_MEDIA_MESSAGE("Need to mute audio?: %d", (int) mute);
792 if (!gst_element_seek(m_playBin.get(), rate, GST_FORMAT_TIME, flags,
793 GST_SEEK_TYPE_SET, start,
794 GST_SEEK_TYPE_SET, end))
795 ERROR_MEDIA_MESSAGE("Set rate to %f failed", rate);
797 g_object_set(m_playBin.get(), "mute", mute, NULL);
800 void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch)
802 m_preservesPitch = preservesPitch;
805 PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
807 RefPtr<TimeRanges> timeRanges = TimeRanges::create();
808 if (m_errorOccured || isLiveStream())
809 return timeRanges.release();
811 #if GST_CHECK_VERSION(0, 10, 31)
812 float mediaDuration(duration());
813 if (!mediaDuration || std::isinf(mediaDuration))
814 return timeRanges.release();
816 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
818 if (!gst_element_query(m_playBin.get(), query)) {
819 gst_query_unref(query);
820 return timeRanges.release();
823 for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) {
824 gint64 rangeStart = 0, rangeStop = 0;
825 if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop))
826 timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / gPercentMax),
827 static_cast<float>((rangeStop * mediaDuration) / gPercentMax));
830 // Fallback to the more general maxTimeLoaded() if no range has
832 if (!timeRanges->length())
833 if (float loaded = maxTimeLoaded())
834 timeRanges->add(0, loaded);
836 gst_query_unref(query);
838 float loaded = maxTimeLoaded();
839 if (!m_errorOccured && !isLiveStream() && loaded > 0)
840 timeRanges->add(0, loaded);
842 return timeRanges.release();
845 gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
848 GOwnPtr<gchar> debug;
849 MediaPlayer::NetworkState error;
850 bool issueError = true;
851 bool attemptNextLocation = false;
852 const GstStructure* structure = gst_message_get_structure(message);
853 GstState requestedState, currentState;
855 m_canFallBackToLastFinishedSeekPositon = false;
858 const gchar* messageTypeName = gst_structure_get_name(structure);
860 // Redirect messages are sent from elements, like qtdemux, to
861 // notify of the new location(s) of the media.
862 if (!g_strcmp0(messageTypeName, "redirect")) {
863 mediaLocationChanged(message);
868 // We ignore state changes from internal elements. They are forwarded to playbin2 anyway.
869 bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get());
871 LOG_MEDIA_MESSAGE("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message));
872 switch (GST_MESSAGE_TYPE(message)) {
873 case GST_MESSAGE_ERROR:
876 if (m_missingPlugins)
878 gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
879 ERROR_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
881 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error");
883 error = MediaPlayer::Empty;
884 if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
885 || err->code == GST_STREAM_ERROR_WRONG_TYPE
886 || err->code == GST_STREAM_ERROR_FAILED
887 || err->code == GST_CORE_ERROR_MISSING_PLUGIN
888 || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
889 error = MediaPlayer::FormatError;
890 else if (err->domain == GST_STREAM_ERROR) {
891 // Let the mediaPlayerClient handle the stream error, in
892 // this case the HTMLMediaElement will emit a stalled
894 if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
895 ERROR_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event.");
898 error = MediaPlayer::DecodeError;
899 attemptNextLocation = true;
900 } else if (err->domain == GST_RESOURCE_ERROR)
901 error = MediaPlayer::NetworkError;
903 if (attemptNextLocation)
904 issueError = !loadNextLocation();
906 loadingFailed(error);
908 case GST_MESSAGE_EOS:
911 case GST_MESSAGE_ASYNC_DONE:
912 if (!messageSourceIsPlaybin || m_delayingLoad)
914 asyncStateChangeDone();
916 case GST_MESSAGE_STATE_CHANGED: {
917 if (!messageSourceIsPlaybin || m_delayingLoad)
921 // Construct a filename for the graphviz dot file output.
923 gst_message_parse_state_changed(message, ¤tState, &newState, 0);
924 CString dotFileName = String::format("webkit-video.%s_%s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState)).utf8();
925 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
929 case GST_MESSAGE_BUFFERING:
930 processBufferingStats(message);
932 #ifdef GST_API_VERSION_1
933 case GST_MESSAGE_DURATION_CHANGED:
935 case GST_MESSAGE_DURATION:
937 if (messageSourceIsPlaybin)
940 case GST_MESSAGE_REQUEST_STATE:
941 gst_message_parse_request_state(message, &requestedState);
942 gst_element_get_state(m_playBin.get(), ¤tState, NULL, 250);
943 if (requestedState < currentState) {
944 GOwnPtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(message)));
945 INFO_MEDIA_MESSAGE("Element %s requested state change to %s", elementName.get(),
946 gst_element_state_get_name(requestedState));
947 m_requestedState = requestedState;
948 if (!changePipelineState(requestedState))
949 loadingFailed(MediaPlayer::Empty);
952 case GST_MESSAGE_ELEMENT:
953 if (gst_is_missing_plugin_message(message)) {
954 gchar* detail = gst_missing_plugin_message_get_installer_detail(message);
955 gchar* detailArray[2] = {detail, 0};
956 GstInstallPluginsReturn result = gst_install_plugins_async(detailArray, 0, mediaPlayerPrivatePluginInstallerResultFunction, this);
957 m_missingPlugins = result == GST_INSTALL_PLUGINS_STARTED_OK;
962 LOG_MEDIA_MESSAGE("Unhandled GStreamer message type: %s",
963 GST_MESSAGE_TYPE_NAME(message));
969 void MediaPlayerPrivateGStreamer::handlePluginInstallerResult(GstInstallPluginsReturn result)
971 m_missingPlugins = false;
972 if (result == GST_INSTALL_PLUGINS_SUCCESS) {
973 changePipelineState(GST_STATE_READY);
974 changePipelineState(GST_STATE_PAUSED);
978 void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
981 const GstStructure *structure = gst_message_get_structure(message);
982 gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage);
984 LOG_MEDIA_MESSAGE("[Buffering] Buffering: %d%%.", m_bufferingPercentage);
989 void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*)
991 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
993 if (!gst_element_query(m_playBin.get(), query)) {
994 gst_query_unref(query);
999 gdouble fillStatus = 100.0;
1001 gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
1002 gst_query_unref(query);
1005 fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
1007 LOG_MEDIA_MESSAGE("[Buffering] Download buffer filled up to %f%%", fillStatus);
1009 if (!m_mediaDuration)
1012 // Update maxTimeLoaded only if the media duration is
1013 // available. Otherwise we can't compute it.
1014 if (m_mediaDuration) {
1015 if (fillStatus == 100.0)
1016 m_maxTimeLoaded = m_mediaDuration;
1018 m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0);
1019 LOG_MEDIA_MESSAGE("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
1022 m_downloadFinished = fillStatus == 100.0;
1023 if (!m_downloadFinished) {
1028 // Media is now fully loaded. It will play even if network
1029 // connection is cut. Buffering is done, remove the fill source
1030 // from the main loop.
1035 float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
1040 LOG_MEDIA_MESSAGE("maxTimeSeekable");
1041 // infinite duration means live stream
1042 if (std::isinf(duration()))
1048 float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
1053 float loaded = m_maxTimeLoaded;
1054 if (m_isEndReached && m_mediaDuration)
1055 loaded = m_mediaDuration;
1056 LOG_MEDIA_MESSAGE("maxTimeLoaded: %f", loaded);
1060 bool MediaPlayerPrivateGStreamer::didLoadingProgress() const
1062 if (!m_playBin || !m_mediaDuration || !totalBytes())
1064 float currentMaxTimeLoaded = maxTimeLoaded();
1065 bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
1066 m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
1067 LOG_MEDIA_MESSAGE("didLoadingProgress: %d", didLoadingProgress);
1068 return didLoadingProgress;
1071 unsigned MediaPlayerPrivateGStreamer::totalBytes() const
1076 if (m_totalBytes != -1)
1077 return m_totalBytes;
1082 GstFormat fmt = GST_FORMAT_BYTES;
1084 #ifdef GST_API_VERSION_1
1085 if (gst_element_query_duration(m_source.get(), fmt, &length)) {
1087 if (gst_element_query_duration(m_source.get(), &fmt, &length)) {
1089 INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
1090 m_totalBytes = static_cast<unsigned>(length);
1091 m_isStreaming = !length;
1092 return m_totalBytes;
1095 // Fall back to querying the source pads manually.
1096 // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749
1097 GstIterator* iter = gst_element_iterate_src_pads(m_source.get());
1100 #ifdef GST_API_VERSION_1
1101 GValue item = G_VALUE_INIT;
1102 switch (gst_iterator_next(iter, &item)) {
1103 case GST_ITERATOR_OK: {
1104 GstPad* pad = static_cast<GstPad*>(g_value_get_object(&item));
1105 gint64 padLength = 0;
1106 if (gst_pad_query_duration(pad, fmt, &padLength) && padLength > length)
1113 switch (gst_iterator_next(iter, &data)) {
1114 case GST_ITERATOR_OK: {
1115 GRefPtr<GstPad> pad = adoptGRef(GST_PAD_CAST(data));
1116 gint64 padLength = 0;
1117 if (gst_pad_query_duration(pad.get(), &fmt, &padLength) && padLength > length)
1122 case GST_ITERATOR_RESYNC:
1123 gst_iterator_resync(iter);
1125 case GST_ITERATOR_ERROR:
1127 case GST_ITERATOR_DONE:
1132 #ifdef GST_API_VERSION_1
1133 g_value_unset(&item);
1137 gst_iterator_free(iter);
1139 INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
1140 m_totalBytes = static_cast<unsigned>(length);
1141 m_isStreaming = !length;
1142 return m_totalBytes;
1145 void MediaPlayerPrivateGStreamer::updateAudioSink()
1150 GstElement* sinkPtr = 0;
1152 g_object_get(m_playBin.get(), "audio-sink", &sinkPtr, NULL);
1153 m_webkitAudioSink = adoptGRef(sinkPtr);
1157 GstElement* MediaPlayerPrivateGStreamer::audioSink() const
1159 return m_webkitAudioSink.get();
1162 void MediaPlayerPrivateGStreamer::sourceChanged()
1164 GstElement* srcPtr = 0;
1166 g_object_get(m_playBin.get(), "source", &srcPtr, NULL);
1167 m_source = adoptGRef(srcPtr);
1169 if (WEBKIT_IS_WEB_SRC(m_source.get()))
1170 webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player);
1173 void MediaPlayerPrivateGStreamer::cancelLoad()
1175 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
1179 changePipelineState(GST_STATE_READY);
1182 void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
1184 if (!m_playBin || m_errorOccured)
1188 if (m_seekIsPending)
1191 LOG_MEDIA_MESSAGE("[Seek] seeked to %f", m_seekTime);
1193 if (m_timeOfOverlappingSeek != m_seekTime && m_timeOfOverlappingSeek != -1) {
1194 seek(m_timeOfOverlappingSeek);
1195 m_timeOfOverlappingSeek = -1;
1198 m_timeOfOverlappingSeek = -1;
1200 // The pipeline can still have a pending state. In this case a position query will fail.
1201 // Right now we can use m_seekTime as a fallback.
1202 m_canFallBackToLastFinishedSeekPositon = true;
1209 void MediaPlayerPrivateGStreamer::updateStates()
1217 MediaPlayer::NetworkState oldNetworkState = m_networkState;
1218 MediaPlayer::ReadyState oldReadyState = m_readyState;
1222 GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, &pending, 250 * GST_NSECOND);
1224 bool shouldUpdatePlaybackState = false;
1225 switch (getStateResult) {
1226 case GST_STATE_CHANGE_SUCCESS: {
1227 LOG_MEDIA_MESSAGE("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1229 // Do nothing if on EOS and state changed to READY to avoid recreating the player
1230 // on HTMLMediaElement and properly generate the video 'ended' event.
1231 if (m_isEndReached && state == GST_STATE_READY)
1234 if (state <= GST_STATE_READY) {
1235 m_resetPipeline = true;
1236 m_mediaDuration = 0;
1238 m_resetPipeline = false;
1242 bool didBuffering = m_buffering;
1244 // Update ready and network states.
1246 case GST_STATE_NULL:
1247 m_readyState = MediaPlayer::HaveNothing;
1248 m_networkState = MediaPlayer::Empty;
1250 case GST_STATE_READY:
1251 m_readyState = MediaPlayer::HaveMetadata;
1252 m_networkState = MediaPlayer::Empty;
1254 case GST_STATE_PAUSED:
1255 case GST_STATE_PLAYING:
1257 if (m_bufferingPercentage == 100) {
1258 LOG_MEDIA_MESSAGE("[Buffering] Complete.");
1259 m_buffering = false;
1260 m_readyState = MediaPlayer::HaveEnoughData;
1261 m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading;
1263 m_readyState = MediaPlayer::HaveCurrentData;
1264 m_networkState = MediaPlayer::Loading;
1266 } else if (m_downloadFinished) {
1267 m_readyState = MediaPlayer::HaveEnoughData;
1268 m_networkState = MediaPlayer::Loaded;
1270 m_readyState = MediaPlayer::HaveFutureData;
1271 m_networkState = MediaPlayer::Loading;
1276 ASSERT_NOT_REACHED();
1280 // Sync states where needed.
1281 if (state == GST_STATE_PAUSED) {
1282 if (!m_webkitAudioSink)
1285 if (!m_volumeAndMuteInitialized) {
1286 notifyPlayerOfVolumeChange();
1287 notifyPlayerOfMute();
1288 m_volumeAndMuteInitialized = true;
1291 if (didBuffering && !m_buffering && !m_paused) {
1292 LOG_MEDIA_MESSAGE("[Buffering] Restarting playback.");
1293 changePipelineState(GST_STATE_PLAYING);
1295 } else if (state == GST_STATE_PLAYING) {
1298 if (m_buffering && !isLiveStream()) {
1299 LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering.");
1300 changePipelineState(GST_STATE_PAUSED);
1305 if (m_changingRate) {
1306 m_player->rateChanged();
1307 m_changingRate = false;
1310 if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
1311 shouldUpdatePlaybackState = true;
1312 LOG_MEDIA_MESSAGE("Requested state change to %s was completed", gst_element_state_get_name(state));
1317 case GST_STATE_CHANGE_ASYNC:
1318 LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1319 // Change in progress.
1321 case GST_STATE_CHANGE_FAILURE:
1322 LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1325 case GST_STATE_CHANGE_NO_PREROLL:
1326 LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
1328 // Live pipelines go in PAUSED without prerolling.
1329 m_isStreaming = true;
1330 setDownloadBuffering();
1332 if (state == GST_STATE_READY)
1333 m_readyState = MediaPlayer::HaveNothing;
1334 else if (state == GST_STATE_PAUSED) {
1335 m_readyState = MediaPlayer::HaveEnoughData;
1337 } else if (state == GST_STATE_PLAYING)
1341 changePipelineState(GST_STATE_PLAYING);
1343 m_networkState = MediaPlayer::Loading;
1346 LOG_MEDIA_MESSAGE("Else : %d", getStateResult);
1350 m_requestedState = GST_STATE_VOID_PENDING;
1352 if (shouldUpdatePlaybackState)
1353 m_player->playbackStateChanged();
1355 if (m_networkState != oldNetworkState) {
1356 LOG_MEDIA_MESSAGE("Network State Changed from %u to %u", oldNetworkState, m_networkState);
1357 m_player->networkStateChanged();
1359 if (m_readyState != oldReadyState) {
1360 LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u", oldReadyState, m_readyState);
1361 m_player->readyStateChanged();
1364 if (m_seekIsPending && getStateResult == GST_STATE_CHANGE_SUCCESS && (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)) {
1365 LOG_MEDIA_MESSAGE("[Seek] committing pending seek to %f", m_seekTime);
1366 m_seekIsPending = false;
1367 m_seeking = gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
1368 GST_SEEK_TYPE_SET, toGstClockTime(m_seekTime), GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
1370 LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", m_seekTime);
1374 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
1376 if (m_mediaLocations)
1377 gst_structure_free(m_mediaLocations);
1379 const GstStructure* structure = gst_message_get_structure(message);
1381 // This structure can contain:
1382 // - both a new-location string and embedded locations structure
1383 // - or only a new-location string.
1384 m_mediaLocations = gst_structure_copy(structure);
1385 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1388 m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1;
1394 bool MediaPlayerPrivateGStreamer::loadNextLocation()
1396 if (!m_mediaLocations)
1399 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1400 const gchar* newLocation = 0;
1403 // Fallback on new-location string.
1404 newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
1410 if (m_mediaLocationCurrentIndex < 0) {
1411 m_mediaLocations = 0;
1415 const GValue* location = gst_value_list_get_value(locations,
1416 m_mediaLocationCurrentIndex);
1417 const GstStructure* structure = gst_value_get_structure(location);
1420 m_mediaLocationCurrentIndex--;
1424 newLocation = gst_structure_get_string(structure, "new-location");
1428 // Found a candidate. new-location is not always an absolute url
1429 // though. We need to take the base of the current url and
1430 // append the value of new-location to it.
1431 KURL baseUrl = gst_uri_is_valid(newLocation) ? KURL() : m_url;
1432 KURL newUrl = KURL(baseUrl, newLocation);
1434 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(m_url);
1435 if (securityOrigin->canRequest(newUrl)) {
1436 INFO_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data());
1438 // Reset player states.
1439 m_networkState = MediaPlayer::Loading;
1440 m_player->networkStateChanged();
1441 m_readyState = MediaPlayer::HaveNothing;
1442 m_player->readyStateChanged();
1444 // Reset pipeline state.
1445 m_resetPipeline = true;
1446 changePipelineState(GST_STATE_READY);
1449 gst_element_get_state(m_playBin.get(), &state, 0, 0);
1450 if (state <= GST_STATE_READY) {
1451 // Set the new uri and start playing.
1452 g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL);
1454 changePipelineState(GST_STATE_PLAYING);
1458 INFO_MEDIA_MESSAGE("Not allowed to load new media location: %s", newUrl.string().utf8().data());
1460 m_mediaLocationCurrentIndex--;
1464 void MediaPlayerPrivateGStreamer::loadStateChanged()
1469 void MediaPlayerPrivateGStreamer::timeChanged()
1472 m_player->timeChanged();
1475 void MediaPlayerPrivateGStreamer::didEnd()
1477 // Synchronize position and duration values to not confuse the
1478 // HTMLMediaElement. In some cases like reverse playback the
1479 // position is not always reported as 0 for instance.
1480 float now = currentTime();
1481 if (now > 0 && now <= duration() && m_mediaDuration != now) {
1482 m_mediaDurationKnown = true;
1483 m_mediaDuration = now;
1484 m_player->durationChanged();
1487 m_isEndReached = true;
1490 if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) {
1492 changePipelineState(GST_STATE_READY);
1493 m_downloadFinished = false;
1497 void MediaPlayerPrivateGStreamer::cacheDuration()
1499 if (m_mediaDuration || !m_mediaDurationKnown)
1502 float newDuration = duration();
1503 if (std::isinf(newDuration)) {
1504 // Only pretend that duration is not available if the the query failed in a stable pipeline state.
1506 if (gst_element_get_state(m_playBin.get(), &state, 0, 0) == GST_STATE_CHANGE_SUCCESS && state > GST_STATE_READY)
1507 m_mediaDurationKnown = false;
1511 m_mediaDuration = newDuration;
1514 void MediaPlayerPrivateGStreamer::durationChanged()
1516 float previousDuration = m_mediaDuration;
1519 // Avoid emiting durationchanged in the case where the previous
1520 // duration was 0 because that case is already handled by the
1521 // HTMLMediaElement.
1522 if (previousDuration && m_mediaDuration != previousDuration)
1523 m_player->durationChanged();
1526 void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
1528 m_errorOccured = true;
1529 if (m_networkState != error) {
1530 m_networkState = error;
1531 m_player->networkStateChanged();
1533 if (m_readyState != MediaPlayer::HaveNothing) {
1534 m_readyState = MediaPlayer::HaveNothing;
1535 m_player->readyStateChanged();
1538 // Loading failed, remove ready timer.
1539 if (m_readyTimerHandler) {
1540 g_source_remove(m_readyTimerHandler);
1541 m_readyTimerHandler = 0;
1545 static HashSet<String> mimeTypeCache()
1547 initializeGStreamerAndRegisterWebKitElements();
1549 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1550 static bool typeListInitialized = false;
1552 if (typeListInitialized)
1555 const char* mimeTypes[] = {
1557 "application/vnd.apple.mpegurl",
1558 "application/vnd.rn-realmedia",
1559 "application/x-3gp",
1560 "application/x-pn-realaudio",
1581 "audio/x-amr-nb-sh",
1582 "audio/x-amr-wb-sh",
1613 "audio/x-vorbis+ogg",
1617 "audio/x-wavpack-correction",
1644 for (unsigned i = 0; i < (sizeof(mimeTypes) / sizeof(*mimeTypes)); ++i)
1645 cache.add(String(mimeTypes[i]));
1647 typeListInitialized = true;
1651 void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types)
1653 types = mimeTypeCache();
1656 MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String& type, const String& codecs, const KURL&)
1658 if (type.isNull() || type.isEmpty())
1659 return MediaPlayer::IsNotSupported;
1661 // spec says we should not return "probably" if the codecs string is empty
1662 if (mimeTypeCache().contains(type))
1663 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
1664 return MediaPlayer::IsNotSupported;
1667 void MediaPlayerPrivateGStreamer::setDownloadBuffering()
1673 g_object_get(m_playBin.get(), "flags", &flags, NULL);
1675 // We don't want to stop downloading if we already started it.
1676 if (flags & GST_PLAY_FLAG_DOWNLOAD && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline)
1679 bool shouldDownload = !isLiveStream() && m_preload == MediaPlayer::Auto;
1680 if (shouldDownload) {
1681 LOG_MEDIA_MESSAGE("Enabling on-disk buffering");
1682 g_object_set(m_playBin.get(), "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
1683 m_fillTimer.startRepeating(0.2);
1685 LOG_MEDIA_MESSAGE("Disabling on-disk buffering");
1686 g_object_set(m_playBin.get(), "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
1691 void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
1693 if (preload == MediaPlayer::Auto && isLiveStream())
1696 m_preload = preload;
1697 setDownloadBuffering();
1699 if (m_delayingLoad && m_preload != MediaPlayer::None) {
1700 m_delayingLoad = false;
1705 void MediaPlayerPrivateGStreamer::createAudioSink()
1707 // Construct audio sink if pitch preserving is enabled.
1708 if (!m_preservesPitch)
1714 GstElement* scale = gst_element_factory_make("scaletempo", 0);
1716 GST_WARNING("Failed to create scaletempo");
1720 GstElement* convert = gst_element_factory_make("audioconvert", 0);
1721 GstElement* resample = gst_element_factory_make("audioresample", 0);
1722 GstElement* sink = gst_element_factory_make("autoaudiosink", 0);
1724 m_autoAudioSink = sink;
1726 g_signal_connect(sink, "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this);
1728 GstElement* audioSink = gst_bin_new("audio-sink");
1729 gst_bin_add_many(GST_BIN(audioSink), scale, convert, resample, sink, NULL);
1731 if (!gst_element_link_many(scale, convert, resample, sink, NULL)) {
1732 GST_WARNING("Failed to link audio sink elements");
1733 gst_object_unref(audioSink);
1737 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
1738 gst_element_add_pad(audioSink, gst_ghost_pad_new("sink", pad.get()));
1740 g_object_set(m_playBin.get(), "audio-sink", audioSink, NULL);
1743 void MediaPlayerPrivateGStreamer::createGSTPlayBin()
1747 // gst_element_factory_make() returns a floating reference so
1748 // we should not adopt.
1749 m_playBin = gst_element_factory_make(gPlaybinName, "play");
1750 setStreamVolumeElement(GST_STREAM_VOLUME(m_playBin.get()));
1752 GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
1753 gst_bus_add_signal_watch(bus.get());
1754 g_signal_connect(bus.get(), "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
1756 g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL);
1758 g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
1759 g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
1760 g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
1761 #if ENABLE(VIDEO_TRACK) && defined(GST_API_VERSION_1)
1762 if (webkitGstCheckVersion(1, 1, 2)) {
1763 g_signal_connect(m_playBin.get(), "text-changed", G_CALLBACK(mediaPlayerPrivateTextChangedCallback), this);
1765 GstElement* textCombiner = webkitTextCombinerNew();
1766 ASSERT(textCombiner);
1767 g_object_set(m_playBin.get(), "text-stream-combiner", textCombiner, NULL);
1769 m_textAppSink = webkitTextSinkNew();
1770 ASSERT(m_textAppSink);
1772 m_textAppSinkPad = adoptGRef(gst_element_get_static_pad(m_textAppSink.get(), "sink"));
1773 ASSERT(m_textAppSinkPad);
1775 g_object_set(m_textAppSink.get(), "emit-signals", true, "enable-last-sample", false, "caps", gst_caps_new_empty_simple("text/vtt"), NULL);
1776 g_signal_connect(m_textAppSink.get(), "new-sample", G_CALLBACK(mediaPlayerPrivateNewTextSampleCallback), this);
1778 g_object_set(m_playBin.get(), "text-sink", m_textAppSink.get(), NULL);
1782 GstElement* videoElement = createVideoSink(m_playBin.get());
1784 g_object_set(m_playBin.get(), "video-sink", videoElement, NULL);
1786 GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink"));
1788 g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
1793 void MediaPlayerPrivateGStreamer::simulateAudioInterruption()
1795 GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_playBin.get()), GST_STATE_PAUSED);
1796 gst_element_post_message(m_playBin.get(), message);
1801 #endif // USE(GSTREAMER)