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 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 "ColorSpace.h"
30 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
31 #include "CookieJar.h"
36 #include "FrameView.h"
37 #include "GStreamerGWorld.h"
38 #include "GStreamerUtilities.h"
39 #include "GStreamerVersioning.h"
40 #include "GraphicsContext.h"
41 #include "GraphicsTypes.h"
42 #if ENABLE(TIZEN_MEDIA_STREAM)
43 #include "HTMLMediaElement.h"
45 #include "ImageGStreamer.h"
46 #include "ImageOrientation.h"
49 #include "MIMETypeRegistry.h"
50 #include "MediaPlayer.h"
51 #include "NotImplemented.h"
52 #include "SecurityOrigin.h"
53 #include "TimeRanges.h"
54 #include "VideoSinkGStreamer.h"
55 #include "WebKitWebSourceGStreamer.h"
57 #include <gst/video/video.h>
60 #include <wtf/gobject/GOwnPtr.h>
61 #include <wtf/text/CString.h>
63 #ifdef GST_API_VERSION_1
64 #include <gst/video/videooverlay.h>
65 #include <gst/audio/streamvolume.h>
67 #include <gst/interfaces/xoverlay.h>
68 #include <gst/interfaces/streamvolume.h>
71 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
74 #include "VideoLayerTizen.h"
75 #endif // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
77 #if ENABLE(TIZEN_MEDIA_STREAM) || ENABLE(TIZEN_FILE_SYSTEM)
79 #endif // ENABLE(TIZEN_MEDIA_STREAM) || ENABLE(TIZEN_FILE_SYSTEM)
81 #if ENABLE(TIZEN_MEDIA_STREAM)
82 #include "WebKitCameraSourceGStreamerTizen.h"
83 #endif // ENABLE(TIZEN_MEDIA_STREAM)
85 // GstPlayFlags flags from playbin2. It is the policy of GStreamer to
86 // not publicly expose element-specific enums. That's why this
87 // GstPlayFlags enum has been copied here.
89 GST_PLAY_FLAG_VIDEO = 0x00000001,
90 GST_PLAY_FLAG_AUDIO = 0x00000002,
91 GST_PLAY_FLAG_TEXT = 0x00000004,
92 GST_PLAY_FLAG_VIS = 0x00000008,
93 GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010,
94 GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020,
95 GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040,
96 GST_PLAY_FLAG_DOWNLOAD = 0x00000080,
97 GST_PLAY_FLAG_BUFFERING = 0x000000100
100 #ifdef GST_API_VERSION_1
101 static const char* gPlaybinName = "playbin";
103 static const char* gPlaybinName = "playbin2";
106 GST_DEBUG_CATEGORY_STATIC(webkit_media_player_debug);
107 #define GST_CAT_DEFAULT webkit_media_player_debug
113 static int greatestCommonDivisor(int a, int b)
124 static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
126 return player->handleMessage(message);
129 static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
131 player->sourceChanged();
134 static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
136 // This is called when playbin receives the notify::volume signal.
137 player->volumeChanged();
140 static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
142 // This is the callback of the timeout source created in ::volumeChanged.
143 player->notifyPlayerOfVolumeChange();
147 static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
149 // This is called when playbin receives the notify::mute signal.
150 player->muteChanged();
153 static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
155 // This is the callback of the timeout source created in ::muteChanged.
156 player->notifyPlayerOfMute();
160 static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
162 player->videoChanged();
165 static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
167 player->videoChanged();
170 static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
172 player->audioChanged();
175 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
176 static void mediaPlayerPrivateSourceSetupCallback(GObject*, GstElement* source, MediaPlayerPrivateGStreamer* player)
178 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player->player()->mediaPlayerClient());
180 // Grab the current document
181 Document* document = element->document();
183 document = element->ownerDocument();
185 // Grab the frame and network manager
186 Frame* frame = document ? document->frame() : 0;
187 FrameLoader* frameLoader = frame ? frame->loader() : 0;
189 String cookie = cookies(document, player->url());
190 if (!cookie.length())
191 cookie = cookieRequestHeaderFieldValue(document, player->url());
193 GstStructure* headers = gst_structure_new("extra-headers", "Cookie", G_TYPE_STRING, cookie.utf8().data(), NULL);
194 g_object_set(source, "extra-headers", headers, NULL);
195 gst_structure_free(headers);
197 #if ENABLE(TIZEN_WEBKIT2_PROXY)
198 player->setProxy(source);
202 g_object_set(source, "user-agent", frameLoader->userAgent(player->url()).utf8().data(), NULL);
206 static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
208 // This is the callback of the timeout source created in ::audioChanged.
209 player->notifyPlayerOfAudio();
213 static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
215 // This is the callback of the timeout source created in ::videoChanged.
216 player->notifyPlayerOfVideo();
220 #if ENABLE(TIZEN_GSTREAMER_VIDEO) && (!ENABLE(TIZEN_ACCELERATED_COMPOSITING) || !USE(TIZEN_TEXTURE_MAPPER) || !ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) || CPU(X86) || CPU(X86_64 || USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)))
221 static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamer* playerPrivate)
223 playerPrivate->triggerRepaint(buffer);
227 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) && !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
228 static GstBusSyncReply mediaPlayerPrivateSyncHandler(GstBus* bus, GstMessage* message, MediaPlayerPrivateGStreamer* playerPrivate)
230 #ifdef GST_API_VERSION_1
231 const GstStructure *structure;
232 structure = gst_message_get_structure (message);
235 // ignore anything but 'prepare-xid' message
236 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT)
238 #ifdef GST_API_VERSION_1
239 if (!gst_is_video_overlay_prepare_window_handle_message(message))
241 if (!gst_structure_has_name (message->structure, "prepare-xid"))
245 playerPrivate->xWindowIdPrepared(message);
250 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
251 static ASM_cb_result_t MediaPlayerAudioSessionEventSourcePause(ASM_event_sources_t eventSource, void* callbackData)
253 MediaPlayer* player = static_cast<MediaPlayer*>(callbackData);
255 return ASM_CB_RES_IGNORE;
257 switch (eventSource) {
258 case ASM_EVENT_SOURCE_CALL_START:
259 case ASM_EVENT_SOURCE_ALARM_START:
260 case ASM_EVENT_SOURCE_MEDIA:
261 case ASM_EVENT_SOURCE_EMERGENCY_START:
262 case ASM_EVENT_SOURCE_OTHER_PLAYER_APP:
263 case ASM_EVENT_SOURCE_RESOURCE_CONFLICT:
265 return ASM_CB_RES_PAUSE;
266 case ASM_EVENT_SOURCE_EARJACK_UNPLUG:
267 if (!player->url().string().contains("camera://")) {
269 return ASM_CB_RES_PAUSE;
272 return ASM_CB_RES_NONE;
276 static ASM_cb_result_t MediaPlayerAudioSessionEventSourcePlay(ASM_event_sources_t eventSource, void* callbackData)
278 MediaPlayer* player = static_cast<MediaPlayer*>(callbackData);
280 return ASM_CB_RES_IGNORE;
282 switch (eventSource) {
283 case ASM_EVENT_SOURCE_ALARM_END:
284 if (!player->hasVideo()) {
286 return ASM_CB_RES_PLAYING;
287 } else if (player->url().string().contains("camera://")) {
289 return ASM_CB_RES_PLAYING;
291 return ASM_CB_RES_NONE;
293 return ASM_CB_RES_NONE;
297 static ASM_cb_result_t mediaPlayerPrivateAudioSessionNotifyCallback(int, ASM_event_sources_t eventSource, ASM_sound_commands_t command, unsigned int, void* callbackData)
299 if (command == ASM_COMMAND_STOP || command == ASM_COMMAND_PAUSE)
300 return MediaPlayerAudioSessionEventSourcePause(eventSource, callbackData);
301 if (command == ASM_COMMAND_PLAY || command == ASM_COMMAND_RESUME)
302 return MediaPlayerAudioSessionEventSourcePlay(eventSource, callbackData);
304 return ASM_CB_RES_NONE;
308 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player)
310 return adoptPtr(new MediaPlayerPrivateGStreamer(player));
313 void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar)
316 registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
319 bool initializeGStreamerAndRegisterWebKitElements()
321 if (!initializeGStreamer())
324 GRefPtr<GstElementFactory> srcFactory = gst_element_factory_find("webkitwebsrc");
326 #if ENABLE(TIZEN_MEDIA_STREAM)
327 return gst_element_register(0, "webkitcamerasrc", GST_RANK_PRIMARY + 200, WEBKIT_TYPE_CAMERA_SRC);
329 GST_DEBUG_CATEGORY_INIT(webkit_media_player_debug, "webkitmediaplayer", 0, "WebKit media player");
330 return gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
331 #endif // ENABLE(TIZEN_MEDIA_STREAM)
336 bool MediaPlayerPrivateGStreamer::isAvailable()
338 if (!initializeGStreamerAndRegisterWebKitElements())
341 GRefPtr<GstElementFactory> factory = gst_element_factory_find(gPlaybinName);
345 MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
347 , m_webkitVideoSink(0)
348 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
354 , m_changingRate(false)
355 , m_endTime(numeric_limits<float>::infinity())
356 , m_isEndReached(false)
357 , m_networkState(MediaPlayer::Empty)
358 , m_readyState(MediaPlayer::HaveNothing)
359 , m_isStreaming(false)
362 , m_mediaLocations(0)
363 , m_mediaLocationCurrentIndex(0)
364 , m_resetPipeline(false)
369 , m_errorOccured(false)
371 , m_startedBuffering(false)
372 , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired)
374 , m_bufferingPercentage(0)
375 , m_preload(MediaPlayer::Auto)
376 , m_delayingLoad(false)
377 , m_mediaDurationKnown(true)
378 , m_maxTimeLoadedAtLastDidLoadingProgress(0)
379 , m_volumeTimerHandler(0)
380 , m_muteTimerHandler(0)
383 , m_audioTimerHandler(0)
384 , m_videoTimerHandler(0)
385 , m_webkitAudioSink(0)
387 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
388 , m_videoLayer(VideoLayerTizen::create(static_cast<HTMLMediaElement*>(player->mediaPlayerClient())))
389 #endif // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
390 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
391 , m_audioSessionManager(AudioSessionManagerGStreamerTizen::createAudioSessionManager())
393 , m_originalPreloadWasAutoAndWasOverridden(false)
394 , m_preservesPitch(false)
395 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
401 MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
403 if (m_fillTimer.isActive())
407 gst_buffer_unref(m_buffer);
410 if (m_mediaLocations) {
411 gst_structure_free(m_mediaLocations);
412 m_mediaLocations = 0;
415 #ifndef GST_API_VERSION_1
416 if (m_videoSinkBin) {
417 gst_object_unref(m_videoSinkBin);
423 GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
425 g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateMessageCallback), this);
426 gst_bus_remove_signal_watch(bus.get());
428 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
434 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
435 if (m_audioSessionManager)
436 m_audioSessionManager->setSoundState(ASM_STATE_STOP);
439 if (m_muteTimerHandler)
440 g_source_remove(m_muteTimerHandler);
442 if (m_volumeTimerHandler)
443 g_source_remove(m_volumeTimerHandler);
445 if (m_videoTimerHandler)
446 g_source_remove(m_videoTimerHandler);
448 if (m_audioTimerHandler)
449 g_source_remove(m_audioTimerHandler);
452 void MediaPlayerPrivateGStreamer::load(const String& url)
454 if (!initializeGStreamerAndRegisterWebKitElements())
457 #if ENABLE(TIZEN_MEDIA_STREAM) || ENABLE(TIZEN_FILE_SYSTEM)
459 #endif // ENABLE(TIZEN_MEDIA_STREAM) || ENABLE(TIZEN_FILE_SYSTEM)
460 KURL kurl(KURL(), url);
461 String cleanUrl(url);
463 // Clean out everything after file:// url path.
464 if (kurl.isLocalFile())
465 cleanUrl = cleanUrl.substring(0, kurl.pathEnd());
469 setDownloadBuffering();
474 m_url = KURL(KURL(), cleanUrl);
475 g_object_set(m_playBin.get(), "uri", cleanUrl.utf8().data(), NULL);
477 LOG_MEDIA_MESSAGE("Load %s", cleanUrl.utf8().data());
479 if (m_preload == MediaPlayer::None) {
480 LOG_MEDIA_MESSAGE("Delaying load.");
481 m_delayingLoad = true;
484 // Reset network and ready states. Those will be set properly once
485 // the pipeline pre-rolled.
486 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
488 m_networkState = MediaPlayer::Loading;
490 m_networkState = MediaPlayer::Idle;
492 m_networkState = MediaPlayer::Loading;
494 m_player->networkStateChanged();
495 m_readyState = MediaPlayer::HaveNothing;
496 m_player->readyStateChanged();
497 m_volumeAndMuteInitialized = false;
499 // GStreamer needs to have the pipeline set to a paused state to
500 // start providing anything useful.
501 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
504 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
506 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
507 if (m_audioSessionManager && !m_delayingLoad)
508 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
515 void MediaPlayerPrivateGStreamer::commitLoad()
517 ASSERT(!m_delayingLoad);
518 LOG_MEDIA_MESSAGE("Committing load.");
522 float MediaPlayerPrivateGStreamer::playbackPosition() const
524 if (m_isEndReached) {
525 // Position queries on a null pipeline return 0. If we're at
526 // the end of the stream the pipeline is null but we want to
527 // report either the seek time or the duration because this is
528 // what the Media element spec expects us to do.
531 #if !ENABLE(TIZEN_GSTREAMER_VIDEO)
532 // We don't set pipeline state to NULL when end of file is reached.
534 return m_mediaDuration;
540 GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
541 if (!gst_element_query(m_playBin.get(), query)) {
542 LOG_MEDIA_MESSAGE("Position query failed...");
543 gst_query_unref(query);
548 gst_query_parse_position(query, 0, &position);
550 // Position is available only if the pipeline is not in GST_STATE_NULL or
551 // GST_STATE_READY state.
552 if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE))
553 ret = static_cast<double>(position) / GST_SECOND;
555 LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
557 gst_query_unref(query);
562 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
564 ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED);
566 GstState currentState;
569 gst_element_get_state(m_playBin.get(), ¤tState, &pending, 0);
570 LOG_MEDIA_MESSAGE("Current state: %s, pending: %s", gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
571 #if ENABLE(TIZEN_DLOG_SUPPORT)
572 TIZEN_LOGI("Current state: %s, New state: %s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState));
574 if (currentState == newState || pending == newState)
577 GstStateChangeReturn setStateResult = gst_element_set_state(m_playBin.get(), newState);
578 GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
579 if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE) {
580 loadingFailed(MediaPlayer::Empty);
586 void MediaPlayerPrivateGStreamer::prepareToPlay()
588 m_isEndReached = false;
591 if (m_delayingLoad) {
592 m_delayingLoad = false;
597 void MediaPlayerPrivateGStreamer::play()
599 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
600 if (m_audioSessionManager && !m_audioSessionManager->setSoundState(ASM_STATE_PLAYING))
604 if (m_playBinReady == false) {
605 m_playerDeferredState = 1; // play
609 m_playerDeferredState = -1; // ignore
611 if (changePipelineState(GST_STATE_PLAYING)) {
612 m_isEndReached = false;
613 LOG_MEDIA_MESSAGE("Play");
617 void MediaPlayerPrivateGStreamer::pause()
619 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
620 if (m_audioSessionManager && !m_audioSessionManager->setSoundState(ASM_STATE_PAUSE))
624 if (m_playBinReady == false) {
625 m_playerDeferredState = 0; // pause
629 m_playerDeferredState = -1; // ignore
634 if (changePipelineState(GST_STATE_PAUSED))
635 LOG_MEDIA_MESSAGE("Pause");
638 float MediaPlayerPrivateGStreamer::duration() const
646 // Media duration query failed already, don't attempt new useless queries.
647 if (!m_mediaDurationKnown)
648 return numeric_limits<float>::infinity();
651 return m_mediaDuration;
653 GstFormat timeFormat = GST_FORMAT_TIME;
654 gint64 timeLength = 0;
656 #ifdef GST_API_VERSION_1
657 bool failure = !gst_element_query_duration(m_playBin.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
659 bool failure = !gst_element_query_duration(m_playBin.get(), &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
662 LOG_MEDIA_MESSAGE("Time duration query failed for %s", m_url.string().utf8().data());
663 return numeric_limits<float>::infinity();
666 LOG_MEDIA_MESSAGE("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
668 return static_cast<double>(timeLength) / GST_SECOND;
669 // FIXME: handle 3.14.9.5 properly
672 float MediaPlayerPrivateGStreamer::currentTime() const
684 // https://bugzilla.gnome.org/show_bug.cgi?id=639941 In GStreamer
685 // 0.10.35 basesink reports wrong duration in case of EOS and
686 // negative playback rate. There's no upstream accepted patch for
687 // this bug yet, hence this temporary workaround.
688 if (m_isEndReached && m_playbackRate < 0)
691 return playbackPosition();
695 void MediaPlayerPrivateGStreamer::seek(float time)
703 LOG_MEDIA_MESSAGE("Seek attempt to %f secs", time);
704 #if ENABLE(TIZEN_DLOG_SUPPORT)
705 TIZEN_LOGI("Seek attempt to %f secs", time);
708 // Avoid useless seeking.
709 if (time == currentTime())
712 // Extract the integer part of the time (seconds) and the
713 // fractional part (microseconds). Attempt to round the
714 // microseconds so no floating point precision is lost and we can
715 // perform an accurate seek.
717 float microSeconds = modf(time, &seconds) * 1000000;
719 timeValue.tv_sec = static_cast<glong>(seconds);
720 timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
722 GstClockTime clockTime = GST_TIMEVAL_TO_TIME(timeValue);
723 LOG_MEDIA_MESSAGE("Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(clockTime));
725 if (!gst_element_seek(m_playBin.get(), m_player->rate(),
727 (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
728 GST_SEEK_TYPE_SET, clockTime,
729 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
730 LOG_MEDIA_MESSAGE("Seek to %f failed", time);
731 #if ENABLE(TIZEN_DLOG_SUPPORT)
732 TIZEN_LOGE("Seek to %f failed", time);
740 bool MediaPlayerPrivateGStreamer::paused() const
742 if (m_isEndReached) {
743 LOG_MEDIA_MESSAGE("Ignoring pause at EOS");
748 gst_element_get_state(m_playBin.get(), &state, 0, 0);
749 return state == GST_STATE_PAUSED;
752 bool MediaPlayerPrivateGStreamer::seeking() const
757 // Returns the size of the video
758 IntSize MediaPlayerPrivateGStreamer::naturalSize() const
763 if (!m_videoSize.isEmpty())
766 GRefPtr<GstCaps> caps = webkitGstGetPadCaps(m_videoSinkPad.get());
771 // TODO: handle possible clean aperture data. See
772 // https://bugzilla.gnome.org/show_bug.cgi?id=596571
773 // TODO: handle possible transformation matrix. See
774 // https://bugzilla.gnome.org/show_bug.cgi?id=596326
776 // Get the video PAR and original size, if this fails the
777 // video-sink has likely not yet negotiated its caps.
778 int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride;
779 IntSize originalSize;
780 GstVideoFormat format;
781 if (!getVideoSizeAndFormatFromCaps(caps.get(), originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride))
784 LOG_MEDIA_MESSAGE("Original video size: %dx%d", originalSize.width(), originalSize.height());
785 LOG_MEDIA_MESSAGE("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator);
787 // Calculate DAR based on PAR and video size.
788 int displayWidth = originalSize.width() * pixelAspectRatioNumerator;
789 int displayHeight = originalSize.height() * pixelAspectRatioDenominator;
791 // Divide display width and height by their GCD to avoid possible overflows.
792 int displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight);
793 displayWidth /= displayAspectRatioGCD;
794 displayHeight /= displayAspectRatioGCD;
796 // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function.
797 guint64 width = 0, height = 0;
798 if (!(originalSize.height() % displayHeight)) {
799 LOG_MEDIA_MESSAGE("Keeping video original height");
800 width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
801 height = static_cast<guint64>(originalSize.height());
802 } else if (!(originalSize.width() % displayWidth)) {
803 LOG_MEDIA_MESSAGE("Keeping video original width");
804 height = gst_util_uint64_scale_int(originalSize.width(), displayHeight, displayWidth);
805 width = static_cast<guint64>(originalSize.width());
807 LOG_MEDIA_MESSAGE("Approximating while keeping original video height");
808 width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
809 height = static_cast<guint64>(originalSize.height());
812 LOG_MEDIA_MESSAGE("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height);
813 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) && USE(ACCELERATED_VIDEO_VAAPI) && !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
814 if (!m_displaySize.isEmpty())
815 m_videoSize = scaleHDVideoToDisplaySize(static_cast<int>(width), static_cast<int>(height), m_displaySize.width(), m_displaySize.height());
818 m_videoSize = IntSize(static_cast<int>(width), static_cast<int>(height));
822 void MediaPlayerPrivateGStreamer::videoChanged()
824 if (m_videoTimerHandler)
825 g_source_remove(m_videoTimerHandler);
826 m_videoTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this);
829 void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
831 m_videoTimerHandler = 0;
833 gint videoTracks = 0;
835 g_object_get(m_playBin.get(), "n-video", &videoTracks, NULL);
837 m_hasVideo = videoTracks > 0;
839 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) && !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
840 #ifndef GST_API_VERSION_1
843 if (gst_video_get_size(m_videoSinkPad.get(), &width, &height)) {
844 IntSize size(width, height);
845 if (m_videoSize != size) {
846 m_videoSize = IntSize();
847 m_videoLayer->setOverlay(naturalSize());
852 m_videoSize = IntSize();
854 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
857 void MediaPlayerPrivateGStreamer::audioChanged()
859 if (m_audioTimerHandler)
860 g_source_remove(m_audioTimerHandler);
861 m_audioTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this);
864 void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
866 m_audioTimerHandler = 0;
868 gint audioTracks = 0;
870 g_object_get(m_playBin.get(), "n-audio", &audioTracks, NULL);
871 m_hasAudio = audioTracks > 0;
872 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
875 void MediaPlayerPrivateGStreamer::setVolume(float volume)
880 gst_stream_volume_set_volume(GST_STREAM_VOLUME(m_playBin.get()), GST_STREAM_VOLUME_FORMAT_CUBIC,
881 static_cast<double>(volume));
884 void MediaPlayerPrivateGStreamer::notifyPlayerOfVolumeChange()
886 m_volumeTimerHandler = 0;
888 if (!m_player || !m_playBin)
891 volume = gst_stream_volume_get_volume(GST_STREAM_VOLUME(m_playBin.get()), GST_STREAM_VOLUME_FORMAT_CUBIC);
892 // get_volume() can return values superior to 1.0 if the user
893 // applies software user gain via third party application (GNOME
894 // volume control for instance).
895 volume = CLAMP(volume, 0.0, 1.0);
896 m_player->volumeChanged(static_cast<float>(volume));
899 void MediaPlayerPrivateGStreamer::volumeChanged()
901 if (m_volumeTimerHandler)
902 g_source_remove(m_volumeTimerHandler);
903 m_volumeTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this);
906 void MediaPlayerPrivateGStreamer::setRate(float rate)
908 // Avoid useless playback rate update.
909 if (m_playbackRate == rate)
915 gst_element_get_state(m_playBin.get(), &state, &pending, 0);
916 if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
917 || (pending == GST_STATE_PAUSED))
923 m_playbackRate = rate;
924 m_changingRate = true;
927 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
928 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
929 if (m_audioSessionManager)
930 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
935 float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND);
936 GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
940 LOG_MEDIA_MESSAGE("Set Rate to %f", rate);
942 // Mute the sound if the playback rate is too extreme and
943 // audio pitch is not adjusted.
944 mute = (!m_preservesPitch && (rate < 0.8 || rate > 2));
945 start = currentPosition;
946 end = GST_CLOCK_TIME_NONE;
951 // If we are at beginning of media, start from the end to
952 // avoid immediate EOS.
953 if (currentPosition <= 0)
954 end = static_cast<gint64>(duration() * GST_SECOND);
956 end = currentPosition;
959 LOG_MEDIA_MESSAGE("Need to mute audio: %d", (int) mute);
961 if (!gst_element_seek(m_playBin.get(), rate, GST_FORMAT_TIME, flags,
962 GST_SEEK_TYPE_SET, start,
963 GST_SEEK_TYPE_SET, end))
964 LOG_MEDIA_MESSAGE("Set rate to %f failed", rate);
966 g_object_set(m_playBin.get(), "mute", mute, NULL);
969 void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch)
971 m_preservesPitch = preservesPitch;
974 MediaPlayer::NetworkState MediaPlayerPrivateGStreamer::networkState() const
976 return m_networkState;
979 MediaPlayer::ReadyState MediaPlayerPrivateGStreamer::readyState() const
984 PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
986 RefPtr<TimeRanges> timeRanges = TimeRanges::create();
987 if (m_errorOccured || isLiveStream())
988 return timeRanges.release();
990 #if GST_CHECK_VERSION(0, 10, 31)
991 float mediaDuration(duration());
992 if (!mediaDuration || isinf(mediaDuration))
993 return timeRanges.release();
995 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
997 if (!gst_element_query(m_playBin.get(), query)) {
998 gst_query_unref(query);
999 return timeRanges.release();
1002 gint64 rangeStart = 0, rangeStop = 0;
1003 for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) {
1004 if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop))
1005 timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / 100),
1006 static_cast<float>((rangeStop * mediaDuration) / 100));
1009 // Fallback to the more general maxTimeLoaded() if no range has
1011 if (!timeRanges->length())
1012 if (float loaded = maxTimeLoaded())
1013 timeRanges->add(0, loaded);
1015 gst_query_unref(query);
1017 float loaded = maxTimeLoaded();
1018 if (!m_errorOccured && !isLiveStream() && loaded > 0)
1019 timeRanges->add(0, loaded);
1021 return timeRanges.release();
1024 gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
1026 GOwnPtr<GError> err;
1027 GOwnPtr<gchar> debug;
1028 MediaPlayer::NetworkState error;
1029 bool issueError = true;
1030 bool attemptNextLocation = false;
1031 const GstStructure* structure = gst_message_get_structure(message);
1034 const gchar* messageTypeName = gst_structure_get_name(structure);
1036 // Redirect messages are sent from elements, like qtdemux, to
1037 // notify of the new location(s) of the media.
1038 if (!g_strcmp0(messageTypeName, "redirect")) {
1039 mediaLocationChanged(message);
1044 LOG_MEDIA_MESSAGE("Message received from element %s", GST_MESSAGE_SRC_NAME(message));
1045 switch (GST_MESSAGE_TYPE(message)) {
1046 case GST_MESSAGE_ERROR:
1047 if (m_resetPipeline)
1049 gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
1050 LOG_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
1052 #if ENABLE(TIZEN_DLOG_SUPPORT)
1053 TIZEN_LOGE("GST_MESSAGE_ERROR %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
1056 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error");
1058 error = MediaPlayer::Empty;
1059 if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
1060 || err->code == GST_STREAM_ERROR_WRONG_TYPE
1061 || err->code == GST_STREAM_ERROR_FAILED
1062 || err->code == GST_CORE_ERROR_MISSING_PLUGIN
1063 || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
1064 error = MediaPlayer::FormatError;
1065 else if (err->domain == GST_STREAM_ERROR) {
1066 // Let the mediaPlayerClient handle the stream error, in
1067 // this case the HTMLMediaElement will emit a stalled
1069 #if !ENABLE(TIZEN_MEDIA_STREAM)
1070 if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
1071 LOG_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event.");
1075 error = MediaPlayer::DecodeError;
1076 attemptNextLocation = true;
1077 } else if (err->domain == GST_RESOURCE_ERROR)
1078 error = MediaPlayer::NetworkError;
1080 if (attemptNextLocation)
1081 issueError = !loadNextLocation();
1083 loadingFailed(error);
1085 case GST_MESSAGE_EOS:
1086 LOG_MEDIA_MESSAGE("End of Stream");
1089 case GST_MESSAGE_STATE_CHANGED:
1090 // Ignore state changes if load is delayed (preload=none). The
1091 // player state will be updated once commitLoad() is called.
1092 if (m_delayingLoad) {
1093 LOG_MEDIA_MESSAGE("Media load has been delayed. Ignoring state changes for now");
1097 // Ignore state changes from internal elements. They are
1098 // forwarded to playbin2 anyway.
1099 if (GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get())) {
1102 // Construct a filename for the graphviz dot file output.
1103 GstState oldState, newState;
1104 gst_message_parse_state_changed(message, &oldState, &newState, 0);
1106 CString dotFileName = String::format("webkit-video.%s_%s",
1107 gst_element_state_get_name(oldState),
1108 gst_element_state_get_name(newState)).utf8();
1110 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
1112 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1113 if (m_suspendTime > 0 && newState == GST_STATE_PAUSED) {
1114 if (!isLocalMediaStream())
1115 seek(m_suspendTime);
1117 m_player->mediaPlayerClient()->setSuspended(false);
1122 case GST_MESSAGE_BUFFERING:
1123 processBufferingStats(message);
1125 case GST_MESSAGE_DURATION:
1126 LOG_MEDIA_MESSAGE("Duration changed");
1129 case GST_MESSAGE_ELEMENT:
1130 #ifdef GST_API_VERSION_1
1131 if (gst_structure_has_name (structure, "frame-rendered")) {
1133 if (gst_structure_has_name (message->structure, "frame-rendered")) {
1135 m_videoLayer->notifySyncRequired();
1139 LOG_MEDIA_MESSAGE("Unhandled GStreamer message type: %s",
1140 GST_MESSAGE_TYPE_NAME(message));
1146 void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
1148 // This is the immediate buffering that needs to happen so we have
1149 // enough to play right now.
1151 const GstStructure *structure = gst_message_get_structure(message);
1152 gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage);
1154 LOG_MEDIA_MESSAGE("[Buffering] Buffering: %d%%.", m_bufferingPercentage);
1156 GstBufferingMode mode;
1157 gst_message_parse_buffering_stats(message, &mode, 0, 0, 0);
1158 if (mode != GST_BUFFERING_DOWNLOAD) {
1163 // This is on-disk buffering, that allows us to download much more
1164 // than needed for right now.
1165 if (!m_startedBuffering) {
1166 LOG_MEDIA_MESSAGE("[Buffering] Starting on-disk buffering.");
1168 m_startedBuffering = true;
1170 if (m_fillTimer.isActive())
1173 m_fillTimer.startRepeating(0.2);
1177 void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*)
1179 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
1181 if (!gst_element_query(m_playBin.get(), query)) {
1182 gst_query_unref(query);
1187 gdouble fillStatus = 100.0;
1189 gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
1190 gst_query_unref(query);
1193 fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
1195 LOG_MEDIA_MESSAGE("[Buffering] Download buffer filled up to %f%%", fillStatus);
1197 if (!m_mediaDuration)
1200 // Update maxTimeLoaded only if the media duration is
1201 // available. Otherwise we can't compute it.
1202 if (m_mediaDuration) {
1203 if (fillStatus == 100.0)
1204 m_maxTimeLoaded = m_mediaDuration;
1206 m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0);
1207 LOG_MEDIA_MESSAGE("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
1210 if (fillStatus != 100.0) {
1215 // Media is now fully loaded. It will play even if network
1216 // connection is cut. Buffering is done, remove the fill source
1217 // from the main loop.
1219 m_startedBuffering = false;
1223 float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
1228 LOG_MEDIA_MESSAGE("maxTimeSeekable");
1229 // infinite duration means live stream
1230 if (isinf(duration()))
1236 float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
1241 float loaded = m_maxTimeLoaded;
1242 if (!loaded && !m_fillTimer.isActive())
1243 loaded = duration();
1244 LOG_MEDIA_MESSAGE("maxTimeLoaded: %f", loaded);
1248 bool MediaPlayerPrivateGStreamer::didLoadingProgress() const
1250 if (!m_playBin || !m_mediaDuration || !totalBytes())
1252 float currentMaxTimeLoaded = maxTimeLoaded();
1253 bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
1254 m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
1255 LOG_MEDIA_MESSAGE("didLoadingProgress: %d", didLoadingProgress);
1256 return didLoadingProgress;
1259 unsigned MediaPlayerPrivateGStreamer::totalBytes() const
1264 if (m_totalBytes != -1)
1265 return m_totalBytes;
1270 GstFormat fmt = GST_FORMAT_BYTES;
1272 #ifdef GST_API_VERSION_1
1273 if (gst_element_query_duration(m_source.get(), fmt, &length)) {
1275 if (gst_element_query_duration(m_source.get(), &fmt, &length)) {
1277 LOG_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
1278 m_totalBytes = static_cast<unsigned>(length);
1279 m_isStreaming = !length;
1280 return m_totalBytes;
1283 // Fall back to querying the source pads manually.
1284 // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749
1285 GstIterator* iter = gst_element_iterate_src_pads(m_source.get());
1288 #ifdef GST_API_VERSION_1
1289 GValue item = G_VALUE_INIT;
1290 switch (gst_iterator_next(iter, &item)) {
1291 case GST_ITERATOR_OK: {
1292 GstPad* pad = static_cast<GstPad*>(g_value_get_object(&item));
1293 gint64 padLength = 0;
1294 if (gst_pad_query_duration(pad, fmt, &padLength) && padLength > length)
1301 switch (gst_iterator_next(iter, &data)) {
1302 case GST_ITERATOR_OK: {
1303 GRefPtr<GstPad> pad = adoptGRef(GST_PAD_CAST(data));
1304 gint64 padLength = 0;
1305 if (gst_pad_query_duration(pad.get(), &fmt, &padLength) && padLength > length)
1310 case GST_ITERATOR_RESYNC:
1311 gst_iterator_resync(iter);
1313 case GST_ITERATOR_ERROR:
1315 case GST_ITERATOR_DONE:
1320 #ifdef GST_API_VERSION_1
1321 g_value_unset(&item);
1325 gst_iterator_free(iter);
1327 LOG_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
1328 m_totalBytes = static_cast<unsigned>(length);
1329 m_isStreaming = !length;
1330 return m_totalBytes;
1333 unsigned MediaPlayerPrivateGStreamer::decodedFrameCount() const
1335 guint64 decodedFrames = 0;
1337 g_object_get(m_fpsSink, "frames-rendered", &decodedFrames, NULL);
1338 return static_cast<unsigned>(decodedFrames);
1341 unsigned MediaPlayerPrivateGStreamer::droppedFrameCount() const
1343 guint64 framesDropped = 0;
1345 g_object_get(m_fpsSink, "frames-dropped", &framesDropped, NULL);
1346 return static_cast<unsigned>(framesDropped);
1349 unsigned MediaPlayerPrivateGStreamer::audioDecodedByteCount() const
1351 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
1352 gint64 position = 0;
1354 if (m_webkitAudioSink && gst_element_query(m_webkitAudioSink.get(), query))
1355 gst_query_parse_position(query, 0, &position);
1357 gst_query_unref(query);
1358 return static_cast<unsigned>(position);
1361 unsigned MediaPlayerPrivateGStreamer::videoDecodedByteCount() const
1363 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
1364 gint64 position = 0;
1366 if (gst_element_query(m_webkitVideoSink, query))
1367 gst_query_parse_position(query, 0, &position);
1369 gst_query_unref(query);
1370 return static_cast<unsigned>(position);
1373 void MediaPlayerPrivateGStreamer::updateAudioSink()
1378 GstElement* sinkPtr = 0;
1380 g_object_get(m_playBin.get(), "audio-sink", &sinkPtr, NULL);
1381 m_webkitAudioSink = adoptGRef(sinkPtr);
1386 void MediaPlayerPrivateGStreamer::sourceChanged()
1388 GstElement* srcPtr = 0;
1390 g_object_get(m_playBin.get(), "source", &srcPtr, NULL);
1391 m_source = adoptGRef(srcPtr);
1393 if (WEBKIT_IS_WEB_SRC(m_source.get()))
1394 webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player);
1397 void MediaPlayerPrivateGStreamer::cancelLoad()
1399 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
1403 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1404 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1405 if (m_audioSessionManager)
1406 m_audioSessionManager->setSoundState(ASM_STATE_STOP);
1410 void MediaPlayerPrivateGStreamer::updateStates()
1418 MediaPlayer::NetworkState oldNetworkState = m_networkState;
1419 MediaPlayer::ReadyState oldReadyState = m_readyState;
1423 GstStateChangeReturn ret = gst_element_get_state(m_playBin.get(),
1424 &state, &pending, 250 * GST_NSECOND);
1426 bool shouldUpdateAfterSeek = false;
1428 case GST_STATE_CHANGE_SUCCESS:
1429 LOG_MEDIA_MESSAGE("State: %s, pending: %s",
1430 gst_element_state_get_name(state),
1431 gst_element_state_get_name(pending));
1433 m_resetPipeline = state <= GST_STATE_READY;
1435 // Try to figure out ready and network states.
1436 if (state == GST_STATE_READY) {
1437 m_readyState = MediaPlayer::HaveMetadata;
1438 m_networkState = MediaPlayer::Empty;
1439 // Cache the duration without emiting the durationchange
1440 // event because it's taken care of by the media element
1441 // in this precise case.
1442 if (!m_isEndReached)
1444 } else if ((state == GST_STATE_NULL) || (maxTimeLoaded() == duration())) {
1445 m_networkState = MediaPlayer::Loaded;
1446 m_readyState = MediaPlayer::HaveEnoughData;
1448 m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
1449 m_networkState = MediaPlayer::Loading;
1452 if (m_buffering && state != GST_STATE_READY) {
1453 m_readyState = MediaPlayer::HaveCurrentData;
1454 m_networkState = MediaPlayer::Loading;
1457 // Now let's try to get the states in more detail using
1458 // information from GStreamer, while we sync states where
1460 if (state == GST_STATE_PAUSED) {
1461 if (!m_webkitAudioSink)
1464 if (!m_volumeAndMuteInitialized) {
1465 notifyPlayerOfVolumeChange();
1466 notifyPlayerOfMute();
1467 m_volumeAndMuteInitialized = true;
1470 if (m_buffering && m_bufferingPercentage == 100) {
1471 m_buffering = false;
1472 m_bufferingPercentage = 0;
1473 m_readyState = MediaPlayer::HaveEnoughData;
1475 LOG_MEDIA_MESSAGE("[Buffering] Complete.");
1478 LOG_MEDIA_MESSAGE("[Buffering] Restarting playback.");
1479 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1480 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1481 if (m_audioSessionManager)
1482 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1485 } else if (!m_buffering && (currentTime() < duration())) {
1488 } else if (state == GST_STATE_PLAYING) {
1489 m_readyState = MediaPlayer::HaveEnoughData;
1492 if (m_buffering && !isLiveStream()) {
1493 m_readyState = MediaPlayer::HaveCurrentData;
1494 m_networkState = MediaPlayer::Loading;
1496 LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering.");
1498 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1499 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1500 if (m_audioSessionManager)
1501 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
1507 // Is on-disk buffering in progress?
1508 if (m_fillTimer.isActive())
1509 m_networkState = MediaPlayer::Loading;
1511 if (m_changingRate) {
1512 m_player->rateChanged();
1513 m_changingRate = false;
1517 shouldUpdateAfterSeek = true;
1522 case GST_STATE_CHANGE_ASYNC:
1523 LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s",
1524 gst_element_state_get_name(state),
1525 gst_element_state_get_name(pending));
1526 // Change in progress
1528 // On-disk buffering was attempted but the media is live. This
1529 // can't work so disable on-disk buffering and reset the
1531 if (state == GST_STATE_READY && isLiveStream() && m_preload == MediaPlayer::Auto) {
1532 setPreload(MediaPlayer::None);
1533 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1534 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1535 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1536 if (m_audioSessionManager)
1537 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
1541 // A live stream was paused, reset the pipeline.
1542 if (state == GST_STATE_PAUSED && pending == GST_STATE_PLAYING && isLiveStream()) {
1543 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1544 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1545 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1546 if (m_audioSessionManager)
1547 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1551 if (!isLiveStream() && !m_buffering)
1555 shouldUpdateAfterSeek = true;
1559 case GST_STATE_CHANGE_FAILURE:
1560 LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s",
1561 gst_element_state_get_name(state),
1562 gst_element_state_get_name(pending));
1565 case GST_STATE_CHANGE_NO_PREROLL:
1566 LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s",
1567 gst_element_state_get_name(state),
1568 gst_element_state_get_name(pending));
1570 if (state == GST_STATE_READY)
1571 m_readyState = MediaPlayer::HaveNothing;
1572 else if (state == GST_STATE_PAUSED) {
1573 m_readyState = MediaPlayer::HaveEnoughData;
1575 // Live pipelines go in PAUSED without prerolling.
1576 m_isStreaming = true;
1577 } else if (state == GST_STATE_PLAYING)
1581 shouldUpdateAfterSeek = true;
1584 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1585 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1586 if (m_audioSessionManager)
1587 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1590 } else if (!m_paused) {
1591 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1592 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1593 if (m_audioSessionManager)
1594 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1598 m_networkState = MediaPlayer::Loading;
1601 LOG_MEDIA_MESSAGE("Else : %d", ret);
1606 m_readyState = MediaPlayer::HaveNothing;
1608 if (shouldUpdateAfterSeek)
1611 if (m_networkState != oldNetworkState) {
1612 LOG_MEDIA_MESSAGE("Network State Changed from %u to %u",
1613 oldNetworkState, m_networkState);
1614 m_player->networkStateChanged();
1616 if (m_readyState != oldReadyState) {
1617 LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u",
1618 oldReadyState, m_readyState);
1619 m_player->readyStateChanged();
1620 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
1621 if (state == GST_STATE_PLAYING)
1622 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
1627 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
1629 if (m_mediaLocations)
1630 gst_structure_free(m_mediaLocations);
1632 const GstStructure* structure = gst_message_get_structure(message);
1634 // This structure can contain:
1635 // - both a new-location string and embedded locations structure
1636 // - or only a new-location string.
1637 m_mediaLocations = gst_structure_copy(structure);
1638 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1641 m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1;
1647 bool MediaPlayerPrivateGStreamer::loadNextLocation()
1649 if (!m_mediaLocations)
1652 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1653 const gchar* newLocation = 0;
1656 // Fallback on new-location string.
1657 newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
1663 if (m_mediaLocationCurrentIndex < 0) {
1664 m_mediaLocations = 0;
1668 const GValue* location = gst_value_list_get_value(locations,
1669 m_mediaLocationCurrentIndex);
1670 const GstStructure* structure = gst_value_get_structure(location);
1673 m_mediaLocationCurrentIndex--;
1677 newLocation = gst_structure_get_string(structure, "new-location");
1681 // Found a candidate. new-location is not always an absolute url
1682 // though. We need to take the base of the current url and
1683 // append the value of new-location to it.
1685 gchar* currentLocation = 0;
1686 g_object_get(m_playBin.get(), "uri", ¤tLocation, NULL);
1688 KURL currentUrl(KURL(), currentLocation);
1689 g_free(currentLocation);
1693 if (gst_uri_is_valid(newLocation))
1694 newUrl = KURL(KURL(), newLocation);
1696 newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation);
1698 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl);
1699 if (securityOrigin->canRequest(newUrl)) {
1700 LOG_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data());
1702 // Reset player states.
1703 m_networkState = MediaPlayer::Loading;
1704 m_player->networkStateChanged();
1705 m_readyState = MediaPlayer::HaveNothing;
1706 m_player->readyStateChanged();
1708 // Reset pipeline state.
1709 m_resetPipeline = true;
1710 gst_element_set_state(m_playBin.get(), GST_STATE_READY);
1711 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1712 if (m_audioSessionManager)
1713 m_audioSessionManager->setSoundState(ASM_STATE_NONE);
1716 gst_element_get_state(m_playBin.get(), &state, 0, 0);
1717 if (state <= GST_STATE_READY) {
1718 // Set the new uri and start playing.
1719 g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL);
1720 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1721 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1722 if (m_audioSessionManager)
1723 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1729 m_mediaLocationCurrentIndex--;
1734 void MediaPlayerPrivateGStreamer::loadStateChanged()
1739 void MediaPlayerPrivateGStreamer::sizeChanged()
1744 void MediaPlayerPrivateGStreamer::timeChanged()
1747 m_player->timeChanged();
1750 void MediaPlayerPrivateGStreamer::didEnd()
1752 // Synchronize position and duration values to not confuse the
1753 // HTMLMediaElement. In some cases like reverse playback the
1754 // position is not always reported as 0 for instance.
1755 float now = currentTime();
1756 if (now > 0 && now <= duration() && m_mediaDuration != now) {
1757 m_mediaDurationKnown = true;
1758 m_mediaDuration = now;
1759 m_player->durationChanged();
1762 m_isEndReached = true;
1765 if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) {
1767 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1768 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1769 if (m_audioSessionManager)
1770 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
1772 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1775 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1776 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player()->mediaPlayerClient());
1777 if (element->isVideo())
1778 power_unlock_state(POWER_STATE_NORMAL);
1783 void MediaPlayerPrivateGStreamer::cacheDuration()
1785 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1786 float previousDuration = m_mediaDuration;
1788 // Reset cached media duration
1789 m_mediaDuration = 0;
1791 // And re-cache it if possible.
1793 gst_element_get_state(m_playBin.get(), &state, 0, 0);
1794 float newDuration = duration();
1796 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1797 if (state > GST_STATE_READY) {
1798 // Don't set m_mediaDurationKnown yet if the pipeline is not
1799 // paused. This allows duration() query to fail at least once
1800 // before playback starts and duration becomes known.
1801 m_mediaDurationKnown = !isinf(newDuration);
1804 if (!isinf(newDuration))
1805 m_mediaDuration = newDuration;
1807 m_mediaDuration = previousDuration;
1809 if (state <= GST_STATE_READY) {
1810 // Don't set m_mediaDurationKnown yet if the pipeline is not
1811 // paused. This allows duration() query to fail at least once
1812 // before playback starts and duration becomes known.
1813 if (!isinf(newDuration))
1814 m_mediaDuration = newDuration;
1816 m_mediaDurationKnown = !isinf(newDuration);
1817 if (m_mediaDurationKnown)
1818 m_mediaDuration = newDuration;
1821 if (!isinf(newDuration))
1822 m_mediaDuration = newDuration;
1826 void MediaPlayerPrivateGStreamer::durationChanged()
1828 float previousDuration = m_mediaDuration;
1831 // Avoid emiting durationchanged in the case where the previous
1832 // duration was 0 because that case is already handled by the
1833 // HTMLMediaElement.
1834 if (previousDuration && m_mediaDuration != previousDuration)
1835 m_player->durationChanged();
1837 if (m_preload == MediaPlayer::None && m_originalPreloadWasAutoAndWasOverridden) {
1839 if (totalBytes() && !isLiveStream()) {
1840 setPreload(MediaPlayer::Auto);
1841 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1842 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1843 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
1844 if (m_audioSessionManager)
1845 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
1851 bool MediaPlayerPrivateGStreamer::supportsMuting() const
1856 void MediaPlayerPrivateGStreamer::setMuted(bool muted)
1861 g_object_set(m_playBin.get(), "mute", muted, NULL);
1864 void MediaPlayerPrivateGStreamer::notifyPlayerOfMute()
1866 m_muteTimerHandler = 0;
1868 if (!m_player || !m_playBin)
1872 g_object_get(m_playBin.get(), "mute", &muted, NULL);
1873 m_player->muteChanged(static_cast<bool>(muted));
1876 void MediaPlayerPrivateGStreamer::muteChanged()
1878 if (m_muteTimerHandler)
1879 g_source_remove(m_muteTimerHandler);
1880 m_muteTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this);
1883 void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
1885 #if ENABLE(TIZEN_DLOG_SUPPORT)
1886 TIZEN_LOGE("NetworkState : %d", error);
1889 m_errorOccured = true;
1890 if (m_networkState != error) {
1891 m_networkState = error;
1892 m_player->networkStateChanged();
1894 if (m_readyState != MediaPlayer::HaveNothing) {
1895 m_readyState = MediaPlayer::HaveNothing;
1896 m_player->readyStateChanged();
1900 void MediaPlayerPrivateGStreamer::setSize(const IntSize& size)
1905 void MediaPlayerPrivateGStreamer::setVisible(bool visible)
1909 void MediaPlayerPrivateGStreamer::triggerRepaint(GstBuffer* buffer)
1911 g_return_if_fail(GST_IS_BUFFER(buffer));
1912 gst_buffer_replace(&m_buffer, buffer);
1913 #if !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
1914 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
1915 #if !ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
1916 if (m_videoSize.isEmpty())
1917 m_videoSize = naturalSize();
1918 m_videoLayer->paintVideoLayer(m_videoSize);
1923 m_player->repaint();
1926 void MediaPlayerPrivateGStreamer::paint(GraphicsContext* context, const IntRect& rect)
1928 if (context->paintingDisabled())
1931 if (!m_player->visible())
1937 GRefPtr<GstCaps> caps = webkitGstGetPadCaps(m_videoSinkPad.get());
1941 RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer, caps.get());
1945 context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB,
1946 rect, gstImage->rect(), CompositeCopy, DoNotRespectImageOrientation, false);
1949 static HashSet<String> mimeTypeCache()
1951 initializeGStreamerAndRegisterWebKitElements();
1953 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1954 static bool typeListInitialized = false;
1956 if (typeListInitialized)
1958 const char* mimeTypes[] = {
1960 "application/vnd.apple.mpegurl",
1961 "application/vnd.rn-realmedia",
1962 "application/x-3gp",
1963 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1964 "application/x-mpegurl",
1965 "application/x-mpegURL",
1967 "application/x-pn-realaudio",
1987 "audio/x-amr-nb-sh",
1988 "audio/x-amr-wb-sh",
2019 "audio/x-vorbis+ogg",
2023 "audio/x-wavpack-correction",
2050 for (unsigned i = 0; i < (sizeof(mimeTypes) / sizeof(*mimeTypes)); ++i)
2051 cache.add(String(mimeTypes[i]));
2053 typeListInitialized = true;
2057 void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types)
2059 types = mimeTypeCache();
2062 MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String& type, const String& codecs, const KURL&)
2064 if (type.isNull() || type.isEmpty())
2065 return MediaPlayer::IsNotSupported;
2067 // spec says we should not return "probably" if the codecs string is empty
2068 if (mimeTypeCache().contains(type))
2069 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
2070 return MediaPlayer::IsNotSupported;
2073 bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const
2078 bool MediaPlayerPrivateGStreamer::supportsFullscreen() const
2080 #if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1050
2081 // See <rdar://problem/7389945>
2088 PlatformMedia MediaPlayerPrivateGStreamer::platformMedia() const
2091 p.type = PlatformMedia::GStreamerGWorldType;
2092 p.media.gstreamerGWorld = m_gstGWorld.get();
2096 MediaPlayer::MovieLoadType MediaPlayerPrivateGStreamer::movieLoadType() const
2098 if (m_readyState == MediaPlayer::HaveNothing)
2099 return MediaPlayer::Unknown;
2102 return MediaPlayer::LiveStream;
2104 return MediaPlayer::Download;
2107 void MediaPlayerPrivateGStreamer::setDownloadBuffering()
2114 g_object_get(m_playBin.get(), "flags", &flags, NULL);
2115 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) && !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
2116 flags = (GstPlayFlags)(flags | GST_PLAY_FLAG_NATIVE_VIDEO);
2118 if (m_preload == MediaPlayer::Auto) {
2119 LOG_MEDIA_MESSAGE("Enabling on-disk buffering");
2120 g_object_set(m_playBin.get(), "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
2122 LOG_MEDIA_MESSAGE("Disabling on-disk buffering");
2123 g_object_set(m_playBin.get(), "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
2127 void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
2129 m_originalPreloadWasAutoAndWasOverridden = m_preload != preload && m_preload == MediaPlayer::Auto;
2131 m_preload = preload;
2133 setDownloadBuffering();
2135 if (m_delayingLoad && m_preload != MediaPlayer::None) {
2136 m_delayingLoad = false;
2141 void MediaPlayerPrivateGStreamer::createAudioSink()
2143 // Construct audio sink if pitch preserving is enabled.
2144 if (!m_preservesPitch)
2150 GstElement* scale = gst_element_factory_make("scaletempo", 0);
2152 GST_WARNING("Failed to create scaletempo");
2156 GstElement* convert = gst_element_factory_make("audioconvert", 0);
2157 GstElement* resample = gst_element_factory_make("audioresample", 0);
2158 GstElement* sink = gst_element_factory_make("autoaudiosink", 0);
2160 GstElement* audioSink = gst_bin_new("audio-sink");
2161 gst_bin_add_many(GST_BIN(audioSink), scale, convert, resample, sink, NULL);
2163 if (!gst_element_link_many(scale, convert, resample, sink, NULL)) {
2164 GST_WARNING("Failed to link audio sink elements");
2165 gst_object_unref(audioSink);
2169 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
2170 gst_element_add_pad(audioSink, gst_ghost_pad_new("sink", pad.get()));
2172 g_object_set(m_playBin.get(), "audio-sink", audioSink, NULL);
2175 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2176 GstElement* MediaPlayerPrivateGStreamer::createVideoSink()
2178 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) && !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
2179 m_webkitVideoSink = m_videoLayer->createVideoSink();
2180 m_videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink, "sink"));
2182 // Primary video should be OFF and Secondary video should be FULL SCREEN mode
2183 g_object_set(m_webkitVideoSink, "display-mode", 2, NULL);
2184 #else // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2185 m_webkitVideoSink = webkitVideoSinkNew(m_gstGWorld.get());
2186 m_videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink, "sink"));
2187 g_signal_connect(m_webkitVideoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
2188 #endif // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2190 // Build a new video sink consisting of a bin containing a tee
2191 // (meant to distribute data to multiple video sinks) and our
2192 // internal video sink. For fullscreen we create an autovideosink
2193 // and initially block the data flow towards it and configure it
2194 m_videoSinkBin = gst_bin_new("video-sink");
2195 GstElement* videoTee = gst_element_factory_make("tee", "videoTee");
2197 gst_object_ref_sink(m_videoSinkBin);
2198 gst_bin_add(GST_BIN(m_videoSinkBin), videoTee);
2200 #if 0 // (was !ENABLE(TIZEN_GSTREAMER_VIDEO) )
2201 GstElement* queue = gst_element_factory_make("queue", 0);
2202 gst_bin_add(GST_BIN(m_videoSinkBin), queue);
2203 // Link a new src pad from tee to queue1.
2204 gst_element_link_pads_full(videoTee, 0, queue, "sink", GST_PAD_LINK_CHECK_NOTHING);
2207 GstElement* actualVideoSink = 0;
2208 m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink");
2210 // The verbose property has been added in -bad 0.10.22. Making
2211 // this whole code depend on it because we don't want
2212 // fpsdiplaysink to spit data on stdout.
2213 GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(m_fpsSink)->elementfactory);
2214 if (gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 22)) {
2215 g_object_set(m_fpsSink, "silent", TRUE , NULL);
2217 // Turn off text overlay unless logging is enabled.
2219 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL);
2221 WTFLogChannel* channel = getChannelFromName("Media");
2222 if (channel->state != WTFLogChannelOn)
2223 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL);
2224 #endif // LOG_DISABLED
2226 if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) {
2227 g_object_set(m_fpsSink, "video-sink", m_webkitVideoSink, NULL);
2228 gst_bin_add(GST_BIN(m_videoSinkBin), m_fpsSink);
2229 actualVideoSink = m_fpsSink;
2237 gst_bin_add(GST_BIN(m_videoSinkBin), m_webkitVideoSink);
2238 actualVideoSink = m_webkitVideoSink;
2241 ASSERT(actualVideoSink);
2243 // Link a new src pad from tee to queue1.
2244 #ifdef GST_API_VERSION_1
2245 GRefPtr<GstPad> srcPad = adoptGRef(gst_element_get_request_pad(videoTee, "src_%u"));
2247 GRefPtr<GstPad> srcPad = adoptGRef(gst_element_get_request_pad(videoTee, "src%d"));
2249 GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(actualVideoSink, "sink"));
2250 gst_pad_link(srcPad.get(), sinkPad.get());
2252 // Add a ghostpad to the bin so it can proxy to tee.
2253 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(videoTee, "sink"));
2254 gst_element_add_pad(m_videoSinkBin, gst_ghost_pad_new("sink", pad.get()));
2256 // Set the bin as video sink of playbin.
2257 return m_videoSinkBin;
2261 void MediaPlayerPrivateGStreamer::createGSTPlayBin()
2265 m_playerDeferredState = -1; // ignore
2266 m_playBinReady = false;
2268 // gst_element_factory_make() returns a floating reference so
2269 // we should not adopt.
2270 m_playBin = gst_element_factory_make(gPlaybinName, "play");
2272 m_gstGWorld = GStreamerGWorld::createGWorld(m_playBin.get());
2274 GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
2275 gst_bus_add_signal_watch(bus.get());
2276 g_signal_connect(bus.get(), "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
2277 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) && !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
2278 #ifdef GST_API_VERSION_1
2279 gst_bus_set_sync_handler(bus.get(), GstBusSyncHandler(mediaPlayerPrivateSyncHandler), this, NULL);
2281 gst_bus_set_sync_handler(bus.get(), GstBusSyncHandler(mediaPlayerPrivateSyncHandler), this);
2284 gst_object_unref(bus.get());
2286 g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL);
2288 g_signal_connect(m_playBin.get(), "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
2289 g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
2290 g_signal_connect(m_playBin.get(), "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
2291 g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
2292 g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
2293 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2294 g_signal_connect(m_playBin.get(), "source-setup", G_CALLBACK(mediaPlayerPrivateSourceSetupCallback), this);
2297 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2298 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player()->mediaPlayerClient());
2299 if (element->isVideo()) {
2300 GstElement* videoElement = createVideoSink();
2301 g_object_set(m_playBin.get(), "video-sink", videoElement, NULL);
2303 GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink, "sink"));
2305 g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
2307 GstElement* fakeSink = gst_element_factory_make("fakesink", 0);
2308 g_object_set(fakeSink, "sync", true, NULL);
2309 g_object_set(m_playBin.get(), "video-sink", fakeSink, NULL);
2313 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
2314 #if (!ENABLE(TIZEN_AUDIO_SESSION_MANAGER_WITH_MURPHY))
2315 GstElement* realSink = gst_element_factory_make("avsysaudiosink", 0);
2316 g_object_set(realSink, "close-handle-on-prepare", 1, NULL);
2317 g_object_set(m_playBin.get(), "audio-sink", realSink, NULL);
2319 if (m_audioSessionManager)
2320 m_audioSessionManager->registerAudioSessionManager(MM_SESSION_TYPE_SHARE, mediaPlayerPrivateAudioSessionNotifyCallback, player());
2321 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER_WITH_MURPHY)
2322 GstElement *sink = gst_element_factory_make("pulsesink", "audio-sink");
2324 GstStructure *props;
2328 GOwnPtr<gchar> filename;
2329 GOwnPtr<gchar> propname;
2331 filename.set(g_strdup_printf("/tmp/murphy/mapping-file-%d", getpid()));
2333 if ((f = fopen(filename.get(), "r"))) {
2334 if (fread((void *)ids, sizeof(guint32), 256, f) == 256)
2335 rsetid = ids[(m_audioSessionManager->getAsmHandle() & 0xff)];
2340 propname.set(g_strdup_printf("props,resource.set.id=(string)%u,media.role=%s", rsetid, "browser"));
2342 props = gst_structure_from_string(propname.get(), NULL);
2343 g_object_set(sink, "stream-properties", props, NULL);
2344 gst_structure_free(props);
2346 g_object_set(m_playBin.get(), "audio-sink", sink, NULL);
2353 m_playBinReady = true;
2355 if (m_playerDeferredState == 1)
2357 else if (m_playerDeferredState == 0)
2360 m_playerDeferredState = -1;
2363 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
2364 bool MediaPlayerPrivateGStreamer::supportsAcceleratedRendering() const
2366 bool isEmbedVideo = false;
2367 #if !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
2368 if (m_player->mediaPlayerClient()) {
2369 Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
2370 if (document && document->settings()) {
2371 isEmbedVideo = document->settings()->acceleratedCompositingForVideoEnabled()
2372 && document->settings()->acceleratedCompositingEnabled();
2373 LOG_MEDIA_MESSAGE("isEmbedVideo: %d", isEmbedVideo);
2377 return isEmbedVideo;
2380 PlatformLayer* MediaPlayerPrivateGStreamer::platformLayer() const
2382 return m_videoLayer->platformLayer();
2385 #if ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) && !USE(TIZEN_GSTREAMER_VIDEO_SET_SINK)
2386 void MediaPlayerPrivateGStreamer::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
2388 m_videoLayer->paintCurrentFrameInContext(context, rect);
2391 #if USE(ACCELERATED_VIDEO_VAAPI)
2392 // Other device doesn't prefer such optimization, so enclose it in USE(ACCELERATED_VIDEO_VAAPI) macro
2393 IntSize MediaPlayerPrivateGStreamer::scaleHDVideoToDisplaySize(int videoWidth, int videoHeight, int displayWidth, int displayHeight) const
2395 int fitWidth, fitHeight;
2397 #if ENABLE(TIZEN_DLOG_SUPPORT)
2398 TIZEN_LOGI("videoWidth: %d, videoHeight: %d, displayWidth: %d, displayHeight: %d ", videoWidth, videoHeight, displayWidth, displayHeight);
2400 fitWidth = videoWidth;
2401 fitHeight = videoHeight;
2402 if (displayWidth > 0 && displayHeight > 0) {
2403 // In case video is rotated, we always use a 'better'
2404 // orientation (landscape or portrait) to calculate fitWidth/fitHeight.
2405 // It means we use a bigger size which doesn't lose quality
2406 // in either landscape or portrait orientation
2407 if ((videoWidth > videoHeight && displayWidth < displayHeight)
2408 || (videoWidth < videoHeight && displayWidth > displayHeight)) {
2411 displayWidth = displayHeight;
2412 displayHeight = tmp;
2415 if (videoWidth > displayWidth || videoHeight > displayHeight) {
2416 if (videoWidth * displayHeight > videoHeight * displayWidth) {
2417 fitWidth = displayWidth;
2418 fitHeight = videoHeight * displayWidth / videoWidth;
2419 ASSERT(fitHeight <= displayHeight);
2421 fitWidth = videoWidth * displayHeight / videoHeight;
2422 fitHeight = displayHeight;
2423 ASSERT(fitWidth <= displayWidth);
2428 return IntSize(fitWidth, fitHeight);
2431 void MediaPlayerPrivateGStreamer::xWindowIdPrepared(GstMessage* message)
2433 #if !PLATFORM(WAYLAND)
2434 #if USE(ACCELERATED_VIDEO_VAAPI)
2435 int displayWidth, displayHeight;
2437 int videoWidth, videoHeight;
2438 const GstStructure *structure;
2440 #ifdef GST_API_VERSION_1
2441 structure = gst_message_get_structure (message);
2443 structure = message->structure;
2446 gst_structure_get_int(structure, "video-width", &videoWidth);
2447 gst_structure_get_int(structure, "video-height", &videoHeight);
2449 #if ENABLE(TIZEN_DLOG_SUPPORT)
2450 TIZEN_LOGI("videoWidth: %d, videoHeight: %d", videoWidth, videoHeight);
2453 #if USE(ACCELERATED_VIDEO_VAAPI)
2454 gst_structure_get_int(structure, "display-width", &displayWidth);
2455 gst_structure_get_int(structure, "display-height", &displayHeight);
2456 m_displaySize = IntSize(displayWidth, displayHeight);
2457 m_videoSize = scaleHDVideoToDisplaySize(videoWidth, videoHeight, displayWidth, displayHeight);
2459 m_videoSize = IntSize(videoWidth, videoHeight);
2461 m_videoLayer->setOverlay(m_videoSize);
2463 m_videoLayer->setOverlay(IntSize(1,1));
2467 #endif // ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2468 #endif // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
2470 #if ENABLE(TIZEN_WEBKIT2_PROXY)
2471 void MediaPlayerPrivateGStreamer::setProxy(GstElement* source)
2473 SoupSession* session = WebCore::ResourceHandle::defaultSession();
2477 SoupURI* proxyUri = 0;
2478 g_object_get(session, SOUP_SESSION_PROXY_URI, &proxyUri, NULL);
2482 char* proxy = soup_uri_to_string(proxyUri, false);
2483 g_object_set(source, "proxy", proxy, NULL);
2485 soup_uri_free(proxyUri);
2490 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2491 void MediaPlayerPrivateGStreamer::suspend()
2493 m_suspendTime = currentTime();
2494 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
2495 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
2496 if (m_audioSessionManager)
2497 m_audioSessionManager->setSoundState(ASM_STATE_STOP);
2501 void MediaPlayerPrivateGStreamer::resume()
2503 if (isLocalMediaStream()) {
2504 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
2505 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
2506 if (m_audioSessionManager)
2507 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
2510 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
2511 #if ENABLE(TIZEN_AUDIO_SESSION_MANAGER)
2512 if (m_audioSessionManager)
2513 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
2518 bool MediaPlayerPrivateGStreamer::isLocalMediaStream()
2520 if (m_url.string().contains("camera://"))
2525 #endif // ENABLE(TIZEN_GSTREAMER_VIDEO)
2529 #endif // USE(GSTREAMER)