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 (at) atoker.com> 5 * Copyright (C) 2009 Gustavo Noronha Silva <gns (at) 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 "MediaPlayerPrivateGStreamer.h" 26 27 #if USE(GSTREAMER) 28 29 #include "ColorSpace.h" 30 #include "Document.h" 31 #include "Frame.h" 32 #include "FrameView.h" 33 #include "GOwnPtrGStreamer.h" 34 #include "GStreamerGWorld.h" 35 #include "GraphicsContext.h" 36 #include "GraphicsTypes.h" 37 #include "ImageGStreamer.h" 38 #include "IntRect.h" 39 #include "KURL.h" 40 #include "MIMETypeRegistry.h" 41 #include "MediaPlayer.h" 42 #include "NotImplemented.h" 43 #include "SecurityOrigin.h" 44 #include "TimeRanges.h" 45 #include "VideoSinkGStreamer.h" 46 #include "WebKitWebSourceGStreamer.h" 47 #include <GOwnPtr.h> 48 #include <gst/gst.h> 49 #include <gst/interfaces/streamvolume.h> 50 #include <gst/video/video.h> 51 #include <limits> 52 #include <math.h> 53 54 // GstPlayFlags flags from playbin2. It is the policy of GStreamer to 55 // not publicly expose element-specific enums. That's why this 56 // GstPlayFlags enum has been copied here. 57 typedef enum { 58 GST_PLAY_FLAG_VIDEO = 0x00000001, 59 GST_PLAY_FLAG_AUDIO = 0x00000002, 60 GST_PLAY_FLAG_TEXT = 0x00000004, 61 GST_PLAY_FLAG_VIS = 0x00000008, 62 GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, 63 GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, 64 GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, 65 GST_PLAY_FLAG_DOWNLOAD = 0x00000080, 66 GST_PLAY_FLAG_BUFFERING = 0x000000100 67 } GstPlayFlags; 68 69 using namespace std; 70 71 namespace WebCore { 72 73 static int greatestCommonDivisor(int a, int b) 74 { 75 while (b) { 76 int temp = a; 77 a = b; 78 b = temp % b; 79 } 80 81 return ABS(a); 82 } 83 84 static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player) 85 { 86 return player->handleMessage(message); 87 } 88 89 static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player) 90 { 91 player->sourceChanged(); 92 } 93 94 static void mediaPlayerPrivateVolumeChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player) 95 { 96 // This is called when playbin receives the notify::volume signal. 97 player->volumeChanged(); 98 } 99 100 static gboolean mediaPlayerPrivateVolumeChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) 101 { 102 // This is the callback of the timeout source created in ::volumeChanged. 103 player->notifyPlayerOfVolumeChange(); 104 return FALSE; 105 } 106 107 static void mediaPlayerPrivateMuteChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player) 108 { 109 // This is called when playbin receives the notify::mute signal. 110 player->muteChanged(); 111 } 112 113 static gboolean mediaPlayerPrivateMuteChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) 114 { 115 // This is the callback of the timeout source created in ::muteChanged. 116 player->notifyPlayerOfMute(); 117 return FALSE; 118 } 119 120 static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player) 121 { 122 player->videoChanged(); 123 } 124 125 static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player) 126 { 127 player->videoChanged(); 128 } 129 130 static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player) 131 { 132 player->audioChanged(); 133 } 134 135 static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) 136 { 137 // This is the callback of the timeout source created in ::audioChanged. 138 player->notifyPlayerOfAudio(); 139 return FALSE; 140 } 141 142 static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) 143 { 144 // This is the callback of the timeout source created in ::videoChanged. 145 player->notifyPlayerOfVideo(); 146 return FALSE; 147 } 148 149 static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivateGStreamer* playerPrivate) 150 { 151 playerPrivate->triggerRepaint(buffer); 152 } 153 154 MediaPlayerPrivateInterface* MediaPlayerPrivateGStreamer::create(MediaPlayer* player) 155 { 156 return new MediaPlayerPrivateGStreamer(player); 157 } 158 159 void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar) 160 { 161 if (isAvailable()) 162 registrar(create, getSupportedTypes, supportsType, 0, 0, 0); 163 } 164 165 static bool gstInitialized = false; 166 167 static bool doGstInit() 168 { 169 // FIXME: We should pass the arguments from the command line 170 if (!gstInitialized) { 171 GOwnPtr<GError> error; 172 gstInitialized = gst_init_check(0, 0, &error.outPtr()); 173 if (!gstInitialized) 174 LOG_VERBOSE(Media, "Could not initialize GStreamer: %s", 175 error ? error->message : "unknown error occurred"); 176 else 177 gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, 178 WEBKIT_TYPE_WEB_SRC); 179 } 180 return gstInitialized; 181 } 182 183 bool MediaPlayerPrivateGStreamer::isAvailable() 184 { 185 if (!doGstInit()) 186 return false; 187 188 GstElementFactory* factory = gst_element_factory_find("playbin2"); 189 if (factory) { 190 gst_object_unref(GST_OBJECT(factory)); 191 return true; 192 } 193 return false; 194 } 195 196 MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player) 197 : m_player(player) 198 , m_playBin(0) 199 , m_webkitVideoSink(0) 200 , m_fpsSink(0) 201 , m_source(0) 202 , m_seekTime(0) 203 , m_changingRate(false) 204 , m_endTime(numeric_limits<float>::infinity()) 205 , m_networkState(MediaPlayer::Empty) 206 , m_readyState(MediaPlayer::HaveNothing) 207 , m_isStreaming(false) 208 , m_size(IntSize()) 209 , m_buffer(0) 210 , m_mediaLocations(0) 211 , m_mediaLocationCurrentIndex(0) 212 , m_resetPipeline(false) 213 , m_paused(true) 214 , m_seeking(false) 215 , m_buffering(false) 216 , m_playbackRate(1) 217 , m_errorOccured(false) 218 , m_mediaDuration(0) 219 , m_startedBuffering(false) 220 , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired) 221 , m_maxTimeLoaded(0) 222 , m_bufferingPercentage(0) 223 , m_preload(MediaPlayer::Auto) 224 , m_delayingLoad(false) 225 , m_mediaDurationKnown(true) 226 , m_volumeTimerHandler(0) 227 , m_muteTimerHandler(0) 228 , m_hasVideo(false) 229 , m_hasAudio(false) 230 , m_audioTimerHandler(0) 231 , m_videoTimerHandler(0) 232 , m_webkitAudioSink(0) 233 { 234 if (doGstInit()) 235 createGSTPlayBin(); 236 } 237 238 MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer() 239 { 240 if (m_fillTimer.isActive()) 241 m_fillTimer.stop(); 242 243 if (m_buffer) 244 gst_buffer_unref(m_buffer); 245 m_buffer = 0; 246 247 if (m_mediaLocations) { 248 gst_structure_free(m_mediaLocations); 249 m_mediaLocations = 0; 250 } 251 252 if (m_source) { 253 gst_object_unref(m_source); 254 m_source = 0; 255 } 256 257 if (m_videoSinkBin) { 258 gst_object_unref(m_videoSinkBin); 259 m_videoSinkBin = 0; 260 } 261 262 if (m_playBin) { 263 gst_element_set_state(m_playBin, GST_STATE_NULL); 264 gst_object_unref(GST_OBJECT(m_playBin)); 265 m_playBin = 0; 266 } 267 268 m_player = 0; 269 270 if (m_muteTimerHandler) 271 g_source_remove(m_muteTimerHandler); 272 273 if (m_volumeTimerHandler) 274 g_source_remove(m_volumeTimerHandler); 275 276 if (m_videoTimerHandler) 277 g_source_remove(m_videoTimerHandler); 278 279 if (m_audioTimerHandler) 280 g_source_remove(m_audioTimerHandler); 281 } 282 283 void MediaPlayerPrivateGStreamer::load(const String& url) 284 { 285 g_object_set(m_playBin, "uri", url.utf8().data(), NULL); 286 287 LOG_VERBOSE(Media, "Load %s", url.utf8().data()); 288 289 if (m_preload == MediaPlayer::None) { 290 LOG_VERBOSE(Media, "Delaying load."); 291 m_delayingLoad = true; 292 } 293 294 // GStreamer needs to have the pipeline set to a paused state to 295 // start providing anything useful. 296 gst_element_set_state(m_playBin, GST_STATE_PAUSED); 297 298 if (!m_delayingLoad) 299 commitLoad(); 300 } 301 302 void MediaPlayerPrivateGStreamer::commitLoad() 303 { 304 ASSERT(!m_delayingLoad); 305 LOG_VERBOSE(Media, "Committing load."); 306 updateStates(); 307 } 308 309 float MediaPlayerPrivateGStreamer::playbackPosition() const 310 { 311 float ret = 0.0f; 312 313 GstQuery* query = gst_query_new_position(GST_FORMAT_TIME); 314 if (!gst_element_query(m_playBin, query)) { 315 LOG_VERBOSE(Media, "Position query failed..."); 316 gst_query_unref(query); 317 return ret; 318 } 319 320 gint64 position; 321 gst_query_parse_position(query, 0, &position); 322 323 // Position is available only if the pipeline is not in GST_STATE_NULL or 324 // GST_STATE_READY state. 325 if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE)) 326 ret = static_cast<float>(position) / static_cast<float>(GST_SECOND); 327 328 LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position)); 329 330 gst_query_unref(query); 331 332 return ret; 333 } 334 335 bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState) 336 { 337 ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED); 338 339 GstState currentState; 340 GstState pending; 341 342 gst_element_get_state(m_playBin, ¤tState, &pending, 0); 343 if (currentState != newState && pending != newState) { 344 GstStateChangeReturn ret = gst_element_set_state(m_playBin, newState); 345 GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING; 346 if (currentState != pausedOrPlaying && ret == GST_STATE_CHANGE_FAILURE) { 347 loadingFailed(MediaPlayer::Empty); 348 return false; 349 } 350 } 351 return true; 352 } 353 354 void MediaPlayerPrivateGStreamer::prepareToPlay() 355 { 356 if (m_delayingLoad) { 357 m_delayingLoad = false; 358 commitLoad(); 359 } 360 } 361 362 void MediaPlayerPrivateGStreamer::play() 363 { 364 if (changePipelineState(GST_STATE_PLAYING)) 365 LOG_VERBOSE(Media, "Play"); 366 } 367 368 void MediaPlayerPrivateGStreamer::pause() 369 { 370 if (changePipelineState(GST_STATE_PAUSED)) 371 LOG_VERBOSE(Media, "Pause"); 372 } 373 374 float MediaPlayerPrivateGStreamer::duration() const 375 { 376 if (!m_playBin) 377 return 0.0f; 378 379 if (m_errorOccured) 380 return 0.0f; 381 382 // Media duration query failed already, don't attempt new useless queries. 383 if (!m_mediaDurationKnown) 384 return numeric_limits<float>::infinity(); 385 386 if (m_mediaDuration) 387 return m_mediaDuration; 388 389 GstFormat timeFormat = GST_FORMAT_TIME; 390 gint64 timeLength = 0; 391 392 if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE) { 393 LOG_VERBOSE(Media, "Time duration query failed."); 394 return numeric_limits<float>::infinity(); 395 } 396 397 LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength)); 398 399 return (float) ((guint64) timeLength / 1000000000.0); 400 // FIXME: handle 3.14.9.5 properly 401 } 402 403 float MediaPlayerPrivateGStreamer::currentTime() const 404 { 405 if (!m_playBin) 406 return 0.0f; 407 408 if (m_errorOccured) 409 return 0.0f; 410 411 if (m_seeking) 412 return m_seekTime; 413 414 return playbackPosition(); 415 416 } 417 418 void MediaPlayerPrivateGStreamer::seek(float time) 419 { 420 // Avoid useless seeking. 421 if (time == playbackPosition()) 422 return; 423 424 if (!m_playBin) 425 return; 426 427 if (m_errorOccured) 428 return; 429 430 // Extract the integer part of the time (seconds) and the 431 // fractional part (microseconds). Attempt to round the 432 // microseconds so no floating point precision is lost and we can 433 // perform an accurate seek. 434 float seconds; 435 float microSeconds = modf(time, &seconds) * 1000000; 436 GTimeVal timeValue; 437 timeValue.tv_sec = static_cast<glong>(seconds); 438 timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000); 439 440 GstClockTime clockTime = GST_TIMEVAL_TO_TIME(timeValue); 441 LOG_VERBOSE(Media, "Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(clockTime)); 442 443 if (!gst_element_seek(m_playBin, m_player->rate(), 444 GST_FORMAT_TIME, 445 (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), 446 GST_SEEK_TYPE_SET, clockTime, 447 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) 448 LOG_VERBOSE(Media, "Seek to %f failed", time); 449 else { 450 m_seeking = true; 451 m_seekTime = time; 452 } 453 } 454 455 bool MediaPlayerPrivateGStreamer::paused() const 456 { 457 return m_paused; 458 } 459 460 bool MediaPlayerPrivateGStreamer::seeking() const 461 { 462 return m_seeking; 463 } 464 465 // Returns the size of the video 466 IntSize MediaPlayerPrivateGStreamer::naturalSize() const 467 { 468 if (!hasVideo()) 469 return IntSize(); 470 471 GstPad* pad = gst_element_get_static_pad(m_webkitVideoSink, "sink"); 472 if (!pad) 473 return IntSize(); 474 475 guint64 width = 0, height = 0; 476 GstCaps* caps = GST_PAD_CAPS(pad); 477 int pixelAspectRatioNumerator, pixelAspectRatioDenominator; 478 int displayWidth, displayHeight, displayAspectRatioGCD; 479 int originalWidth = 0, originalHeight = 0; 480 481 // TODO: handle possible clean aperture data. See 482 // https://bugzilla.gnome.org/show_bug.cgi?id=596571 483 // TODO: handle possible transformation matrix. See 484 // https://bugzilla.gnome.org/show_bug.cgi?id=596326 485 486 // Get the video PAR and original size. 487 if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps) 488 || !gst_video_format_parse_caps(caps, 0, &originalWidth, &originalHeight) 489 || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator, 490 &pixelAspectRatioDenominator)) { 491 gst_object_unref(GST_OBJECT(pad)); 492 // The video-sink has likely not yet negotiated its caps. 493 return IntSize(); 494 } 495 496 gst_object_unref(GST_OBJECT(pad)); 497 498 LOG_VERBOSE(Media, "Original video size: %dx%d", originalWidth, originalHeight); 499 LOG_VERBOSE(Media, "Pixel aspect ratio: %d/%d", pixelAspectRatioNumerator, pixelAspectRatioDenominator); 500 501 // Calculate DAR based on PAR and video size. 502 displayWidth = originalWidth * pixelAspectRatioNumerator; 503 displayHeight = originalHeight * pixelAspectRatioDenominator; 504 505 // Divide display width and height by their GCD to avoid possible overflows. 506 displayAspectRatioGCD = greatestCommonDivisor(displayWidth, displayHeight); 507 displayWidth /= displayAspectRatioGCD; 508 displayHeight /= displayAspectRatioGCD; 509 510 // Apply DAR to original video size. This is the same behavior as in xvimagesink's setcaps function. 511 if (!(originalHeight % displayHeight)) { 512 LOG_VERBOSE(Media, "Keeping video original height"); 513 width = gst_util_uint64_scale_int(originalHeight, displayWidth, displayHeight); 514 height = static_cast<guint64>(originalHeight); 515 } else if (!(originalWidth % displayWidth)) { 516 LOG_VERBOSE(Media, "Keeping video original width"); 517 height = gst_util_uint64_scale_int(originalWidth, displayHeight, displayWidth); 518 width = static_cast<guint64>(originalWidth); 519 } else { 520 LOG_VERBOSE(Media, "Approximating while keeping original video height"); 521 width = gst_util_uint64_scale_int(originalHeight, displayWidth, displayHeight); 522 height = static_cast<guint64>(originalHeight); 523 } 524 525 LOG_VERBOSE(Media, "Natural size: %" G_GUINT64_FORMAT "x%" G_GUINT64_FORMAT, width, height); 526 return IntSize(static_cast<int>(width), static_cast<int>(height)); 527 } 528 529 void MediaPlayerPrivateGStreamer::videoChanged() 530 { 531 if (m_videoTimerHandler) 532 g_source_remove(m_videoTimerHandler); 533 m_videoTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this); 534 } 535 536 void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo() 537 { 538 m_videoTimerHandler = 0; 539 540 gint videoTracks = 0; 541 if (m_playBin) 542 g_object_get(m_playBin, "n-video", &videoTracks, NULL); 543 544 m_hasVideo = videoTracks > 0; 545 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player); 546 } 547 548 void MediaPlayerPrivateGStreamer::audioChanged() 549 { 550 if (m_audioTimerHandler) 551 g_source_remove(m_audioTimerHandler); 552 m_audioTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this); 553 } 554 555 void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio() 556 { 557 m_audioTimerHandler = 0; 558 559 gint audioTracks = 0; 560 if (m_playBin) 561 g_object_get(m_playBin, "n-audio", &audioTracks, NULL); 562 m_hasAudio = audioTracks > 0; 563 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player); 564 } 565 566 void MediaPlayerPrivateGStreamer::setVolume(float volume) 567 { 568 if (!m_playBin) 569 return; 570 571 gst_stream_volume_set_volume(GST_STREAM_VOLUME(m_playBin), GST_STREAM_VOLUME_FORMAT_CUBIC, 572 static_cast<double>(volume)); 573 } 574 575 void MediaPlayerPrivateGStreamer::notifyPlayerOfVolumeChange() 576 { 577 m_volumeTimerHandler = 0; 578 579 if (!m_player || !m_playBin) 580 return; 581 double volume; 582 volume = gst_stream_volume_get_volume(GST_STREAM_VOLUME(m_playBin), GST_STREAM_VOLUME_FORMAT_CUBIC); 583 // get_volume() can return values superior to 1.0 if the user 584 // applies software user gain via third party application (GNOME 585 // volume control for instance). 586 volume = CLAMP(volume, 0.0, 1.0); 587 m_player->volumeChanged(static_cast<float>(volume)); 588 } 589 590 void MediaPlayerPrivateGStreamer::volumeChanged() 591 { 592 if (m_volumeTimerHandler) 593 g_source_remove(m_volumeTimerHandler); 594 m_volumeTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVolumeChangeTimeoutCallback), this); 595 } 596 597 void MediaPlayerPrivateGStreamer::setRate(float rate) 598 { 599 // Avoid useless playback rate update. 600 if (m_playbackRate == rate) 601 return; 602 603 GstState state; 604 GstState pending; 605 606 gst_element_get_state(m_playBin, &state, &pending, 0); 607 if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED) 608 || (pending == GST_STATE_PAUSED)) 609 return; 610 611 if (m_isStreaming) 612 return; 613 614 m_playbackRate = rate; 615 m_changingRate = true; 616 617 if (!rate) { 618 gst_element_set_state(m_playBin, GST_STATE_PAUSED); 619 return; 620 } 621 622 float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND); 623 GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH); 624 gint64 start, end; 625 bool mute = false; 626 627 LOG_VERBOSE(Media, "Set Rate to %f", rate); 628 if (rate > 0) { 629 // Mute the sound if the playback rate is too extreme. 630 // TODO: in other cases we should perform pitch adjustments. 631 mute = (bool) (rate < 0.8 || rate > 2); 632 start = currentPosition; 633 end = GST_CLOCK_TIME_NONE; 634 } else { 635 start = 0; 636 mute = true; 637 638 // If we are at beginning of media, start from the end to 639 // avoid immediate EOS. 640 if (currentPosition <= 0) 641 end = static_cast<gint64>(duration() * GST_SECOND); 642 else 643 end = currentPosition; 644 } 645 646 LOG_VERBOSE(Media, "Need to mute audio: %d", (int) mute); 647 648 if (!gst_element_seek(m_playBin, rate, GST_FORMAT_TIME, flags, 649 GST_SEEK_TYPE_SET, start, 650 GST_SEEK_TYPE_SET, end)) 651 LOG_VERBOSE(Media, "Set rate to %f failed", rate); 652 else 653 g_object_set(m_playBin, "mute", mute, NULL); 654 } 655 656 MediaPlayer::NetworkState MediaPlayerPrivateGStreamer::networkState() const 657 { 658 return m_networkState; 659 } 660 661 MediaPlayer::ReadyState MediaPlayerPrivateGStreamer::readyState() const 662 { 663 return m_readyState; 664 } 665 666 PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const 667 { 668 RefPtr<TimeRanges> timeRanges = TimeRanges::create(); 669 if (m_errorOccured || m_isStreaming) 670 return timeRanges.release(); 671 672 #if GST_CHECK_VERSION(0, 10, 31) 673 float mediaDuration(duration()); 674 if (!mediaDuration || isinf(mediaDuration)) 675 return timeRanges.release(); 676 677 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); 678 679 if (!gst_element_query(m_playBin, query)) { 680 gst_query_unref(query); 681 return timeRanges.release(); 682 } 683 684 gint64 rangeStart = 0, rangeStop = 0; 685 for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) { 686 if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop)) 687 timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / 100), 688 static_cast<float>((rangeStop * mediaDuration) / 100)); 689 } 690 691 // Fallback to the more general maxTimeLoaded() if no range has 692 // been found. 693 if (!timeRanges->length()) 694 if (float loaded = maxTimeLoaded()) 695 timeRanges->add(0, loaded); 696 697 gst_query_unref(query); 698 #else 699 float loaded = maxTimeLoaded(); 700 if (!m_errorOccured && !m_isStreaming && loaded > 0) 701 timeRanges->add(0, loaded); 702 #endif 703 return timeRanges.release(); 704 } 705 706 gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) 707 { 708 GOwnPtr<GError> err; 709 GOwnPtr<gchar> debug; 710 MediaPlayer::NetworkState error; 711 bool issueError = true; 712 bool attemptNextLocation = false; 713 714 if (message->structure) { 715 const gchar* messageTypeName = gst_structure_get_name(message->structure); 716 717 // Redirect messages are sent from elements, like qtdemux, to 718 // notify of the new location(s) of the media. 719 if (!g_strcmp0(messageTypeName, "redirect")) { 720 mediaLocationChanged(message); 721 return TRUE; 722 } 723 } 724 725 switch (GST_MESSAGE_TYPE(message)) { 726 case GST_MESSAGE_ERROR: 727 if (m_resetPipeline) 728 break; 729 gst_message_parse_error(message, &err.outPtr(), &debug.outPtr()); 730 LOG_VERBOSE(Media, "Error: %d, %s", err->code, err->message); 731 732 error = MediaPlayer::Empty; 733 if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND 734 || err->code == GST_STREAM_ERROR_WRONG_TYPE 735 || err->code == GST_STREAM_ERROR_FAILED 736 || err->code == GST_CORE_ERROR_MISSING_PLUGIN 737 || err->code == GST_RESOURCE_ERROR_NOT_FOUND) 738 error = MediaPlayer::FormatError; 739 else if (err->domain == GST_STREAM_ERROR) { 740 // Let the mediaPlayerClient handle the stream error, in 741 // this case the HTMLMediaElement will emit a stalled 742 // event. 743 if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) { 744 LOG_VERBOSE(Media, "Decode error, let the Media element emit a stalled event."); 745 break; 746 } 747 error = MediaPlayer::DecodeError; 748 attemptNextLocation = true; 749 } else if (err->domain == GST_RESOURCE_ERROR) 750 error = MediaPlayer::NetworkError; 751 752 if (attemptNextLocation) 753 issueError = !loadNextLocation(); 754 if (issueError) 755 loadingFailed(error); 756 break; 757 case GST_MESSAGE_EOS: 758 LOG_VERBOSE(Media, "End of Stream"); 759 didEnd(); 760 break; 761 case GST_MESSAGE_STATE_CHANGED: 762 // Ignore state changes if load is delayed (preload=none). The 763 // player state will be updated once commitLoad() is called. 764 if (m_delayingLoad) { 765 LOG_VERBOSE(Media, "Media load has been delayed. Ignoring state changes for now"); 766 break; 767 } 768 769 // Ignore state changes from internal elements. They are 770 // forwarded to playbin2 anyway. 771 if (GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin)) 772 updateStates(); 773 break; 774 case GST_MESSAGE_BUFFERING: 775 processBufferingStats(message); 776 break; 777 case GST_MESSAGE_DURATION: 778 LOG_VERBOSE(Media, "Duration changed"); 779 durationChanged(); 780 break; 781 default: 782 LOG_VERBOSE(Media, "Unhandled GStreamer message type: %s", 783 GST_MESSAGE_TYPE_NAME(message)); 784 break; 785 } 786 return TRUE; 787 } 788 789 void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message) 790 { 791 // This is the immediate buffering that needs to happen so we have 792 // enough to play right now. 793 m_buffering = true; 794 const GstStructure *structure = gst_message_get_structure(message); 795 gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage); 796 797 LOG_VERBOSE(Media, "[Buffering] Buffering: %d%%.", m_bufferingPercentage); 798 799 GstBufferingMode mode; 800 gst_message_parse_buffering_stats(message, &mode, 0, 0, 0); 801 if (mode != GST_BUFFERING_DOWNLOAD) { 802 updateStates(); 803 return; 804 } 805 806 // This is on-disk buffering, that allows us to download much more 807 // than needed for right now. 808 if (!m_startedBuffering) { 809 LOG_VERBOSE(Media, "[Buffering] Starting on-disk buffering."); 810 811 m_startedBuffering = true; 812 813 if (m_fillTimer.isActive()) 814 m_fillTimer.stop(); 815 816 m_fillTimer.startRepeating(0.2); 817 } 818 } 819 820 void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*) 821 { 822 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); 823 824 if (!gst_element_query(m_playBin, query)) { 825 gst_query_unref(query); 826 return; 827 } 828 829 gint64 start, stop; 830 gdouble fillStatus = 100.0; 831 832 gst_query_parse_buffering_range(query, 0, &start, &stop, 0); 833 gst_query_unref(query); 834 835 if (stop != -1) 836 fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX; 837 838 LOG_VERBOSE(Media, "[Buffering] Download buffer filled up to %f%%", fillStatus); 839 840 if (!m_mediaDuration) 841 durationChanged(); 842 843 // Update maxTimeLoaded only if the media duration is 844 // available. Otherwise we can't compute it. 845 if (m_mediaDuration) { 846 if (fillStatus == 100.0) 847 m_maxTimeLoaded = m_mediaDuration; 848 else 849 m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0); 850 LOG_VERBOSE(Media, "[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded); 851 } 852 853 if (fillStatus != 100.0) { 854 updateStates(); 855 return; 856 } 857 858 // Media is now fully loaded. It will play even if network 859 // connection is cut. Buffering is done, remove the fill source 860 // from the main loop. 861 m_fillTimer.stop(); 862 m_startedBuffering = false; 863 updateStates(); 864 } 865 866 float MediaPlayerPrivateGStreamer::maxTimeSeekable() const 867 { 868 if (m_errorOccured) 869 return 0.0f; 870 871 LOG_VERBOSE(Media, "maxTimeSeekable"); 872 // infinite duration means live stream 873 if (isinf(duration())) 874 return 0.0f; 875 876 return duration(); 877 } 878 879 float MediaPlayerPrivateGStreamer::maxTimeLoaded() const 880 { 881 if (m_errorOccured) 882 return 0.0f; 883 884 float loaded = m_maxTimeLoaded; 885 if (!loaded && !m_fillTimer.isActive()) 886 loaded = duration(); 887 LOG_VERBOSE(Media, "maxTimeLoaded: %f", loaded); 888 return loaded; 889 } 890 891 unsigned MediaPlayerPrivateGStreamer::bytesLoaded() const 892 { 893 if (!m_playBin) 894 return 0; 895 896 if (!m_mediaDuration) 897 return 0; 898 899 unsigned loaded = totalBytes() * maxTimeLoaded() / m_mediaDuration; 900 LOG_VERBOSE(Media, "bytesLoaded: %d", loaded); 901 return loaded; 902 } 903 904 unsigned MediaPlayerPrivateGStreamer::totalBytes() const 905 { 906 if (!m_source) 907 return 0; 908 909 if (m_errorOccured) 910 return 0; 911 912 GstFormat fmt = GST_FORMAT_BYTES; 913 gint64 length = 0; 914 if (gst_element_query_duration(m_source, &fmt, &length)) { 915 LOG_VERBOSE(Media, "totalBytes %" G_GINT64_FORMAT, length); 916 return static_cast<unsigned>(length); 917 } 918 919 // Fall back to querying the source pads manually. 920 // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749 921 GstIterator* iter = gst_element_iterate_src_pads(m_source); 922 bool done = false; 923 while (!done) { 924 gpointer data; 925 926 switch (gst_iterator_next(iter, &data)) { 927 case GST_ITERATOR_OK: { 928 GstPad* pad = GST_PAD_CAST(data); 929 gint64 padLength = 0; 930 if (gst_pad_query_duration(pad, &fmt, &padLength) 931 && padLength > length) 932 length = padLength; 933 gst_object_unref(pad); 934 break; 935 } 936 case GST_ITERATOR_RESYNC: 937 gst_iterator_resync(iter); 938 break; 939 case GST_ITERATOR_ERROR: 940 // Fall through. 941 case GST_ITERATOR_DONE: 942 done = true; 943 break; 944 } 945 } 946 gst_iterator_free(iter); 947 948 LOG_VERBOSE(Media, "totalBytes %" G_GINT64_FORMAT, length); 949 950 return static_cast<unsigned>(length); 951 } 952 953 unsigned MediaPlayerPrivateGStreamer::decodedFrameCount() const 954 { 955 guint64 decodedFrames = 0; 956 if (m_fpsSink) 957 g_object_get(m_fpsSink, "frames-rendered", &decodedFrames, NULL); 958 return static_cast<unsigned>(decodedFrames); 959 } 960 961 unsigned MediaPlayerPrivateGStreamer::droppedFrameCount() const 962 { 963 guint64 framesDropped = 0; 964 if (m_fpsSink) 965 g_object_get(m_fpsSink, "frames-dropped", &framesDropped, NULL); 966 return static_cast<unsigned>(framesDropped); 967 } 968 969 unsigned MediaPlayerPrivateGStreamer::audioDecodedByteCount() const 970 { 971 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES); 972 gint64 position = 0; 973 974 if (m_webkitAudioSink && gst_element_query(m_webkitAudioSink, query)) 975 gst_query_parse_position(query, 0, &position); 976 977 gst_query_unref(query); 978 return static_cast<unsigned>(position); 979 } 980 981 unsigned MediaPlayerPrivateGStreamer::videoDecodedByteCount() const 982 { 983 GstQuery* query = gst_query_new_position(GST_FORMAT_BYTES); 984 gint64 position = 0; 985 986 if (gst_element_query(m_webkitVideoSink, query)) 987 gst_query_parse_position(query, 0, &position); 988 989 gst_query_unref(query); 990 return static_cast<unsigned>(position); 991 } 992 993 void MediaPlayerPrivateGStreamer::updateAudioSink() 994 { 995 if (!m_playBin) 996 return; 997 998 GOwnPtr<GstElement> element; 999 1000 g_object_get(m_playBin, "audio-sink", &element.outPtr(), NULL); 1001 gst_object_replace(reinterpret_cast<GstObject**>(&m_webkitAudioSink), 1002 reinterpret_cast<GstObject*>(element.get())); 1003 } 1004 1005 1006 void MediaPlayerPrivateGStreamer::sourceChanged() 1007 { 1008 GOwnPtr<GstElement> element; 1009 1010 g_object_get(m_playBin, "source", &element.outPtr(), NULL); 1011 gst_object_replace(reinterpret_cast<GstObject**>(&m_source), 1012 reinterpret_cast<GstObject*>(element.get())); 1013 1014 if (WEBKIT_IS_WEB_SRC(element.get())) { 1015 Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0; 1016 1017 if (frame) 1018 webKitWebSrcSetFrame(WEBKIT_WEB_SRC(element.get()), frame); 1019 } 1020 } 1021 1022 void MediaPlayerPrivateGStreamer::cancelLoad() 1023 { 1024 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) 1025 return; 1026 1027 if (m_playBin) 1028 gst_element_set_state(m_playBin, GST_STATE_NULL); 1029 } 1030 1031 void MediaPlayerPrivateGStreamer::updateStates() 1032 { 1033 if (!m_playBin) 1034 return; 1035 1036 if (m_errorOccured) 1037 return; 1038 1039 MediaPlayer::NetworkState oldNetworkState = m_networkState; 1040 MediaPlayer::ReadyState oldReadyState = m_readyState; 1041 GstState state; 1042 GstState pending; 1043 1044 GstStateChangeReturn ret = gst_element_get_state(m_playBin, 1045 &state, &pending, 250 * GST_NSECOND); 1046 1047 bool shouldUpdateAfterSeek = false; 1048 switch (ret) { 1049 case GST_STATE_CHANGE_SUCCESS: 1050 LOG_VERBOSE(Media, "State: %s, pending: %s", 1051 gst_element_state_get_name(state), 1052 gst_element_state_get_name(pending)); 1053 1054 m_resetPipeline = state <= GST_STATE_READY; 1055 1056 // Try to figure out ready and network states. 1057 if (state == GST_STATE_READY) { 1058 m_readyState = MediaPlayer::HaveMetadata; 1059 m_networkState = MediaPlayer::Empty; 1060 // Cache the duration without emiting the durationchange 1061 // event because it's taken care of by the media element 1062 // in this precise case. 1063 cacheDuration(); 1064 } else if (maxTimeLoaded() == duration()) { 1065 m_networkState = MediaPlayer::Loaded; 1066 m_readyState = MediaPlayer::HaveEnoughData; 1067 } else { 1068 m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; 1069 m_networkState = MediaPlayer::Loading; 1070 } 1071 1072 if (m_buffering && state != GST_STATE_READY) { 1073 m_readyState = MediaPlayer::HaveCurrentData; 1074 m_networkState = MediaPlayer::Loading; 1075 } 1076 1077 // Now let's try to get the states in more detail using 1078 // information from GStreamer, while we sync states where 1079 // needed. 1080 if (state == GST_STATE_PAUSED) { 1081 if (!m_webkitAudioSink) 1082 updateAudioSink(); 1083 if (m_buffering && m_bufferingPercentage == 100) { 1084 m_buffering = false; 1085 m_bufferingPercentage = 0; 1086 m_readyState = MediaPlayer::HaveEnoughData; 1087 1088 LOG_VERBOSE(Media, "[Buffering] Complete."); 1089 1090 if (!m_paused) { 1091 LOG_VERBOSE(Media, "[Buffering] Restarting playback."); 1092 gst_element_set_state(m_playBin, GST_STATE_PLAYING); 1093 } 1094 } else if (!m_buffering && (currentTime() < duration())) { 1095 m_paused = true; 1096 } 1097 } else if (state == GST_STATE_PLAYING) { 1098 m_readyState = MediaPlayer::HaveEnoughData; 1099 m_paused = false; 1100 1101 if (m_buffering) { 1102 m_readyState = MediaPlayer::HaveCurrentData; 1103 m_networkState = MediaPlayer::Loading; 1104 1105 LOG_VERBOSE(Media, "[Buffering] Pausing stream for buffering."); 1106 1107 gst_element_set_state(m_playBin, GST_STATE_PAUSED); 1108 } 1109 } else 1110 m_paused = true; 1111 1112 // Is on-disk buffering in progress? 1113 if (m_fillTimer.isActive()) 1114 m_networkState = MediaPlayer::Loading; 1115 1116 if (m_changingRate) { 1117 m_player->rateChanged(); 1118 m_changingRate = false; 1119 } 1120 1121 if (m_seeking) { 1122 shouldUpdateAfterSeek = true; 1123 m_seeking = false; 1124 } 1125 1126 break; 1127 case GST_STATE_CHANGE_ASYNC: 1128 LOG_VERBOSE(Media, "Async: State: %s, pending: %s", 1129 gst_element_state_get_name(state), 1130 gst_element_state_get_name(pending)); 1131 // Change in progress 1132 1133 if (!m_isStreaming && !m_buffering) 1134 return; 1135 1136 if (m_seeking) { 1137 shouldUpdateAfterSeek = true; 1138 m_seeking = false; 1139 } 1140 break; 1141 case GST_STATE_CHANGE_FAILURE: 1142 LOG_VERBOSE(Media, "Failure: State: %s, pending: %s", 1143 gst_element_state_get_name(state), 1144 gst_element_state_get_name(pending)); 1145 // Change failed 1146 return; 1147 case GST_STATE_CHANGE_NO_PREROLL: 1148 LOG_VERBOSE(Media, "No preroll: State: %s, pending: %s", 1149 gst_element_state_get_name(state), 1150 gst_element_state_get_name(pending)); 1151 1152 if (state == GST_STATE_READY) 1153 m_readyState = MediaPlayer::HaveNothing; 1154 else if (state == GST_STATE_PAUSED) { 1155 m_readyState = MediaPlayer::HaveEnoughData; 1156 m_paused = true; 1157 // Live pipelines go in PAUSED without prerolling. 1158 m_isStreaming = true; 1159 } else if (state == GST_STATE_PLAYING) 1160 m_paused = false; 1161 1162 if (m_seeking) { 1163 shouldUpdateAfterSeek = true; 1164 m_seeking = false; 1165 if (!m_paused) 1166 gst_element_set_state(m_playBin, GST_STATE_PLAYING); 1167 } else if (!m_paused) 1168 gst_element_set_state(m_playBin, GST_STATE_PLAYING); 1169 1170 m_networkState = MediaPlayer::Loading; 1171 break; 1172 default: 1173 LOG_VERBOSE(Media, "Else : %d", ret); 1174 break; 1175 } 1176 1177 if (seeking()) 1178 m_readyState = MediaPlayer::HaveNothing; 1179 1180 if (shouldUpdateAfterSeek) 1181 timeChanged(); 1182 1183 if (m_networkState != oldNetworkState) { 1184 LOG_VERBOSE(Media, "Network State Changed from %u to %u", 1185 oldNetworkState, m_networkState); 1186 m_player->networkStateChanged(); 1187 } 1188 if (m_readyState != oldReadyState) { 1189 LOG_VERBOSE(Media, "Ready State Changed from %u to %u", 1190 oldReadyState, m_readyState); 1191 m_player->readyStateChanged(); 1192 } 1193 } 1194 1195 void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message) 1196 { 1197 if (m_mediaLocations) 1198 gst_structure_free(m_mediaLocations); 1199 1200 if (message->structure) { 1201 // This structure can contain: 1202 // - both a new-location string and embedded locations structure 1203 // - or only a new-location string. 1204 m_mediaLocations = gst_structure_copy(message->structure); 1205 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); 1206 1207 if (locations) 1208 m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1; 1209 1210 loadNextLocation(); 1211 } 1212 } 1213 1214 bool MediaPlayerPrivateGStreamer::loadNextLocation() 1215 { 1216 if (!m_mediaLocations) 1217 return false; 1218 1219 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); 1220 const gchar* newLocation = 0; 1221 1222 if (!locations) { 1223 // Fallback on new-location string. 1224 newLocation = gst_structure_get_string(m_mediaLocations, "new-location"); 1225 if (!newLocation) 1226 return false; 1227 } 1228 1229 if (!newLocation) { 1230 if (m_mediaLocationCurrentIndex < 0) { 1231 m_mediaLocations = 0; 1232 return false; 1233 } 1234 1235 const GValue* location = gst_value_list_get_value(locations, 1236 m_mediaLocationCurrentIndex); 1237 const GstStructure* structure = gst_value_get_structure(location); 1238 1239 if (!structure) { 1240 m_mediaLocationCurrentIndex--; 1241 return false; 1242 } 1243 1244 newLocation = gst_structure_get_string(structure, "new-location"); 1245 } 1246 1247 if (newLocation) { 1248 // Found a candidate. new-location is not always an absolute url 1249 // though. We need to take the base of the current url and 1250 // append the value of new-location to it. 1251 1252 gchar* currentLocation = 0; 1253 g_object_get(m_playBin, "uri", ¤tLocation, NULL); 1254 1255 KURL currentUrl(KURL(), currentLocation); 1256 g_free(currentLocation); 1257 1258 KURL newUrl; 1259 1260 if (gst_uri_is_valid(newLocation)) 1261 newUrl = KURL(KURL(), newLocation); 1262 else 1263 newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation); 1264 1265 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl); 1266 if (securityOrigin->canRequest(newUrl)) { 1267 LOG_VERBOSE(Media, "New media url: %s", newUrl.string().utf8().data()); 1268 1269 // Reset player states. 1270 m_networkState = MediaPlayer::Loading; 1271 m_player->networkStateChanged(); 1272 m_readyState = MediaPlayer::HaveNothing; 1273 m_player->readyStateChanged(); 1274 1275 // Reset pipeline state. 1276 m_resetPipeline = true; 1277 gst_element_set_state(m_playBin, GST_STATE_READY); 1278 1279 GstState state; 1280 gst_element_get_state(m_playBin, &state, 0, 0); 1281 if (state <= GST_STATE_READY) { 1282 // Set the new uri and start playing. 1283 g_object_set(m_playBin, "uri", newUrl.string().utf8().data(), NULL); 1284 gst_element_set_state(m_playBin, GST_STATE_PLAYING); 1285 return true; 1286 } 1287 } 1288 } 1289 m_mediaLocationCurrentIndex--; 1290 return false; 1291 1292 } 1293 1294 void MediaPlayerPrivateGStreamer::loadStateChanged() 1295 { 1296 updateStates(); 1297 } 1298 1299 void MediaPlayerPrivateGStreamer::sizeChanged() 1300 { 1301 notImplemented(); 1302 } 1303 1304 void MediaPlayerPrivateGStreamer::timeChanged() 1305 { 1306 updateStates(); 1307 m_player->timeChanged(); 1308 } 1309 1310 void MediaPlayerPrivateGStreamer::didEnd() 1311 { 1312 // EOS was reached but in case of reverse playback the position is 1313 // not always 0. So to not confuse the HTMLMediaElement we 1314 // synchronize position and duration values. 1315 float now = currentTime(); 1316 if (now > 0) { 1317 m_mediaDuration = now; 1318 m_mediaDurationKnown = true; 1319 m_player->durationChanged(); 1320 } 1321 1322 gst_element_set_state(m_playBin, GST_STATE_PAUSED); 1323 1324 timeChanged(); 1325 } 1326 1327 void MediaPlayerPrivateGStreamer::cacheDuration() 1328 { 1329 // Reset cached media duration 1330 m_mediaDuration = 0; 1331 1332 // And re-cache it if possible. 1333 GstState state; 1334 gst_element_get_state(m_playBin, &state, 0, 0); 1335 float newDuration = duration(); 1336 1337 if (state <= GST_STATE_READY) { 1338 // Don't set m_mediaDurationKnown yet if the pipeline is not 1339 // paused. This allows duration() query to fail at least once 1340 // before playback starts and duration becomes known. 1341 if (!isinf(newDuration)) 1342 m_mediaDuration = newDuration; 1343 } else { 1344 m_mediaDurationKnown = !isinf(newDuration); 1345 if (m_mediaDurationKnown) 1346 m_mediaDuration = newDuration; 1347 } 1348 1349 if (!isinf(newDuration)) 1350 m_mediaDuration = newDuration; 1351 } 1352 1353 void MediaPlayerPrivateGStreamer::durationChanged() 1354 { 1355 float previousDuration = m_mediaDuration; 1356 1357 cacheDuration(); 1358 // Avoid emiting durationchanged in the case where the previous 1359 // duration was 0 because that case is already handled by the 1360 // HTMLMediaElement. 1361 if (previousDuration && m_mediaDuration != previousDuration) 1362 m_player->durationChanged(); 1363 } 1364 1365 bool MediaPlayerPrivateGStreamer::supportsMuting() const 1366 { 1367 return true; 1368 } 1369 1370 void MediaPlayerPrivateGStreamer::setMuted(bool muted) 1371 { 1372 if (!m_playBin) 1373 return; 1374 1375 g_object_set(m_playBin, "mute", muted, NULL); 1376 } 1377 1378 void MediaPlayerPrivateGStreamer::notifyPlayerOfMute() 1379 { 1380 m_muteTimerHandler = 0; 1381 1382 if (!m_player || !m_playBin) 1383 return; 1384 1385 gboolean muted; 1386 g_object_get(m_playBin, "mute", &muted, NULL); 1387 m_player->muteChanged(static_cast<bool>(muted)); 1388 } 1389 1390 void MediaPlayerPrivateGStreamer::muteChanged() 1391 { 1392 if (m_muteTimerHandler) 1393 g_source_remove(m_muteTimerHandler); 1394 m_muteTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateMuteChangeTimeoutCallback), this); 1395 } 1396 1397 void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error) 1398 { 1399 m_errorOccured = true; 1400 if (m_networkState != error) { 1401 m_networkState = error; 1402 m_player->networkStateChanged(); 1403 } 1404 if (m_readyState != MediaPlayer::HaveNothing) { 1405 m_readyState = MediaPlayer::HaveNothing; 1406 m_player->readyStateChanged(); 1407 } 1408 } 1409 1410 void MediaPlayerPrivateGStreamer::setSize(const IntSize& size) 1411 { 1412 m_size = size; 1413 } 1414 1415 void MediaPlayerPrivateGStreamer::setVisible(bool visible) 1416 { 1417 } 1418 1419 1420 void MediaPlayerPrivateGStreamer::triggerRepaint(GstBuffer* buffer) 1421 { 1422 g_return_if_fail(GST_IS_BUFFER(buffer)); 1423 gst_buffer_replace(&m_buffer, buffer); 1424 m_player->repaint(); 1425 } 1426 1427 void MediaPlayerPrivateGStreamer::paint(GraphicsContext* context, const IntRect& rect) 1428 { 1429 if (context->paintingDisabled()) 1430 return; 1431 1432 if (!m_player->visible()) 1433 return; 1434 1435 if (!m_buffer) 1436 return; 1437 1438 RefPtr<ImageGStreamer> gstImage = ImageGStreamer::createImage(m_buffer); 1439 if (!gstImage) 1440 return; 1441 1442 context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB, 1443 rect, CompositeCopy, false); 1444 } 1445 1446 static HashSet<String> mimeTypeCache() 1447 { 1448 1449 doGstInit(); 1450 1451 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); 1452 static bool typeListInitialized = false; 1453 1454 if (!typeListInitialized) { 1455 // Build a whitelist of mime-types known to be supported by 1456 // GStreamer. 1457 HashSet<String> handledApplicationSubtypes; 1458 handledApplicationSubtypes.add(String("ogg")); 1459 handledApplicationSubtypes.add(String("vnd.rn-realmedia")); 1460 handledApplicationSubtypes.add(String("x-pn-realaudio")); 1461 1462 GList* factories = gst_type_find_factory_get_list(); 1463 for (GList* iterator = factories; iterator; iterator = iterator->next) { 1464 GstTypeFindFactory* factory = GST_TYPE_FIND_FACTORY(iterator->data); 1465 GstCaps* caps = gst_type_find_factory_get_caps(factory); 1466 gchar** extensions; 1467 1468 if (!caps) 1469 continue; 1470 1471 for (guint structureIndex = 0; structureIndex < gst_caps_get_size(caps); structureIndex++) { 1472 GstStructure* structure = gst_caps_get_structure(caps, structureIndex); 1473 const gchar* name = gst_structure_get_name(structure); 1474 bool cached = false; 1475 1476 // These formats are supported by GStreamer, but not 1477 // correctly advertised. 1478 if (g_str_equal(name, "video/x-h264")) { 1479 cache.add(String("video/mp4")); 1480 cached = true; 1481 } 1482 1483 if (g_str_equal(name, "audio/x-m4a")) { 1484 cache.add(String("audio/aac")); 1485 cache.add(String("audio/mp4")); 1486 cache.add(String("audio/x-m4a")); 1487 cached = true; 1488 } 1489 1490 if (g_str_equal(name, "application/x-3gp")) { 1491 cache.add(String("audio/3gpp")); 1492 cache.add(String("video/3gpp")); 1493 cache.add(String("application/x-3gp")); 1494 cached = true; 1495 } 1496 1497 if (g_str_equal(name, "video/x-theora")) { 1498 cache.add(String("video/ogg")); 1499 cached = true; 1500 } 1501 1502 if (g_str_equal(name, "audio/x-vorbis")) { 1503 cache.add(String("audio/ogg")); 1504 cache.add(String("audio/x-vorbis+ogg")); 1505 cached = true; 1506 } 1507 1508 if (g_str_equal(name, "audio/x-wav")) { 1509 cache.add(String("audio/wav")); 1510 cache.add(String("audio/x-wav")); 1511 cached = true; 1512 } 1513 1514 if (g_str_equal(name, "audio/mpeg")) { 1515 cache.add(String(name)); 1516 cache.add(String("audio/x-mpeg")); 1517 cached = true; 1518 1519 // This is what we are handling: 1520 // mpegversion=(int)1, layer=(int)[ 1, 3 ] 1521 gint mpegVersion = 0; 1522 if (gst_structure_get_int(structure, "mpegversion", &mpegVersion) && (mpegVersion == 1)) { 1523 const GValue* layer = gst_structure_get_value(structure, "layer"); 1524 if (G_VALUE_TYPE(layer) == GST_TYPE_INT_RANGE) { 1525 gint minLayer = gst_value_get_int_range_min(layer); 1526 gint maxLayer = gst_value_get_int_range_max(layer); 1527 if (minLayer <= 1 && 1 <= maxLayer) 1528 cache.add(String("audio/mp1")); 1529 if (minLayer <= 2 && 2 <= maxLayer) 1530 cache.add(String("audio/mp2")); 1531 if (minLayer <= 3 && 3 <= maxLayer) { 1532 cache.add(String("audio/x-mp3")); 1533 cache.add(String("audio/mp3")); 1534 } 1535 } 1536 } 1537 } 1538 1539 if (!cached) { 1540 // GStreamer plugins can be capable of supporting 1541 // types which WebKit supports by default. In that 1542 // case, we should not consider these types 1543 // supportable by GStreamer. Examples of what 1544 // GStreamer can support but should not be added: 1545 // text/plain, text/html, image/jpeg, 1546 // application/xml 1547 gchar** mimetype = g_strsplit(name, "/", 2); 1548 if (g_str_equal(mimetype[0], "audio") 1549 || g_str_equal(mimetype[0], "video") 1550 || (g_str_equal(mimetype[0], "application") 1551 && handledApplicationSubtypes.contains(String(mimetype[1])))) 1552 cache.add(String(name)); 1553 else if (g_str_equal(name, "application/x-hls")) 1554 cache.add(String("application/vnd.apple.mpegurl")); 1555 1556 1557 g_strfreev(mimetype); 1558 } 1559 1560 // As a last resort try some special cases depending 1561 // on the file extensions registered with the typefind 1562 // factory. 1563 if (!cached && (extensions = gst_type_find_factory_get_extensions(factory))) { 1564 for (int index = 0; extensions[index]; index++) { 1565 if (g_str_equal(extensions[index], "m4v")) 1566 cache.add(String("video/x-m4v")); 1567 1568 // Workaround for 1569 // https://bugzilla.gnome.org/show_bug.cgi?id=640709. 1570 // typefindfunctions <= 0.10.32 doesn't 1571 // register the H264 typefinder correctly so 1572 // as a workaround we check the registered 1573 // file extensions for it. 1574 if (g_str_equal(extensions[index], "h264")) 1575 cache.add(String("video/mp4")); 1576 } 1577 } 1578 } 1579 } 1580 1581 gst_plugin_feature_list_free(factories); 1582 typeListInitialized = true; 1583 } 1584 1585 return cache; 1586 } 1587 1588 void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types) 1589 { 1590 types = mimeTypeCache(); 1591 } 1592 1593 MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String& type, const String& codecs) 1594 { 1595 if (type.isNull() || type.isEmpty()) 1596 return MediaPlayer::IsNotSupported; 1597 1598 // spec says we should not return "probably" if the codecs string is empty 1599 if (mimeTypeCache().contains(type)) 1600 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; 1601 return MediaPlayer::IsNotSupported; 1602 } 1603 1604 bool MediaPlayerPrivateGStreamer::hasSingleSecurityOrigin() const 1605 { 1606 return true; 1607 } 1608 1609 bool MediaPlayerPrivateGStreamer::supportsFullscreen() const 1610 { 1611 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) 1612 // See <rdar://problem/7389945> 1613 return false; 1614 #else 1615 return true; 1616 #endif 1617 } 1618 1619 PlatformMedia MediaPlayerPrivateGStreamer::platformMedia() const 1620 { 1621 PlatformMedia p; 1622 p.type = PlatformMedia::GStreamerGWorldType; 1623 p.media.gstreamerGWorld = m_gstGWorld.get(); 1624 return p; 1625 } 1626 1627 void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload) 1628 { 1629 ASSERT(m_playBin); 1630 1631 m_preload = preload; 1632 1633 GstPlayFlags flags; 1634 g_object_get(m_playBin, "flags", &flags, NULL); 1635 if (preload == MediaPlayer::None) 1636 g_object_set(m_playBin, "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL); 1637 else 1638 g_object_set(m_playBin, "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL); 1639 1640 if (m_delayingLoad && m_preload != MediaPlayer::None) { 1641 m_delayingLoad = false; 1642 commitLoad(); 1643 } 1644 } 1645 1646 void MediaPlayerPrivateGStreamer::createGSTPlayBin() 1647 { 1648 ASSERT(!m_playBin); 1649 m_playBin = gst_element_factory_make("playbin2", "play"); 1650 1651 m_gstGWorld = GStreamerGWorld::createGWorld(m_playBin); 1652 1653 GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin)); 1654 gst_bus_add_signal_watch(bus); 1655 g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this); 1656 gst_object_unref(bus); 1657 1658 g_object_set(m_playBin, "mute", m_player->muted(), NULL); 1659 1660 g_signal_connect(m_playBin, "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this); 1661 g_signal_connect(m_playBin, "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this); 1662 g_signal_connect(m_playBin, "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this); 1663 g_signal_connect(m_playBin, "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this); 1664 g_signal_connect(m_playBin, "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this); 1665 1666 m_webkitVideoSink = webkit_video_sink_new(); 1667 1668 g_signal_connect(m_webkitVideoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this); 1669 1670 m_videoSinkBin = gst_bin_new("sink"); 1671 GstElement* videoTee = gst_element_factory_make("tee", "videoTee"); 1672 GstElement* queue = gst_element_factory_make("queue", 0); 1673 GstElement* identity = gst_element_factory_make("identity", "videoValve"); 1674 1675 // Take ownership. 1676 gst_object_ref_sink(m_videoSinkBin); 1677 1678 // Build a new video sink consisting of a bin containing a tee 1679 // (meant to distribute data to multiple video sinks) and our 1680 // internal video sink. For fullscreen we create an autovideosink 1681 // and initially block the data flow towards it and configure it 1682 1683 gst_bin_add_many(GST_BIN(m_videoSinkBin), videoTee, queue, identity, NULL); 1684 1685 // Link a new src pad from tee to queue1. 1686 GstPad* srcPad = gst_element_get_request_pad(videoTee, "src%d"); 1687 GstPad* sinkPad = gst_element_get_static_pad(queue, "sink"); 1688 gst_pad_link(srcPad, sinkPad); 1689 gst_object_unref(GST_OBJECT(srcPad)); 1690 gst_object_unref(GST_OBJECT(sinkPad)); 1691 1692 GstElement* actualVideoSink = 0; 1693 m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink"); 1694 if (m_fpsSink) { 1695 // The verbose property has been added in -bad 0.10.22. Making 1696 // this whole code depend on it because we don't want 1697 // fpsdiplaysink to spit data on stdout. 1698 GstElementFactory* factory = GST_ELEMENT_FACTORY(GST_ELEMENT_GET_CLASS(m_fpsSink)->elementfactory); 1699 if (gst_plugin_feature_check_version(GST_PLUGIN_FEATURE(factory), 0, 10, 22)) { 1700 g_object_set(m_fpsSink, "silent", TRUE , NULL); 1701 1702 // Turn off text overlay unless logging is enabled. 1703 WTFLogChannel* channel = getChannelFromName("Media"); 1704 if (channel->state != WTFLogChannelOn) 1705 g_object_set(m_fpsSink, "text-overlay", FALSE , NULL); 1706 1707 if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) { 1708 g_object_set(m_fpsSink, "video-sink", m_webkitVideoSink, NULL); 1709 gst_bin_add(GST_BIN(m_videoSinkBin), m_fpsSink); 1710 actualVideoSink = m_fpsSink; 1711 } else 1712 m_fpsSink = 0; 1713 } else 1714 m_fpsSink = 0; 1715 } 1716 1717 if (!m_fpsSink) { 1718 gst_bin_add(GST_BIN(m_videoSinkBin), m_webkitVideoSink); 1719 actualVideoSink = m_webkitVideoSink; 1720 } 1721 1722 ASSERT(actualVideoSink); 1723 #if GST_CHECK_VERSION(0, 10, 30) 1724 // Faster elements linking, if possible. 1725 gst_element_link_pads_full(queue, "src", identity, "sink", GST_PAD_LINK_CHECK_NOTHING); 1726 gst_element_link_pads_full(identity, "src", actualVideoSink, "sink", GST_PAD_LINK_CHECK_NOTHING); 1727 #else 1728 gst_element_link_many(queue, identity, actualVideoSink, NULL); 1729 #endif 1730 1731 // Add a ghostpad to the bin so it can proxy to tee. 1732 GstPad* pad = gst_element_get_static_pad(videoTee, "sink"); 1733 gst_element_add_pad(m_videoSinkBin, gst_ghost_pad_new("sink", pad)); 1734 gst_object_unref(GST_OBJECT(pad)); 1735 1736 // Set the bin as video sink of playbin. 1737 g_object_set(m_playBin, "video-sink", m_videoSinkBin, NULL); 1738 1739 1740 pad = gst_element_get_static_pad(m_webkitVideoSink, "sink"); 1741 if (pad) { 1742 g_signal_connect(pad, "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this); 1743 gst_object_unref(GST_OBJECT(pad)); 1744 } 1745 1746 } 1747 1748 } 1749 1750 #endif // USE(GSTREAMER) 1751