1/* 2 * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Collabora Ltd. All rights reserved. 4 * Copyright (C) 2007 Alp Toker <alp@atoker.com> 5 * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> 6 * Copyright (C) 2009, 2010 Igalia S.L 7 * 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. 12 * 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. 17 * 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. 22 */ 23 24#include "config.h" 25#include "MediaPlayerPrivateGStreamerBase.h" 26 27#if ENABLE(VIDEO) && USE(GSTREAMER) 28 29#include "ColorSpace.h" 30#include "FullscreenVideoControllerGStreamer.h" 31#include "GStreamerGWorld.h" 32#include "GStreamerUtilities.h" 33#include "GStreamerVersioning.h" 34#include "GraphicsContext.h" 35#include "GraphicsTypes.h" 36#include "ImageGStreamer.h" 37#include "ImageOrientation.h" 38#include "IntRect.h" 39#include "Logging.h" 40#include "MediaPlayer.h" 41#include "NotImplemented.h" 42#include "VideoSinkGStreamer.h" 43#include "WebKitWebSourceGStreamer.h" 44#include <gst/gst.h> 45#include <gst/video/video.h> 46#include <wtf/text/CString.h> 47 48#ifdef GST_API_VERSION_1 49#include <gst/audio/streamvolume.h> 50#else 51#include <gst/interfaces/streamvolume.h> 52#endif 53 54#if GST_CHECK_VERSION(1, 1, 0) && USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) 55#include "TextureMapperGL.h" 56#endif 57 58GST_DEBUG_CATEGORY(webkit_media_player_debug); 59#define GST_CAT_DEFAULT webkit_media_player_debug 60 61using namespace std; 62 63namespace WebCore { 64 65static int greatestCommonDivisor(int a, int b) 66{ 67 while (b) { 68 int temp = a; 69 a = b; 70 b = temp % b; 71 } 72 73 return ABS(a); 74} 75 76static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamerBase* player) 77{ 78 // This is called when m_volumeElement receives the notify::volume signal. 79 player->volumeChanged(); 80} 81 82static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamerBase* player) 83{ 84 // This is the callback of the timeout source created in ::volumeChanged. 85 player->notifyPlayerOfVolumeChange(); 86 return FALSE; 87} 88 89static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamerBase* player) 90{ 91 // This is called when m_volumeElement receives the notify::mute signal. 92 player->muteChanged(); 93} 94 95static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamerBase* player) 96{ 97 // This is the callback of the timeout source created in ::muteChanged. 98 player->notifyPlayerOfMute(); 99 return FALSE; 100} 101 102static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamerBase* playerPrivate) 103{ 104 playerPrivate->triggerRepaint(buffer); 105} 106 107MediaPlayerPrivateGStreamerBase::MediaPlayerPrivateGStreamerBase(MediaPlayer* player) 108 : m_player(player) 109 , m_fpsSink(0) 110 , m_readyState(MediaPlayer::HaveNothing) 111 , m_networkState(MediaPlayer::Empty) 112 , m_buffer(0) 113 , m_volumeTimerHandler(0) 114 , m_muteTimerHandler(0) 115 , m_repaintHandler(0) 116 , m_volumeSignalHandler(0) 117 , m_muteSignalHandler(0) 118#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) 119 , m_texture(0) 120#endif 121{ 122#if GLIB_CHECK_VERSION(2, 31, 0) 123 m_bufferMutex = WTF::fastNew<GMutex>(); 124 g_mutex_init(m_bufferMutex); 125#else 126 m_bufferMutex = g_mutex_new(); 127#endif 128} 129 130MediaPlayerPrivateGStreamerBase::~MediaPlayerPrivateGStreamerBase() 131{ 132 g_signal_handler_disconnect(m_webkitVideoSink.get(), m_repaintHandler); 133 134#if GLIB_CHECK_VERSION(2, 31, 0) 135 g_mutex_clear(m_bufferMutex); 136 WTF::fastDelete(m_bufferMutex); 137#else 138 g_mutex_free(m_bufferMutex); 139#endif 140 141 if (m_buffer) 142 gst_buffer_unref(m_buffer); 143 m_buffer = 0; 144 145 m_player = 0; 146 147 if (m_muteTimerHandler) 148 g_source_remove(m_muteTimerHandler); 149 150 if (m_volumeTimerHandler) 151 g_source_remove(m_volumeTimerHandler); 152 153 if (m_volumeSignalHandler) { 154 g_signal_handler_disconnect(m_volumeElement.get(), m_volumeSignalHandler); 155 m_volumeSignalHandler = 0; 156 } 157 158 if (m_muteSignalHandler) { 159 g_signal_handler_disconnect(m_volumeElement.get(), m_muteSignalHandler); 160 m_muteSignalHandler = 0; 161 } 162 163#if USE(NATIVE_FULLSCREEN_VIDEO) 164 if (m_fullscreenVideoController) 165 exitFullscreen(); 166#endif 167} 168 169// Returns the size of the video 170IntSize MediaPlayerPrivateGStreamerBase::naturalSize() const 171{ 172 if (!hasVideo()) 173 return IntSize(); 174 175 if (!m_videoSize.isEmpty()) 176 return m_videoSize; 177 178#ifdef GST_API_VERSION_1 179 /* FIXME this has a race with the pad setting caps as the buffer (m_buffer) 180 * and the caps won't match and might cause a crash. (In case a 181 * renegotiation happens) 182 */ 183 GRefPtr<GstCaps> caps = webkitGstGetPadCaps(m_videoSinkPad.get()); 184#else 185 g_mutex_lock(m_bufferMutex); 186 GRefPtr<GstCaps> caps = m_buffer ? GST_BUFFER_CAPS(m_buffer) : 0; 187 g_mutex_unlock(m_bufferMutex); 188#endif 189 if (!caps) 190 return IntSize(); 191 192 193 // TODO: handle possible clean aperture data. See 194 // https://bugzilla.gnome.org/show_bug.cgi?id=596571 195 // TODO: handle possible transformation matrix. See 196 // https://bugzilla.gnome.org/show_bug.cgi?id=596326 197 198 // Get the video PAR and original size, if this fails the 199 // video-sink has likely not yet negotiated its caps. 200 int pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride; 201 IntSize originalSize; 202 GstVideoFormat format; 203 if (!getVideoSizeAndFormatFromCaps(caps.get(), originalSize, format, pixelAspectRatioNumerator, pixelAspectRatioDenominator, stride)) 204 return IntSize(); 205 206 LOG_MEDIA_MESSAGE("Original video size: %dx%d", originalSize.width(), originalSize.height()); 207 LOG_MEDIA_MESSAGE("Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator); 208 209 // Calculate DAR based on PAR and video size. 210 int displayWidth = originalSize.width() * pixelAspectRatioNumerator; 211 int displayHeight = originalSize.height() * pixelAspectRatioDenominator; 212 213 // Divide display width and height by their GCD to avoid possible overflows. 214 int displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight); 215 displayWidth /= displayAspectRatioGCD; 216 displayHeight /= displayAspectRatioGCD; 217 218 // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function. 219 guint64 width = 0, height = 0; 220 if (!(originalSize.height() % displayHeight)) { 221 LOG_MEDIA_MESSAGE("Keeping video original height"); 222 width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight); 223 height = static_cast<guint64>(originalSize.height()); 224 } else if (!(originalSize.width() % displayWidth)) { 225 LOG_MEDIA_MESSAGE("Keeping video original width"); 226 height = gst_util_uint64_scale_int(originalSize.width(), displayHeight, displayWidth); 227 width = static_cast<guint64>(originalSize.width()); 228 } else { 229 LOG_MEDIA_MESSAGE("Approximating while keeping original video height"); 230 width = gst_util_uint64_scale_int(originalSize.height(), displayWidth, displayHeight); 231 height = static_cast<guint64>(originalSize.height()); 232 } 233 234 LOG_MEDIA_MESSAGE("Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height); 235 m_videoSize = IntSize(static_cast<int>(width), static_cast<int>(height)); 236 return m_videoSize; 237} 238 239void MediaPlayerPrivateGStreamerBase::setVolume(float volume) 240{ 241 if (!m_volumeElement) 242 return; 243 244 gst_stream_volume_set_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC, static_cast<double>(volume)); 245} 246 247float MediaPlayerPrivateGStreamerBase::volume() const 248{ 249 if (!m_volumeElement) 250 return 0; 251 252 return gst_stream_volume_get_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC); 253} 254 255 256void MediaPlayerPrivateGStreamerBase::notifyPlayerOfVolumeChange() 257{ 258 m_volumeTimerHandler = 0; 259 260 if (!m_player || !m_volumeElement) 261 return; 262 double volume; 263 volume = gst_stream_volume_get_volume(m_volumeElement.get(), GST_STREAM_VOLUME_FORMAT_CUBIC); 264 // get_volume() can return values superior to 1.0 if the user 265 // applies software user gain via third party application (GNOME 266 // volume control for instance). 267 volume = CLAMP(volume, 0.0, 1.0); 268 m_player->volumeChanged(static_cast<float>(volume)); 269} 270 271void MediaPlayerPrivateGStreamerBase::volumeChanged() 272{ 273 if (m_volumeTimerHandler) 274 g_source_remove(m_volumeTimerHandler); 275 m_volumeTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this); 276} 277 278MediaPlayer::NetworkState MediaPlayerPrivateGStreamerBase::networkState() const 279{ 280 return m_networkState; 281} 282 283MediaPlayer::ReadyState MediaPlayerPrivateGStreamerBase::readyState() const 284{ 285 return m_readyState; 286} 287 288void MediaPlayerPrivateGStreamerBase::sizeChanged() 289{ 290 notImplemented(); 291} 292 293void MediaPlayerPrivateGStreamerBase::setMuted(bool muted) 294{ 295 if (!m_volumeElement) 296 return; 297 298 g_object_set(m_volumeElement.get(), "mute", muted, NULL); 299} 300 301bool MediaPlayerPrivateGStreamerBase::muted() const 302{ 303 if (!m_volumeElement) 304 return false; 305 306 bool muted; 307 g_object_get(m_volumeElement.get(), "mute", &muted, NULL); 308 return muted; 309} 310 311void MediaPlayerPrivateGStreamerBase::notifyPlayerOfMute() 312{ 313 m_muteTimerHandler = 0; 314 315 if (!m_player || !m_volumeElement) 316 return; 317 318 gboolean muted; 319 g_object_get(m_volumeElement.get(), "mute", &muted, NULL); 320 m_player->muteChanged(static_cast<bool>(muted)); 321} 322 323void MediaPlayerPrivateGStreamerBase::muteChanged() 324{ 325 if (m_muteTimerHandler) 326 g_source_remove(m_muteTimerHandler); 327 m_muteTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this); 328} 329 330 331#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) 332void MediaPlayerPrivateGStreamerBase::updateTexture(GstBuffer* buffer) 333{ 334 if (!m_texture) 335 return; 336 337 if (!client()) 338 return; 339 340 const void* srcData = 0; 341 IntSize size = naturalSize(); 342 343 if (m_texture->size() != size) 344 m_texture->reset(size); 345 346#if GST_CHECK_VERSION(1, 1, 0) 347 GstVideoGLTextureUploadMeta* meta; 348 if ((meta = gst_buffer_get_video_gl_texture_upload_meta(buffer))) { 349 if (meta->n_textures == 1) { // BRGx & BGRA formats use only one texture. 350 const BitmapTextureGL* textureGL = static_cast<const BitmapTextureGL*>(m_texture.get()); 351 guint ids[4] = { textureGL->id(), 0, 0, 0 }; 352 353 if (gst_video_gl_texture_upload_meta_upload(meta, ids)) { 354 client()->setPlatformLayerNeedsDisplay(); 355 return; 356 } 357 } 358 } 359#endif 360 361#ifdef GST_API_VERSION_1 362 GstMapInfo srcInfo; 363 gst_buffer_map(buffer, &srcInfo, GST_MAP_READ); 364 srcData = srcInfo.data; 365#else 366 srcData = GST_BUFFER_DATA(buffer); 367#endif 368 369 m_texture->updateContents(srcData, WebCore::IntRect(WebCore::IntPoint(0, 0), size), WebCore::IntPoint(0, 0), size.width() * 4, BitmapTexture::UpdateCannotModifyOriginalImageData); 370 371#ifdef GST_API_VERSION_1 372 gst_buffer_unmap(buffer, &srcInfo); 373#endif 374 375 client()->setPlatformLayerNeedsDisplay(); 376} 377#endif 378 379void MediaPlayerPrivateGStreamerBase::triggerRepaint(GstBuffer* buffer) 380{ 381 g_return_if_fail(GST_IS_BUFFER(buffer)); 382 383#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) 384 if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) 385 updateTexture(buffer); 386 else 387#endif 388 { 389 g_mutex_lock(m_bufferMutex); 390 gst_buffer_replace(&m_buffer, buffer); 391 g_mutex_unlock(m_bufferMutex); 392 m_player->repaint(); 393 } 394} 395 396void MediaPlayerPrivateGStreamerBase::setSize(const IntSize& size) 397{ 398 m_size = size; 399} 400 401void MediaPlayerPrivateGStreamerBase::paint(GraphicsContext* context, const IntRect& rect) 402{ 403#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) 404 if (m_texture) 405 return; 406#endif 407 408 if (context->paintingDisabled()) 409 return; 410 411 if (!m_player->visible()) 412 return; 413 414 g_mutex_lock(m_bufferMutex); 415 if (!m_buffer) { 416 g_mutex_unlock(m_bufferMutex); 417 return; 418 } 419 420#ifdef GST_API_VERSION_1 421 /* FIXME this has a race with the pad setting caps as the buffer (m_buffer) 422 * and the caps won't match and might cause a crash. (In case a 423 * renegotiation happens) 424 */ 425 GRefPtr<GstCaps> caps = webkitGstGetPadCaps(m_videoSinkPad.get()); 426#else 427 GRefPtr<GstCaps> caps = GST_BUFFER_CAPS(m_buffer); 428#endif 429 if (!caps) { 430 g_mutex_unlock(m_bufferMutex); 431 return; 432 } 433 434 RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer, caps.get()); 435 if (!gstImage) { 436 g_mutex_unlock(m_bufferMutex); 437 return; 438 } 439 440 context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB, 441 rect, gstImage->rect(), CompositeCopy, DoNotRespectImageOrientation, false); 442 g_mutex_unlock(m_bufferMutex); 443} 444 445#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER_GL) && !USE(COORDINATED_GRAPHICS) 446void MediaPlayerPrivateGStreamerBase::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity) 447{ 448 if (textureMapper->accelerationMode() != TextureMapper::OpenGLMode) 449 return; 450 451 if (!m_texture) { 452 m_texture = textureMapper->acquireTextureFromPool(naturalSize()); 453 return; 454 } 455 456 textureMapper->drawTexture(*m_texture.get(), targetRect, matrix, opacity); 457} 458#endif 459 460#if USE(NATIVE_FULLSCREEN_VIDEO) 461void MediaPlayerPrivateGStreamerBase::enterFullscreen() 462{ 463 ASSERT(!m_fullscreenVideoController); 464 m_fullscreenVideoController = FullscreenVideoControllerGStreamer::create(this); 465 if (m_fullscreenVideoController) 466 m_fullscreenVideoController->enterFullscreen(); 467} 468 469void MediaPlayerPrivateGStreamerBase::exitFullscreen() 470{ 471 if (!m_fullscreenVideoController) 472 return; 473 m_fullscreenVideoController->exitFullscreen(); 474 m_fullscreenVideoController.release(); 475} 476#endif 477 478bool MediaPlayerPrivateGStreamerBase::supportsFullscreen() const 479{ 480 return true; 481} 482 483PlatformMedia MediaPlayerPrivateGStreamerBase::platformMedia() const 484{ 485#if USE(NATIVE_FULLSCREEN_VIDEO) 486 PlatformMedia p; 487 p.type = PlatformMedia::GStreamerGWorldType; 488 p.media.gstreamerGWorld = m_gstGWorld.get(); 489 return p; 490#else 491 return NoPlatformMedia; 492#endif 493} 494 495MediaPlayer::MovieLoadType MediaPlayerPrivateGStreamerBase::movieLoadType() const 496{ 497 if (m_readyState == MediaPlayer::HaveNothing) 498 return MediaPlayer::Unknown; 499 500 if (isLiveStream()) 501 return MediaPlayer::LiveStream; 502 503 return MediaPlayer::Download; 504} 505 506// This function creates and initializes some internal variables, and returns a 507// pointer to the element that should receive the data flow first 508GstElement* MediaPlayerPrivateGStreamerBase::createVideoSink(GstElement* pipeline) 509{ 510 if (!initializeGStreamer()) 511 return 0; 512 513#if USE(NATIVE_FULLSCREEN_VIDEO) 514 m_gstGWorld = GStreamerGWorld::createGWorld(pipeline); 515 m_webkitVideoSink = webkitVideoSinkNew(m_gstGWorld.get()); 516#else 517 UNUSED_PARAM(pipeline); 518 m_webkitVideoSink = webkitVideoSinkNew(); 519#endif 520 m_videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink")); 521 522 m_repaintHandler = g_signal_connect(m_webkitVideoSink.get(), "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this); 523 524#if USE(NATIVE_FULLSCREEN_VIDEO) 525 // Build a new video sink consisting of a bin containing a tee 526 // (meant to distribute data to multiple video sinks) and our 527 // internal video sink. For fullscreen we create an autovideosink 528 // and initially block the data flow towards it and configure it 529 530 m_videoSinkBin = gst_bin_new("video-sink"); 531 532 GstElement* videoTee = gst_element_factory_make("tee", "videoTee"); 533 GstElement* queue = gst_element_factory_make("queue", 0); 534 535#ifdef GST_API_VERSION_1 536 GRefPtr<GstPad> sinkPad = adoptGRef(gst_element_get_static_pad(videoTee, "sink")); 537 GST_OBJECT_FLAG_SET(GST_OBJECT(sinkPad.get()), GST_PAD_FLAG_PROXY_ALLOCATION); 538#endif 539 540 gst_bin_add_many(GST_BIN(m_videoSinkBin.get()), videoTee, queue, NULL); 541 542 // Link a new src pad from tee to queue1. 543 gst_element_link_pads_full(videoTee, 0, queue, "sink", GST_PAD_LINK_CHECK_NOTHING); 544#endif 545 546 GstElement* actualVideoSink = 0; 547 m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink"); 548 if (m_fpsSink) { 549 // The verbose property has been added in -bad 0.10.22. Making 550 // this whole code depend on it because we don't want 551 // fpsdiplaysink to spit data on stdout. 552 GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(m_fpsSink)->elementfactory); 553 if (gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 22)) { 554 g_object_set(m_fpsSink, "silent", TRUE , NULL); 555 556 // Turn off text overlay unless logging is enabled. 557#if LOG_DISABLED 558 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL); 559#else 560 WTFLogChannel* channel = getChannelFromName("Media"); 561 if (channel->state != WTFLogChannelOn) 562 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL); 563#endif // LOG_DISABLED 564 565 if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) { 566 g_object_set(m_fpsSink, "video-sink", m_webkitVideoSink.get(), NULL); 567#if USE(NATIVE_FULLSCREEN_VIDEO) 568 gst_bin_add(GST_BIN(m_videoSinkBin.get()), m_fpsSink); 569#endif 570 actualVideoSink = m_fpsSink; 571 } else 572 m_fpsSink = 0; 573 } else 574 m_fpsSink = 0; 575 } 576 577 if (!m_fpsSink) { 578#if USE(NATIVE_FULLSCREEN_VIDEO) 579 gst_bin_add(GST_BIN(m_videoSinkBin.get()), m_webkitVideoSink.get()); 580#endif 581 actualVideoSink = m_webkitVideoSink.get(); 582 } 583 584 ASSERT(actualVideoSink); 585 586#if USE(NATIVE_FULLSCREEN_VIDEO) 587 // Faster elements linking. 588 gst_element_link_pads_full(queue, "src", actualVideoSink, "sink", GST_PAD_LINK_CHECK_NOTHING); 589 590 // Add a ghostpad to the bin so it can proxy to tee. 591 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(videoTee, "sink")); 592 gst_element_add_pad(m_videoSinkBin.get(), gst_ghost_pad_new("sink", pad.get())); 593 594 // Set the bin as video sink of playbin. 595 return m_videoSinkBin.get(); 596#else 597 return actualVideoSink; 598#endif 599} 600 601void MediaPlayerPrivateGStreamerBase::setStreamVolumeElement(GstStreamVolume* volume) 602{ 603 ASSERT(!m_volumeElement); 604 m_volumeElement = volume; 605 606 g_object_set(m_volumeElement.get(), "mute", m_player->muted(), "volume", m_player->volume(), NULL); 607 608 m_volumeSignalHandler = g_signal_connect(m_volumeElement.get(), "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this); 609 m_muteSignalHandler = g_signal_connect(m_volumeElement.get(), "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this); 610} 611 612unsigned MediaPlayerPrivateGStreamerBase::decodedFrameCount() const 613{ 614 guint64 decodedFrames = 0; 615 if (m_fpsSink) 616 g_object_get(m_fpsSink, "frames-rendered", &decodedFrames, NULL); 617 return static_cast<unsigned>(decodedFrames); 618} 619 620unsigned MediaPlayerPrivateGStreamerBase::droppedFrameCount() const 621{ 622 guint64 framesDropped = 0; 623 if (m_fpsSink) 624 g_object_get(m_fpsSink, "frames-dropped", &framesDropped, NULL); 625 return static_cast<unsigned>(framesDropped); 626} 627 628unsigned MediaPlayerPrivateGStreamerBase::audioDecodedByteCount() const 629{ 630 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES); 631 gint64 position = 0; 632 633 if (audioSink() && gst_element_query(audioSink(), query)) 634 gst_query_parse_position(query, 0, &position); 635 636 gst_query_unref(query); 637 return static_cast<unsigned>(position); 638} 639 640unsigned MediaPlayerPrivateGStreamerBase::videoDecodedByteCount() const 641{ 642 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES); 643 gint64 position = 0; 644 645 if (gst_element_query(m_webkitVideoSink.get(), query)) 646 gst_query_parse_position(query, 0, &position); 647 648 gst_query_unref(query); 649 return static_cast<unsigned>(position); 650} 651 652} 653 654#endif // USE(GSTREAMER) 655