summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp')
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp1294
1 files changed, 520 insertions, 774 deletions
diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
index 6b69cb006..de27ee07d 100644
--- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
+++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp
@@ -3,7 +3,7 @@
* Copyright (C) 2007 Collabora Ltd. All rights reserved.
* Copyright (C) 2007 Alp Toker <alp@atoker.com>
* Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
- * Copyright (C) 2009, 2010 Igalia S.L
+ * Copyright (C) 2009, 2010, 2011, 2012, 2013 Igalia S.L
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -26,30 +26,19 @@
#if ENABLE(VIDEO) && USE(GSTREAMER)
-#include "ColorSpace.h"
-#include "Document.h"
-#include "Frame.h"
-#include "FrameView.h"
-#include "GStreamerGWorld.h"
#include "GStreamerUtilities.h"
#include "GStreamerVersioning.h"
-#include "GraphicsContext.h"
-#include "GraphicsTypes.h"
-#include "ImageGStreamer.h"
-#include "ImageOrientation.h"
-#include "IntRect.h"
#include "KURL.h"
+#include "Logging.h"
#include "MIMETypeRegistry.h"
#include "MediaPlayer.h"
#include "NotImplemented.h"
#include "SecurityOrigin.h"
#include "TimeRanges.h"
-#include "VideoSinkGStreamer.h"
#include "WebKitWebSourceGStreamer.h"
#include <gst/gst.h>
-#include <gst/video/video.h>
+#include <gst/pbutils/missing-plugins.h>
#include <limits>
-#include <math.h>
#include <wtf/gobject/GOwnPtr.h>
#include <wtf/text/CString.h>
@@ -87,24 +76,13 @@ static const char* gPlaybinName = "playbin2";
static const gint64 gPercentMax = 100;
#endif
-GST_DEBUG_CATEGORY_STATIC(webkit_media_player_debug);
+GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug);
#define GST_CAT_DEFAULT webkit_media_player_debug
using namespace std;
namespace WebCore {
-static int greatestCommonDivisor(int a, int b)
-{
- while (b) {
- int temp = a;
- a = b;
- b = temp % b;
- }
-
- return ABS(a);
-}
-
static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
{
return player->handleMessage(message);
@@ -115,32 +93,6 @@ static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, Media
player->sourceChanged();
}
-static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
-{
- // This is called when playbin receives the notify::volume signal.
- player->volumeChanged();
-}
-
-static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
-{
- // This is the callback of the timeout source created in ::volumeChanged.
- player->notifyPlayerOfVolumeChange();
- return FALSE;
-}
-
-static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
-{
- // This is called when playbin receives the notify::mute signal.
- player->muteChanged();
-}
-
-static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
-{
- // This is the callback of the timeout source created in ::muteChanged.
- player->notifyPlayerOfMute();
- return FALSE;
-}
-
static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
{
player->videoChanged();
@@ -163,6 +115,16 @@ static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateG
return FALSE;
}
+#ifdef GST_API_VERSION_1
+static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, gchar*,
+ MediaPlayerPrivateGStreamer* player)
+#else
+static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, MediaPlayerPrivateGStreamer* player)
+#endif
+{
+ player->setAudioStreamProperties(object);
+}
+
static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
{
// This is the callback of the timeout source created in ::videoChanged.
@@ -170,9 +132,36 @@ static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateG
return FALSE;
}
-static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamer* playerPrivate)
+static void mediaPlayerPrivatePluginInstallerResultFunction(GstInstallPluginsReturn result, gpointer userData)
{
- playerPrivate->triggerRepaint(buffer);
+ MediaPlayerPrivateGStreamer* player = reinterpret_cast<MediaPlayerPrivateGStreamer*>(userData);
+ player->handlePluginInstallerResult(result);
+}
+
+static GstClockTime toGstClockTime(float time)
+{
+ // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to
+ // round the microseconds so no floating point precision is lost and we can perform an accurate seek.
+ float seconds;
+ float microSeconds = modf(time, &seconds) * 1000000;
+ GTimeVal timeValue;
+ timeValue.tv_sec = static_cast<glong>(seconds);
+ timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
+ return GST_TIMEVAL_TO_TIME(timeValue);
+}
+
+void MediaPlayerPrivateGStreamer::setAudioStreamProperties(GObject* object)
+{
+ if (g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
+ return;
+
+ const char* role = m_player->mediaPlayerClient() && m_player->mediaPlayerClient()->mediaPlayerIsVideo()
+ ? "video" : "music";
+ GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, NULL);
+ g_object_set(object, "stream-properties", structure, NULL);
+ gst_structure_free(structure);
+ GOwnPtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(object)));
+ LOG_MEDIA_MESSAGE("Set media.role as %s at %s", role, elementName.get());
}
PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player)
@@ -210,48 +199,43 @@ bool MediaPlayerPrivateGStreamer::isAvailable()
}
MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
- : m_player(player)
- , m_webkitVideoSink(0)
- , m_fpsSink(0)
+ : MediaPlayerPrivateGStreamerBase(player)
, m_source(0)
, m_seekTime(0)
, m_changingRate(false)
, m_endTime(numeric_limits<float>::infinity())
, m_isEndReached(false)
- , m_networkState(MediaPlayer::Empty)
- , m_readyState(MediaPlayer::HaveNothing)
, m_isStreaming(false)
- , m_size(IntSize())
- , m_buffer(0)
, m_mediaLocations(0)
, m_mediaLocationCurrentIndex(0)
, m_resetPipeline(false)
, m_paused(true)
, m_seeking(false)
+ , m_seekIsPending(false)
+ , m_timeOfOverlappingSeek(-1)
, m_buffering(false)
, m_playbackRate(1)
, m_errorOccured(false)
, m_mediaDuration(0)
- , m_startedBuffering(false)
+ , m_downloadFinished(false)
, m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired)
, m_maxTimeLoaded(0)
, m_bufferingPercentage(0)
- , m_preload(MediaPlayer::Auto)
+ , m_preload(player->preload())
, m_delayingLoad(false)
, m_mediaDurationKnown(true)
, m_maxTimeLoadedAtLastDidLoadingProgress(0)
- , m_volumeTimerHandler(0)
- , m_muteTimerHandler(0)
+ , m_volumeAndMuteInitialized(false)
, m_hasVideo(false)
, m_hasAudio(false)
, m_audioTimerHandler(0)
, m_videoTimerHandler(0)
, m_webkitAudioSink(0)
, m_totalBytes(-1)
- , m_originalPreloadWasAutoAndWasOverridden(false)
+ , m_preservesPitch(false)
+ , m_requestedState(GST_STATE_VOID_PENDING)
+ , m_missingPlugins(false)
{
- if (initializeGStreamerAndRegisterWebKitElements())
- createGSTPlayBin();
}
MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
@@ -259,34 +243,31 @@ MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
if (m_fillTimer.isActive())
m_fillTimer.stop();
- if (m_buffer)
- gst_buffer_unref(m_buffer);
- m_buffer = 0;
-
if (m_mediaLocations) {
gst_structure_free(m_mediaLocations);
m_mediaLocations = 0;
}
-#ifndef GST_API_VERSION_1
- if (m_videoSinkBin) {
- gst_object_unref(m_videoSinkBin);
- m_videoSinkBin = 0;
- }
-#endif
+ if (m_autoAudioSink)
+ g_signal_handlers_disconnect_by_func(G_OBJECT(m_autoAudioSink.get()),
+ reinterpret_cast<gpointer>(setAudioStreamPropertiesCallback), this);
if (m_playBin) {
- gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
- m_playBin = 0;
- }
+ GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
+ ASSERT(bus);
+ g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateMessageCallback), this);
+ gst_bus_remove_signal_watch(bus.get());
- m_player = 0;
+ g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateSourceChangedCallback), this);
+ g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoChangedCallback), this);
+ g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateAudioChangedCallback), this);
- if (m_muteTimerHandler)
- g_source_remove(m_muteTimerHandler);
+ gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
+ m_playBin.clear();
+ }
- if (m_volumeTimerHandler)
- g_source_remove(m_volumeTimerHandler);
+ GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink"));
+ g_signal_handlers_disconnect_by_func(videoSinkPad.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
if (m_videoTimerHandler)
g_source_remove(m_videoTimerHandler);
@@ -297,6 +278,8 @@ MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
void MediaPlayerPrivateGStreamer::load(const String& url)
{
+ if (!initializeGStreamerAndRegisterWebKitElements())
+ return;
KURL kurl(KURL(), url);
String cleanUrl(url);
@@ -305,10 +288,15 @@ void MediaPlayerPrivateGStreamer::load(const String& url)
if (kurl.isLocalFile())
cleanUrl = cleanUrl.substring(0, kurl.pathEnd());
+ if (!m_playBin)
+ createGSTPlayBin();
+
+ ASSERT(m_playBin);
+
m_url = KURL(KURL(), cleanUrl);
g_object_set(m_playBin.get(), "uri", cleanUrl.utf8().data(), NULL);
- LOG_MEDIA_MESSAGE("Load %s", cleanUrl.utf8().data());
+ INFO_MEDIA_MESSAGE("Load %s", cleanUrl.utf8().data());
if (m_preload == MediaPlayer::None) {
LOG_MEDIA_MESSAGE("Delaying load.");
@@ -321,19 +309,29 @@ void MediaPlayerPrivateGStreamer::load(const String& url)
m_player->networkStateChanged();
m_readyState = MediaPlayer::HaveNothing;
m_player->readyStateChanged();
-
- // GStreamer needs to have the pipeline set to a paused state to
- // start providing anything useful.
- gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
+ m_volumeAndMuteInitialized = false;
if (!m_delayingLoad)
commitLoad();
}
+#if ENABLE(MEDIA_SOURCE)
+void MediaPlayerPrivateGStreamer::load(const String& url, PassRefPtr<MediaSource>)
+{
+ notImplemented();
+}
+#endif
+
void MediaPlayerPrivateGStreamer::commitLoad()
{
ASSERT(!m_delayingLoad);
LOG_MEDIA_MESSAGE("Committing load.");
+
+ // GStreamer needs to have the pipeline set to a paused state to
+ // start providing anything useful.
+ gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
+
+ setDownloadBuffering();
updateStates();
}
@@ -348,30 +346,26 @@ float MediaPlayerPrivateGStreamer::playbackPosition() const
return m_seekTime;
if (m_mediaDuration)
return m_mediaDuration;
+ return 0;
}
- float ret = 0.0f;
-
- GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
- if (!gst_element_query(m_playBin.get(), query)) {
- LOG_MEDIA_MESSAGE("Position query failed...");
- gst_query_unref(query);
- return ret;
- }
-
- gint64 position;
- gst_query_parse_position(query, 0, &position);
+ // Position is only available if no async state change is going on and the state is either paused or playing.
+ gint64 position = GST_CLOCK_TIME_NONE;
+ GstQuery* query= gst_query_new_position(GST_FORMAT_TIME);
+ if (gst_element_query(m_playBin.get(), query))
+ gst_query_parse_position(query, 0, &position);
- // Position is available only if the pipeline is not in GST_STATE_NULL or
- // GST_STATE_READY state.
- if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE))
- ret = static_cast<double>(position) / GST_SECOND;
+ float result = 0.0f;
+ if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE)
+ result = static_cast<double>(position) / GST_SECOND;
+ else if (m_canFallBackToLastFinishedSeekPositon)
+ result = m_seekTime;
LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
gst_query_unref(query);
- return ret;
+ return result;
}
bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
@@ -382,9 +376,14 @@ bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
GstState pending;
gst_element_get_state(m_playBin.get(), &currentState, &pending, 0);
- LOG_MEDIA_MESSAGE("Current state: %s, pending: %s", gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
- if (currentState == newState || pending == newState)
+ if (currentState == newState || pending == newState) {
+ LOG_MEDIA_MESSAGE("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState),
+ gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
return true;
+ }
+
+ LOG_MEDIA_MESSAGE("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState),
+ gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
GstStateChangeReturn setStateResult = gst_element_set_state(m_playBin.get(), newState);
GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
@@ -397,9 +396,7 @@ bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
void MediaPlayerPrivateGStreamer::prepareToPlay()
{
- m_isEndReached = false;
- m_seeking = false;
-
+ m_preload = MediaPlayer::Auto;
if (m_delayingLoad) {
m_delayingLoad = false;
commitLoad();
@@ -410,17 +407,22 @@ void MediaPlayerPrivateGStreamer::play()
{
if (changePipelineState(GST_STATE_PLAYING)) {
m_isEndReached = false;
+ m_delayingLoad = false;
+ m_preload = MediaPlayer::Auto;
+ setDownloadBuffering();
LOG_MEDIA_MESSAGE("Play");
}
}
void MediaPlayerPrivateGStreamer::pause()
{
- if (m_isEndReached)
+ GstState currentState, pendingState;
+ gst_element_get_state(m_playBin.get(), &currentState, &pendingState, 0);
+ if (currentState < GST_STATE_PAUSED && pendingState <= GST_STATE_PAUSED)
return;
if (changePipelineState(GST_STATE_PAUSED))
- LOG_MEDIA_MESSAGE("Pause");
+ INFO_MEDIA_MESSAGE("Pause");
}
float MediaPlayerPrivateGStreamer::duration() const
@@ -453,7 +455,8 @@ float MediaPlayerPrivateGStreamer::duration() const
LOG_MEDIA_MESSAGE("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
- return static_cast<double>(timeLength) / GST_SECOND;
+ m_mediaDuration = static_cast<double>(timeLength) / GST_SECOND;
+ return m_mediaDuration;
// FIXME: handle 3.14.9.5 properly
}
@@ -477,7 +480,6 @@ float MediaPlayerPrivateGStreamer::currentTime() const
return 0.0f;
return playbackPosition();
-
}
void MediaPlayerPrivateGStreamer::seek(float time)
@@ -488,35 +490,51 @@ void MediaPlayerPrivateGStreamer::seek(float time)
if (m_errorOccured)
return;
- LOG_MEDIA_MESSAGE("Seek attempt to %f secs", time);
+ INFO_MEDIA_MESSAGE("[Seek] seek attempt to %f secs", time);
// Avoid useless seeking.
if (time == currentTime())
return;
- // Extract the integer part of the time (seconds) and the
- // fractional part (microseconds). Attempt to round the
- // microseconds so no floating point precision is lost and we can
- // perform an accurate seek.
- float seconds;
- float microSeconds = modf(time, &seconds) * 1000000;
- GTimeVal timeValue;
- timeValue.tv_sec = static_cast<glong>(seconds);
- timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
+ if (isLiveStream())
+ return;
- GstClockTime clockTime = GST_TIMEVAL_TO_TIME(timeValue);
- LOG_MEDIA_MESSAGE("Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(clockTime));
-
- if (!gst_element_seek(m_playBin.get(), m_player->rate(),
- GST_FORMAT_TIME,
- (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
- GST_SEEK_TYPE_SET, clockTime,
- GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
- LOG_MEDIA_MESSAGE("Seek to %f failed", time);
- else {
- m_seeking = true;
- m_seekTime = time;
+ GstClockTime clockTime = toGstClockTime(time);
+ INFO_MEDIA_MESSAGE("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time);
+
+ if (m_seeking) {
+ m_timeOfOverlappingSeek = time;
+ if (m_seekIsPending) {
+ m_seekTime = time;
+ return;
+ }
+ }
+
+ GstState state;
+ GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, 0, 0);
+ if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) {
+ LOG_MEDIA_MESSAGE("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult));
+ return;
}
+ if (getStateResult == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED || m_isEndReached) {
+ m_seekIsPending = true;
+ if (m_isEndReached) {
+ LOG_MEDIA_MESSAGE("[Seek] reset pipeline");
+ m_resetPipeline = true;
+ changePipelineState(GST_STATE_PAUSED);
+ }
+ } else {
+ // We can seek now.
+ if (!gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
+ GST_SEEK_TYPE_SET, clockTime, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
+ LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", time);
+ return;
+ }
+ }
+
+ m_seeking = true;
+ m_seekTime = time;
+ m_isEndReached = false;
}
bool MediaPlayerPrivateGStreamer::paused() const
@@ -536,66 +554,6 @@ bool MediaPlayerPrivateGStreamer::seeking() const
return m_seeking;
}
-// Returns the size of the video
-IntSize MediaPlayerPrivateGStreamer::naturalSize() const
-{
- if (!hasVideo())
- return IntSize();
-
- if (!m_videoSize.isEmpty())
- return m_videoSize;
-
- GRefPtr<GstCaps> caps = webkitGstGetPadCaps(m_videoSinkPad.get());
- if (!caps)
- return IntSize();
-
-
- // TODO: handle possible clean aperture data. See
- // https://bugzilla.gnome.org/show_bug.cgi?id=596571
- // TODO: handle possible transformation matrix. See
- // https://bugzilla.gnome.org/show_bug.cgi?id=596326
-
- // Get the video PAR and original size, if this fails the
- // video-sink has likely not yet negotiated its caps.
- int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride;
- IntSize originalSize;
- GstVideoFormat format;
- if (!getVideoSizeAndFormatFromCaps(caps.get(), originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride))
- return IntSize();
-
- LOG_MEDIA_MESSAGE("Original video size: %dx%d", originalSize.width(), originalSize.height());
- LOG_MEDIA_MESSAGE("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator);
-
- // Calculate DAR based on PAR and video size.
- int displayWidth = originalSize.width() * pixelAspectRatioNumerator;
- int displayHeight = originalSize.height() * pixelAspectRatioDenominator;
-
- // Divide display width and height by their GCD to avoid possible overflows.
- int displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight);
- displayWidth /= displayAspectRatioGCD;
- displayHeight /= displayAspectRatioGCD;
-
- // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function.
- guint64 width = 0, height = 0;
- if (!(originalSize.height() % displayHeight)) {
- LOG_MEDIA_MESSAGE("Keeping video original height");
- width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
- height = static_cast<guint64>(originalSize.height());
- } else if (!(originalSize.width() % displayWidth)) {
- LOG_MEDIA_MESSAGE("Keeping video original width");
- height = gst_util_uint64_scale_int(originalSize.width(), displayHeight, displayWidth);
- width = static_cast<guint64>(originalSize.width());
- } else {
- LOG_MEDIA_MESSAGE("Approximating while keeping original video height");
- width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
- height = static_cast<guint64>(originalSize.height());
- }
-
- LOG_MEDIA_MESSAGE("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height);
- m_videoSize = IntSize(static_cast<int>(width), static_cast<int>(height));
- return m_videoSize;
-}
-
void MediaPlayerPrivateGStreamer::videoChanged()
{
if (m_videoTimerHandler)
@@ -636,37 +594,6 @@ void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
}
-void MediaPlayerPrivateGStreamer::setVolume(float volume)
-{
- if (!m_playBin)
- return;
-
- gst_stream_volume_set_volume(GST_STREAM_VOLUME(m_playBin.get()), GST_STREAM_VOLUME_FORMAT_CUBIC,
- static_cast<double>(volume));
-}
-
-void MediaPlayerPrivateGStreamer::notifyPlayerOfVolumeChange()
-{
- m_volumeTimerHandler = 0;
-
- if (!m_player || !m_playBin)
- return;
- double volume;
- volume = gst_stream_volume_get_volume(GST_STREAM_VOLUME(m_playBin.get()), GST_STREAM_VOLUME_FORMAT_CUBIC);
- // get_volume() can return values superior to 1.0 if the user
- // applies software user gain via third party application (GNOME
- // volume control for instance).
- volume = CLAMP(volume, 0.0, 1.0);
- m_player->volumeChanged(static_cast<float>(volume));
-}
-
-void MediaPlayerPrivateGStreamer::volumeChanged()
-{
- if (m_volumeTimerHandler)
- g_source_remove(m_volumeTimerHandler);
- m_volumeTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this);
-}
-
void MediaPlayerPrivateGStreamer::setRate(float rate)
{
// Avoid useless playback rate update.
@@ -697,11 +624,11 @@ void MediaPlayerPrivateGStreamer::setRate(float rate)
gint64 start, end;
bool mute = false;
- LOG_MEDIA_MESSAGE("Set Rate to %f", rate);
+ INFO_MEDIA_MESSAGE("Set Rate to %f", rate);
if (rate > 0) {
- // Mute the sound if the playback rate is too extreme.
- // TODO: in other cases we should perform pitch adjustments.
- mute = (bool) (rate < 0.8 || rate > 2);
+ // Mute the sound if the playback rate is too extreme and
+ // audio pitch is not adjusted.
+ mute = (!m_preservesPitch && (rate < 0.8 || rate > 2));
start = currentPosition;
end = GST_CLOCK_TIME_NONE;
} else {
@@ -716,24 +643,19 @@ void MediaPlayerPrivateGStreamer::setRate(float rate)
end = currentPosition;
}
- LOG_MEDIA_MESSAGE("Need to mute audio: %d", (int) mute);
+ INFO_MEDIA_MESSAGE("Need to mute audio?: %d", (int) mute);
if (!gst_element_seek(m_playBin.get(), rate, GST_FORMAT_TIME, flags,
GST_SEEK_TYPE_SET, start,
GST_SEEK_TYPE_SET, end))
- LOG_MEDIA_MESSAGE("Set rate to %f failed", rate);
+ ERROR_MEDIA_MESSAGE("Set rate to %f failed", rate);
else
g_object_set(m_playBin.get(), "mute", mute, NULL);
}
-MediaPlayer::NetworkState MediaPlayerPrivateGStreamer::networkState() const
-{
- return m_networkState;
-}
-
-MediaPlayer::ReadyState MediaPlayerPrivateGStreamer::readyState() const
+void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch)
{
- return m_readyState;
+ m_preservesPitch = preservesPitch;
}
PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
@@ -744,7 +666,7 @@ PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
#if GST_CHECK_VERSION(0, 10, 31)
float mediaDuration(duration());
- if (!mediaDuration || isinf(mediaDuration))
+ if (!mediaDuration || std::isinf(mediaDuration))
return timeRanges.release();
GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
@@ -784,6 +706,9 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
bool issueError = true;
bool attemptNextLocation = false;
const GstStructure* structure = gst_message_get_structure(message);
+ GstState requestedState, currentState;
+
+ m_canFallBackToLastFinishedSeekPositon = false;
if (structure) {
const gchar* messageTypeName = gst_structure_get_name(structure);
@@ -796,13 +721,18 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
}
}
- LOG_MEDIA_MESSAGE("Message received from element %s", GST_MESSAGE_SRC_NAME(message));
+ // We ignore state changes from internal elements. They are forwarded to playbin2 anyway.
+ bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get());
+
+ LOG_MEDIA_MESSAGE("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message));
switch (GST_MESSAGE_TYPE(message)) {
case GST_MESSAGE_ERROR:
if (m_resetPipeline)
break;
+ if (m_missingPlugins)
+ break;
gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
- LOG_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
+ ERROR_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error");
@@ -818,7 +748,7 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
// this case the HTMLMediaElement will emit a stalled
// event.
if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
- LOG_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event.");
+ ERROR_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event.");
break;
}
error = MediaPlayer::DecodeError;
@@ -832,33 +762,26 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
loadingFailed(error);
break;
case GST_MESSAGE_EOS:
- LOG_MEDIA_MESSAGE("End of Stream");
didEnd();
break;
- case GST_MESSAGE_STATE_CHANGED:
- // Ignore state changes if load is delayed (preload=none). The
- // player state will be updated once commitLoad() is called.
- if (m_delayingLoad) {
- LOG_MEDIA_MESSAGE("Media load has been delayed. Ignoring state changes for now");
+ case GST_MESSAGE_ASYNC_DONE:
+ if (!messageSourceIsPlaybin || m_delayingLoad)
break;
- }
-
- // Ignore state changes from internal elements. They are
- // forwarded to playbin2 anyway.
- if (GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get())) {
- updateStates();
-
- // Construct a filename for the graphviz dot file output.
- GstState oldState, newState;
- gst_message_parse_state_changed(message, &oldState, &newState, 0);
+ asyncStateChangeDone();
+ break;
+ case GST_MESSAGE_STATE_CHANGED: {
+ if (!messageSourceIsPlaybin || m_delayingLoad)
+ break;
+ updateStates();
- CString dotFileName = String::format("webkit-video.%s_%s",
- gst_element_state_get_name(oldState),
- gst_element_state_get_name(newState)).utf8();
+ // Construct a filename for the graphviz dot file output.
+ GstState newState;
+ gst_message_parse_state_changed(message, &currentState, &newState, 0);
+ CString dotFileName = String::format("webkit-video.%s_%s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState)).utf8();
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
- GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
- }
break;
+ }
case GST_MESSAGE_BUFFERING:
processBufferingStats(message);
break;
@@ -867,8 +790,28 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
#else
case GST_MESSAGE_DURATION:
#endif
- LOG_MEDIA_MESSAGE("Duration changed");
- durationChanged();
+ if (messageSourceIsPlaybin)
+ durationChanged();
+ break;
+ case GST_MESSAGE_REQUEST_STATE:
+ gst_message_parse_request_state(message, &requestedState);
+ gst_element_get_state(m_playBin.get(), &currentState, NULL, 250);
+ if (requestedState < currentState) {
+ GOwnPtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(message)));
+ INFO_MEDIA_MESSAGE("Element %s requested state change to %s", elementName.get(),
+ gst_element_state_get_name(requestedState));
+ m_requestedState = requestedState;
+ changePipelineState(requestedState);
+ }
+ break;
+ case GST_MESSAGE_ELEMENT:
+ if (gst_is_missing_plugin_message(message)) {
+ gchar* detail = gst_missing_plugin_message_get_installer_detail(message);
+ gchar* detailArray[2] = {detail, 0};
+ GstInstallPluginsReturn result = gst_install_plugins_async(detailArray, 0, mediaPlayerPrivatePluginInstallerResultFunction, this);
+ m_missingPlugins = result == GST_INSTALL_PLUGINS_STARTED_OK;
+ g_free(detail);
+ }
break;
default:
LOG_MEDIA_MESSAGE("Unhandled GStreamer message type: %s",
@@ -878,35 +821,24 @@ gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
return TRUE;
}
+void MediaPlayerPrivateGStreamer::handlePluginInstallerResult(GstInstallPluginsReturn result)
+{
+ m_missingPlugins = false;
+ if (result == GST_INSTALL_PLUGINS_SUCCESS) {
+ gst_element_set_state(m_playBin.get(), GST_STATE_READY);
+ gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
+ }
+}
+
void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
{
- // This is the immediate buffering that needs to happen so we have
- // enough to play right now.
m_buffering = true;
const GstStructure *structure = gst_message_get_structure(message);
gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage);
LOG_MEDIA_MESSAGE("[Buffering] Buffering: %d%%.", m_bufferingPercentage);
- GstBufferingMode mode;
- gst_message_parse_buffering_stats(message, &mode, 0, 0, 0);
- if (mode != GST_BUFFERING_DOWNLOAD) {
- updateStates();
- return;
- }
-
- // This is on-disk buffering, that allows us to download much more
- // than needed for right now.
- if (!m_startedBuffering) {
- LOG_MEDIA_MESSAGE("[Buffering] Starting on-disk buffering.");
-
- m_startedBuffering = true;
-
- if (m_fillTimer.isActive())
- m_fillTimer.stop();
-
- m_fillTimer.startRepeating(0.2);
- }
+ updateStates();
}
void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*)
@@ -942,7 +874,8 @@ void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStream
LOG_MEDIA_MESSAGE("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
}
- if (fillStatus != 100.0) {
+ m_downloadFinished = fillStatus == 100.0;
+ if (!m_downloadFinished) {
updateStates();
return;
}
@@ -951,7 +884,6 @@ void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStream
// connection is cut. Buffering is done, remove the fill source
// from the main loop.
m_fillTimer.stop();
- m_startedBuffering = false;
updateStates();
}
@@ -962,7 +894,7 @@ float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
LOG_MEDIA_MESSAGE("maxTimeSeekable");
// infinite duration means live stream
- if (isinf(duration()))
+ if (std::isinf(duration()))
return 0.0f;
return duration();
@@ -974,8 +906,8 @@ float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
return 0.0f;
float loaded = m_maxTimeLoaded;
- if (!loaded && !m_fillTimer.isActive())
- loaded = duration();
+ if (m_isEndReached && m_mediaDuration)
+ loaded = m_mediaDuration;
LOG_MEDIA_MESSAGE("maxTimeLoaded: %f", loaded);
return loaded;
}
@@ -1009,7 +941,7 @@ unsigned MediaPlayerPrivateGStreamer::totalBytes() const
#else
if (gst_element_query_duration(m_source.get(), &fmt, &length)) {
#endif
- LOG_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
+ INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
m_totalBytes = static_cast<unsigned>(length);
m_isStreaming = !length;
return m_totalBytes;
@@ -1059,52 +991,12 @@ unsigned MediaPlayerPrivateGStreamer::totalBytes() const
gst_iterator_free(iter);
- LOG_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
+ INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
m_totalBytes = static_cast<unsigned>(length);
m_isStreaming = !length;
return m_totalBytes;
}
-unsigned MediaPlayerPrivateGStreamer::decodedFrameCount() const
-{
- guint64 decodedFrames = 0;
- if (m_fpsSink)
- g_object_get(m_fpsSink, "frames-rendered", &decodedFrames, NULL);
- return static_cast<unsigned>(decodedFrames);
-}
-
-unsigned MediaPlayerPrivateGStreamer::droppedFrameCount() const
-{
- guint64 framesDropped = 0;
- if (m_fpsSink)
- g_object_get(m_fpsSink, "frames-dropped", &framesDropped, NULL);
- return static_cast<unsigned>(framesDropped);
-}
-
-unsigned MediaPlayerPrivateGStreamer::audioDecodedByteCount() const
-{
- GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
- gint64 position = 0;
-
- if (m_webkitAudioSink && gst_element_query(m_webkitAudioSink.get(), query))
- gst_query_parse_position(query, 0, &position);
-
- gst_query_unref(query);
- return static_cast<unsigned>(position);
-}
-
-unsigned MediaPlayerPrivateGStreamer::videoDecodedByteCount() const
-{
- GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
- gint64 position = 0;
-
- if (gst_element_query(m_webkitVideoSink, query))
- gst_query_parse_position(query, 0, &position);
-
- gst_query_unref(query);
- return static_cast<unsigned>(position);
-}
-
void MediaPlayerPrivateGStreamer::updateAudioSink()
{
if (!m_playBin)
@@ -1117,6 +1009,10 @@ void MediaPlayerPrivateGStreamer::updateAudioSink()
}
+GstElement* MediaPlayerPrivateGStreamer::audioSink() const
+{
+ return m_webkitAudioSink.get();
+}
void MediaPlayerPrivateGStreamer::sourceChanged()
{
@@ -1138,6 +1034,33 @@ void MediaPlayerPrivateGStreamer::cancelLoad()
gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
}
+void MediaPlayerPrivateGStreamer::asyncStateChangeDone()
+{
+ if (!m_playBin || m_errorOccured)
+ return;
+
+ if (m_seeking) {
+ if (m_seekIsPending)
+ updateStates();
+ else {
+ LOG_MEDIA_MESSAGE("[Seek] seeked to %f", m_seekTime);
+ m_seeking = false;
+ if (m_timeOfOverlappingSeek != m_seekTime && m_timeOfOverlappingSeek != -1) {
+ seek(m_timeOfOverlappingSeek);
+ m_timeOfOverlappingSeek = -1;
+ return;
+ }
+ m_timeOfOverlappingSeek = -1;
+
+ // The pipeline can still have a pending state. In this case a position query will fail.
+ // Right now we can use m_seekTime as a fallback.
+ m_canFallBackToLastFinishedSeekPositon = true;
+ timeChanged();
+ }
+ } else
+ updateStates();
+}
+
void MediaPlayerPrivateGStreamer::updateStates()
{
if (!m_playBin)
@@ -1151,104 +1074,99 @@ void MediaPlayerPrivateGStreamer::updateStates()
GstState state;
GstState pending;
- GstStateChangeReturn ret = gst_element_get_state(m_playBin.get(),
- &state, &pending, 250 * GST_NSECOND);
+ GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, &pending, 250 * GST_NSECOND);
- bool shouldUpdateAfterSeek = false;
- switch (ret) {
- case GST_STATE_CHANGE_SUCCESS:
- LOG_MEDIA_MESSAGE("State: %s, pending: %s",
- gst_element_state_get_name(state),
- gst_element_state_get_name(pending));
+ bool shouldUpdatePlaybackState = false;
+ switch (getStateResult) {
+ case GST_STATE_CHANGE_SUCCESS: {
+ LOG_MEDIA_MESSAGE("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
- m_resetPipeline = state <= GST_STATE_READY;
+ if (state <= GST_STATE_READY) {
+ m_resetPipeline = true;
+ m_mediaDuration = 0;
+ } else {
+ m_resetPipeline = false;
+ cacheDuration();
+ }
+
+ bool didBuffering = m_buffering;
- // Try to figure out ready and network states.
- if (state == GST_STATE_READY) {
+ // Update ready and network states.
+ switch (state) {
+ case GST_STATE_NULL:
+ m_readyState = MediaPlayer::HaveNothing;
+ m_networkState = MediaPlayer::Empty;
+ break;
+ case GST_STATE_READY:
m_readyState = MediaPlayer::HaveMetadata;
m_networkState = MediaPlayer::Empty;
- // Cache the duration without emiting the durationchange
- // event because it's taken care of by the media element
- // in this precise case.
- if (!m_isEndReached)
- cacheDuration();
- } else if ((state == GST_STATE_NULL) || (maxTimeLoaded() == duration())) {
- m_networkState = MediaPlayer::Loaded;
- m_readyState = MediaPlayer::HaveEnoughData;
- } else {
- m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
- m_networkState = MediaPlayer::Loading;
- }
+ break;
+ case GST_STATE_PAUSED:
+ case GST_STATE_PLAYING:
+ if (m_buffering) {
+ if (m_bufferingPercentage == 100) {
+ LOG_MEDIA_MESSAGE("[Buffering] Complete.");
+ m_buffering = false;
+ m_readyState = MediaPlayer::HaveEnoughData;
+ m_networkState = m_downloadFinished ? MediaPlayer::Idle : MediaPlayer::Loading;
+ } else {
+ m_readyState = MediaPlayer::HaveCurrentData;
+ m_networkState = MediaPlayer::Loading;
+ }
+ } else if (m_downloadFinished) {
+ m_readyState = MediaPlayer::HaveEnoughData;
+ m_networkState = MediaPlayer::Loaded;
+ } else {
+ m_readyState = MediaPlayer::HaveFutureData;
+ m_networkState = MediaPlayer::Loading;
+ }
- if (m_buffering && state != GST_STATE_READY) {
- m_readyState = MediaPlayer::HaveCurrentData;
- m_networkState = MediaPlayer::Loading;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
}
- // Now let's try to get the states in more detail using
- // information from GStreamer, while we sync states where
- // needed.
+ // Sync states where needed.
if (state == GST_STATE_PAUSED) {
if (!m_webkitAudioSink)
updateAudioSink();
- if (m_buffering && m_bufferingPercentage == 100) {
- m_buffering = false;
- m_bufferingPercentage = 0;
- m_readyState = MediaPlayer::HaveEnoughData;
- LOG_MEDIA_MESSAGE("[Buffering] Complete.");
+ if (!m_volumeAndMuteInitialized) {
+ notifyPlayerOfVolumeChange();
+ notifyPlayerOfMute();
+ m_volumeAndMuteInitialized = true;
+ }
- if (!m_paused) {
- LOG_MEDIA_MESSAGE("[Buffering] Restarting playback.");
- gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
- }
- } else if (!m_buffering && (currentTime() < duration())) {
- m_paused = true;
+ if (didBuffering && !m_buffering && !m_paused) {
+ LOG_MEDIA_MESSAGE("[Buffering] Restarting playback.");
+ gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
}
} else if (state == GST_STATE_PLAYING) {
- m_readyState = MediaPlayer::HaveEnoughData;
m_paused = false;
if (m_buffering && !isLiveStream()) {
- m_readyState = MediaPlayer::HaveCurrentData;
- m_networkState = MediaPlayer::Loading;
-
LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering.");
-
gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
}
} else
m_paused = true;
- // Is on-disk buffering in progress?
- if (m_fillTimer.isActive())
- m_networkState = MediaPlayer::Loading;
-
if (m_changingRate) {
m_player->rateChanged();
m_changingRate = false;
}
- if (m_seeking) {
- shouldUpdateAfterSeek = true;
- m_seeking = false;
+ if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) {
+ shouldUpdatePlaybackState = true;
+ LOG_MEDIA_MESSAGE("Requested state change to %s was completed", gst_element_state_get_name(state));
}
break;
+ }
case GST_STATE_CHANGE_ASYNC:
- LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s",
- gst_element_state_get_name(state),
- gst_element_state_get_name(pending));
- // Change in progress
-
- // On-disk buffering was attempted but the media is live. This
- // can't work so disable on-disk buffering and reset the
- // pipeline.
- if (state == GST_STATE_READY && isLiveStream() && m_preload == MediaPlayer::Auto) {
- setPreload(MediaPlayer::None);
- gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
- gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
- }
+ LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+ // Change in progress.
// A live stream was paused, reset the pipeline.
if (state == GST_STATE_PAUSED && pending == GST_STATE_PLAYING && isLiveStream()) {
@@ -1256,66 +1174,58 @@ void MediaPlayerPrivateGStreamer::updateStates()
gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
}
- if (!isLiveStream() && !m_buffering)
- return;
-
- if (m_seeking) {
- shouldUpdateAfterSeek = true;
- m_seeking = false;
- }
break;
case GST_STATE_CHANGE_FAILURE:
- LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s",
- gst_element_state_get_name(state),
- gst_element_state_get_name(pending));
+ LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
// Change failed
return;
case GST_STATE_CHANGE_NO_PREROLL:
- LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s",
- gst_element_state_get_name(state),
- gst_element_state_get_name(pending));
+ LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending));
+
+ // Live pipelines go in PAUSED without prerolling.
+ m_isStreaming = true;
+ setDownloadBuffering();
if (state == GST_STATE_READY)
m_readyState = MediaPlayer::HaveNothing;
else if (state == GST_STATE_PAUSED) {
m_readyState = MediaPlayer::HaveEnoughData;
m_paused = true;
- // Live pipelines go in PAUSED without prerolling.
- m_isStreaming = true;
} else if (state == GST_STATE_PLAYING)
m_paused = false;
- if (m_seeking) {
- shouldUpdateAfterSeek = true;
- m_seeking = false;
- if (!m_paused)
- gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
- } else if (!m_paused)
+ if (!m_paused)
gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
m_networkState = MediaPlayer::Loading;
break;
default:
- LOG_MEDIA_MESSAGE("Else : %d", ret);
+ LOG_MEDIA_MESSAGE("Else : %d", getStateResult);
break;
}
- if (seeking())
- m_readyState = MediaPlayer::HaveNothing;
+ m_requestedState = GST_STATE_VOID_PENDING;
- if (shouldUpdateAfterSeek)
- timeChanged();
+ if (shouldUpdatePlaybackState)
+ m_player->playbackStateChanged();
if (m_networkState != oldNetworkState) {
- LOG_MEDIA_MESSAGE("Network State Changed from %u to %u",
- oldNetworkState, m_networkState);
+ LOG_MEDIA_MESSAGE("Network State Changed from %u to %u", oldNetworkState, m_networkState);
m_player->networkStateChanged();
}
if (m_readyState != oldReadyState) {
- LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u",
- oldReadyState, m_readyState);
+ LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u", oldReadyState, m_readyState);
m_player->readyStateChanged();
}
+
+ if (m_seekIsPending && getStateResult == GST_STATE_CHANGE_SUCCESS && (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)) {
+ LOG_MEDIA_MESSAGE("[Seek] committing pending seek to %f", m_seekTime);
+ m_seekIsPending = false;
+ 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),
+ GST_SEEK_TYPE_SET, toGstClockTime(m_seekTime), GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+ if (!m_seeking)
+ LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", m_seekTime);
+ }
}
void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
@@ -1375,23 +1285,12 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation()
// Found a candidate. new-location is not always an absolute url
// though. We need to take the base of the current url and
// append the value of new-location to it.
+ KURL baseUrl = gst_uri_is_valid(newLocation) ? KURL() : m_url;
+ KURL newUrl = KURL(baseUrl, newLocation);
- gchar* currentLocation = 0;
- g_object_get(m_playBin.get(), "uri", &currentLocation, NULL);
-
- KURL currentUrl(KURL(), currentLocation);
- g_free(currentLocation);
-
- KURL newUrl;
-
- if (gst_uri_is_valid(newLocation))
- newUrl = KURL(KURL(), newLocation);
- else
- newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation);
-
- RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl);
+ RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(m_url);
if (securityOrigin->canRequest(newUrl)) {
- LOG_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data());
+ INFO_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data());
// Reset player states.
m_networkState = MediaPlayer::Loading;
@@ -1408,14 +1307,15 @@ bool MediaPlayerPrivateGStreamer::loadNextLocation()
if (state <= GST_STATE_READY) {
// Set the new uri and start playing.
g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL);
+ m_url = newUrl;
gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
return true;
}
- }
+ } else
+ INFO_MEDIA_MESSAGE("Not allowed to load new media location: %s", newUrl.string().utf8().data());
}
m_mediaLocationCurrentIndex--;
return false;
-
}
void MediaPlayerPrivateGStreamer::loadStateChanged()
@@ -1423,11 +1323,6 @@ void MediaPlayerPrivateGStreamer::loadStateChanged()
updateStates();
}
-void MediaPlayerPrivateGStreamer::sizeChanged()
-{
- notImplemented();
-}
-
void MediaPlayerPrivateGStreamer::timeChanged()
{
updateStates();
@@ -1452,33 +1347,25 @@ void MediaPlayerPrivateGStreamer::didEnd()
if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) {
m_paused = true;
gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
+ m_downloadFinished = false;
}
}
void MediaPlayerPrivateGStreamer::cacheDuration()
{
- // Reset cached media duration
- m_mediaDuration = 0;
+ if (m_mediaDuration || !m_mediaDurationKnown)
+ return;
- // And re-cache it if possible.
- GstState state;
- gst_element_get_state(m_playBin.get(), &state, 0, 0);
float newDuration = duration();
-
- if (state <= GST_STATE_READY) {
- // Don't set m_mediaDurationKnown yet if the pipeline is not
- // paused. This allows duration() query to fail at least once
- // before playback starts and duration becomes known.
- if (!isinf(newDuration))
- m_mediaDuration = newDuration;
- } else {
- m_mediaDurationKnown = !isinf(newDuration);
- if (m_mediaDurationKnown)
- m_mediaDuration = newDuration;
+ if (std::isinf(newDuration)) {
+ // Only pretend that duration is not available if the the query failed in a stable pipeline state.
+ GstState state;
+ if (gst_element_get_state(m_playBin.get(), &state, 0, 0) == GST_STATE_CHANGE_SUCCESS && state > GST_STATE_READY)
+ m_mediaDurationKnown = false;
+ return;
}
- if (!isinf(newDuration))
- m_mediaDuration = newDuration;
+ m_mediaDuration = newDuration;
}
void MediaPlayerPrivateGStreamer::durationChanged()
@@ -1491,47 +1378,6 @@ void MediaPlayerPrivateGStreamer::durationChanged()
// HTMLMediaElement.
if (previousDuration && m_mediaDuration != previousDuration)
m_player->durationChanged();
-
- if (m_preload == MediaPlayer::None && m_originalPreloadWasAutoAndWasOverridden) {
- m_totalBytes = -1;
- if (totalBytes() && !isLiveStream()) {
- setPreload(MediaPlayer::Auto);
- gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
- gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
- }
- }
-}
-
-bool MediaPlayerPrivateGStreamer::supportsMuting() const
-{
- return true;
-}
-
-void MediaPlayerPrivateGStreamer::setMuted(bool muted)
-{
- if (!m_playBin)
- return;
-
- g_object_set(m_playBin.get(), "mute", muted, NULL);
-}
-
-void MediaPlayerPrivateGStreamer::notifyPlayerOfMute()
-{
- m_muteTimerHandler = 0;
-
- if (!m_player || !m_playBin)
- return;
-
- gboolean muted;
- g_object_get(m_playBin.get(), "mute", &muted, NULL);
- m_player->muteChanged(static_cast<bool>(muted));
-}
-
-void MediaPlayerPrivateGStreamer::muteChanged()
-{
- if (m_muteTimerHandler)
- g_source_remove(m_muteTimerHandler);
- m_muteTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this);
}
void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
@@ -1547,46 +1393,6 @@ void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
}
}
-void MediaPlayerPrivateGStreamer::setSize(const IntSize& size)
-{
- m_size = size;
-}
-
-void MediaPlayerPrivateGStreamer::setVisible(bool)
-{
-}
-
-
-void MediaPlayerPrivateGStreamer::triggerRepaint(GstBuffer* buffer)
-{
- g_return_if_fail(GST_IS_BUFFER(buffer));
- gst_buffer_replace(&m_buffer, buffer);
- m_player->repaint();
-}
-
-void MediaPlayerPrivateGStreamer::paint(GraphicsContext* context, const IntRect& rect)
-{
- if (context->paintingDisabled())
- return;
-
- if (!m_player->visible())
- return;
-
- if (!m_buffer)
- return;
-
- GRefPtr<GstCaps> caps = webkitGstGetPadCaps(m_videoSinkPad.get());
- if (!caps)
- return;
-
- RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer, caps.get());
- if (!gstImage)
- return;
-
- context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB,
- rect, gstImage->rect(), CompositeCopy, DoNotRespectImageOrientation, false);
-}
-
static HashSet<String> mimeTypeCache()
{
initializeGStreamerAndRegisterWebKitElements();
@@ -1596,90 +1402,95 @@ static HashSet<String> mimeTypeCache()
if (typeListInitialized)
return cache;
- const char* mimeTypes[] = {"application/ogg",
- "application/vnd.apple.mpegurl",
- "application/vnd.rn-realmedia",
- "application/x-3gp",
- "application/x-pn-realaudio",
- "audio/3gpp",
- "audio/aac",
- "audio/flac",
- "audio/iLBC-sh",
- "audio/midi",
- "audio/mobile-xmf",
- "audio/mp1",
- "audio/mp2",
- "audio/mp3",
- "audio/mp4",
- "audio/mpeg",
- "audio/ogg",
- "audio/qcelp",
- "audio/riff-midi",
- "audio/wav",
- "audio/webm",
- "audio/x-ac3",
- "audio/x-aiff",
- "audio/x-amr-nb-sh",
- "audio/x-amr-wb-sh",
- "audio/x-au",
- "audio/x-ay",
- "audio/x-celt",
- "audio/x-dts",
- "audio/x-flac",
- "audio/x-gbs",
- "audio/x-gsm",
- "audio/x-gym",
- "audio/x-imelody",
- "audio/x-ircam",
- "audio/x-kss",
- "audio/x-m4a",
- "audio/x-mod",
- "audio/x-mp3",
- "audio/x-mpeg",
- "audio/x-musepack",
- "audio/x-nist",
- "audio/x-nsf",
- "audio/x-paris",
- "audio/x-sap",
- "audio/x-sbc",
- "audio/x-sds",
- "audio/x-shorten",
- "audio/x-sid",
- "audio/x-spc",
- "audio/x-speex",
- "audio/x-svx",
- "audio/x-ttafile",
- "audio/x-vgm",
- "audio/x-voc",
- "audio/x-vorbis+ogg",
- "audio/x-w64",
- "audio/x-wav",
- "audio/x-wavpack",
- "audio/x-wavpack-correction",
- "video/3gpp",
- "video/mj2",
- "video/mp4",
- "video/mpeg",
- "video/mpegts",
- "video/ogg",
- "video/quicktime",
- "video/vivo",
- "video/webm",
- "video/x-cdxa",
- "video/x-dirac",
- "video/x-dv",
- "video/x-fli",
- "video/x-flv",
- "video/x-h263",
- "video/x-ivf",
- "video/x-m4v",
- "video/x-matroska",
- "video/x-mng",
- "video/x-ms-asf",
- "video/x-msvideo",
- "video/x-mve",
- "video/x-nuv",
- "video/x-vcd"};
+
+ const char* mimeTypes[] = {
+ "application/ogg",
+ "application/vnd.apple.mpegurl",
+ "application/vnd.rn-realmedia",
+ "application/x-3gp",
+ "application/x-pn-realaudio",
+ "audio/3gpp",
+ "audio/aac",
+ "audio/flac",
+ "audio/iLBC-sh",
+ "audio/midi",
+ "audio/mobile-xmf",
+ "audio/mp1",
+ "audio/mp2",
+ "audio/mp3",
+ "audio/mp4",
+ "audio/mpeg",
+ "audio/ogg",
+ "audio/opus",
+ "audio/qcelp",
+ "audio/riff-midi",
+ "audio/speex",
+ "audio/wav",
+ "audio/webm",
+ "audio/x-ac3",
+ "audio/x-aiff",
+ "audio/x-amr-nb-sh",
+ "audio/x-amr-wb-sh",
+ "audio/x-au",
+ "audio/x-ay",
+ "audio/x-celt",
+ "audio/x-dts",
+ "audio/x-flac",
+ "audio/x-gbs",
+ "audio/x-gsm",
+ "audio/x-gym",
+ "audio/x-imelody",
+ "audio/x-ircam",
+ "audio/x-kss",
+ "audio/x-m4a",
+ "audio/x-mod",
+ "audio/x-mp3",
+ "audio/x-mpeg",
+ "audio/x-musepack",
+ "audio/x-nist",
+ "audio/x-nsf",
+ "audio/x-paris",
+ "audio/x-sap",
+ "audio/x-sbc",
+ "audio/x-sds",
+ "audio/x-shorten",
+ "audio/x-sid",
+ "audio/x-spc",
+ "audio/x-speex",
+ "audio/x-svx",
+ "audio/x-ttafile",
+ "audio/x-vgm",
+ "audio/x-voc",
+ "audio/x-vorbis+ogg",
+ "audio/x-w64",
+ "audio/x-wav",
+ "audio/x-wavpack",
+ "audio/x-wavpack-correction",
+ "video/3gpp",
+ "video/mj2",
+ "video/mp4",
+ "video/mpeg",
+ "video/mpegts",
+ "video/ogg",
+ "video/quicktime",
+ "video/vivo",
+ "video/webm",
+ "video/x-cdxa",
+ "video/x-dirac",
+ "video/x-dv",
+ "video/x-fli",
+ "video/x-flv",
+ "video/x-h263",
+ "video/x-ivf",
+ "video/x-m4v",
+ "video/x-matroska",
+ "video/x-mng",
+ "video/x-ms-asf",
+ "video/x-msvideo",
+ "video/x-mve",
+ "video/x-nuv",
+ "video/x-vcd"
+ };
for (unsigned i = 0; i < (sizeof(mimeTypes) / sizeof(*mimeTypes)); ++i)
cache.add(String(mimeTypes[i]));
@@ -1704,59 +1515,37 @@ MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String
return MediaPlayer::IsNotSupported;
}
-bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const
+void MediaPlayerPrivateGStreamer::setDownloadBuffering()
{
- return true;
-}
-
-bool MediaPlayerPrivateGStreamer::supportsFullscreen() const
-{
-#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1050
- // See <rdar://problem/7389945>
- return false;
-#else
- return true;
-#endif
-}
-
-PlatformMedia MediaPlayerPrivateGStreamer::platformMedia() const
-{
- PlatformMedia p;
-#ifndef GST_API_VERSION_1
- p.type = PlatformMedia::GStreamerGWorldType;
- p.media.gstreamerGWorld = m_gstGWorld.get();
-#endif
- return p;
-}
-
-MediaPlayer::MovieLoadType MediaPlayerPrivateGStreamer::movieLoadType() const
-{
- if (m_readyState == MediaPlayer::HaveNothing)
- return MediaPlayer::Unknown;
-
- if (isLiveStream())
- return MediaPlayer::LiveStream;
-
- return MediaPlayer::Download;
-}
-
-void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
-{
- m_originalPreloadWasAutoAndWasOverridden = m_preload != preload && m_preload == MediaPlayer::Auto;
-
- m_preload = preload;
-
- ASSERT(m_playBin);
+ if (!m_playBin)
+ return;
GstPlayFlags flags;
g_object_get(m_playBin.get(), "flags", &flags, NULL);
- if (m_preload == MediaPlayer::Auto) {
+
+ // We don't want to stop downloading if we already started it.
+ if (flags & GST_PLAY_FLAG_DOWNLOAD && m_readyState > MediaPlayer::HaveNothing && !m_resetPipeline)
+ return;
+
+ bool shouldDownload = !isLiveStream() && m_preload == MediaPlayer::Auto;
+ if (shouldDownload) {
LOG_MEDIA_MESSAGE("Enabling on-disk buffering");
g_object_set(m_playBin.get(), "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
+ m_fillTimer.startRepeating(0.2);
} else {
LOG_MEDIA_MESSAGE("Disabling on-disk buffering");
g_object_set(m_playBin.get(), "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
+ m_fillTimer.stop();
}
+}
+
+void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
+{
+ if (preload == MediaPlayer::Auto && isLiveStream())
+ return;
+
+ m_preload = preload;
+ setDownloadBuffering();
if (m_delayingLoad && m_preload != MediaPlayer::None) {
m_delayingLoad = false;
@@ -1764,121 +1553,78 @@ void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
}
}
-void MediaPlayerPrivateGStreamer::createGSTPlayBin()
+void MediaPlayerPrivateGStreamer::createAudioSink()
{
- ASSERT(!m_playBin);
-
- // gst_element_factory_make() returns a floating reference so
- // we should not adopt.
- m_playBin = gst_element_factory_make(gPlaybinName, "play");
-
-#ifndef GST_API_VERSION_1
- m_gstGWorld = GStreamerGWorld::createGWorld(m_playBin.get());
-#endif
-
- GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin.get()));
- gst_bus_add_signal_watch(bus);
- g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
- gst_object_unref(bus);
-
- g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL);
+ // Construct audio sink if pitch preserving is enabled.
+ if (!m_preservesPitch)
+ return;
- g_signal_connect(m_playBin.get(), "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
- g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
- g_signal_connect(m_playBin.get(), "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
- g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
- g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
+ if (!m_playBin)
+ return;
-#ifndef GST_API_VERSION_1
- m_webkitVideoSink = webkitVideoSinkNew(m_gstGWorld.get());
-#else
- m_webkitVideoSink = webkitVideoSinkNew();
-#endif
- m_videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink, "sink"));
+ GstElement* scale = gst_element_factory_make("scaletempo", 0);
+ if (!scale) {
+ GST_WARNING("Failed to create scaletempo");
+ return;
+ }
- g_signal_connect(m_webkitVideoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
+ GstElement* convert = gst_element_factory_make("audioconvert", 0);
+ GstElement* resample = gst_element_factory_make("audioresample", 0);
+ GstElement* sink = gst_element_factory_make("autoaudiosink", 0);
+ m_autoAudioSink = sink;
-#ifndef GST_API_VERSION_1
- m_videoSinkBin = gst_bin_new("sink");
+ g_signal_connect(sink, "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this);
- GstElement* videoTee = gst_element_factory_make("tee", "videoTee");
- GstElement* queue = gst_element_factory_make("queue", 0);
+ GstElement* audioSink = gst_bin_new("audio-sink");
+ gst_bin_add_many(GST_BIN(audioSink), scale, convert, resample, sink, NULL);
- // Take ownership.
- gst_object_ref_sink(m_videoSinkBin);
+ if (!gst_element_link_many(scale, convert, resample, sink, NULL)) {
+ GST_WARNING("Failed to link audio sink elements");
+ gst_object_unref(audioSink);
+ return;
+ }
- // Build a new video sink consisting of a bin containing a tee
- // (meant to distribute data to multiple video sinks) and our
- // internal video sink. For fullscreen we create an autovideosink
- // and initially block the data flow towards it and configure it
+ GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
+ gst_element_add_pad(audioSink, gst_ghost_pad_new("sink", pad.get()));
- gst_bin_add_many(GST_BIN(m_videoSinkBin), videoTee, queue, NULL);
+ g_object_set(m_playBin.get(), "audio-sink", audioSink, NULL);
+}
- // Link a new src pad from tee to queue1.
- GRefPtr<GstPad> srcPad = adoptGRef(gst_element_get_request_pad(videoTee, "src%d"));
- GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(queue, "sink"));
- gst_pad_link(srcPad.get(), sinkPad.get());
-#endif
+void MediaPlayerPrivateGStreamer::createGSTPlayBin()
+{
+ ASSERT(!m_playBin);
- GstElement* actualVideoSink = 0;
- m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink");
- if (m_fpsSink) {
- // The verbose property has been added in -bad 0.10.22. Making
- // this whole code depend on it because we don't want
- // fpsdiplaysink to spit data on stdout.
- GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(m_fpsSink)->elementfactory);
- if (gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 22)) {
- g_object_set(m_fpsSink, "silent", TRUE , NULL);
-
- // Turn off text overlay unless logging is enabled.
-#if LOG_DISABLED
- g_object_set(m_fpsSink, "text-overlay", FALSE , NULL);
-#else
- WTFLogChannel* channel = getChannelFromName("Media");
- if (channel->state != WTFLogChannelOn)
- g_object_set(m_fpsSink, "text-overlay", FALSE , NULL);
-#endif // LOG_DISABLED
-
- if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) {
- g_object_set(m_fpsSink, "video-sink", m_webkitVideoSink, NULL);
-#ifndef GST_API_VERSION_1
- gst_bin_add(GST_BIN(m_videoSinkBin), m_fpsSink);
-#endif
- actualVideoSink = m_fpsSink;
- } else
- m_fpsSink = 0;
- } else
- m_fpsSink = 0;
- }
+ // gst_element_factory_make() returns a floating reference so
+ // we should not adopt.
+ m_playBin = gst_element_factory_make(gPlaybinName, "play");
+ setStreamVolumeElement(GST_STREAM_VOLUME(m_playBin.get()));
- if (!m_fpsSink) {
-#ifndef GST_API_VERSION_1
- gst_bin_add(GST_BIN(m_videoSinkBin), m_webkitVideoSink);
-#endif
- actualVideoSink = m_webkitVideoSink;
- }
+ GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
+ gst_bus_add_signal_watch(bus.get());
+ g_signal_connect(bus.get(), "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
- ASSERT(actualVideoSink);
+ g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL);
-#ifndef GST_API_VERSION_1
- // Faster elements linking.
- gst_element_link_pads_full(queue, "src", actualVideoSink, "sink", GST_PAD_LINK_CHECK_NOTHING);
+ g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
+ g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
+ g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
- // Add a ghostpad to the bin so it can proxy to tee.
- GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(videoTee, "sink"));
- gst_element_add_pad(m_videoSinkBin, gst_ghost_pad_new("sink", pad.get()));
+ GstElement* videoElement = createVideoSink(m_playBin.get());
- // Set the bin as video sink of playbin.
- g_object_set(m_playBin.get(), "video-sink", m_videoSinkBin, NULL);
-#else
- g_object_set(m_playBin.get(), "video-sink", actualVideoSink, NULL);
-#endif
+ g_object_set(m_playBin.get(), "video-sink", videoElement, NULL);
- GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink, "sink"));
+ GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink"));
if (videoSinkPad)
g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
+ createAudioSink();
+}
+
+void MediaPlayerPrivateGStreamer::simulateAudioInterruption()
+{
+ GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_playBin.get()), GST_STATE_PAUSED);
+ gst_element_post_message(m_playBin.get(), message);
}
}