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/audio/streamvolume.h>
66 #include <gst/interfaces/streamvolume.h>
69 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
72 #include "VideoLayerTizen.h"
73 #endif // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
75 #if ENABLE(TIZEN_MEDIA_STREAM) || ENABLE(TIZEN_FILE_SYSTEM)
77 #endif // ENABLE(TIZEN_MEDIA_STREAM) || ENABLE(TIZEN_FILE_SYSTEM)
79 #if ENABLE(TIZEN_MEDIA_STREAM)
80 #include "WebKitCameraSourceGStreamerTizen.h"
81 #endif // ENABLE(TIZEN_MEDIA_STREAM)
83 // GstPlayFlags flags from playbin2. It is the policy of GStreamer to
84 // not publicly expose element-specific enums. That's why this
85 // GstPlayFlags enum has been copied here.
87 GST_PLAY_FLAG_VIDEO = 0x00000001,
88 GST_PLAY_FLAG_AUDIO = 0x00000002,
89 GST_PLAY_FLAG_TEXT = 0x00000004,
90 GST_PLAY_FLAG_VIS = 0x00000008,
91 GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010,
92 GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020,
93 GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040,
94 GST_PLAY_FLAG_DOWNLOAD = 0x00000080,
95 GST_PLAY_FLAG_BUFFERING = 0x000000100
98 #ifdef GST_API_VERSION_1
99 static const char* gPlaybinName = "playbin";
101 static const char* gPlaybinName = "playbin2";
104 GST_DEBUG_CATEGORY_STATIC(webkit_media_player_debug);
105 #define GST_CAT_DEFAULT webkit_media_player_debug
111 static int greatestCommonDivisor(int a, int b)
122 static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player)
124 return player->handleMessage(message);
127 static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
129 player->sourceChanged();
132 static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
134 // This is called when playbin receives the notify::volume signal.
135 player->volumeChanged();
138 static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
140 // This is the callback of the timeout source created in ::volumeChanged.
141 player->notifyPlayerOfVolumeChange();
145 static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
147 // This is called when playbin receives the notify::mute signal.
148 player->muteChanged();
151 static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
153 // This is the callback of the timeout source created in ::muteChanged.
154 player->notifyPlayerOfMute();
158 static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player)
160 player->videoChanged();
163 static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
165 player->videoChanged();
168 static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player)
170 player->audioChanged();
173 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
174 static void mediaPlayerPrivateSourceSetupCallback(GObject*, GstElement* source, MediaPlayerPrivateGStreamer* player)
176 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player->player()->mediaPlayerClient());
178 // Grab the current document
179 Document* document = element->document();
181 document = element->ownerDocument();
183 // Grab the frame and network manager
184 Frame* frame = document ? document->frame() : 0;
185 FrameLoader* frameLoader = frame ? frame->loader() : 0;
187 String cookie = cookies(document, player->url());
188 if (!cookie.length())
189 cookie = cookieRequestHeaderFieldValue(document, player->url());
191 GstStructure* headers = gst_structure_new("extra-headers", "Cookie", G_TYPE_STRING, cookie.utf8().data(), NULL);
192 g_object_set(source, "extra-headers", headers, NULL);
193 gst_structure_free(headers);
195 #if ENABLE(TIZEN_WEBKIT2_PROXY)
196 player->setProxy(source);
200 g_object_set(source, "user-agent", frameLoader->userAgent(player->url()).utf8().data(), NULL);
204 static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
206 // This is the callback of the timeout source created in ::audioChanged.
207 player->notifyPlayerOfAudio();
211 static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player)
213 // This is the callback of the timeout source created in ::videoChanged.
214 player->notifyPlayerOfVideo();
218 #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))
219 static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamer* playerPrivate)
221 playerPrivate->triggerRepaint(buffer);
225 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
226 static GstBusSyncReply mediaPlayerPrivateSyncHandler(GstBus* bus, GstMessage* message, MediaPlayerPrivateGStreamer* playerPrivate)
228 // ignore anything but 'prepare-xid' message
229 if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT)
231 if (!gst_structure_has_name (message->structure, "prepare-xid"))
234 playerPrivate->xWindowIdPrepared(message);
239 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
240 static ASM_cb_result_t MediaPlayerAudioSessionEventSourcePause(ASM_event_sources_t eventSource, void* callbackData)
242 MediaPlayer* player = static_cast<MediaPlayer*>(callbackData);
243 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player->mediaPlayerClient());
246 return ASM_CB_RES_IGNORE;
248 switch (eventSource) {
249 case ASM_EVENT_SOURCE_CALL_START:
250 case ASM_EVENT_SOURCE_ALARM_START:
251 case ASM_EVENT_SOURCE_MEDIA:
252 case ASM_EVENT_SOURCE_EMERGENCY_START:
253 case ASM_EVENT_SOURCE_OTHER_PLAYER_APP:
254 case ASM_EVENT_SOURCE_RESOURCE_CONFLICT:
255 case ASM_EVENT_SOURCE_EARJACK_UNPLUG:
256 if (!player->url().string().contains("camera://")) {
258 return ASM_CB_RES_PAUSE;
261 return ASM_CB_RES_NONE;
265 static ASM_cb_result_t MediaPlayerAudioSessionEventSourcePlay(ASM_event_sources_t eventSource, void* callbackData)
267 MediaPlayer* player = static_cast<MediaPlayer*>(callbackData);
268 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player->mediaPlayerClient());
271 return ASM_CB_RES_IGNORE;
273 switch (eventSource) {
274 case ASM_EVENT_SOURCE_ALARM_END:
275 if (!element->isVideo() && !player->url().string().contains("camera://")) {
277 return ASM_CB_RES_PLAYING;
279 return ASM_CB_RES_NONE;
281 return ASM_CB_RES_NONE;
285 static ASM_cb_result_t mediaPlayerPrivateAudioSessionNotifyCallback(int, ASM_event_sources_t eventSource, ASM_sound_commands_t command, unsigned int, void* callbackData)
287 if (command == ASM_COMMAND_STOP || command == ASM_COMMAND_PAUSE)
288 return MediaPlayerAudioSessionEventSourcePause(eventSource, callbackData);
289 if (command == ASM_COMMAND_PLAY || command == ASM_COMMAND_RESUME)
290 return MediaPlayerAudioSessionEventSourcePlay(eventSource, callbackData);
292 return ASM_CB_RES_NONE;
296 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player)
298 return adoptPtr(new MediaPlayerPrivateGStreamer(player));
301 void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar)
304 registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
307 bool initializeGStreamerAndRegisterWebKitElements()
309 if (!initializeGStreamer())
312 GRefPtr<GstElementFactory> srcFactory = gst_element_factory_find("webkitwebsrc");
314 #if ENABLE(TIZEN_MEDIA_STREAM)
315 return gst_element_register(0, "webkitcamerasrc", GST_RANK_PRIMARY + 200, WEBKIT_TYPE_CAMERA_SRC);
317 GST_DEBUG_CATEGORY_INIT(webkit_media_player_debug, "webkitmediaplayer", 0, "WebKit media player");
318 return gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC);
319 #endif // ENABLE(TIZEN_MEDIA_STREAM)
324 bool MediaPlayerPrivateGStreamer::isAvailable()
326 if (!initializeGStreamerAndRegisterWebKitElements())
329 GRefPtr<GstElementFactory> factory = gst_element_factory_find(gPlaybinName);
333 MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player)
335 , m_webkitVideoSink(0)
336 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
342 , m_changingRate(false)
343 , m_endTime(numeric_limits<float>::infinity())
344 , m_isEndReached(false)
345 , m_networkState(MediaPlayer::Empty)
346 , m_readyState(MediaPlayer::HaveNothing)
347 , m_isStreaming(false)
350 , m_mediaLocations(0)
351 , m_mediaLocationCurrentIndex(0)
352 , m_resetPipeline(false)
357 , m_errorOccured(false)
359 , m_startedBuffering(false)
360 , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired)
362 , m_bufferingPercentage(0)
363 , m_preload(MediaPlayer::Auto)
364 , m_delayingLoad(false)
365 , m_mediaDurationKnown(true)
366 , m_maxTimeLoadedAtLastDidLoadingProgress(0)
367 , m_volumeTimerHandler(0)
368 , m_muteTimerHandler(0)
371 , m_audioTimerHandler(0)
372 , m_videoTimerHandler(0)
373 , m_webkitAudioSink(0)
375 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
376 , m_videoLayer(VideoLayerTizen::create(static_cast<HTMLMediaElement*>(player->mediaPlayerClient())))
377 #endif // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
378 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
379 , m_audioSessionManager(AudioSessionManagerGStreamerTizen::createAudioSessionManager())
381 , m_originalPreloadWasAutoAndWasOverridden(false)
382 , m_preservesPitch(false)
383 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
384 , m_bufferingMode(GST_BUFFERING_STREAM)
390 MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer()
392 if (m_fillTimer.isActive())
396 gst_buffer_unref(m_buffer);
399 if (m_mediaLocations) {
400 gst_structure_free(m_mediaLocations);
401 m_mediaLocations = 0;
404 #ifndef GST_API_VERSION_1
405 if (m_videoSinkBin) {
406 gst_object_unref(m_videoSinkBin);
412 GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
414 g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateMessageCallback), this);
415 gst_bus_remove_signal_watch(bus.get());
417 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
423 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
424 if (m_audioSessionManager)
425 m_audioSessionManager->setSoundState(ASM_STATE_STOP);
428 if (m_muteTimerHandler)
429 g_source_remove(m_muteTimerHandler);
431 if (m_volumeTimerHandler)
432 g_source_remove(m_volumeTimerHandler);
434 if (m_videoTimerHandler)
435 g_source_remove(m_videoTimerHandler);
437 if (m_audioTimerHandler)
438 g_source_remove(m_audioTimerHandler);
441 void MediaPlayerPrivateGStreamer::load(const String& url)
443 if (!initializeGStreamerAndRegisterWebKitElements())
446 #if ENABLE(TIZEN_MEDIA_STREAM) || ENABLE(TIZEN_FILE_SYSTEM)
448 #endif // ENABLE(TIZEN_MEDIA_STREAM) || ENABLE(TIZEN_FILE_SYSTEM)
449 KURL kurl(KURL(), url);
450 String cleanUrl(url);
452 // Clean out everything after file:// url path.
453 if (kurl.isLocalFile())
454 cleanUrl = cleanUrl.substring(0, kurl.pathEnd());
458 setDownloadBuffering();
463 m_url = KURL(KURL(), cleanUrl);
464 g_object_set(m_playBin.get(), "uri", cleanUrl.utf8().data(), NULL);
466 LOG_MEDIA_MESSAGE("Load %s", cleanUrl.utf8().data());
468 #if ENABLE(TIZEN_MEDIA_STREAM)
469 // Workaround modification for getUserMedia.
470 // When 'playing' event is fired videoWidth and videoHeight are not available.
471 // Set m_videoSize as frame size which comes from webkitCameraSource.
472 // This size is fixed.
473 if (isLocalMediaStream())
474 if (gst_element_factory_find("camerasrc"))
475 m_videoSize = IntSize(480, 640);
477 m_videoSize = IntSize(640, 480);
480 if (m_preload == MediaPlayer::None) {
481 LOG_MEDIA_MESSAGE("Delaying load.");
482 m_delayingLoad = true;
485 // Reset network and ready states. Those will be set properly once
486 // the pipeline pre-rolled.
487 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
489 m_networkState = MediaPlayer::Loading;
491 m_networkState = MediaPlayer::Idle;
493 m_networkState = MediaPlayer::Loading;
495 m_player->networkStateChanged();
496 m_readyState = MediaPlayer::HaveNothing;
497 m_player->readyStateChanged();
498 m_volumeAndMuteInitialized = false;
500 // GStreamer needs to have the pipeline set to a paused state to
501 // start providing anything useful.
502 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
505 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
507 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
508 if (m_audioSessionManager && !m_delayingLoad)
509 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
516 void MediaPlayerPrivateGStreamer::commitLoad()
518 ASSERT(!m_delayingLoad);
519 LOG_MEDIA_MESSAGE("Committing load.");
523 float MediaPlayerPrivateGStreamer::playbackPosition() const
525 if (m_isEndReached) {
526 // Position queries on a null pipeline return 0. If we're at
527 // the end of the stream the pipeline is null but we want to
528 // report either the seek time or the duration because this is
529 // what the Media element spec expects us to do.
533 return m_mediaDuration;
538 GstQuery* query = gst_query_new_position(GST_FORMAT_TIME);
539 if (!gst_element_query(m_playBin.get(), query)) {
540 LOG_MEDIA_MESSAGE("Position query failed...");
541 gst_query_unref(query);
546 gst_query_parse_position(query, 0, &position);
548 // Position is available only if the pipeline is not in GST_STATE_NULL or
549 // GST_STATE_READY state.
550 if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE))
551 ret = static_cast<double>(position) / GST_SECOND;
553 LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position));
555 gst_query_unref(query);
560 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState)
562 ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED);
564 GstState currentState;
567 gst_element_get_state(m_playBin.get(), ¤tState, &pending, 0);
568 LOG_MEDIA_MESSAGE("Current state: %s, pending: %s", gst_element_state_get_name(currentState), gst_element_state_get_name(pending));
569 #if ENABLE(TIZEN_DLOG_SUPPORT)
570 TIZEN_LOGI("Current state: %s, New state: %s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState));
572 if (currentState == newState || pending == newState)
575 GstStateChangeReturn setStateResult = gst_element_set_state(m_playBin.get(), newState);
576 GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING;
577 if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE) {
578 loadingFailed(MediaPlayer::Empty);
584 void MediaPlayerPrivateGStreamer::prepareToPlay()
586 m_isEndReached = false;
589 if (m_delayingLoad) {
590 m_delayingLoad = false;
595 void MediaPlayerPrivateGStreamer::play()
597 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
598 if (!m_audioSessionManager->setSoundState(ASM_STATE_PLAYING) && !isLocalMediaStream())
601 if (changePipelineState(GST_STATE_PLAYING)) {
602 m_isEndReached = false;
603 LOG_MEDIA_MESSAGE("Play");
607 void MediaPlayerPrivateGStreamer::pause()
609 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
610 if (!m_audioSessionManager->setSoundState(ASM_STATE_PAUSE) && !isLocalMediaStream())
616 if (changePipelineState(GST_STATE_PAUSED))
617 LOG_MEDIA_MESSAGE("Pause");
620 float MediaPlayerPrivateGStreamer::duration() const
628 // Media duration query failed already, don't attempt new useless queries.
629 if (!m_mediaDurationKnown)
630 return numeric_limits<float>::infinity();
633 return m_mediaDuration;
635 GstFormat timeFormat = GST_FORMAT_TIME;
636 gint64 timeLength = 0;
638 #ifdef GST_API_VERSION_1
639 bool failure = !gst_element_query_duration(m_playBin.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
641 bool failure = !gst_element_query_duration(m_playBin.get(), &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE;
644 LOG_MEDIA_MESSAGE("Time duration query failed for %s", m_url.string().utf8().data());
645 return numeric_limits<float>::infinity();
648 LOG_MEDIA_MESSAGE("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength));
650 return static_cast<double>(timeLength) / GST_SECOND;
651 // FIXME: handle 3.14.9.5 properly
654 float MediaPlayerPrivateGStreamer::currentTime() const
666 // https://bugzilla.gnome.org/show_bug.cgi?id=639941 In GStreamer
667 // 0.10.35 basesink reports wrong duration in case of EOS and
668 // negative playback rate. There's no upstream accepted patch for
669 // this bug yet, hence this temporary workaround.
670 if (m_isEndReached && m_playbackRate < 0)
673 return playbackPosition();
677 void MediaPlayerPrivateGStreamer::seek(float time)
685 LOG_MEDIA_MESSAGE("Seek attempt to %f secs", time);
686 #if ENABLE(TIZEN_DLOG_SUPPORT)
687 TIZEN_LOGI("Seek attempt to %f secs", time);
690 // Avoid useless seeking.
691 if (time == currentTime())
694 // Extract the integer part of the time (seconds) and the
695 // fractional part (microseconds). Attempt to round the
696 // microseconds so no floating point precision is lost and we can
697 // perform an accurate seek.
699 float microSeconds = modf(time, &seconds) * 1000000;
701 timeValue.tv_sec = static_cast<glong>(seconds);
702 timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000);
704 GstClockTime clockTime = GST_TIMEVAL_TO_TIME(timeValue);
705 LOG_MEDIA_MESSAGE("Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(clockTime));
707 if (!gst_element_seek(m_playBin.get(), m_player->rate(),
709 (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
710 GST_SEEK_TYPE_SET, clockTime,
711 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) {
712 LOG_MEDIA_MESSAGE("Seek to %f failed", time);
713 #if ENABLE(TIZEN_DLOG_SUPPORT)
714 TIZEN_LOGE("Seek to %f failed", time);
719 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
720 if (m_seekTime != m_mediaDuration)
721 m_isEndReached = false;
726 bool MediaPlayerPrivateGStreamer::paused() const
728 if (m_isEndReached) {
729 LOG_MEDIA_MESSAGE("Ignoring pause at EOS");
734 gst_element_get_state(m_playBin.get(), &state, 0, 0);
735 return state == GST_STATE_PAUSED;
738 bool MediaPlayerPrivateGStreamer::seeking() const
743 // Returns the size of the video
744 IntSize MediaPlayerPrivateGStreamer::naturalSize() const
749 if (!m_videoSize.isEmpty())
752 GRefPtr<GstCaps> caps = webkitGstGetPadCaps(m_videoSinkPad.get());
757 // TODO: handle possible clean aperture data. See
758 // https://bugzilla.gnome.org/show_bug.cgi?id=596571
759 // TODO: handle possible transformation matrix. See
760 // https://bugzilla.gnome.org/show_bug.cgi?id=596326
762 // Get the video PAR and original size, if this fails the
763 // video-sink has likely not yet negotiated its caps.
764 int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride;
765 IntSize originalSize;
766 GstVideoFormat format;
767 if (!getVideoSizeAndFormatFromCaps(caps.get(), originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride))
770 LOG_MEDIA_MESSAGE("Original video size: %dx%d", originalSize.width(), originalSize.height());
771 LOG_MEDIA_MESSAGE("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator);
773 // Calculate DAR based on PAR and video size.
774 int displayWidth = originalSize.width() * pixelAspectRatioNumerator;
775 int displayHeight = originalSize.height() * pixelAspectRatioDenominator;
777 // Divide display width and height by their GCD to avoid possible overflows.
778 int displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight);
779 displayWidth /= displayAspectRatioGCD;
780 displayHeight /= displayAspectRatioGCD;
782 // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function.
783 guint64 width = 0, height = 0;
784 if (!(originalSize.height() % displayHeight)) {
785 LOG_MEDIA_MESSAGE("Keeping video original height");
786 width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
787 height = static_cast<guint64>(originalSize.height());
788 } else if (!(originalSize.width() % displayWidth)) {
789 LOG_MEDIA_MESSAGE("Keeping video original width");
790 height = gst_util_uint64_scale_int(originalSize.width(), displayHeight, displayWidth);
791 width = static_cast<guint64>(originalSize.width());
793 LOG_MEDIA_MESSAGE("Approximating while keeping original video height");
794 width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight);
795 height = static_cast<guint64>(originalSize.height());
798 LOG_MEDIA_MESSAGE("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height);
799 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE) && USE(ACCELERATED_VIDEO_VAAPI)
800 if (!m_displaySize.isEmpty())
801 m_videoSize = scaleHDVideoToDisplaySize(static_cast<int>(width), static_cast<int>(height), m_displaySize.width(), m_displaySize.height());
804 m_videoSize = IntSize(static_cast<int>(width), static_cast<int>(height));
808 void MediaPlayerPrivateGStreamer::videoChanged()
810 if (m_videoTimerHandler)
811 g_source_remove(m_videoTimerHandler);
812 m_videoTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this);
815 void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo()
817 m_videoTimerHandler = 0;
819 gint videoTracks = 0;
821 g_object_get(m_playBin.get(), "n-video", &videoTracks, NULL);
823 m_hasVideo = videoTracks > 0;
825 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
827 if (gst_video_get_size(m_videoSinkPad.get(), &width, &height)) {
828 IntSize size(width, height);
829 if (m_videoSize != size) {
830 m_videoSize = IntSize();
831 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
832 m_videoLayer->setOverlay(naturalSize());
837 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
838 #else // ENABLE(TIZEN_GSTREAMER_VIDEO)
839 m_videoSize = IntSize();
841 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
845 void MediaPlayerPrivateGStreamer::audioChanged()
847 if (m_audioTimerHandler)
848 g_source_remove(m_audioTimerHandler);
849 m_audioTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this);
852 void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio()
854 m_audioTimerHandler = 0;
856 gint audioTracks = 0;
858 g_object_get(m_playBin.get(), "n-audio", &audioTracks, NULL);
859 m_hasAudio = audioTracks > 0;
860 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player);
863 void MediaPlayerPrivateGStreamer::setVolume(float volume)
868 gst_stream_volume_set_volume(GST_STREAM_VOLUME(m_playBin.get()), GST_STREAM_VOLUME_FORMAT_CUBIC,
869 static_cast<double>(volume));
872 void MediaPlayerPrivateGStreamer::notifyPlayerOfVolumeChange()
874 m_volumeTimerHandler = 0;
876 if (!m_player || !m_playBin)
879 volume = gst_stream_volume_get_volume(GST_STREAM_VOLUME(m_playBin.get()), GST_STREAM_VOLUME_FORMAT_CUBIC);
880 // get_volume() can return values superior to 1.0 if the user
881 // applies software user gain via third party application (GNOME
882 // volume control for instance).
883 volume = CLAMP(volume, 0.0, 1.0);
884 m_player->volumeChanged(static_cast<float>(volume));
887 void MediaPlayerPrivateGStreamer::volumeChanged()
889 if (m_volumeTimerHandler)
890 g_source_remove(m_volumeTimerHandler);
891 m_volumeTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this);
894 void MediaPlayerPrivateGStreamer::setRate(float rate)
896 // Avoid useless playback rate update.
897 if (m_playbackRate == rate)
903 gst_element_get_state(m_playBin.get(), &state, &pending, 0);
904 if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED)
905 || (pending == GST_STATE_PAUSED))
911 m_playbackRate = rate;
912 m_changingRate = true;
915 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
916 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
917 if (m_audioSessionManager)
918 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
923 float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND);
924 GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH);
928 LOG_MEDIA_MESSAGE("Set Rate to %f", rate);
930 // Mute the sound if the playback rate is too extreme and
931 // audio pitch is not adjusted.
932 mute = (!m_preservesPitch && (rate < 0.8 || rate > 2));
933 start = currentPosition;
934 end = GST_CLOCK_TIME_NONE;
939 // If we are at beginning of media, start from the end to
940 // avoid immediate EOS.
941 if (currentPosition <= 0)
942 end = static_cast<gint64>(duration() * GST_SECOND);
944 end = currentPosition;
947 LOG_MEDIA_MESSAGE("Need to mute audio: %d", (int) mute);
949 if (!gst_element_seek(m_playBin.get(), rate, GST_FORMAT_TIME, flags,
950 GST_SEEK_TYPE_SET, start,
951 GST_SEEK_TYPE_SET, end))
952 LOG_MEDIA_MESSAGE("Set rate to %f failed", rate);
954 g_object_set(m_playBin.get(), "mute", mute, NULL);
957 void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch)
959 m_preservesPitch = preservesPitch;
962 MediaPlayer::NetworkState MediaPlayerPrivateGStreamer::networkState() const
964 return m_networkState;
967 MediaPlayer::ReadyState MediaPlayerPrivateGStreamer::readyState() const
972 PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const
974 RefPtr<TimeRanges> timeRanges = TimeRanges::create();
975 if (m_errorOccured || isLiveStream())
976 return timeRanges.release();
978 #if GST_CHECK_VERSION(0, 10, 31)
979 float mediaDuration(duration());
980 if (!mediaDuration || isinf(mediaDuration))
981 return timeRanges.release();
983 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
985 if (!gst_element_query(m_playBin.get(), query)) {
986 gst_query_unref(query);
987 return timeRanges.release();
990 gint64 rangeStart = 0, rangeStop = 0;
991 for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) {
992 if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop))
993 timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / 100),
994 static_cast<float>((rangeStop * mediaDuration) / 100));
997 // Fallback to the more general maxTimeLoaded() if no range has
999 if (!timeRanges->length())
1000 if (float loaded = maxTimeLoaded())
1001 timeRanges->add(0, loaded);
1003 gst_query_unref(query);
1005 float loaded = maxTimeLoaded();
1006 if (!m_errorOccured && !isLiveStream() && loaded > 0)
1007 timeRanges->add(0, loaded);
1009 return timeRanges.release();
1012 gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message)
1014 GOwnPtr<GError> err;
1015 GOwnPtr<gchar> debug;
1016 MediaPlayer::NetworkState error;
1017 bool issueError = true;
1018 bool attemptNextLocation = false;
1019 const GstStructure* structure = gst_message_get_structure(message);
1022 const gchar* messageTypeName = gst_structure_get_name(structure);
1024 // Redirect messages are sent from elements, like qtdemux, to
1025 // notify of the new location(s) of the media.
1026 if (!g_strcmp0(messageTypeName, "redirect")) {
1027 mediaLocationChanged(message);
1032 LOG_MEDIA_MESSAGE("Message received from element %s", GST_MESSAGE_SRC_NAME(message));
1033 switch (GST_MESSAGE_TYPE(message)) {
1034 case GST_MESSAGE_ERROR:
1035 if (m_resetPipeline)
1037 gst_message_parse_error(message, &err.outPtr(), &debug.outPtr());
1038 LOG_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
1040 #if ENABLE(TIZEN_DLOG_SUPPORT)
1041 TIZEN_LOGE("GST_MESSAGE_ERROR %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data());
1044 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error");
1046 error = MediaPlayer::Empty;
1047 if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
1048 || err->code == GST_STREAM_ERROR_WRONG_TYPE
1049 || err->code == GST_STREAM_ERROR_FAILED
1050 || err->code == GST_CORE_ERROR_MISSING_PLUGIN
1051 || err->code == GST_RESOURCE_ERROR_NOT_FOUND)
1052 error = MediaPlayer::FormatError;
1053 else if (err->domain == GST_STREAM_ERROR) {
1054 // Let the mediaPlayerClient handle the stream error, in
1055 // this case the HTMLMediaElement will emit a stalled
1057 #if !ENABLE(TIZEN_MEDIA_STREAM)
1058 if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) {
1059 LOG_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event.");
1063 error = MediaPlayer::DecodeError;
1064 attemptNextLocation = true;
1065 } else if (err->domain == GST_RESOURCE_ERROR)
1066 error = MediaPlayer::NetworkError;
1068 if (attemptNextLocation)
1069 issueError = !loadNextLocation();
1071 loadingFailed(error);
1073 case GST_MESSAGE_EOS:
1074 LOG_MEDIA_MESSAGE("End of Stream");
1077 case GST_MESSAGE_STATE_CHANGED:
1078 // Ignore state changes if load is delayed (preload=none). The
1079 // player state will be updated once commitLoad() is called.
1080 if (m_delayingLoad) {
1081 LOG_MEDIA_MESSAGE("Media load has been delayed. Ignoring state changes for now");
1085 // Ignore state changes from internal elements. They are
1086 // forwarded to playbin2 anyway.
1087 if (GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get())) {
1090 // Construct a filename for the graphviz dot file output.
1091 GstState oldState, newState;
1092 gst_message_parse_state_changed(message, &oldState, &newState, 0);
1094 CString dotFileName = String::format("webkit-video.%s_%s",
1095 gst_element_state_get_name(oldState),
1096 gst_element_state_get_name(newState)).utf8();
1098 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data());
1100 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1101 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player()->mediaPlayerClient());
1102 if (element->suspended() && newState == GST_STATE_PAUSED) {
1103 if (m_suspendTime > 0 && !(isLocalMediaStream() || m_isEndReached))
1104 seek(m_suspendTime);
1106 m_player->mediaPlayerClient()->setSuspended(false);
1111 case GST_MESSAGE_BUFFERING:
1112 processBufferingStats(message);
1114 case GST_MESSAGE_DURATION:
1115 LOG_MEDIA_MESSAGE("Duration changed");
1119 LOG_MEDIA_MESSAGE("Unhandled GStreamer message type: %s",
1120 GST_MESSAGE_TYPE_NAME(message));
1126 void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message)
1128 // This is the immediate buffering that needs to happen so we have
1129 // enough to play right now.
1131 const GstStructure *structure = gst_message_get_structure(message);
1132 gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage);
1134 LOG_MEDIA_MESSAGE("[Buffering] Buffering: %d%%.", m_bufferingPercentage);
1136 GstBufferingMode mode;
1137 gst_message_parse_buffering_stats(message, &mode, 0, 0, 0);
1138 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1139 m_bufferingMode = mode;
1141 if (mode != GST_BUFFERING_DOWNLOAD) {
1146 // This is on-disk buffering, that allows us to download much more
1147 // than needed for right now.
1148 if (!m_startedBuffering) {
1149 LOG_MEDIA_MESSAGE("[Buffering] Starting on-disk buffering.");
1151 m_startedBuffering = true;
1153 if (m_fillTimer.isActive())
1156 m_fillTimer.startRepeating(0.2);
1160 void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*)
1162 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT);
1164 if (!gst_element_query(m_playBin.get(), query)) {
1165 gst_query_unref(query);
1170 gdouble fillStatus = 100.0;
1172 gst_query_parse_buffering_range(query, 0, &start, &stop, 0);
1173 gst_query_unref(query);
1176 fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX;
1178 LOG_MEDIA_MESSAGE("[Buffering] Download buffer filled up to %f%%", fillStatus);
1180 if (!m_mediaDuration)
1183 // Update maxTimeLoaded only if the media duration is
1184 // available. Otherwise we can't compute it.
1185 if (m_mediaDuration) {
1186 if (fillStatus == 100.0)
1187 m_maxTimeLoaded = m_mediaDuration;
1189 m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0);
1190 LOG_MEDIA_MESSAGE("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded);
1193 if (fillStatus != 100.0) {
1198 // Media is now fully loaded. It will play even if network
1199 // connection is cut. Buffering is done, remove the fill source
1200 // from the main loop.
1202 m_startedBuffering = false;
1206 float MediaPlayerPrivateGStreamer::maxTimeSeekable() const
1211 LOG_MEDIA_MESSAGE("maxTimeSeekable");
1212 // infinite duration means live stream
1213 if (isinf(duration()))
1219 float MediaPlayerPrivateGStreamer::maxTimeLoaded() const
1224 float loaded = m_maxTimeLoaded;
1225 if (!loaded && !m_fillTimer.isActive())
1226 loaded = duration();
1227 LOG_MEDIA_MESSAGE("maxTimeLoaded: %f", loaded);
1231 bool MediaPlayerPrivateGStreamer::didLoadingProgress() const
1233 if (!m_playBin || !m_mediaDuration || !totalBytes())
1235 float currentMaxTimeLoaded = maxTimeLoaded();
1236 bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
1237 m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
1238 LOG_MEDIA_MESSAGE("didLoadingProgress: %d", didLoadingProgress);
1239 return didLoadingProgress;
1242 unsigned MediaPlayerPrivateGStreamer::totalBytes() const
1247 if (m_totalBytes != -1)
1248 return m_totalBytes;
1253 GstFormat fmt = GST_FORMAT_BYTES;
1255 #ifdef GST_API_VERSION_1
1256 if (gst_element_query_duration(m_source.get(), fmt, &length)) {
1258 if (gst_element_query_duration(m_source.get(), &fmt, &length)) {
1260 LOG_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
1261 m_totalBytes = static_cast<unsigned>(length);
1262 m_isStreaming = !length;
1263 return m_totalBytes;
1266 // Fall back to querying the source pads manually.
1267 // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749
1268 GstIterator* iter = gst_element_iterate_src_pads(m_source.get());
1271 #ifdef GST_API_VERSION_1
1272 GValue item = G_VALUE_INIT;
1273 switch (gst_iterator_next(iter, &item)) {
1274 case GST_ITERATOR_OK: {
1275 GstPad* pad = static_cast<GstPad*>(g_value_get_object(&item));
1276 gint64 padLength = 0;
1277 if (gst_pad_query_duration(pad, fmt, &padLength) && padLength > length)
1284 switch (gst_iterator_next(iter, &data)) {
1285 case GST_ITERATOR_OK: {
1286 GRefPtr<GstPad> pad = adoptGRef(GST_PAD_CAST(data));
1287 gint64 padLength = 0;
1288 if (gst_pad_query_duration(pad.get(), &fmt, &padLength) && padLength > length)
1293 case GST_ITERATOR_RESYNC:
1294 gst_iterator_resync(iter);
1296 case GST_ITERATOR_ERROR:
1298 case GST_ITERATOR_DONE:
1303 #ifdef GST_API_VERSION_1
1304 g_value_unset(&item);
1308 gst_iterator_free(iter);
1310 LOG_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length);
1311 m_totalBytes = static_cast<unsigned>(length);
1312 m_isStreaming = !length;
1313 return m_totalBytes;
1316 unsigned MediaPlayerPrivateGStreamer::decodedFrameCount() const
1318 guint64 decodedFrames = 0;
1320 g_object_get(m_fpsSink, "frames-rendered", &decodedFrames, NULL);
1321 return static_cast<unsigned>(decodedFrames);
1324 unsigned MediaPlayerPrivateGStreamer::droppedFrameCount() const
1326 guint64 framesDropped = 0;
1328 g_object_get(m_fpsSink, "frames-dropped", &framesDropped, NULL);
1329 return static_cast<unsigned>(framesDropped);
1332 unsigned MediaPlayerPrivateGStreamer::audioDecodedByteCount() const
1334 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
1335 gint64 position = 0;
1337 if (m_webkitAudioSink && gst_element_query(m_webkitAudioSink.get(), query))
1338 gst_query_parse_position(query, 0, &position);
1340 gst_query_unref(query);
1341 return static_cast<unsigned>(position);
1344 unsigned MediaPlayerPrivateGStreamer::videoDecodedByteCount() const
1346 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES);
1347 gint64 position = 0;
1349 if (gst_element_query(m_webkitVideoSink, query))
1350 gst_query_parse_position(query, 0, &position);
1352 gst_query_unref(query);
1353 return static_cast<unsigned>(position);
1356 void MediaPlayerPrivateGStreamer::updateAudioSink()
1361 GstElement* sinkPtr = 0;
1363 g_object_get(m_playBin.get(), "audio-sink", &sinkPtr, NULL);
1364 m_webkitAudioSink = adoptGRef(sinkPtr);
1369 void MediaPlayerPrivateGStreamer::sourceChanged()
1371 GstElement* srcPtr = 0;
1373 g_object_get(m_playBin.get(), "source", &srcPtr, NULL);
1374 m_source = adoptGRef(srcPtr);
1376 if (WEBKIT_IS_WEB_SRC(m_source.get()))
1377 webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player);
1380 void MediaPlayerPrivateGStreamer::cancelLoad()
1382 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
1386 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1387 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1388 if (m_audioSessionManager)
1389 m_audioSessionManager->setSoundState(ASM_STATE_STOP);
1393 void MediaPlayerPrivateGStreamer::updateStates()
1401 MediaPlayer::NetworkState oldNetworkState = m_networkState;
1402 MediaPlayer::ReadyState oldReadyState = m_readyState;
1406 GstStateChangeReturn ret = gst_element_get_state(m_playBin.get(),
1407 &state, &pending, 250 * GST_NSECOND);
1409 bool shouldUpdateAfterSeek = false;
1411 case GST_STATE_CHANGE_SUCCESS:
1412 LOG_MEDIA_MESSAGE("State: %s, pending: %s",
1413 gst_element_state_get_name(state),
1414 gst_element_state_get_name(pending));
1416 m_resetPipeline = state <= GST_STATE_READY;
1418 // Try to figure out ready and network states.
1419 if (state == GST_STATE_READY) {
1420 m_readyState = MediaPlayer::HaveMetadata;
1421 m_networkState = MediaPlayer::Empty;
1422 // Cache the duration without emiting the durationchange
1423 // event because it's taken care of by the media element
1424 // in this precise case.
1425 if (!m_isEndReached)
1427 } else if ((state == GST_STATE_NULL) || (maxTimeLoaded() == duration())) {
1428 m_networkState = MediaPlayer::Loaded;
1429 m_readyState = MediaPlayer::HaveEnoughData;
1431 m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
1432 m_networkState = MediaPlayer::Loading;
1434 #if !ENABLE(TIZEN_GSTREAMER_VIDEO)
1435 if (m_buffering && state != GST_STATE_READY) {
1436 m_readyState = MediaPlayer::HaveCurrentData;
1437 m_networkState = MediaPlayer::Loading;
1440 // Now let's try to get the states in more detail using
1441 // information from GStreamer, while we sync states where
1443 if (state == GST_STATE_PAUSED) {
1444 if (!m_webkitAudioSink)
1447 if (!m_volumeAndMuteInitialized) {
1448 notifyPlayerOfVolumeChange();
1449 notifyPlayerOfMute();
1450 m_volumeAndMuteInitialized = true;
1453 if (m_buffering && m_bufferingPercentage == 100) {
1454 m_buffering = false;
1455 m_bufferingPercentage = 0;
1456 m_readyState = MediaPlayer::HaveEnoughData;
1458 LOG_MEDIA_MESSAGE("[Buffering] Complete.");
1461 LOG_MEDIA_MESSAGE("[Buffering] Restarting playback.");
1462 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1463 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1464 if (m_audioSessionManager)
1465 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1468 } else if (!m_buffering && (currentTime() < duration())) {
1470 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1471 } else if (m_buffering && m_bufferingMode != GST_BUFFERING_DOWNLOAD) {
1472 m_readyState = MediaPlayer::HaveCurrentData;
1473 m_networkState = MediaPlayer::Loading;
1476 } else if (state == GST_STATE_PLAYING) {
1477 m_readyState = MediaPlayer::HaveEnoughData;
1480 if (m_buffering && !isLiveStream()) {
1481 m_readyState = MediaPlayer::HaveCurrentData;
1482 m_networkState = MediaPlayer::Loading;
1484 LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering.");
1486 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1487 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1488 if (m_audioSessionManager)
1489 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
1495 // Is on-disk buffering in progress?
1496 if (m_fillTimer.isActive())
1497 m_networkState = MediaPlayer::Loading;
1499 if (m_changingRate) {
1500 m_player->rateChanged();
1501 m_changingRate = false;
1505 shouldUpdateAfterSeek = true;
1510 case GST_STATE_CHANGE_ASYNC:
1511 LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s",
1512 gst_element_state_get_name(state),
1513 gst_element_state_get_name(pending));
1514 // Change in progress
1516 // On-disk buffering was attempted but the media is live. This
1517 // can't work so disable on-disk buffering and reset the
1520 #if ENABLE(TIZEN_MEDIA_STREAM)
1521 if (state == GST_STATE_READY && isLiveStream() && m_preload == MediaPlayer::Auto && !isLocalMediaStream()) {
1523 if (state == GST_STATE_READY && isLiveStream() && m_preload == MediaPlayer::Auto) {
1525 setPreload(MediaPlayer::None);
1526 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1527 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1528 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1529 if (m_audioSessionManager)
1530 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
1534 // A live stream was paused, reset the pipeline.
1535 #if ENABLE(TIZEN_MEDIA_STREAM)
1536 if (state == GST_STATE_PAUSED && pending == GST_STATE_PLAYING && isLiveStream() && !isLocalMediaStream()) {
1538 if (state == GST_STATE_PAUSED && pending == GST_STATE_PLAYING && isLiveStream()) {
1540 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1541 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1542 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1543 if (m_audioSessionManager)
1544 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1548 if (!isLiveStream() && !m_buffering)
1552 shouldUpdateAfterSeek = true;
1556 case GST_STATE_CHANGE_FAILURE:
1557 LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s",
1558 gst_element_state_get_name(state),
1559 gst_element_state_get_name(pending));
1562 case GST_STATE_CHANGE_NO_PREROLL:
1563 LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s",
1564 gst_element_state_get_name(state),
1565 gst_element_state_get_name(pending));
1567 if (state == GST_STATE_READY)
1568 m_readyState = MediaPlayer::HaveNothing;
1569 else if (state == GST_STATE_PAUSED) {
1570 m_readyState = MediaPlayer::HaveEnoughData;
1572 // Live pipelines go in PAUSED without prerolling.
1573 m_isStreaming = true;
1574 } else if (state == GST_STATE_PLAYING)
1578 shouldUpdateAfterSeek = true;
1581 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1582 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1583 if (m_audioSessionManager)
1584 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1587 } else if (!m_paused) {
1588 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1589 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1590 if (m_audioSessionManager)
1591 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1595 m_networkState = MediaPlayer::Loading;
1598 LOG_MEDIA_MESSAGE("Else : %d", ret);
1603 m_readyState = MediaPlayer::HaveNothing;
1605 if (shouldUpdateAfterSeek)
1608 if (m_networkState != oldNetworkState) {
1609 LOG_MEDIA_MESSAGE("Network State Changed from %u to %u",
1610 oldNetworkState, m_networkState);
1611 m_player->networkStateChanged();
1613 if (m_readyState != oldReadyState) {
1614 LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u",
1615 oldReadyState, m_readyState);
1616 m_player->readyStateChanged();
1617 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
1618 if (state == GST_STATE_PLAYING)
1619 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
1624 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message)
1626 if (m_mediaLocations)
1627 gst_structure_free(m_mediaLocations);
1629 const GstStructure* structure = gst_message_get_structure(message);
1631 // This structure can contain:
1632 // - both a new-location string and embedded locations structure
1633 // - or only a new-location string.
1634 m_mediaLocations = gst_structure_copy(structure);
1635 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1638 m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1;
1644 bool MediaPlayerPrivateGStreamer::loadNextLocation()
1646 if (!m_mediaLocations)
1649 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations");
1650 const gchar* newLocation = 0;
1653 // Fallback on new-location string.
1654 newLocation = gst_structure_get_string(m_mediaLocations, "new-location");
1660 if (m_mediaLocationCurrentIndex < 0) {
1661 m_mediaLocations = 0;
1665 const GValue* location = gst_value_list_get_value(locations,
1666 m_mediaLocationCurrentIndex);
1667 const GstStructure* structure = gst_value_get_structure(location);
1670 m_mediaLocationCurrentIndex--;
1674 newLocation = gst_structure_get_string(structure, "new-location");
1678 // Found a candidate. new-location is not always an absolute url
1679 // though. We need to take the base of the current url and
1680 // append the value of new-location to it.
1682 gchar* currentLocation = 0;
1683 g_object_get(m_playBin.get(), "uri", ¤tLocation, NULL);
1685 KURL currentUrl(KURL(), currentLocation);
1686 g_free(currentLocation);
1690 if (gst_uri_is_valid(newLocation))
1691 newUrl = KURL(KURL(), newLocation);
1693 newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation);
1695 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl);
1696 if (securityOrigin->canRequest(newUrl)) {
1697 LOG_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data());
1699 // Reset player states.
1700 m_networkState = MediaPlayer::Loading;
1701 m_player->networkStateChanged();
1702 m_readyState = MediaPlayer::HaveNothing;
1703 m_player->readyStateChanged();
1705 // Reset pipeline state.
1706 m_resetPipeline = true;
1707 gst_element_set_state(m_playBin.get(), GST_STATE_READY);
1708 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1709 if (m_audioSessionManager)
1710 m_audioSessionManager->setSoundState(ASM_STATE_NONE);
1713 gst_element_get_state(m_playBin.get(), &state, 0, 0);
1714 if (state <= GST_STATE_READY) {
1715 // Set the new uri and start playing.
1716 g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL);
1717 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
1718 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1719 if (m_audioSessionManager)
1720 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
1726 m_mediaLocationCurrentIndex--;
1731 void MediaPlayerPrivateGStreamer::loadStateChanged()
1736 void MediaPlayerPrivateGStreamer::sizeChanged()
1741 void MediaPlayerPrivateGStreamer::timeChanged()
1744 m_player->timeChanged();
1747 void MediaPlayerPrivateGStreamer::didEnd()
1749 // Synchronize position and duration values to not confuse the
1750 // HTMLMediaElement. In some cases like reverse playback the
1751 // position is not always reported as 0 for instance.
1752 float now = currentTime();
1753 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1754 if (now > 0 && now != duration()) {
1756 if (now > 0 && now <= duration() && m_mediaDuration != now) {
1758 m_mediaDurationKnown = true;
1759 m_mediaDuration = now;
1760 m_player->durationChanged();
1763 m_isEndReached = true;
1766 if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) {
1768 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1769 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1770 if (m_audioSessionManager)
1771 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
1773 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1776 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1777 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player()->mediaPlayerClient());
1778 if (element->isVideo())
1779 power_unlock_state(POWER_STATE_NORMAL);
1784 void MediaPlayerPrivateGStreamer::cacheDuration()
1786 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1787 float previousDuration = m_mediaDuration;
1789 // Reset cached media duration
1790 m_mediaDuration = 0;
1792 // And re-cache it if possible.
1794 gst_element_get_state(m_playBin.get(), &state, 0, 0);
1795 float newDuration = duration();
1797 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1798 if (state > GST_STATE_READY) {
1799 // Don't set m_mediaDurationKnown yet if the pipeline is not
1800 // paused. This allows duration() query to fail at least once
1801 // before playback starts and duration becomes known.
1802 m_mediaDurationKnown = !isinf(newDuration);
1805 if (!isinf(newDuration))
1806 m_mediaDuration = newDuration;
1808 m_mediaDuration = previousDuration;
1810 if (state <= GST_STATE_READY) {
1811 // Don't set m_mediaDurationKnown yet if the pipeline is not
1812 // paused. This allows duration() query to fail at least once
1813 // before playback starts and duration becomes known.
1814 if (!isinf(newDuration))
1815 m_mediaDuration = newDuration;
1817 m_mediaDurationKnown = !isinf(newDuration);
1818 if (m_mediaDurationKnown)
1819 m_mediaDuration = newDuration;
1822 if (!isinf(newDuration))
1823 m_mediaDuration = newDuration;
1827 void MediaPlayerPrivateGStreamer::durationChanged()
1829 float previousDuration = m_mediaDuration;
1832 // Avoid emiting durationchanged in the case where the previous
1833 // duration was 0 because that case is already handled by the
1834 // HTMLMediaElement.
1835 if (previousDuration && m_mediaDuration != previousDuration)
1836 m_player->durationChanged();
1838 if (m_preload == MediaPlayer::None && m_originalPreloadWasAutoAndWasOverridden) {
1840 if (totalBytes() && !isLiveStream()) {
1841 setPreload(MediaPlayer::Auto);
1842 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
1843 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
1844 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1845 if (m_audioSessionManager)
1846 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
1852 bool MediaPlayerPrivateGStreamer::supportsMuting() const
1857 void MediaPlayerPrivateGStreamer::setMuted(bool muted)
1862 g_object_set(m_playBin.get(), "mute", muted, NULL);
1865 void MediaPlayerPrivateGStreamer::notifyPlayerOfMute()
1867 m_muteTimerHandler = 0;
1869 if (!m_player || !m_playBin)
1873 g_object_get(m_playBin.get(), "mute", &muted, NULL);
1874 m_player->muteChanged(static_cast<bool>(muted));
1877 void MediaPlayerPrivateGStreamer::muteChanged()
1879 if (m_muteTimerHandler)
1880 g_source_remove(m_muteTimerHandler);
1881 m_muteTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this);
1884 void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error)
1886 #if ENABLE(TIZEN_DLOG_SUPPORT)
1887 TIZEN_LOGE("NetworkState : %d", error);
1890 m_errorOccured = true;
1891 if (m_networkState != error) {
1892 m_networkState = error;
1893 m_player->networkStateChanged();
1895 if (m_readyState != MediaPlayer::HaveNothing) {
1896 m_readyState = MediaPlayer::HaveNothing;
1897 m_player->readyStateChanged();
1901 void MediaPlayerPrivateGStreamer::setSize(const IntSize& size)
1906 void MediaPlayerPrivateGStreamer::setVisible(bool visible)
1910 void MediaPlayerPrivateGStreamer::triggerRepaint(GstBuffer* buffer)
1912 g_return_if_fail(GST_IS_BUFFER(buffer));
1913 gst_buffer_replace(&m_buffer, buffer);
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);
1922 m_player->repaint();
1925 void MediaPlayerPrivateGStreamer::paint(GraphicsContext* context, const IntRect& rect)
1927 if (context->paintingDisabled())
1930 if (!m_player->visible())
1936 GRefPtr<GstCaps> caps = webkitGstGetPadCaps(m_videoSinkPad.get());
1940 RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer, caps.get());
1944 context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB,
1945 rect, gstImage->rect(), CompositeCopy, DoNotRespectImageOrientation, false);
1948 static HashSet<String> mimeTypeCache()
1950 initializeGStreamerAndRegisterWebKitElements();
1952 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1953 static bool typeListInitialized = false;
1955 if (typeListInitialized)
1957 const char* mimeTypes[] = {
1959 "application/vnd.apple.mpegurl",
1960 "application/vnd.rn-realmedia",
1961 "application/x-3gp",
1962 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1963 "application/x-mpegurl",
1964 "application/x-mpegURL",
1966 "application/x-pn-realaudio",
1986 "audio/x-amr-nb-sh",
1987 "audio/x-amr-wb-sh",
2018 "audio/x-vorbis+ogg",
2022 "audio/x-wavpack-correction",
2031 #if !ENABLE(TIZEN_GSTREAMER_VIDEO)
2051 for (unsigned i = 0; i < (sizeof(mimeTypes) / sizeof(*mimeTypes)); ++i)
2052 cache.add(String(mimeTypes[i]));
2054 typeListInitialized = true;
2058 void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types)
2060 types = mimeTypeCache();
2063 MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String& type, const String& codecs, const KURL&)
2065 if (type.isNull() || type.isEmpty())
2066 return MediaPlayer::IsNotSupported;
2068 // spec says we should not return "probably" if the codecs string is empty
2069 if (mimeTypeCache().contains(type))
2070 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
2071 return MediaPlayer::IsNotSupported;
2074 bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const
2079 bool MediaPlayerPrivateGStreamer::supportsFullscreen() const
2081 #if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED == 1050
2082 // See <rdar://problem/7389945>
2089 PlatformMedia MediaPlayerPrivateGStreamer::platformMedia() const
2092 #ifndef GST_API_VERSION_1
2093 p.type = PlatformMedia::GStreamerGWorldType;
2094 p.media.gstreamerGWorld = m_gstGWorld.get();
2099 MediaPlayer::MovieLoadType MediaPlayerPrivateGStreamer::movieLoadType() const
2101 if (m_readyState == MediaPlayer::HaveNothing)
2102 return MediaPlayer::Unknown;
2105 return MediaPlayer::LiveStream;
2107 return MediaPlayer::Download;
2110 void MediaPlayerPrivateGStreamer::setDownloadBuffering()
2117 g_object_get(m_playBin.get(), "flags", &flags, NULL);
2118 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2119 flags = (GstPlayFlags)(flags | GST_PLAY_FLAG_NATIVE_VIDEO);
2121 if (m_preload == MediaPlayer::Auto) {
2122 LOG_MEDIA_MESSAGE("Enabling on-disk buffering");
2123 g_object_set(m_playBin.get(), "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL);
2125 LOG_MEDIA_MESSAGE("Disabling on-disk buffering");
2126 g_object_set(m_playBin.get(), "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL);
2130 void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload)
2132 m_originalPreloadWasAutoAndWasOverridden = m_preload != preload && m_preload == MediaPlayer::Auto;
2134 m_preload = preload;
2136 setDownloadBuffering();
2138 if (m_delayingLoad && m_preload != MediaPlayer::None) {
2139 m_delayingLoad = false;
2144 void MediaPlayerPrivateGStreamer::createAudioSink()
2146 // Construct audio sink if pitch preserving is enabled.
2147 if (!m_preservesPitch)
2153 GstElement* scale = gst_element_factory_make("scaletempo", 0);
2155 GST_WARNING("Failed to create scaletempo");
2159 GstElement* convert = gst_element_factory_make("audioconvert", 0);
2160 GstElement* resample = gst_element_factory_make("audioresample", 0);
2161 GstElement* sink = gst_element_factory_make("autoaudiosink", 0);
2163 GstElement* audioSink = gst_bin_new("audio-sink");
2164 gst_bin_add_many(GST_BIN(audioSink), scale, convert, resample, sink, NULL);
2166 if (!gst_element_link_many(scale, convert, resample, sink, NULL)) {
2167 GST_WARNING("Failed to link audio sink elements");
2168 gst_object_unref(audioSink);
2172 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink"));
2173 gst_element_add_pad(audioSink, gst_ghost_pad_new("sink", pad.get()));
2175 g_object_set(m_playBin.get(), "audio-sink", audioSink, NULL);
2178 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2179 GstElement* MediaPlayerPrivateGStreamer::createVideoSink()
2181 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2182 m_webkitVideoSink = m_videoLayer->createVideoSink();
2183 m_videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink, "sink"));
2185 // Primary video should be OFF and Secondary video should be FULL SCREEN mode
2186 g_object_set(m_webkitVideoSink, "display-mode", 2, NULL);
2187 #else // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2188 #ifndef GST_API_VERSION_1
2189 m_webkitVideoSink = webkitVideoSinkNew(m_gstGWorld.get());
2191 m_webkitVideoSink = webkitVideoSinkNew();
2193 m_videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink, "sink"));
2194 g_signal_connect(m_webkitVideoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this);
2195 #endif // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2197 #ifndef GST_API_VERSION_1
2198 m_videoSinkBin = gst_bin_new("video-sink");
2199 GstElement* videoTee = gst_element_factory_make("tee", "videoTee");
2200 #if !ENABLE(TIZEN_GSTREAMER_VIDEO)
2201 GstElement* queue = gst_element_factory_make("queue", 0);
2204 gst_object_ref_sink(m_videoSinkBin);
2206 // Build a new video sink consisting of a bin containing a tee
2207 // (meant to distribute data to multiple video sinks) and our
2208 // internal video sink. For fullscreen we create an autovideosink
2209 // and initially block the data flow towards it and configure it
2210 #if !ENABLE(TIZEN_GSTREAMER_VIDEO)
2211 gst_bin_add_many(GST_BIN(m_videoSinkBin), videoTee, queue, NULL);
2213 gst_bin_add_many(GST_BIN(m_videoSinkBin), videoTee, NULL);
2216 #if !ENABLE(TIZEN_GSTREAMER_VIDEO)
2217 // Link a new src pad from tee to queue1.
2218 gst_element_link_pads_full(videoTee, 0, queue, "sink", GST_PAD_LINK_CHECK_NOTHING);
2222 GstElement* actualVideoSink = 0;
2223 m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink");
2225 // The verbose property has been added in -bad 0.10.22. Making
2226 // this whole code depend on it because we don't want
2227 // fpsdiplaysink to spit data on stdout.
2228 GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(m_fpsSink)->elementfactory);
2229 if (gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 22)) {
2230 g_object_set(m_fpsSink, "silent", TRUE , NULL);
2232 // Turn off text overlay unless logging is enabled.
2234 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL);
2236 WTFLogChannel* channel = getChannelFromName("Media");
2237 if (channel->state != WTFLogChannelOn)
2238 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL);
2239 #endif // LOG_DISABLED
2241 if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) {
2242 g_object_set(m_fpsSink, "video-sink", m_webkitVideoSink, NULL);
2243 #ifndef GST_API_VERSION_1
2244 gst_bin_add(GST_BIN(m_videoSinkBin), m_fpsSink);
2246 actualVideoSink = m_fpsSink;
2254 #ifndef GST_API_VERSION_1
2255 gst_bin_add(GST_BIN(m_videoSinkBin), m_webkitVideoSink);
2257 actualVideoSink = m_webkitVideoSink;
2260 ASSERT(actualVideoSink);
2262 #ifndef GST_API_VERSION_1
2263 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2264 // Link a new src pad from tee to queue1.
2265 GRefPtr<GstPad> srcPad = adoptGRef(gst_element_get_request_pad(videoTee, "src%d"));
2266 GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(actualVideoSink, "sink"));
2267 gst_pad_link(srcPad.get(), sinkPad.get());
2269 // Faster elements linking.
2270 gst_element_link_pads_full(queue, "src", actualVideoSink, "sink", GST_PAD_LINK_CHECK_NOTHING);
2272 // Add a ghostpad to the bin so it can proxy to tee.
2273 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(videoTee, "sink"));
2274 gst_element_add_pad(m_videoSinkBin, gst_ghost_pad_new("sink", pad.get()));
2276 // Set the bin as video sink of playbin.
2277 return m_videoSinkBin;
2279 return actualVideoSink;
2284 void MediaPlayerPrivateGStreamer::createGSTPlayBin()
2288 // gst_element_factory_make() returns a floating reference so
2289 // we should not adopt.
2290 m_playBin = gst_element_factory_make(gPlaybinName, "play");
2292 #ifndef GST_API_VERSION_1
2293 m_gstGWorld = GStreamerGWorld::createGWorld(m_playBin.get());
2296 GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get()));
2297 gst_bus_add_signal_watch(bus.get());
2298 g_signal_connect(bus.get(), "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this);
2299 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER) && ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2300 gst_bus_set_sync_handler(bus.get(), GstBusSyncHandler(mediaPlayerPrivateSyncHandler), this);
2302 gst_object_unref(bus.get());
2304 g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL);
2306 g_signal_connect(m_playBin.get(), "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this);
2307 g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this);
2308 g_signal_connect(m_playBin.get(), "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this);
2309 g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this);
2310 g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this);
2311 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2312 g_signal_connect(m_playBin.get(), "source-setup", G_CALLBACK(mediaPlayerPrivateSourceSetupCallback), this);
2315 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2316 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(player()->mediaPlayerClient());
2317 if (element->isVideo()) {
2318 GstElement* videoElement = createVideoSink();
2319 g_object_set(m_playBin.get(), "video-sink", videoElement, NULL);
2321 GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink, "sink"));
2323 g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this);
2325 GstElement* fakeSink = gst_element_factory_make("fakesink", 0);
2326 g_object_set(fakeSink, "sync", true, NULL);
2327 g_object_set(m_playBin.get(), "video-sink", fakeSink, NULL);
2330 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
2331 GstElement* realSink = gst_element_factory_make("avsysaudiosink", 0);
2332 g_object_set(realSink, "close-handle-on-prepare", 1, NULL);
2333 g_object_set(m_playBin.get(), "audio-sink", realSink, NULL);
2335 if (m_audioSessionManager)
2336 m_audioSessionManager->registerAudioSessionManager(MM_SESSION_TYPE_SHARE, mediaPlayerPrivateAudioSessionNotifyCallback, player());
2342 #if ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
2343 bool MediaPlayerPrivateGStreamer::supportsAcceleratedRendering() const
2345 bool isEmbedVideo = false;
2346 if (m_player->mediaPlayerClient()) {
2347 Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
2348 if (document && document->settings()) {
2349 isEmbedVideo = document->settings()->acceleratedCompositingForVideoEnabled()
2350 && document->settings()->acceleratedCompositingEnabled();
2351 LOG_MEDIA_MESSAGE("isEmbedVideo: %d", isEmbedVideo);
2355 return isEmbedVideo;
2358 PlatformLayer* MediaPlayerPrivateGStreamer::platformLayer() const
2360 return m_videoLayer->platformLayer();
2363 #if ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2364 void MediaPlayerPrivateGStreamer::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
2366 m_videoLayer->paintCurrentFrameInContext(context, rect);
2369 #if USE(ACCELERATED_VIDEO_VAAPI)
2370 // Other device doesn't prefer such optimization, so enclose it in USE(ACCELERATED_VIDEO_VAAPI) macro
2371 IntSize MediaPlayerPrivateGStreamer::scaleHDVideoToDisplaySize(int videoWidth, int videoHeight, int displayWidth, int displayHeight) const
2373 int fitWidth, fitHeight;
2375 #if ENABLE(TIZEN_DLOG_SUPPORT)
2376 TIZEN_LOGI("videoWidth: %d, videoHeight: %d, displayWidth: %d, displayHeight: %d ", videoWidth, videoHeight, displayWidth, displayHeight);
2378 fitWidth = videoWidth;
2379 fitHeight = videoHeight;
2380 if (displayWidth > 0 && displayHeight > 0) {
2381 // In case video is rotated, we always use a 'better'
2382 // orientation (landscape or portrait) to calculate fitWidth/fitHeight.
2383 // It means we use a bigger size which doesn't lose quality
2384 // in either landscape or portrait orientation
2385 if ((videoWidth > videoHeight && displayWidth < displayHeight)
2386 || (videoWidth < videoHeight && displayWidth > displayHeight)) {
2389 displayWidth = displayHeight;
2390 displayHeight = tmp;
2393 if (videoWidth > displayWidth || videoHeight > displayHeight) {
2394 if (videoWidth * displayHeight > videoHeight * displayWidth) {
2395 fitWidth = displayWidth;
2396 fitHeight = videoHeight * displayWidth / videoWidth;
2397 ASSERT(fitHeight <= displayHeight);
2399 fitWidth = videoWidth * displayHeight / videoHeight;
2400 fitHeight = displayHeight;
2401 ASSERT(fitWidth <= displayWidth);
2406 return IntSize(fitWidth, fitHeight);
2409 void MediaPlayerPrivateGStreamer::xWindowIdPrepared(GstMessage* message)
2411 // It is called in streaming thread.
2412 // It should be used only to set window handle to video sink.
2414 int videoWidth, videoHeight;
2415 gst_structure_get_int(message->structure, "video-width", &videoWidth);
2416 gst_structure_get_int(message->structure, "video-height", &videoHeight);
2418 #if ENABLE(TIZEN_DLOG_SUPPORT)
2419 TIZEN_LOGI("videoWidth: %d, videoHeight: %d", videoWidth, videoHeight);
2422 #if USE(ACCELERATED_VIDEO_VAAPI)
2423 int displayWidth, displayHeight;
2424 gst_structure_get_int(message->structure, "display-width", &displayWidth);
2425 gst_structure_get_int(message->structure, "display-height", &displayHeight);
2427 m_displaySize = IntSize(displayWidth, displayHeight);
2428 m_videoSize = scaleHDVideoToDisplaySize(videoWidth, videoHeight, displayWidth, displayHeight);
2430 m_videoSize = IntSize(videoWidth, videoHeight);
2432 m_videoLayer->setOverlay(m_videoSize);
2435 #endif // ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
2436 #endif // ENABLE(TIZEN_ACCELERATED_COMPOSITING) && USE(TIZEN_TEXTURE_MAPPER)
2438 #if ENABLE(TIZEN_WEBKIT2_PROXY)
2439 void MediaPlayerPrivateGStreamer::setProxy(GstElement* source)
2441 SoupSession* session = WebCore::ResourceHandle::defaultSession();
2445 SoupURI* proxyUri = 0;
2446 g_object_get(session, SOUP_SESSION_PROXY_URI, &proxyUri, NULL);
2450 char* proxy = soup_uri_to_string(proxyUri, false);
2451 g_object_set(source, "proxy", proxy, NULL);
2453 soup_uri_free(proxyUri);
2458 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2459 void MediaPlayerPrivateGStreamer::suspend()
2461 m_suspendTime = currentTime();
2462 gst_element_set_state(m_playBin.get(), GST_STATE_NULL);
2463 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
2464 if (m_audioSessionManager)
2465 m_audioSessionManager->setSoundState(ASM_STATE_STOP);
2469 void MediaPlayerPrivateGStreamer::resume()
2471 if (isLocalMediaStream()) {
2472 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING);
2473 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
2474 if (m_audioSessionManager)
2475 m_audioSessionManager->setSoundState(ASM_STATE_PLAYING);
2478 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED);
2479 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
2480 if (m_audioSessionManager)
2481 m_audioSessionManager->setSoundState(ASM_STATE_PAUSE);
2486 bool MediaPlayerPrivateGStreamer::isLocalMediaStream()
2488 if (m_url.string().contains("camera://"))
2493 #endif // ENABLE(TIZEN_GSTREAMER_VIDEO)
2497 #endif // USE(GSTREAMER)