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, 2011, 2012, 2013 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 ENABLE(VIDEO) && USE(GSTREAMER) 28 29#include "GStreamerUtilities.h" 30#include "GStreamerVersioning.h" 31#include "KURL.h" 32#include "Logging.h" 33#include "MIMETypeRegistry.h" 34#include "MediaPlayer.h" 35#include "NotImplemented.h" 36#include "SecurityOrigin.h" 37#include "TimeRanges.h" 38#include "WebKitWebSourceGStreamer.h" 39#include <gst/gst.h> 40#include <gst/pbutils/missing-plugins.h> 41#include <limits> 42#include <wtf/gobject/GOwnPtr.h> 43#include <wtf/text/CString.h> 44 45#ifdef GST_API_VERSION_1 46#include <gst/audio/streamvolume.h> 47#else 48#include <gst/interfaces/streamvolume.h> 49#endif 50 51// GstPlayFlags flags from playbin2. It is the policy of GStreamer to 52// not publicly expose element-specific enums. That's why this 53// GstPlayFlags enum has been copied here. 54typedef enum { 55 GST_PLAY_FLAG_VIDEO = 0x00000001, 56 GST_PLAY_FLAG_AUDIO = 0x00000002, 57 GST_PLAY_FLAG_TEXT = 0x00000004, 58 GST_PLAY_FLAG_VIS = 0x00000008, 59 GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, 60 GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, 61 GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, 62 GST_PLAY_FLAG_DOWNLOAD = 0x00000080, 63 GST_PLAY_FLAG_BUFFERING = 0x000000100 64} GstPlayFlags; 65 66// gPercentMax is used when parsing buffering ranges with 67// gst_query_parse_nth_buffering_range as there was a bug in GStreamer 68// 0.10 that was using 100 instead of GST_FORMAT_PERCENT_MAX. This was 69// corrected in 1.0. gst_query_parse_buffering_range worked as 70// expected with GST_FORMAT_PERCENT_MAX in both cases. 71#ifdef GST_API_VERSION_1 72static const char* gPlaybinName = "playbin"; 73static const gint64 gPercentMax = GST_FORMAT_PERCENT_MAX; 74#else 75static const char* gPlaybinName = "playbin2"; 76static const gint64 gPercentMax = 100; 77#endif 78 79GST_DEBUG_CATEGORY_EXTERN(webkit_media_player_debug); 80#define GST_CAT_DEFAULT webkit_media_player_debug 81 82using namespace std; 83 84namespace WebCore { 85 86static gboolean mediaPlayerPrivateMessageCallback(GstBus*, GstMessage* message, MediaPlayerPrivateGStreamer* player) 87{ 88 return player->handleMessage(message); 89} 90 91static void mediaPlayerPrivateSourceChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player) 92{ 93 player->sourceChanged(); 94} 95 96static void mediaPlayerPrivateVideoSinkCapsChangedCallback(GObject*, GParamSpec*, MediaPlayerPrivateGStreamer* player) 97{ 98 player->videoChanged(); 99} 100 101static void mediaPlayerPrivateVideoChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player) 102{ 103 player->videoChanged(); 104} 105 106static void mediaPlayerPrivateAudioChangedCallback(GObject*, MediaPlayerPrivateGStreamer* player) 107{ 108 player->audioChanged(); 109} 110 111static gboolean mediaPlayerPrivateAudioChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) 112{ 113 // This is the callback of the timeout source created in ::audioChanged. 114 player->notifyPlayerOfAudio(); 115 return FALSE; 116} 117 118#ifdef GST_API_VERSION_1 119static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, gchar*, 120 MediaPlayerPrivateGStreamer* player) 121#else 122static void setAudioStreamPropertiesCallback(GstChildProxy*, GObject* object, MediaPlayerPrivateGStreamer* player) 123#endif 124{ 125 player->setAudioStreamProperties(object); 126} 127 128static gboolean mediaPlayerPrivateVideoChangeTimeoutCallback(MediaPlayerPrivateGStreamer* player) 129{ 130 // This is the callback of the timeout source created in ::videoChanged. 131 player->notifyPlayerOfVideo(); 132 return FALSE; 133} 134 135static void mediaPlayerPrivatePluginInstallerResultFunction(GstInstallPluginsReturn result, gpointer userData) 136{ 137 MediaPlayerPrivateGStreamer* player = reinterpret_cast<MediaPlayerPrivateGStreamer*>(userData); 138 player->handlePluginInstallerResult(result); 139} 140 141static GstClockTime toGstClockTime(float time) 142{ 143 // Extract the integer part of the time (seconds) and the fractional part (microseconds). Attempt to 144 // round the microseconds so no floating point precision is lost and we can perform an accurate seek. 145 float seconds; 146 float microSeconds = modf(time, &seconds) * 1000000; 147 GTimeVal timeValue; 148 timeValue.tv_sec = static_cast<glong>(seconds); 149 timeValue.tv_usec = static_cast<glong>(roundf(microSeconds / 10000) * 10000); 150 return GST_TIMEVAL_TO_TIME(timeValue); 151} 152 153void MediaPlayerPrivateGStreamer::setAudioStreamProperties(GObject* object) 154{ 155 if (g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink")) 156 return; 157 158 const char* role = m_player->mediaPlayerClient() && m_player->mediaPlayerClient()->mediaPlayerIsVideo() 159 ? "video" : "music"; 160 GstStructure* structure = gst_structure_new("stream-properties", "media.role", G_TYPE_STRING, role, NULL); 161 g_object_set(object, "stream-properties", structure, NULL); 162 gst_structure_free(structure); 163 GOwnPtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(object))); 164 LOG_MEDIA_MESSAGE("Set media.role as %s at %s", role, elementName.get()); 165} 166 167PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateGStreamer::create(MediaPlayer* player) 168{ 169 return adoptPtr(new MediaPlayerPrivateGStreamer(player)); 170} 171 172void MediaPlayerPrivateGStreamer::registerMediaEngine(MediaEngineRegistrar registrar) 173{ 174 if (isAvailable()) 175 registrar(create, getSupportedTypes, supportsType, 0, 0, 0); 176} 177 178bool initializeGStreamerAndRegisterWebKitElements() 179{ 180 if (!initializeGStreamer()) 181 return false; 182 183 GRefPtr<GstElementFactory> srcFactory = gst_element_factory_find("webkitwebsrc"); 184 if (!srcFactory) { 185 GST_DEBUG_CATEGORY_INIT(webkit_media_player_debug, "webkitmediaplayer", 0, "WebKit media player"); 186 return gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, WEBKIT_TYPE_WEB_SRC); 187 } 188 189 return true; 190} 191 192bool MediaPlayerPrivateGStreamer::isAvailable() 193{ 194 if (!initializeGStreamerAndRegisterWebKitElements()) 195 return false; 196 197 GRefPtr<GstElementFactory> factory = gst_element_factory_find(gPlaybinName); 198 return factory; 199} 200 201MediaPlayerPrivateGStreamer::MediaPlayerPrivateGStreamer(MediaPlayer* player) 202 : MediaPlayerPrivateGStreamerBase(player) 203 , m_source(0) 204 , m_seekTime(0) 205 , m_changingRate(false) 206 , m_endTime(numeric_limits<float>::infinity()) 207 , m_isEndReached(false) 208 , m_isStreaming(false) 209 , m_mediaLocations(0) 210 , m_mediaLocationCurrentIndex(0) 211 , m_resetPipeline(false) 212 , m_paused(true) 213 , m_seeking(false) 214 , m_seekIsPending(false) 215 , m_timeOfOverlappingSeek(-1) 216 , m_buffering(false) 217 , m_playbackRate(1) 218 , m_errorOccured(false) 219 , m_mediaDuration(0) 220 , m_startedBuffering(false) 221 , m_fillTimer(this, &MediaPlayerPrivateGStreamer::fillTimerFired) 222 , m_maxTimeLoaded(0) 223 , m_bufferingPercentage(0) 224 , m_preload(player->preload()) 225 , m_delayingLoad(false) 226 , m_mediaDurationKnown(true) 227 , m_maxTimeLoadedAtLastDidLoadingProgress(0) 228 , m_volumeAndMuteInitialized(false) 229 , m_hasVideo(false) 230 , m_hasAudio(false) 231 , m_audioTimerHandler(0) 232 , m_videoTimerHandler(0) 233 , m_webkitAudioSink(0) 234 , m_totalBytes(-1) 235 , m_preservesPitch(false) 236 , m_requestedState(GST_STATE_VOID_PENDING) 237 , m_missingPlugins(false) 238{ 239} 240 241MediaPlayerPrivateGStreamer::~MediaPlayerPrivateGStreamer() 242{ 243 if (m_fillTimer.isActive()) 244 m_fillTimer.stop(); 245 246 if (m_mediaLocations) { 247 gst_structure_free(m_mediaLocations); 248 m_mediaLocations = 0; 249 } 250 251 if (m_autoAudioSink) 252 g_signal_handlers_disconnect_by_func(G_OBJECT(m_autoAudioSink.get()), 253 reinterpret_cast<gpointer>(setAudioStreamPropertiesCallback), this); 254 255 if (m_playBin) { 256 GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get())); 257 ASSERT(bus); 258 g_signal_handlers_disconnect_by_func(bus.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateMessageCallback), this); 259 gst_bus_remove_signal_watch(bus.get()); 260 261 g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateSourceChangedCallback), this); 262 g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoChangedCallback), this); 263 g_signal_handlers_disconnect_by_func(m_playBin.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateAudioChangedCallback), this); 264 265 gst_element_set_state(m_playBin.get(), GST_STATE_NULL); 266 m_playBin.clear(); 267 } 268 269 GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink")); 270 g_signal_handlers_disconnect_by_func(videoSinkPad.get(), reinterpret_cast<gpointer>(mediaPlayerPrivateVideoSinkCapsChangedCallback), this); 271 272 if (m_videoTimerHandler) 273 g_source_remove(m_videoTimerHandler); 274 275 if (m_audioTimerHandler) 276 g_source_remove(m_audioTimerHandler); 277} 278 279void MediaPlayerPrivateGStreamer::load(const String& url) 280{ 281 if (!initializeGStreamerAndRegisterWebKitElements()) 282 return; 283 284 KURL kurl(KURL(), url); 285 String cleanUrl(url); 286 287 // Clean out everything after file:// url path. 288 if (kurl.isLocalFile()) 289 cleanUrl = cleanUrl.substring(0, kurl.pathEnd()); 290 291 if (!m_playBin) { 292 createGSTPlayBin(); 293 setDownloadBuffering(); 294 } 295 296 ASSERT(m_playBin); 297 298 m_url = KURL(KURL(), cleanUrl); 299 g_object_set(m_playBin.get(), "uri", cleanUrl.utf8().data(), NULL); 300 301 INFO_MEDIA_MESSAGE("Load %s", cleanUrl.utf8().data()); 302 303 if (m_preload == MediaPlayer::None) { 304 LOG_MEDIA_MESSAGE("Delaying load."); 305 m_delayingLoad = true; 306 } 307 308 // Reset network and ready states. Those will be set properly once 309 // the pipeline pre-rolled. 310 m_networkState = MediaPlayer::Loading; 311 m_player->networkStateChanged(); 312 m_readyState = MediaPlayer::HaveNothing; 313 m_player->readyStateChanged(); 314 m_volumeAndMuteInitialized = false; 315 316 if (!m_delayingLoad) 317 commitLoad(); 318} 319 320#if ENABLE(MEDIA_SOURCE) 321void MediaPlayerPrivateGStreamer::load(const String& url, PassRefPtr<MediaSource>) 322{ 323 notImplemented(); 324} 325#endif 326 327void MediaPlayerPrivateGStreamer::commitLoad() 328{ 329 ASSERT(!m_delayingLoad); 330 LOG_MEDIA_MESSAGE("Committing load."); 331 332 // GStreamer needs to have the pipeline set to a paused state to 333 // start providing anything useful. 334 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED); 335 336 updateStates(); 337} 338 339float MediaPlayerPrivateGStreamer::playbackPosition() const 340{ 341 if (m_isEndReached) { 342 // Position queries on a null pipeline return 0. If we're at 343 // the end of the stream the pipeline is null but we want to 344 // report either the seek time or the duration because this is 345 // what the Media element spec expects us to do. 346 if (m_seeking) 347 return m_seekTime; 348 if (m_mediaDuration) 349 return m_mediaDuration; 350 return 0; 351 } 352 353 // Position is only available if no async state change is going on and the state is either paused or playing. 354 gint64 position = GST_CLOCK_TIME_NONE; 355 GstQuery* query= gst_query_new_position(GST_FORMAT_TIME); 356 if (gst_element_query(m_playBin.get(), query)) 357 gst_query_parse_position(query, 0, &position); 358 359 float result = 0.0f; 360 if (static_cast<GstClockTime>(position) != GST_CLOCK_TIME_NONE) 361 result = static_cast<double>(position) / GST_SECOND; 362 else if (m_canFallBackToLastFinishedSeekPositon) 363 result = m_seekTime; 364 365 LOG_MEDIA_MESSAGE("Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position)); 366 367 gst_query_unref(query); 368 369 return result; 370} 371 372bool MediaPlayerPrivateGStreamer::changePipelineState(GstState newState) 373{ 374 ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED); 375 376 GstState currentState; 377 GstState pending; 378 379 gst_element_get_state(m_playBin.get(), ¤tState, &pending, 0); 380 if (currentState == newState || pending == newState) { 381 LOG_MEDIA_MESSAGE("Rejected state change to %s from %s with %s pending", gst_element_state_get_name(newState), 382 gst_element_state_get_name(currentState), gst_element_state_get_name(pending)); 383 return true; 384 } 385 386 LOG_MEDIA_MESSAGE("Changing state change to %s from %s with %s pending", gst_element_state_get_name(newState), 387 gst_element_state_get_name(currentState), gst_element_state_get_name(pending)); 388 389 GstStateChangeReturn setStateResult = gst_element_set_state(m_playBin.get(), newState); 390 GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING; 391 if (currentState != pausedOrPlaying && setStateResult == GST_STATE_CHANGE_FAILURE) { 392 loadingFailed(MediaPlayer::Empty); 393 return false; 394 } 395 return true; 396} 397 398void MediaPlayerPrivateGStreamer::prepareToPlay() 399{ 400 if (m_delayingLoad) { 401 m_delayingLoad = false; 402 commitLoad(); 403 } 404} 405 406void MediaPlayerPrivateGStreamer::play() 407{ 408 if (changePipelineState(GST_STATE_PLAYING)) { 409 m_isEndReached = false; 410 LOG_MEDIA_MESSAGE("Play"); 411 } 412} 413 414void MediaPlayerPrivateGStreamer::pause() 415{ 416 if (m_isEndReached) 417 return; 418 419 if (changePipelineState(GST_STATE_PAUSED)) 420 INFO_MEDIA_MESSAGE("Pause"); 421} 422 423float MediaPlayerPrivateGStreamer::duration() const 424{ 425 if (!m_playBin) 426 return 0.0f; 427 428 if (m_errorOccured) 429 return 0.0f; 430 431 // Media duration query failed already, don't attempt new useless queries. 432 if (!m_mediaDurationKnown) 433 return numeric_limits<float>::infinity(); 434 435 if (m_mediaDuration) 436 return m_mediaDuration; 437 438 GstFormat timeFormat = GST_FORMAT_TIME; 439 gint64 timeLength = 0; 440 441#ifdef GST_API_VERSION_1 442 bool failure = !gst_element_query_duration(m_playBin.get(), timeFormat, &timeLength) || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE; 443#else 444 bool failure = !gst_element_query_duration(m_playBin.get(), &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE; 445#endif 446 if (failure) { 447 LOG_MEDIA_MESSAGE("Time duration query failed for %s", m_url.string().utf8().data()); 448 return numeric_limits<float>::infinity(); 449 } 450 451 LOG_MEDIA_MESSAGE("Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength)); 452 453 m_mediaDuration = static_cast<double>(timeLength) / GST_SECOND; 454 return m_mediaDuration; 455 // FIXME: handle 3.14.9.5 properly 456} 457 458float MediaPlayerPrivateGStreamer::currentTime() const 459{ 460 if (!m_playBin) 461 return 0.0f; 462 463 if (m_errorOccured) 464 return 0.0f; 465 466 if (m_seeking) 467 return m_seekTime; 468 469 // Workaround for 470 // https://bugzilla.gnome.org/show_bug.cgi?id=639941 In GStreamer 471 // 0.10.35 basesink reports wrong duration in case of EOS and 472 // negative playback rate. There's no upstream accepted patch for 473 // this bug yet, hence this temporary workaround. 474 if (m_isEndReached && m_playbackRate < 0) 475 return 0.0f; 476 477 return playbackPosition(); 478} 479 480void MediaPlayerPrivateGStreamer::seek(float time) 481{ 482 if (!m_playBin) 483 return; 484 485 if (m_errorOccured) 486 return; 487 488 INFO_MEDIA_MESSAGE("[Seek] seek attempt to %f secs", time); 489 490 // Avoid useless seeking. 491 if (time == currentTime()) 492 return; 493 494 if (isLiveStream()) 495 return; 496 497 GstClockTime clockTime = toGstClockTime(time); 498 INFO_MEDIA_MESSAGE("[Seek] seeking to %" GST_TIME_FORMAT " (%f)", GST_TIME_ARGS(clockTime), time); 499 500 if (m_seeking) { 501 if (m_seekIsPending) { 502 m_seekTime = time; 503 return; 504 } 505 m_timeOfOverlappingSeek = time; 506 } 507 508 GstState state; 509 GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, 0, 0); 510 if (getStateResult == GST_STATE_CHANGE_FAILURE || getStateResult == GST_STATE_CHANGE_NO_PREROLL) { 511 LOG_MEDIA_MESSAGE("[Seek] cannot seek, current state change is %s", gst_element_state_change_return_get_name(getStateResult)); 512 return; 513 } 514 if (getStateResult == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED || m_isEndReached) { 515 m_seekIsPending = true; 516 if (m_isEndReached) { 517 LOG_MEDIA_MESSAGE("[Seek] reset pipeline"); 518 m_resetPipeline = true; 519 changePipelineState(GST_STATE_PAUSED); 520 } 521 } else { 522 // We can seek now. 523 if (!gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), 524 GST_SEEK_TYPE_SET, clockTime, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) { 525 LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", time); 526 return; 527 } 528 } 529 530 m_seeking = true; 531 m_seekTime = time; 532 m_isEndReached = false; 533} 534 535bool MediaPlayerPrivateGStreamer::paused() const 536{ 537 if (m_isEndReached) { 538 LOG_MEDIA_MESSAGE("Ignoring pause at EOS"); 539 return true; 540 } 541 542 GstState state; 543 gst_element_get_state(m_playBin.get(), &state, 0, 0); 544 return state == GST_STATE_PAUSED; 545} 546 547bool MediaPlayerPrivateGStreamer::seeking() const 548{ 549 return m_seeking; 550} 551 552void MediaPlayerPrivateGStreamer::videoChanged() 553{ 554 if (m_videoTimerHandler) 555 g_source_remove(m_videoTimerHandler); 556 m_videoTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateVideoChangeTimeoutCallback), this); 557} 558 559void MediaPlayerPrivateGStreamer::notifyPlayerOfVideo() 560{ 561 m_videoTimerHandler = 0; 562 563 gint videoTracks = 0; 564 if (m_playBin) 565 g_object_get(m_playBin.get(), "n-video", &videoTracks, NULL); 566 567 m_hasVideo = videoTracks > 0; 568 569 m_videoSize = IntSize(); 570 571 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player); 572} 573 574void MediaPlayerPrivateGStreamer::audioChanged() 575{ 576 if (m_audioTimerHandler) 577 g_source_remove(m_audioTimerHandler); 578 m_audioTimerHandler = g_timeout_add(0, reinterpret_cast<GSourceFunc>(mediaPlayerPrivateAudioChangeTimeoutCallback), this); 579} 580 581void MediaPlayerPrivateGStreamer::notifyPlayerOfAudio() 582{ 583 m_audioTimerHandler = 0; 584 585 gint audioTracks = 0; 586 if (m_playBin) 587 g_object_get(m_playBin.get(), "n-audio", &audioTracks, NULL); 588 m_hasAudio = audioTracks > 0; 589 m_player->mediaPlayerClient()->mediaPlayerEngineUpdated(m_player); 590} 591 592void MediaPlayerPrivateGStreamer::setRate(float rate) 593{ 594 // Avoid useless playback rate update. 595 if (m_playbackRate == rate) 596 return; 597 598 GstState state; 599 GstState pending; 600 601 gst_element_get_state(m_playBin.get(), &state, &pending, 0); 602 if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED) 603 || (pending == GST_STATE_PAUSED)) 604 return; 605 606 if (isLiveStream()) 607 return; 608 609 m_playbackRate = rate; 610 m_changingRate = true; 611 612 if (!rate) { 613 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED); 614 return; 615 } 616 617 float currentPosition = static_cast<float>(playbackPosition() * GST_SECOND); 618 GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH); 619 gint64 start, end; 620 bool mute = false; 621 622 INFO_MEDIA_MESSAGE("Set Rate to %f", rate); 623 if (rate > 0) { 624 // Mute the sound if the playback rate is too extreme and 625 // audio pitch is not adjusted. 626 mute = (!m_preservesPitch && (rate < 0.8 || rate > 2)); 627 start = currentPosition; 628 end = GST_CLOCK_TIME_NONE; 629 } else { 630 start = 0; 631 mute = true; 632 633 // If we are at beginning of media, start from the end to 634 // avoid immediate EOS. 635 if (currentPosition <= 0) 636 end = static_cast<gint64>(duration() * GST_SECOND); 637 else 638 end = currentPosition; 639 } 640 641 INFO_MEDIA_MESSAGE("Need to mute audio?: %d", (int) mute); 642 643 if (!gst_element_seek(m_playBin.get(), rate, GST_FORMAT_TIME, flags, 644 GST_SEEK_TYPE_SET, start, 645 GST_SEEK_TYPE_SET, end)) 646 ERROR_MEDIA_MESSAGE("Set rate to %f failed", rate); 647 else 648 g_object_set(m_playBin.get(), "mute", mute, NULL); 649} 650 651void MediaPlayerPrivateGStreamer::setPreservesPitch(bool preservesPitch) 652{ 653 m_preservesPitch = preservesPitch; 654} 655 656PassRefPtr<TimeRanges> MediaPlayerPrivateGStreamer::buffered() const 657{ 658 RefPtr<TimeRanges> timeRanges = TimeRanges::create(); 659 if (m_errorOccured || isLiveStream()) 660 return timeRanges.release(); 661 662#if GST_CHECK_VERSION(0, 10, 31) 663 float mediaDuration(duration()); 664 if (!mediaDuration || std::isinf(mediaDuration)) 665 return timeRanges.release(); 666 667 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); 668 669 if (!gst_element_query(m_playBin.get(), query)) { 670 gst_query_unref(query); 671 return timeRanges.release(); 672 } 673 674 for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) { 675 gint64 rangeStart = 0, rangeStop = 0; 676 if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop)) 677 timeRanges->add(static_cast<float>((rangeStart * mediaDuration) / gPercentMax), 678 static_cast<float>((rangeStop * mediaDuration) / gPercentMax)); 679 } 680 681 // Fallback to the more general maxTimeLoaded() if no range has 682 // been found. 683 if (!timeRanges->length()) 684 if (float loaded = maxTimeLoaded()) 685 timeRanges->add(0, loaded); 686 687 gst_query_unref(query); 688#else 689 float loaded = maxTimeLoaded(); 690 if (!m_errorOccured && !isLiveStream() && loaded > 0) 691 timeRanges->add(0, loaded); 692#endif 693 return timeRanges.release(); 694} 695 696gboolean MediaPlayerPrivateGStreamer::handleMessage(GstMessage* message) 697{ 698 GOwnPtr<GError> err; 699 GOwnPtr<gchar> debug; 700 MediaPlayer::NetworkState error; 701 bool issueError = true; 702 bool attemptNextLocation = false; 703 const GstStructure* structure = gst_message_get_structure(message); 704 GstState requestedState, currentState; 705 706 m_canFallBackToLastFinishedSeekPositon = false; 707 708 if (structure) { 709 const gchar* messageTypeName = gst_structure_get_name(structure); 710 711 // Redirect messages are sent from elements, like qtdemux, to 712 // notify of the new location(s) of the media. 713 if (!g_strcmp0(messageTypeName, "redirect")) { 714 mediaLocationChanged(message); 715 return TRUE; 716 } 717 } 718 719 // We ignore state changes from internal elements. They are forwarded to playbin2 anyway. 720 bool messageSourceIsPlaybin = GST_MESSAGE_SRC(message) == reinterpret_cast<GstObject*>(m_playBin.get()); 721 722 LOG_MEDIA_MESSAGE("Message %s received from element %s", GST_MESSAGE_TYPE_NAME(message), GST_MESSAGE_SRC_NAME(message)); 723 switch (GST_MESSAGE_TYPE(message)) { 724 case GST_MESSAGE_ERROR: 725 if (m_resetPipeline) 726 break; 727 if (m_missingPlugins) 728 break; 729 gst_message_parse_error(message, &err.outPtr(), &debug.outPtr()); 730 ERROR_MEDIA_MESSAGE("Error %d: %s (url=%s)", err->code, err->message, m_url.string().utf8().data()); 731 732 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, "webkit-video.error"); 733 734 error = MediaPlayer::Empty; 735 if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND 736 || err->code == GST_STREAM_ERROR_WRONG_TYPE 737 || err->code == GST_STREAM_ERROR_FAILED 738 || err->code == GST_CORE_ERROR_MISSING_PLUGIN 739 || err->code == GST_RESOURCE_ERROR_NOT_FOUND) 740 error = MediaPlayer::FormatError; 741 else if (err->domain == GST_STREAM_ERROR) { 742 // Let the mediaPlayerClient handle the stream error, in 743 // this case the HTMLMediaElement will emit a stalled 744 // event. 745 if (err->code == GST_STREAM_ERROR_TYPE_NOT_FOUND) { 746 ERROR_MEDIA_MESSAGE("Decode error, let the Media element emit a stalled event."); 747 break; 748 } 749 error = MediaPlayer::DecodeError; 750 attemptNextLocation = true; 751 } else if (err->domain == GST_RESOURCE_ERROR) 752 error = MediaPlayer::NetworkError; 753 754 if (attemptNextLocation) 755 issueError = !loadNextLocation(); 756 if (issueError) 757 loadingFailed(error); 758 break; 759 case GST_MESSAGE_EOS: 760 didEnd(); 761 break; 762 case GST_MESSAGE_ASYNC_DONE: 763 if (!messageSourceIsPlaybin || m_delayingLoad) 764 break; 765 asyncStateChangeDone(); 766 break; 767 case GST_MESSAGE_STATE_CHANGED: { 768 if (!messageSourceIsPlaybin || m_delayingLoad) 769 break; 770 updateStates(); 771 772 // Construct a filename for the graphviz dot file output. 773 GstState newState; 774 gst_message_parse_state_changed(message, ¤tState, &newState, 0); 775 CString dotFileName = String::format("webkit-video.%s_%s", gst_element_state_get_name(currentState), gst_element_state_get_name(newState)).utf8(); 776 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_playBin.get()), GST_DEBUG_GRAPH_SHOW_ALL, dotFileName.data()); 777 778 break; 779 } 780 case GST_MESSAGE_BUFFERING: 781 processBufferingStats(message); 782 break; 783#ifdef GST_API_VERSION_1 784 case GST_MESSAGE_DURATION_CHANGED: 785#else 786 case GST_MESSAGE_DURATION: 787#endif 788 if (messageSourceIsPlaybin) 789 durationChanged(); 790 break; 791 case GST_MESSAGE_REQUEST_STATE: 792 gst_message_parse_request_state(message, &requestedState); 793 gst_element_get_state(m_playBin.get(), ¤tState, NULL, 250); 794 if (requestedState < currentState) { 795 GOwnPtr<gchar> elementName(gst_element_get_name(GST_ELEMENT(message))); 796 INFO_MEDIA_MESSAGE("Element %s requested state change to %s", elementName.get(), 797 gst_element_state_get_name(requestedState)); 798 m_requestedState = requestedState; 799 changePipelineState(requestedState); 800 } 801 break; 802 case GST_MESSAGE_ELEMENT: 803 if (gst_is_missing_plugin_message(message)) { 804 gchar* detail = gst_missing_plugin_message_get_installer_detail(message); 805 gchar* detailArray[2] = {detail, 0}; 806 GstInstallPluginsReturn result = gst_install_plugins_async(detailArray, 0, mediaPlayerPrivatePluginInstallerResultFunction, this); 807 m_missingPlugins = result == GST_INSTALL_PLUGINS_STARTED_OK; 808 g_free(detail); 809 } 810 break; 811 default: 812 LOG_MEDIA_MESSAGE("Unhandled GStreamer message type: %s", 813 GST_MESSAGE_TYPE_NAME(message)); 814 break; 815 } 816 return TRUE; 817} 818 819void MediaPlayerPrivateGStreamer::handlePluginInstallerResult(GstInstallPluginsReturn result) 820{ 821 m_missingPlugins = false; 822 if (result == GST_INSTALL_PLUGINS_SUCCESS) { 823 gst_element_set_state(m_playBin.get(), GST_STATE_READY); 824 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED); 825 } 826} 827 828void MediaPlayerPrivateGStreamer::processBufferingStats(GstMessage* message) 829{ 830 // This is the immediate buffering that needs to happen so we have 831 // enough to play right now. 832 m_buffering = true; 833 const GstStructure *structure = gst_message_get_structure(message); 834 gst_structure_get_int(structure, "buffer-percent", &m_bufferingPercentage); 835 836 LOG_MEDIA_MESSAGE("[Buffering] Buffering: %d%%.", m_bufferingPercentage); 837 838 GstBufferingMode mode; 839 gst_message_parse_buffering_stats(message, &mode, 0, 0, 0); 840 if (mode != GST_BUFFERING_DOWNLOAD) { 841 updateStates(); 842 return; 843 } 844 845 // This is on-disk buffering, that allows us to download much more 846 // than needed for right now. 847 if (!m_startedBuffering) { 848 LOG_MEDIA_MESSAGE("[Buffering] Starting on-disk buffering."); 849 850 m_startedBuffering = true; 851 852 if (m_fillTimer.isActive()) 853 m_fillTimer.stop(); 854 855 m_fillTimer.startRepeating(0.2); 856 } 857 858 if (!m_paused && m_bufferingPercentage < 100) { 859 LOG_MEDIA_MESSAGE("[Buffering] Download in progress, pausing pipeline."); 860 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED); 861 } 862} 863 864void MediaPlayerPrivateGStreamer::fillTimerFired(Timer<MediaPlayerPrivateGStreamer>*) 865{ 866 GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); 867 868 if (!gst_element_query(m_playBin.get(), query)) { 869 gst_query_unref(query); 870 return; 871 } 872 873 gint64 start, stop; 874 gdouble fillStatus = 100.0; 875 876 gst_query_parse_buffering_range(query, 0, &start, &stop, 0); 877 gst_query_unref(query); 878 879 if (stop != -1) 880 fillStatus = 100.0 * stop / GST_FORMAT_PERCENT_MAX; 881 882 LOG_MEDIA_MESSAGE("[Buffering] Download buffer filled up to %f%%", fillStatus); 883 884 if (!m_mediaDuration) 885 durationChanged(); 886 887 // Update maxTimeLoaded only if the media duration is 888 // available. Otherwise we can't compute it. 889 if (m_mediaDuration) { 890 if (fillStatus == 100.0) 891 m_maxTimeLoaded = m_mediaDuration; 892 else 893 m_maxTimeLoaded = static_cast<float>((fillStatus * m_mediaDuration) / 100.0); 894 LOG_MEDIA_MESSAGE("[Buffering] Updated maxTimeLoaded: %f", m_maxTimeLoaded); 895 } 896 897 if (fillStatus != 100.0) { 898 updateStates(); 899 return; 900 } 901 902 // Media is now fully loaded. It will play even if network 903 // connection is cut. Buffering is done, remove the fill source 904 // from the main loop. 905 m_fillTimer.stop(); 906 m_startedBuffering = false; 907 updateStates(); 908} 909 910float MediaPlayerPrivateGStreamer::maxTimeSeekable() const 911{ 912 if (m_errorOccured) 913 return 0.0f; 914 915 LOG_MEDIA_MESSAGE("maxTimeSeekable"); 916 // infinite duration means live stream 917 if (std::isinf(duration())) 918 return 0.0f; 919 920 return duration(); 921} 922 923float MediaPlayerPrivateGStreamer::maxTimeLoaded() const 924{ 925 if (m_errorOccured) 926 return 0.0f; 927 928 float loaded = m_maxTimeLoaded; 929 if (!loaded && !m_fillTimer.isActive()) 930 loaded = duration(); 931 LOG_MEDIA_MESSAGE("maxTimeLoaded: %f", loaded); 932 return loaded; 933} 934 935bool MediaPlayerPrivateGStreamer::didLoadingProgress() const 936{ 937 if (!m_playBin || !m_mediaDuration || !totalBytes()) 938 return false; 939 float currentMaxTimeLoaded = maxTimeLoaded(); 940 bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress; 941 m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded; 942 LOG_MEDIA_MESSAGE("didLoadingProgress: %d", didLoadingProgress); 943 return didLoadingProgress; 944} 945 946unsigned MediaPlayerPrivateGStreamer::totalBytes() const 947{ 948 if (m_errorOccured) 949 return 0; 950 951 if (m_totalBytes != -1) 952 return m_totalBytes; 953 954 if (!m_source) 955 return 0; 956 957 GstFormat fmt = GST_FORMAT_BYTES; 958 gint64 length = 0; 959#ifdef GST_API_VERSION_1 960 if (gst_element_query_duration(m_source.get(), fmt, &length)) { 961#else 962 if (gst_element_query_duration(m_source.get(), &fmt, &length)) { 963#endif 964 INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length); 965 m_totalBytes = static_cast<unsigned>(length); 966 m_isStreaming = !length; 967 return m_totalBytes; 968 } 969 970 // Fall back to querying the source pads manually. 971 // See also https://bugzilla.gnome.org/show_bug.cgi?id=638749 972 GstIterator* iter = gst_element_iterate_src_pads(m_source.get()); 973 bool done = false; 974 while (!done) { 975#ifdef GST_API_VERSION_1 976 GValue item = G_VALUE_INIT; 977 switch (gst_iterator_next(iter, &item)) { 978 case GST_ITERATOR_OK: { 979 GstPad* pad = static_cast<GstPad*>(g_value_get_object(&item)); 980 gint64 padLength = 0; 981 if (gst_pad_query_duration(pad, fmt, &padLength) && padLength > length) 982 length = padLength; 983 break; 984 } 985#else 986 gpointer data; 987 988 switch (gst_iterator_next(iter, &data)) { 989 case GST_ITERATOR_OK: { 990 GRefPtr<GstPad> pad = adoptGRef(GST_PAD_CAST(data)); 991 gint64 padLength = 0; 992 if (gst_pad_query_duration(pad.get(), &fmt, &padLength) && padLength > length) 993 length = padLength; 994 break; 995 } 996#endif 997 case GST_ITERATOR_RESYNC: 998 gst_iterator_resync(iter); 999 break; 1000 case GST_ITERATOR_ERROR: 1001 // Fall through. 1002 case GST_ITERATOR_DONE: 1003 done = true; 1004 break; 1005 } 1006 1007#ifdef GST_API_VERSION_1 1008 g_value_unset(&item); 1009#endif 1010 } 1011 1012 gst_iterator_free(iter); 1013 1014 INFO_MEDIA_MESSAGE("totalBytes %" G_GINT64_FORMAT, length); 1015 m_totalBytes = static_cast<unsigned>(length); 1016 m_isStreaming = !length; 1017 return m_totalBytes; 1018} 1019 1020void MediaPlayerPrivateGStreamer::updateAudioSink() 1021{ 1022 if (!m_playBin) 1023 return; 1024 1025 GstElement* sinkPtr = 0; 1026 1027 g_object_get(m_playBin.get(), "audio-sink", &sinkPtr, NULL); 1028 m_webkitAudioSink = adoptGRef(sinkPtr); 1029 1030} 1031 1032GstElement* MediaPlayerPrivateGStreamer::audioSink() const 1033{ 1034 return m_webkitAudioSink.get(); 1035} 1036 1037void MediaPlayerPrivateGStreamer::sourceChanged() 1038{ 1039 GstElement* srcPtr = 0; 1040 1041 g_object_get(m_playBin.get(), "source", &srcPtr, NULL); 1042 m_source = adoptGRef(srcPtr); 1043 1044 if (WEBKIT_IS_WEB_SRC(m_source.get())) 1045 webKitWebSrcSetMediaPlayer(WEBKIT_WEB_SRC(m_source.get()), m_player); 1046} 1047 1048void MediaPlayerPrivateGStreamer::cancelLoad() 1049{ 1050 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) 1051 return; 1052 1053 if (m_playBin) 1054 gst_element_set_state(m_playBin.get(), GST_STATE_NULL); 1055} 1056 1057void MediaPlayerPrivateGStreamer::asyncStateChangeDone() 1058{ 1059 if (!m_playBin || m_errorOccured) 1060 return; 1061 1062 if (m_seeking) { 1063 if (m_seekIsPending) 1064 updateStates(); 1065 else { 1066 LOG_MEDIA_MESSAGE("[Seek] seeked to %f", m_seekTime); 1067 m_seeking = false; 1068 if (m_timeOfOverlappingSeek != -1) { 1069 seek(m_timeOfOverlappingSeek); 1070 m_timeOfOverlappingSeek = -1; 1071 return; 1072 } 1073 1074 // The pipeline can still have a pending state. In this case a position query will fail. 1075 // Right now we can use m_seekTime as a fallback. 1076 m_canFallBackToLastFinishedSeekPositon = true; 1077 timeChanged(); 1078 } 1079 } else 1080 updateStates(); 1081} 1082 1083void MediaPlayerPrivateGStreamer::updateStates() 1084{ 1085 if (!m_playBin) 1086 return; 1087 1088 if (m_errorOccured) 1089 return; 1090 1091 MediaPlayer::NetworkState oldNetworkState = m_networkState; 1092 MediaPlayer::ReadyState oldReadyState = m_readyState; 1093 GstState state; 1094 GstState pending; 1095 1096 GstStateChangeReturn getStateResult = gst_element_get_state(m_playBin.get(), &state, &pending, 250 * GST_NSECOND); 1097 1098 bool shouldUpdatePlaybackState = false; 1099 switch (getStateResult) { 1100 case GST_STATE_CHANGE_SUCCESS: 1101 LOG_MEDIA_MESSAGE("State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); 1102 1103 if (state <= GST_STATE_READY) { 1104 m_resetPipeline = true; 1105 m_mediaDuration = 0; 1106 } else 1107 cacheDuration(); 1108 1109 // Try to figure out ready and network states. 1110 if (state == GST_STATE_READY) { 1111 m_readyState = MediaPlayer::HaveMetadata; 1112 m_networkState = MediaPlayer::Empty; 1113 } else if ((state == GST_STATE_NULL) || (maxTimeLoaded() == duration())) { 1114 m_networkState = MediaPlayer::Loaded; 1115 m_readyState = MediaPlayer::HaveEnoughData; 1116 } else { 1117 m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; 1118 m_networkState = MediaPlayer::Loading; 1119 } 1120 1121 if (m_buffering && state != GST_STATE_READY) { 1122 m_readyState = MediaPlayer::HaveCurrentData; 1123 m_networkState = MediaPlayer::Loading; 1124 } 1125 1126 // Now let's try to get the states in more detail using 1127 // information from GStreamer, while we sync states where 1128 // needed. 1129 if (state == GST_STATE_PAUSED) { 1130 if (!m_webkitAudioSink) 1131 updateAudioSink(); 1132 1133 if (!m_volumeAndMuteInitialized) { 1134 notifyPlayerOfVolumeChange(); 1135 notifyPlayerOfMute(); 1136 m_volumeAndMuteInitialized = true; 1137 } 1138 1139 if (m_buffering && m_bufferingPercentage == 100) { 1140 m_buffering = false; 1141 m_bufferingPercentage = 0; 1142 m_readyState = MediaPlayer::HaveEnoughData; 1143 1144 LOG_MEDIA_MESSAGE("[Buffering] Complete."); 1145 1146 if (!m_paused) { 1147 LOG_MEDIA_MESSAGE("[Buffering] Restarting playback."); 1148 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING); 1149 } 1150 } else if (!m_buffering && (currentTime() < duration())) { 1151 m_paused = true; 1152 } 1153 } else if (state == GST_STATE_PLAYING) { 1154 m_readyState = MediaPlayer::HaveEnoughData; 1155 m_paused = false; 1156 1157 if (m_buffering && !isLiveStream()) { 1158 m_readyState = MediaPlayer::HaveCurrentData; 1159 m_networkState = MediaPlayer::Loading; 1160 1161 LOG_MEDIA_MESSAGE("[Buffering] Pausing stream for buffering."); 1162 1163 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED); 1164 } 1165 } else 1166 m_paused = true; 1167 1168 // Is on-disk buffering in progress? 1169 if (m_fillTimer.isActive()) 1170 m_networkState = MediaPlayer::Loading; 1171 1172 if (m_changingRate) { 1173 m_player->rateChanged(); 1174 m_changingRate = false; 1175 } 1176 1177 if (m_requestedState == GST_STATE_PAUSED && state == GST_STATE_PAUSED) { 1178 shouldUpdatePlaybackState = true; 1179 LOG_MEDIA_MESSAGE("Requested state change to %s was completed", gst_element_state_get_name(state)); 1180 } 1181 1182 break; 1183 case GST_STATE_CHANGE_ASYNC: 1184 LOG_MEDIA_MESSAGE("Async: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); 1185 // Change in progress 1186 1187 // On-disk buffering was attempted but the media is live. This 1188 // can't work so disable on-disk buffering and reset the 1189 // pipeline. 1190 if (state == GST_STATE_READY && isLiveStream() && m_preload == MediaPlayer::Auto) { 1191 setPreload(MediaPlayer::None); 1192 gst_element_set_state(m_playBin.get(), GST_STATE_NULL); 1193 gst_element_set_state(m_playBin.get(), GST_STATE_PAUSED); 1194 } 1195 1196 // A live stream was paused, reset the pipeline. 1197 if (state == GST_STATE_PAUSED && pending == GST_STATE_PLAYING && isLiveStream()) { 1198 gst_element_set_state(m_playBin.get(), GST_STATE_NULL); 1199 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING); 1200 } 1201 1202 break; 1203 case GST_STATE_CHANGE_FAILURE: 1204 LOG_MEDIA_MESSAGE("Failure: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); 1205 // Change failed 1206 return; 1207 case GST_STATE_CHANGE_NO_PREROLL: 1208 LOG_MEDIA_MESSAGE("No preroll: State: %s, pending: %s", gst_element_state_get_name(state), gst_element_state_get_name(pending)); 1209 1210 if (state == GST_STATE_READY) 1211 m_readyState = MediaPlayer::HaveNothing; 1212 else if (state == GST_STATE_PAUSED) { 1213 m_readyState = MediaPlayer::HaveEnoughData; 1214 m_paused = true; 1215 // Live pipelines go in PAUSED without prerolling. 1216 m_isStreaming = true; 1217 } else if (state == GST_STATE_PLAYING) 1218 m_paused = false; 1219 1220 if (!m_paused) 1221 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING); 1222 1223 m_networkState = MediaPlayer::Loading; 1224 break; 1225 default: 1226 LOG_MEDIA_MESSAGE("Else : %d", getStateResult); 1227 break; 1228 } 1229 1230 m_requestedState = GST_STATE_VOID_PENDING; 1231 1232 if (shouldUpdatePlaybackState) 1233 m_player->playbackStateChanged(); 1234 1235 if (m_networkState != oldNetworkState) { 1236 LOG_MEDIA_MESSAGE("Network State Changed from %u to %u", oldNetworkState, m_networkState); 1237 m_player->networkStateChanged(); 1238 } 1239 if (m_readyState != oldReadyState) { 1240 LOG_MEDIA_MESSAGE("Ready State Changed from %u to %u", oldReadyState, m_readyState); 1241 m_player->readyStateChanged(); 1242 } 1243 1244 if (m_seekIsPending && getStateResult == GST_STATE_CHANGE_SUCCESS && (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING)) { 1245 LOG_MEDIA_MESSAGE("[Seek] committing pending seek to %f", m_seekTime); 1246 m_seekIsPending = false; 1247 m_seeking = gst_element_seek(m_playBin.get(), m_player->rate(), GST_FORMAT_TIME, static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), 1248 GST_SEEK_TYPE_SET, toGstClockTime(m_seekTime), GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); 1249 if (!m_seeking) 1250 LOG_MEDIA_MESSAGE("[Seek] seeking to %f failed", m_seekTime); 1251 } 1252} 1253 1254void MediaPlayerPrivateGStreamer::mediaLocationChanged(GstMessage* message) 1255{ 1256 if (m_mediaLocations) 1257 gst_structure_free(m_mediaLocations); 1258 1259 const GstStructure* structure = gst_message_get_structure(message); 1260 if (structure) { 1261 // This structure can contain: 1262 // - both a new-location string and embedded locations structure 1263 // - or only a new-location string. 1264 m_mediaLocations = gst_structure_copy(structure); 1265 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); 1266 1267 if (locations) 1268 m_mediaLocationCurrentIndex = static_cast<int>(gst_value_list_get_size(locations)) -1; 1269 1270 loadNextLocation(); 1271 } 1272} 1273 1274bool MediaPlayerPrivateGStreamer::loadNextLocation() 1275{ 1276 if (!m_mediaLocations) 1277 return false; 1278 1279 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); 1280 const gchar* newLocation = 0; 1281 1282 if (!locations) { 1283 // Fallback on new-location string. 1284 newLocation = gst_structure_get_string(m_mediaLocations, "new-location"); 1285 if (!newLocation) 1286 return false; 1287 } 1288 1289 if (!newLocation) { 1290 if (m_mediaLocationCurrentIndex < 0) { 1291 m_mediaLocations = 0; 1292 return false; 1293 } 1294 1295 const GValue* location = gst_value_list_get_value(locations, 1296 m_mediaLocationCurrentIndex); 1297 const GstStructure* structure = gst_value_get_structure(location); 1298 1299 if (!structure) { 1300 m_mediaLocationCurrentIndex--; 1301 return false; 1302 } 1303 1304 newLocation = gst_structure_get_string(structure, "new-location"); 1305 } 1306 1307 if (newLocation) { 1308 // Found a candidate. new-location is not always an absolute url 1309 // though. We need to take the base of the current url and 1310 // append the value of new-location to it. 1311 KURL baseUrl = gst_uri_is_valid(newLocation) ? KURL() : m_url; 1312 KURL newUrl = KURL(baseUrl, newLocation); 1313 1314 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(m_url); 1315 if (securityOrigin->canRequest(newUrl)) { 1316 INFO_MEDIA_MESSAGE("New media url: %s", newUrl.string().utf8().data()); 1317 1318 // Reset player states. 1319 m_networkState = MediaPlayer::Loading; 1320 m_player->networkStateChanged(); 1321 m_readyState = MediaPlayer::HaveNothing; 1322 m_player->readyStateChanged(); 1323 1324 // Reset pipeline state. 1325 m_resetPipeline = true; 1326 gst_element_set_state(m_playBin.get(), GST_STATE_READY); 1327 1328 GstState state; 1329 gst_element_get_state(m_playBin.get(), &state, 0, 0); 1330 if (state <= GST_STATE_READY) { 1331 // Set the new uri and start playing. 1332 g_object_set(m_playBin.get(), "uri", newUrl.string().utf8().data(), NULL); 1333 m_url = newUrl; 1334 gst_element_set_state(m_playBin.get(), GST_STATE_PLAYING); 1335 return true; 1336 } 1337 } else 1338 INFO_MEDIA_MESSAGE("Not allowed to load new media location: %s", newUrl.string().utf8().data()); 1339 } 1340 m_mediaLocationCurrentIndex--; 1341 return false; 1342 1343} 1344 1345void MediaPlayerPrivateGStreamer::loadStateChanged() 1346{ 1347 updateStates(); 1348} 1349 1350void MediaPlayerPrivateGStreamer::timeChanged() 1351{ 1352 updateStates(); 1353 m_player->timeChanged(); 1354} 1355 1356void MediaPlayerPrivateGStreamer::didEnd() 1357{ 1358 // Synchronize position and duration values to not confuse the 1359 // HTMLMediaElement. In some cases like reverse playback the 1360 // position is not always reported as 0 for instance. 1361 float now = currentTime(); 1362 if (now > 0 && now <= duration() && m_mediaDuration != now) { 1363 m_mediaDurationKnown = true; 1364 m_mediaDuration = now; 1365 m_player->durationChanged(); 1366 } 1367 1368 m_isEndReached = true; 1369 timeChanged(); 1370 1371 if (!m_player->mediaPlayerClient()->mediaPlayerIsLooping()) { 1372 m_paused = true; 1373 gst_element_set_state(m_playBin.get(), GST_STATE_NULL); 1374 } 1375} 1376 1377void MediaPlayerPrivateGStreamer::cacheDuration() 1378{ 1379 if (m_mediaDuration || !m_mediaDurationKnown) 1380 return; 1381 1382 float newDuration = duration(); 1383 if (std::isinf(newDuration)) { 1384 // Only pretend that duration is not available if the the query failed in a stable pipeline state. 1385 GstState state; 1386 if (gst_element_get_state(m_playBin.get(), &state, 0, 0) == GST_STATE_CHANGE_SUCCESS && state > GST_STATE_READY) 1387 m_mediaDurationKnown = false; 1388 return; 1389 } 1390 1391 m_mediaDuration = newDuration; 1392} 1393 1394void MediaPlayerPrivateGStreamer::durationChanged() 1395{ 1396 float previousDuration = m_mediaDuration; 1397 1398 cacheDuration(); 1399 // Avoid emiting durationchanged in the case where the previous 1400 // duration was 0 because that case is already handled by the 1401 // HTMLMediaElement. 1402 if (previousDuration && m_mediaDuration != previousDuration) 1403 m_player->durationChanged(); 1404} 1405 1406void MediaPlayerPrivateGStreamer::loadingFailed(MediaPlayer::NetworkState error) 1407{ 1408 m_errorOccured = true; 1409 if (m_networkState != error) { 1410 m_networkState = error; 1411 m_player->networkStateChanged(); 1412 } 1413 if (m_readyState != MediaPlayer::HaveNothing) { 1414 m_readyState = MediaPlayer::HaveNothing; 1415 m_player->readyStateChanged(); 1416 } 1417} 1418 1419static HashSet<String> mimeTypeCache() 1420{ 1421 initializeGStreamerAndRegisterWebKitElements(); 1422 1423 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); 1424 static bool typeListInitialized = false; 1425 1426 if (typeListInitialized) 1427 return cache; 1428 1429 const char* mimeTypes[] = { 1430 "application/ogg", 1431 "application/vnd.apple.mpegurl", 1432 "application/vnd.rn-realmedia", 1433 "application/x-3gp", 1434 "application/x-pn-realaudio", 1435 "audio/3gpp", 1436 "audio/aac", 1437 "audio/flac", 1438 "audio/iLBC-sh", 1439 "audio/midi", 1440 "audio/mobile-xmf", 1441 "audio/mp1", 1442 "audio/mp2", 1443 "audio/mp3", 1444 "audio/mp4", 1445 "audio/mpeg", 1446 "audio/ogg", 1447 "audio/opus", 1448 "audio/qcelp", 1449 "audio/riff-midi", 1450 "audio/speex", 1451 "audio/wav", 1452 "audio/webm", 1453 "audio/x-ac3", 1454 "audio/x-aiff", 1455 "audio/x-amr-nb-sh", 1456 "audio/x-amr-wb-sh", 1457 "audio/x-au", 1458 "audio/x-ay", 1459 "audio/x-celt", 1460 "audio/x-dts", 1461 "audio/x-flac", 1462 "audio/x-gbs", 1463 "audio/x-gsm", 1464 "audio/x-gym", 1465 "audio/x-imelody", 1466 "audio/x-ircam", 1467 "audio/x-kss", 1468 "audio/x-m4a", 1469 "audio/x-mod", 1470 "audio/x-mp3", 1471 "audio/x-mpeg", 1472 "audio/x-musepack", 1473 "audio/x-nist", 1474 "audio/x-nsf", 1475 "audio/x-paris", 1476 "audio/x-sap", 1477 "audio/x-sbc", 1478 "audio/x-sds", 1479 "audio/x-shorten", 1480 "audio/x-sid", 1481 "audio/x-spc", 1482 "audio/x-speex", 1483 "audio/x-svx", 1484 "audio/x-ttafile", 1485 "audio/x-vgm", 1486 "audio/x-voc", 1487 "audio/x-vorbis+ogg", 1488 "audio/x-w64", 1489 "audio/x-wav", 1490 "audio/x-wavpack", 1491 "audio/x-wavpack-correction", 1492 "video/3gpp", 1493 "video/mj2", 1494 "video/mp4", 1495 "video/mpeg", 1496 "video/mpegts", 1497 "video/ogg", 1498 "video/quicktime", 1499 "video/vivo", 1500 "video/webm", 1501 "video/x-cdxa", 1502 "video/x-dirac", 1503 "video/x-dv", 1504 "video/x-fli", 1505 "video/x-flv", 1506 "video/x-h263", 1507 "video/x-ivf", 1508 "video/x-m4v", 1509 "video/x-matroska", 1510 "video/x-mng", 1511 "video/x-ms-asf", 1512 "video/x-msvideo", 1513 "video/x-mve", 1514 "video/x-nuv", 1515 "video/x-vcd" 1516 }; 1517 1518 for (unsigned i = 0; i < (sizeof(mimeTypes) / sizeof(*mimeTypes)); ++i) 1519 cache.add(String(mimeTypes[i])); 1520 1521 typeListInitialized = true; 1522 return cache; 1523} 1524 1525void MediaPlayerPrivateGStreamer::getSupportedTypes(HashSet<String>& types) 1526{ 1527 types = mimeTypeCache(); 1528} 1529 1530MediaPlayer::SupportsType MediaPlayerPrivateGStreamer::supportsType(const String& type, const String& codecs, const KURL&) 1531{ 1532 if (type.isNull() || type.isEmpty()) 1533 return MediaPlayer::IsNotSupported; 1534 1535 // spec says we should not return "probably" if the codecs string is empty 1536 if (mimeTypeCache().contains(type)) 1537 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; 1538 return MediaPlayer::IsNotSupported; 1539} 1540 1541void MediaPlayerPrivateGStreamer::setDownloadBuffering() 1542{ 1543 if (!m_playBin) 1544 return; 1545 1546 GstPlayFlags flags; 1547 g_object_get(m_playBin.get(), "flags", &flags, NULL); 1548 if (m_preload == MediaPlayer::Auto) { 1549 LOG_MEDIA_MESSAGE("Enabling on-disk buffering"); 1550 g_object_set(m_playBin.get(), "flags", flags | GST_PLAY_FLAG_DOWNLOAD, NULL); 1551 } else { 1552 LOG_MEDIA_MESSAGE("Disabling on-disk buffering"); 1553 g_object_set(m_playBin.get(), "flags", flags & ~GST_PLAY_FLAG_DOWNLOAD, NULL); 1554 } 1555} 1556 1557void MediaPlayerPrivateGStreamer::setPreload(MediaPlayer::Preload preload) 1558{ 1559 if (preload == MediaPlayer::Auto && isLiveStream()) 1560 return; 1561 1562 m_preload = preload; 1563 1564 setDownloadBuffering(); 1565 1566 if (m_delayingLoad && m_preload != MediaPlayer::None) { 1567 m_delayingLoad = false; 1568 commitLoad(); 1569 } 1570} 1571 1572void MediaPlayerPrivateGStreamer::createAudioSink() 1573{ 1574 // Construct audio sink if pitch preserving is enabled. 1575 if (!m_preservesPitch) 1576 return; 1577 1578 if (!m_playBin) 1579 return; 1580 1581 GstElement* scale = gst_element_factory_make("scaletempo", 0); 1582 if (!scale) { 1583 GST_WARNING("Failed to create scaletempo"); 1584 return; 1585 } 1586 1587 GstElement* convert = gst_element_factory_make("audioconvert", 0); 1588 GstElement* resample = gst_element_factory_make("audioresample", 0); 1589 GstElement* sink = gst_element_factory_make("autoaudiosink", 0); 1590 1591 m_autoAudioSink = sink; 1592 1593 g_signal_connect(sink, "child-added", G_CALLBACK(setAudioStreamPropertiesCallback), this); 1594 1595 GstElement* audioSink = gst_bin_new("audio-sink"); 1596 gst_bin_add_many(GST_BIN(audioSink), scale, convert, resample, sink, NULL); 1597 1598 if (!gst_element_link_many(scale, convert, resample, sink, NULL)) { 1599 GST_WARNING("Failed to link audio sink elements"); 1600 gst_object_unref(audioSink); 1601 return; 1602 } 1603 1604 GRefPtr<GstPad> pad = adoptGRef(gst_element_get_static_pad(scale, "sink")); 1605 gst_element_add_pad(audioSink, gst_ghost_pad_new("sink", pad.get())); 1606 1607 g_object_set(m_playBin.get(), "audio-sink", audioSink, NULL); 1608} 1609 1610void MediaPlayerPrivateGStreamer::createGSTPlayBin() 1611{ 1612 ASSERT(!m_playBin); 1613 1614 // gst_element_factory_make() returns a floating reference so 1615 // we should not adopt. 1616 m_playBin = gst_element_factory_make(gPlaybinName, "play"); 1617 setStreamVolumeElement(GST_STREAM_VOLUME(m_playBin.get())); 1618 1619 GRefPtr<GstBus> bus = webkitGstPipelineGetBus(GST_PIPELINE(m_playBin.get())); 1620 gst_bus_add_signal_watch(bus.get()); 1621 g_signal_connect(bus.get(), "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this); 1622 1623 g_object_set(m_playBin.get(), "mute", m_player->muted(), NULL); 1624 1625 g_signal_connect(m_playBin.get(), "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this); 1626 g_signal_connect(m_playBin.get(), "video-changed", G_CALLBACK(mediaPlayerPrivateVideoChangedCallback), this); 1627 g_signal_connect(m_playBin.get(), "audio-changed", G_CALLBACK(mediaPlayerPrivateAudioChangedCallback), this); 1628 1629 GstElement* videoElement = createVideoSink(m_playBin.get()); 1630 1631 g_object_set(m_playBin.get(), "video-sink", videoElement, NULL); 1632 1633 GRefPtr<GstPad> videoSinkPad = adoptGRef(gst_element_get_static_pad(m_webkitVideoSink.get(), "sink")); 1634 if (videoSinkPad) 1635 g_signal_connect(videoSinkPad.get(), "notify::caps", G_CALLBACK(mediaPlayerPrivateVideoSinkCapsChangedCallback), this); 1636 1637 createAudioSink(); 1638} 1639 1640void MediaPlayerPrivateGStreamer::simulateAudioInterruption() 1641{ 1642 GstMessage* message = gst_message_new_request_state(GST_OBJECT(m_playBin.get()), GST_STATE_PAUSED); 1643 gst_element_post_message(m_playBin.get(), message); 1644} 1645 1646} 1647 1648#endif // USE(GSTREAMER) 1649