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 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * aint with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 */ 22 23#include "config.h" 24 25#if ENABLE(VIDEO) 26 27#include "MediaPlayerPrivateGStreamer.h" 28 29 30#include "CString.h" 31#include "DataSourceGStreamer.h" 32#include "Document.h" 33#include "Frame.h" 34#include "FrameView.h" 35#include "GOwnPtrGtk.h" 36#include "GraphicsContext.h" 37#include "IntRect.h" 38#include "KURL.h" 39#include "MIMETypeRegistry.h" 40#include "MediaPlayer.h" 41#include "NotImplemented.h" 42#include "ScrollView.h" 43#include "SecurityOrigin.h" 44#include "TimeRanges.h" 45#include "VideoSinkGStreamer.h" 46#include "WebKitWebSourceGStreamer.h" 47#include "Widget.h" 48 49#include <gst/gst.h> 50#include <gst/interfaces/mixer.h> 51#include <gst/interfaces/xoverlay.h> 52#include <gst/video/video.h> 53#include <limits> 54#include <math.h> 55#include <webkit/webkitwebview.h> 56#include <wtf/gtk/GOwnPtr.h> 57 58using namespace std; 59 60namespace WebCore { 61 62gboolean mediaPlayerPrivateMessageCallback(GstBus* bus, GstMessage* message, gpointer data) 63{ 64 GOwnPtr<GError> err; 65 GOwnPtr<gchar> debug; 66 MediaPlayer::NetworkState error; 67 MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); 68 gint percent = 0; 69 bool issueError = true; 70 bool attemptNextLocation = false; 71 72 if (message->structure) { 73 const gchar* messageTypeName = gst_structure_get_name(message->structure); 74 75 // Redirect messages are sent from elements, like qtdemux, to 76 // notify of the new location(s) of the media. 77 if (!g_strcmp0(messageTypeName, "redirect")) { 78 mp->mediaLocationChanged(message); 79 return true; 80 } 81 } 82 83 switch (GST_MESSAGE_TYPE(message)) { 84 case GST_MESSAGE_ERROR: 85 if (mp && mp->pipelineReset()) 86 break; 87 gst_message_parse_error(message, &err.outPtr(), &debug.outPtr()); 88 LOG_VERBOSE(Media, "Error: %d, %s", err->code, err->message); 89 90 error = MediaPlayer::Empty; 91 if (err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND 92 || err->code == GST_STREAM_ERROR_WRONG_TYPE 93 || err->code == GST_STREAM_ERROR_FAILED 94 || err->code == GST_CORE_ERROR_MISSING_PLUGIN 95 || err->code == GST_RESOURCE_ERROR_NOT_FOUND) 96 error = MediaPlayer::FormatError; 97 else if (err->domain == GST_STREAM_ERROR) { 98 error = MediaPlayer::DecodeError; 99 attemptNextLocation = true; 100 } else if (err->domain == GST_RESOURCE_ERROR) 101 error = MediaPlayer::NetworkError; 102 103 if (mp) { 104 if (attemptNextLocation) 105 issueError = !mp->loadNextLocation(); 106 if (issueError) 107 mp->loadingFailed(error); 108 } 109 break; 110 case GST_MESSAGE_EOS: 111 LOG_VERBOSE(Media, "End of Stream"); 112 mp->didEnd(); 113 break; 114 case GST_MESSAGE_STATE_CHANGED: 115 mp->updateStates(); 116 break; 117 case GST_MESSAGE_BUFFERING: 118 gst_message_parse_buffering(message, &percent); 119 LOG_VERBOSE(Media, "Buffering %d", percent); 120 break; 121 case GST_MESSAGE_DURATION: 122 LOG_VERBOSE(Media, "Duration changed"); 123 mp->durationChanged(); 124 break; 125 default: 126 LOG_VERBOSE(Media, "Unhandled GStreamer message type: %s", 127 GST_MESSAGE_TYPE_NAME(message)); 128 break; 129 } 130 return true; 131} 132 133void mediaPlayerPrivateSourceChangedCallback(GObject *object, GParamSpec *pspec, gpointer data) 134{ 135 MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); 136 GOwnPtr<GstElement> element; 137 138 g_object_get(mp->m_playBin, "source", &element.outPtr(), NULL); 139 gst_object_replace((GstObject**) &mp->m_source, (GstObject*) element.get()); 140 141 if (WEBKIT_IS_WEB_SRC(element.get())) { 142 Frame* frame = mp->m_player->frameView() ? mp->m_player->frameView()->frame() : 0; 143 144 if (frame) 145 webKitWebSrcSetFrame(WEBKIT_WEB_SRC(element.get()), frame); 146 } 147} 148 149void mediaPlayerPrivateVolumeChangedCallback(GObject *element, GParamSpec *pspec, gpointer data) 150{ 151 // This is called when playbin receives the notify::volume signal. 152 MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); 153 mp->volumeChanged(); 154} 155 156gboolean notifyVolumeIdleCallback(gpointer data) 157{ 158 MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); 159 mp->volumeChangedCallback(); 160 return FALSE; 161} 162 163void mediaPlayerPrivateMuteChangedCallback(GObject *element, GParamSpec *pspec, gpointer data) 164{ 165 // This is called when playbin receives the notify::mute signal. 166 MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); 167 mp->muteChanged(); 168} 169 170gboolean notifyMuteIdleCallback(gpointer data) 171{ 172 MediaPlayerPrivate* mp = reinterpret_cast<MediaPlayerPrivate*>(data); 173 mp->muteChangedCallback(); 174 return FALSE; 175} 176 177static float playbackPosition(GstElement* playbin) 178{ 179 180 float ret = 0.0; 181 182 GstQuery* query = gst_query_new_position(GST_FORMAT_TIME); 183 if (!gst_element_query(playbin, query)) { 184 LOG_VERBOSE(Media, "Position query failed..."); 185 gst_query_unref(query); 186 return ret; 187 } 188 189 gint64 position; 190 gst_query_parse_position(query, 0, &position); 191 192 // Position is available only if the pipeline is not in GST_STATE_NULL or 193 // GST_STATE_READY state. 194 if (position != static_cast<gint64>(GST_CLOCK_TIME_NONE)) 195 ret = static_cast<float>(position) / static_cast<float>(GST_SECOND); 196 197 LOG_VERBOSE(Media, "Position %" GST_TIME_FORMAT, GST_TIME_ARGS(position)); 198 199 gst_query_unref(query); 200 201 return ret; 202} 203 204 205void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, GstBuffer *buffer, MediaPlayerPrivate* playerPrivate) 206{ 207 g_return_if_fail(GST_IS_BUFFER(buffer)); 208 gst_buffer_replace(&playerPrivate->m_buffer, buffer); 209 playerPrivate->repaint(); 210} 211 212MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) 213{ 214 return new MediaPlayerPrivate(player); 215} 216 217void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) 218{ 219 if (isAvailable()) 220 registrar(create, getSupportedTypes, supportsType); 221} 222 223static bool gstInitialized = false; 224 225static bool doGstInit() 226{ 227 // FIXME: We should pass the arguments from the command line 228 if (!gstInitialized) { 229 GOwnPtr<GError> error; 230 gstInitialized = gst_init_check(0, 0, &error.outPtr()); 231 if (!gstInitialized) { 232 LOG_VERBOSE(Media, "Could not initialize GStreamer: %s", 233 error ? error->message : "unknown error occurred"); 234 } else { 235 gst_element_register(0, "webkitmediasrc", GST_RANK_PRIMARY, 236 WEBKIT_TYPE_DATA_SRC); 237 gst_element_register(0, "webkitwebsrc", GST_RANK_PRIMARY + 100, 238 WEBKIT_TYPE_WEB_SRC); 239 } 240 241 } 242 return gstInitialized; 243} 244 245bool MediaPlayerPrivate::isAvailable() 246{ 247 if (!doGstInit()) 248 return false; 249 250 GstElementFactory* factory = gst_element_factory_find("playbin2"); 251 if (factory) { 252 gst_object_unref(GST_OBJECT(factory)); 253 return true; 254 } 255 return false; 256} 257 258MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) 259 : m_player(player) 260 , m_playBin(0) 261 , m_videoSink(0) 262 , m_fpsSink(0) 263 , m_source(0) 264 , m_seekTime(0) 265 , m_changingRate(false) 266 , m_endTime(numeric_limits<float>::infinity()) 267 , m_networkState(MediaPlayer::Empty) 268 , m_readyState(MediaPlayer::HaveNothing) 269 , m_startedPlaying(false) 270 , m_isStreaming(false) 271 , m_size(IntSize()) 272 , m_buffer(0) 273 , m_mediaLocations(0) 274 , m_mediaLocationCurrentIndex(0) 275 , m_resetPipeline(false) 276 , m_paused(true) 277 , m_seeking(false) 278 , m_playbackRate(1) 279 , m_errorOccured(false) 280 , m_volumeIdleId(0) 281 , m_mediaDuration(0.0) 282 , m_muteIdleId(0) 283{ 284 doGstInit(); 285} 286 287MediaPlayerPrivate::~MediaPlayerPrivate() 288{ 289 if (m_volumeIdleId) { 290 g_source_remove(m_volumeIdleId); 291 m_volumeIdleId = 0; 292 } 293 294 if (m_muteIdleId) { 295 g_source_remove(m_muteIdleId); 296 m_muteIdleId = 0; 297 } 298 299 if (m_buffer) 300 gst_buffer_unref(m_buffer); 301 m_buffer = 0; 302 303 if (m_mediaLocations) { 304 gst_structure_free(m_mediaLocations); 305 m_mediaLocations = 0; 306 } 307 308 if (m_source) { 309 gst_object_unref(m_source); 310 m_source = 0; 311 } 312 313 if (m_playBin) { 314 gst_element_set_state(m_playBin, GST_STATE_NULL); 315 gst_object_unref(GST_OBJECT(m_playBin)); 316 } 317 318 if (m_videoSink) { 319 g_object_unref(m_videoSink); 320 m_videoSink = 0; 321 } 322 323 if (m_fpsSink) { 324 g_object_unref(m_fpsSink); 325 m_fpsSink = 0; 326 } 327} 328 329void MediaPlayerPrivate::load(const String& url) 330{ 331 LOG_VERBOSE(Media, "Load %s", url.utf8().data()); 332 if (m_networkState != MediaPlayer::Loading) { 333 m_networkState = MediaPlayer::Loading; 334 m_player->networkStateChanged(); 335 } 336 if (m_readyState != MediaPlayer::HaveNothing) { 337 m_readyState = MediaPlayer::HaveNothing; 338 m_player->readyStateChanged(); 339 } 340 341 createGSTPlayBin(url); 342 pause(); 343} 344 345bool MediaPlayerPrivate::changePipelineState(GstState newState) 346{ 347 ASSERT(newState == GST_STATE_PLAYING || newState == GST_STATE_PAUSED); 348 349 GstState currentState; 350 GstState pending; 351 352 gst_element_get_state(m_playBin, ¤tState, &pending, 0); 353 if (currentState != newState && pending != newState) { 354 GstStateChangeReturn ret = gst_element_set_state(m_playBin, newState); 355 GstState pausedOrPlaying = newState == GST_STATE_PLAYING ? GST_STATE_PAUSED : GST_STATE_PLAYING; 356 if (currentState != pausedOrPlaying && ret == GST_STATE_CHANGE_FAILURE) { 357 loadingFailed(MediaPlayer::Empty); 358 return false; 359 } 360 } 361 return true; 362} 363 364void MediaPlayerPrivate::play() 365{ 366 if (changePipelineState(GST_STATE_PLAYING)) 367 LOG_VERBOSE(Media, "Play"); 368} 369 370void MediaPlayerPrivate::pause() 371{ 372 if (changePipelineState(GST_STATE_PAUSED)) 373 LOG_VERBOSE(Media, "Pause"); 374} 375 376float MediaPlayerPrivate::duration() const 377{ 378 if (!m_playBin) 379 return 0.0; 380 381 if (m_errorOccured) 382 return 0.0; 383 384 if (m_mediaDuration) 385 return m_mediaDuration; 386 387 GstFormat timeFormat = GST_FORMAT_TIME; 388 gint64 timeLength = 0; 389 390 if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeFormat != GST_FORMAT_TIME || static_cast<guint64>(timeLength) == GST_CLOCK_TIME_NONE) { 391 LOG_VERBOSE(Media, "Time duration query failed."); 392 return numeric_limits<float>::infinity(); 393 } 394 395 LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength)); 396 397 return (float) ((guint64) timeLength / 1000000000.0); 398 // FIXME: handle 3.14.9.5 properly 399} 400 401float MediaPlayerPrivate::currentTime() const 402{ 403 if (!m_playBin) 404 return 0; 405 406 if (m_errorOccured) 407 return 0; 408 409 if (m_seeking) 410 return m_seekTime; 411 412 return playbackPosition(m_playBin); 413 414} 415 416void MediaPlayerPrivate::seek(float time) 417{ 418 // Avoid useless seeking. 419 if (time == playbackPosition(m_playBin)) 420 return; 421 422 if (!m_playBin) 423 return; 424 425 if (m_isStreaming) 426 return; 427 428 if (m_errorOccured) 429 return; 430 431 GstClockTime sec = (GstClockTime)(time * GST_SECOND); 432 LOG_VERBOSE(Media, "Seek: %" GST_TIME_FORMAT, GST_TIME_ARGS(sec)); 433 if (!gst_element_seek(m_playBin, m_player->rate(), 434 GST_FORMAT_TIME, 435 (GstSeekFlags)(GST_SEEK_FLAG_FLUSH), 436 GST_SEEK_TYPE_SET, sec, 437 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) 438 LOG_VERBOSE(Media, "Seek to %f failed", time); 439 else { 440 m_seeking = true; 441 m_seekTime = sec; 442 } 443} 444 445void MediaPlayerPrivate::startEndPointTimerIfNeeded() 446{ 447 notImplemented(); 448} 449 450void MediaPlayerPrivate::cancelSeek() 451{ 452 notImplemented(); 453} 454 455void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*) 456{ 457 notImplemented(); 458} 459 460bool MediaPlayerPrivate::paused() const 461{ 462 return m_paused; 463} 464 465bool MediaPlayerPrivate::seeking() const 466{ 467 return m_seeking; 468} 469 470// Returns the size of the video 471IntSize MediaPlayerPrivate::naturalSize() const 472{ 473 if (!hasVideo()) 474 return IntSize(); 475 476 // TODO: handle possible clean aperture data. See 477 // https://bugzilla.gnome.org/show_bug.cgi?id=596571 478 // TODO: handle possible transformation matrix. See 479 // https://bugzilla.gnome.org/show_bug.cgi?id=596326 480 int width = 0, height = 0; 481 if (GstPad* pad = gst_element_get_static_pad(m_videoSink, "sink")) { 482 GstCaps* caps = GST_PAD_CAPS(pad); 483 gfloat pixelAspectRatio; 484 gint pixelAspectRatioNumerator, pixelAspectRatioDenominator; 485 486 if (!GST_IS_CAPS(caps) || !gst_caps_is_fixed(caps) 487 || !gst_video_format_parse_caps(caps, 0, &width, &height) 488 || !gst_video_parse_caps_pixel_aspect_ratio(caps, &pixelAspectRatioNumerator, 489 &pixelAspectRatioDenominator)) { 490 gst_object_unref(GST_OBJECT(pad)); 491 return IntSize(); 492 } 493 494 pixelAspectRatio = (gfloat) pixelAspectRatioNumerator / (gfloat) pixelAspectRatioDenominator; 495 width *= pixelAspectRatio; 496 height /= pixelAspectRatio; 497 gst_object_unref(GST_OBJECT(pad)); 498 } 499 500 return IntSize(width, height); 501} 502 503bool MediaPlayerPrivate::hasVideo() const 504{ 505 gint currentVideo = -1; 506 if (m_playBin) 507 g_object_get(m_playBin, "current-video", ¤tVideo, NULL); 508 return currentVideo > -1; 509} 510 511bool MediaPlayerPrivate::hasAudio() const 512{ 513 gint currentAudio = -1; 514 if (m_playBin) 515 g_object_get(m_playBin, "current-audio", ¤tAudio, NULL); 516 return currentAudio > -1; 517} 518 519void MediaPlayerPrivate::setVolume(float volume) 520{ 521 if (!m_playBin) 522 return; 523 524 g_object_set(m_playBin, "volume", static_cast<double>(volume), NULL); 525} 526 527void MediaPlayerPrivate::volumeChangedCallback() 528{ 529 double volume; 530 g_object_get(m_playBin, "volume", &volume, NULL); 531 m_player->volumeChanged(static_cast<float>(volume)); 532} 533 534void MediaPlayerPrivate::volumeChanged() 535{ 536 if (m_volumeIdleId) 537 g_source_remove(m_volumeIdleId); 538 m_volumeIdleId = g_idle_add((GSourceFunc) notifyVolumeIdleCallback, this); 539} 540 541void MediaPlayerPrivate::setRate(float rate) 542{ 543 // Avoid useless playback rate update. 544 if (m_playbackRate == rate) 545 return; 546 547 GstState state; 548 GstState pending; 549 550 gst_element_get_state(m_playBin, &state, &pending, 0); 551 if ((state != GST_STATE_PLAYING && state != GST_STATE_PAUSED) 552 || (pending == GST_STATE_PAUSED)) 553 return; 554 555 if (m_isStreaming) 556 return; 557 558 m_playbackRate = rate; 559 m_changingRate = true; 560 float currentPosition = playbackPosition(m_playBin) * GST_SECOND; 561 GstSeekFlags flags = (GstSeekFlags)(GST_SEEK_FLAG_FLUSH); 562 gint64 start, end; 563 bool mute = false; 564 565 LOG_VERBOSE(Media, "Set Rate to %f", rate); 566 if (rate >= 0) { 567 // Mute the sound if the playback rate is too extreme. 568 // TODO: in other cases we should perform pitch adjustments. 569 mute = (bool) (rate < 0.8 || rate > 2); 570 start = currentPosition; 571 end = GST_CLOCK_TIME_NONE; 572 } else { 573 start = 0; 574 mute = true; 575 576 // If we are at beginning of media, start from the end to 577 // avoid immediate EOS. 578 if (currentPosition <= 0) 579 end = duration() * GST_SECOND; 580 else 581 end = currentPosition; 582 } 583 584 LOG_VERBOSE(Media, "Need to mute audio: %d", (int) mute); 585 586 if (!gst_element_seek(m_playBin, rate, GST_FORMAT_TIME, flags, 587 GST_SEEK_TYPE_SET, start, 588 GST_SEEK_TYPE_SET, end)) 589 LOG_VERBOSE(Media, "Set rate to %f failed", rate); 590 else 591 g_object_set(m_playBin, "mute", mute, NULL); 592} 593 594MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const 595{ 596 return m_networkState; 597} 598 599MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const 600{ 601 return m_readyState; 602} 603 604PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const 605{ 606 RefPtr<TimeRanges> timeRanges = TimeRanges::create(); 607 float loaded = maxTimeLoaded(); 608 if (!m_errorOccured && !m_isStreaming && loaded > 0) 609 timeRanges->add(0, loaded); 610 return timeRanges.release(); 611} 612 613float MediaPlayerPrivate::maxTimeSeekable() const 614{ 615 if (m_errorOccured) 616 return 0.0; 617 618 // TODO 619 LOG_VERBOSE(Media, "maxTimeSeekable"); 620 if (m_isStreaming) 621 return numeric_limits<float>::infinity(); 622 // infinite duration means live stream 623 return maxTimeLoaded(); 624} 625 626float MediaPlayerPrivate::maxTimeLoaded() const 627{ 628 if (m_errorOccured) 629 return 0.0; 630 631 // TODO 632 LOG_VERBOSE(Media, "maxTimeLoaded"); 633 notImplemented(); 634 return duration(); 635} 636 637unsigned MediaPlayerPrivate::bytesLoaded() const 638{ 639 notImplemented(); 640 LOG_VERBOSE(Media, "bytesLoaded"); 641 /*if (!m_playBin) 642 return 0; 643 float dur = duration(); 644 float maxTime = maxTimeLoaded(); 645 if (!dur) 646 return 0;*/ 647 648 return 1; // totalBytes() * maxTime / dur; 649} 650 651unsigned MediaPlayerPrivate::totalBytes() const 652{ 653 LOG_VERBOSE(Media, "totalBytes"); 654 if (!m_source) 655 return 0; 656 657 if (m_errorOccured) 658 return 0; 659 660 GstFormat fmt = GST_FORMAT_BYTES; 661 gint64 length = 0; 662 gst_element_query_duration(m_source, &fmt, &length); 663 664 return length; 665} 666 667void MediaPlayerPrivate::cancelLoad() 668{ 669 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) 670 return; 671 672 if (m_playBin) 673 gst_element_set_state(m_playBin, GST_STATE_NULL); 674} 675 676void MediaPlayerPrivate::updateStates() 677{ 678 // There is no (known) way to get such level of information about 679 // the state of GStreamer, therefore, when in PAUSED state, 680 // we are sure we can display the first frame and go to play 681 682 if (!m_playBin) 683 return; 684 685 if (m_errorOccured) 686 return; 687 688 MediaPlayer::NetworkState oldNetworkState = m_networkState; 689 MediaPlayer::ReadyState oldReadyState = m_readyState; 690 GstState state; 691 GstState pending; 692 693 GstStateChangeReturn ret = gst_element_get_state(m_playBin, 694 &state, &pending, 250 * GST_NSECOND); 695 696 bool shouldUpdateAfterSeek = false; 697 switch (ret) { 698 case GST_STATE_CHANGE_SUCCESS: 699 LOG_VERBOSE(Media, "State: %s, pending: %s", 700 gst_element_state_get_name(state), 701 gst_element_state_get_name(pending)); 702 703 m_resetPipeline = state <= GST_STATE_READY; 704 705 if (state == GST_STATE_READY) 706 m_readyState = MediaPlayer::HaveNothing; 707 else if (state == GST_STATE_PAUSED) 708 m_readyState = MediaPlayer::HaveEnoughData; 709 710 if (state == GST_STATE_PLAYING) { 711 m_readyState = MediaPlayer::HaveEnoughData; 712 m_paused = false; 713 if (!m_mediaDuration) { 714 float newDuration = duration(); 715 if (!isinf(newDuration)) 716 m_mediaDuration = newDuration; 717 } 718 } else 719 m_paused = true; 720 721 if (m_changingRate) { 722 m_player->rateChanged(); 723 m_changingRate = false; 724 } 725 726 if (m_seeking) { 727 shouldUpdateAfterSeek = true; 728 m_seeking = false; 729 } 730 731 m_networkState = MediaPlayer::Loaded; 732 break; 733 case GST_STATE_CHANGE_ASYNC: 734 LOG_VERBOSE(Media, "Async: State: %s, pending: %s", 735 gst_element_state_get_name(state), 736 gst_element_state_get_name(pending)); 737 // Change in progress 738 return; 739 case GST_STATE_CHANGE_FAILURE: 740 LOG_VERBOSE(Media, "Failure: State: %s, pending: %s", 741 gst_element_state_get_name(state), 742 gst_element_state_get_name(pending)); 743 // Change failed 744 return; 745 case GST_STATE_CHANGE_NO_PREROLL: 746 LOG_VERBOSE(Media, "No preroll: State: %s, pending: %s", 747 gst_element_state_get_name(state), 748 gst_element_state_get_name(pending)); 749 750 if (state == GST_STATE_READY) 751 m_readyState = MediaPlayer::HaveNothing; 752 else if (state == GST_STATE_PAUSED) 753 m_readyState = MediaPlayer::HaveCurrentData; 754 755 m_networkState = MediaPlayer::Loading; 756 break; 757 default: 758 LOG_VERBOSE(Media, "Else : %d", ret); 759 break; 760 } 761 762 if (seeking()) 763 m_readyState = MediaPlayer::HaveNothing; 764 765 if (shouldUpdateAfterSeek) 766 timeChanged(); 767 768 if (m_networkState != oldNetworkState) { 769 LOG_VERBOSE(Media, "Network State Changed from %u to %u", 770 oldNetworkState, m_networkState); 771 m_player->networkStateChanged(); 772 } 773 if (m_readyState != oldReadyState) { 774 LOG_VERBOSE(Media, "Ready State Changed from %u to %u", 775 oldReadyState, m_readyState); 776 m_player->readyStateChanged(); 777 } 778} 779 780void MediaPlayerPrivate::mediaLocationChanged(GstMessage* message) 781{ 782 if (m_mediaLocations) 783 gst_structure_free(m_mediaLocations); 784 785 if (message->structure) { 786 // This structure can contain: 787 // - both a new-location string and embedded locations structure 788 // - or only a new-location string. 789 m_mediaLocations = gst_structure_copy(message->structure); 790 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); 791 792 if (locations) 793 m_mediaLocationCurrentIndex = gst_value_list_get_size(locations) -1; 794 795 loadNextLocation(); 796 } 797} 798 799bool MediaPlayerPrivate::loadNextLocation() 800{ 801 if (!m_mediaLocations) 802 return false; 803 804 const GValue* locations = gst_structure_get_value(m_mediaLocations, "locations"); 805 const gchar* newLocation = 0; 806 807 if (!locations) { 808 // Fallback on new-location string. 809 newLocation = gst_structure_get_string(m_mediaLocations, "new-location"); 810 if (!newLocation) 811 return false; 812 } 813 814 if (!newLocation) { 815 if (m_mediaLocationCurrentIndex < 0) { 816 m_mediaLocations = 0; 817 return false; 818 } 819 820 const GValue* location = gst_value_list_get_value(locations, 821 m_mediaLocationCurrentIndex); 822 const GstStructure* structure = gst_value_get_structure(location); 823 824 if (!structure) { 825 m_mediaLocationCurrentIndex--; 826 return false; 827 } 828 829 newLocation = gst_structure_get_string(structure, "new-location"); 830 } 831 832 if (newLocation) { 833 // Found a candidate. new-location is not always an absolute url 834 // though. We need to take the base of the current url and 835 // append the value of new-location to it. 836 837 gchar* currentLocation = 0; 838 g_object_get(m_playBin, "uri", ¤tLocation, NULL); 839 840 KURL currentUrl(KURL(), currentLocation); 841 g_free(currentLocation); 842 843 KURL newUrl; 844 845 if (gst_uri_is_valid(newLocation)) 846 newUrl = KURL(KURL(), newLocation); 847 else 848 newUrl = KURL(KURL(), currentUrl.baseAsString() + newLocation); 849 850 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(currentUrl); 851 if (securityOrigin->canRequest(newUrl)) { 852 LOG_VERBOSE(Media, "New media url: %s", newUrl.string().utf8().data()); 853 854 // Reset player states. 855 m_networkState = MediaPlayer::Loading; 856 m_player->networkStateChanged(); 857 m_readyState = MediaPlayer::HaveNothing; 858 m_player->readyStateChanged(); 859 860 // Reset pipeline state. 861 m_resetPipeline = true; 862 gst_element_set_state(m_playBin, GST_STATE_READY); 863 864 GstState state; 865 gst_element_get_state(m_playBin, &state, 0, 0); 866 if (state <= GST_STATE_READY) { 867 // Set the new uri and start playing. 868 g_object_set(m_playBin, "uri", newUrl.string().utf8().data(), NULL); 869 gst_element_set_state(m_playBin, GST_STATE_PLAYING); 870 return true; 871 } 872 } 873 } 874 m_mediaLocationCurrentIndex--; 875 return false; 876 877} 878 879void MediaPlayerPrivate::loadStateChanged() 880{ 881 updateStates(); 882} 883 884void MediaPlayerPrivate::sizeChanged() 885{ 886 notImplemented(); 887} 888 889void MediaPlayerPrivate::timeChanged() 890{ 891 updateStates(); 892 m_player->timeChanged(); 893} 894 895void MediaPlayerPrivate::didEnd() 896{ 897 // EOS was reached but in case of reverse playback the position is 898 // not always 0. So to not confuse the HTMLMediaElement we 899 // synchronize position and duration values. 900 float now = currentTime(); 901 if (now > 0) 902 m_mediaDuration = now; 903 gst_element_set_state(m_playBin, GST_STATE_PAUSED); 904 905 timeChanged(); 906} 907 908void MediaPlayerPrivate::durationChanged() 909{ 910 // Reset cached media duration 911 m_mediaDuration = 0; 912 913 // And re-cache it if possible. 914 float newDuration = duration(); 915 if (!isinf(newDuration)) 916 m_mediaDuration = newDuration; 917 918 m_player->durationChanged(); 919} 920 921bool MediaPlayerPrivate::supportsMuting() const 922{ 923 return true; 924} 925 926void MediaPlayerPrivate::setMuted(bool muted) 927{ 928 if (!m_playBin) 929 return; 930 931 g_object_set(m_playBin, "mute", muted, NULL); 932} 933 934void MediaPlayerPrivate::muteChangedCallback() 935{ 936 gboolean muted; 937 g_object_get(m_playBin, "mute", &muted, NULL); 938 m_player->muteChanged(static_cast<bool>(muted)); 939} 940 941void MediaPlayerPrivate::muteChanged() 942{ 943 if (m_muteIdleId) 944 g_source_remove(m_muteIdleId); 945 946 m_muteIdleId = g_idle_add((GSourceFunc) notifyMuteIdleCallback, this); 947} 948 949void MediaPlayerPrivate::loadingFailed(MediaPlayer::NetworkState error) 950{ 951 m_errorOccured = true; 952 if (m_networkState != error) { 953 m_networkState = error; 954 m_player->networkStateChanged(); 955 } 956 if (m_readyState != MediaPlayer::HaveNothing) { 957 m_readyState = MediaPlayer::HaveNothing; 958 m_player->readyStateChanged(); 959 } 960} 961 962void MediaPlayerPrivate::setSize(const IntSize& size) 963{ 964 m_size = size; 965} 966 967void MediaPlayerPrivate::setVisible(bool visible) 968{ 969} 970 971void MediaPlayerPrivate::repaint() 972{ 973 m_player->repaint(); 974} 975 976void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) 977{ 978 if (context->paintingDisabled()) 979 return; 980 981 if (!m_player->visible()) 982 return; 983 if (!m_buffer) 984 return; 985 986 int width = 0, height = 0; 987 GstCaps* caps = gst_buffer_get_caps(m_buffer); 988 GstVideoFormat format; 989 990 if (!gst_video_format_parse_caps(caps, &format, &width, &height)) { 991 gst_caps_unref(caps); 992 return; 993 } 994 995 cairo_format_t cairoFormat; 996 if (format == GST_VIDEO_FORMAT_ARGB || format == GST_VIDEO_FORMAT_BGRA) 997 cairoFormat = CAIRO_FORMAT_ARGB32; 998 else 999 cairoFormat = CAIRO_FORMAT_RGB24; 1000 1001 cairo_t* cr = context->platformContext(); 1002 cairo_surface_t* src = cairo_image_surface_create_for_data(GST_BUFFER_DATA(m_buffer), 1003 cairoFormat, 1004 width, height, 1005 4 * width); 1006 1007 cairo_save(cr); 1008 1009 // translate and scale the context to correct size 1010 cairo_translate(cr, rect.x(), rect.y()); 1011 cairo_scale(cr, static_cast<double>(rect.width()) / width, static_cast<double>(rect.height()) / height); 1012 1013 // And paint it. 1014 cairo_set_source_surface(cr, src, 0, 0); 1015 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_PAD); 1016 cairo_rectangle(cr, 0, 0, width, height); 1017 cairo_fill(cr); 1018 cairo_restore(cr); 1019 1020 cairo_surface_destroy(src); 1021 gst_caps_unref(caps); 1022} 1023 1024static HashSet<String> mimeTypeCache() 1025{ 1026 1027 doGstInit(); 1028 1029 static HashSet<String> cache; 1030 static bool typeListInitialized = false; 1031 1032 if (!typeListInitialized) { 1033 // Build a whitelist of mime-types known to be supported by 1034 // GStreamer. 1035 HashSet<String> handledApplicationSubtypes; 1036 handledApplicationSubtypes.add(String("ogg")); 1037 handledApplicationSubtypes.add(String("x-3gp")); 1038 handledApplicationSubtypes.add(String("vnd.rn-realmedia")); 1039 handledApplicationSubtypes.add(String("x-pn-realaudio")); 1040 1041 GList* factories = gst_type_find_factory_get_list(); 1042 for (GList* iterator = factories; iterator; iterator = iterator->next) { 1043 GstTypeFindFactory* factory = GST_TYPE_FIND_FACTORY(iterator->data); 1044 GstCaps* caps = gst_type_find_factory_get_caps(factory); 1045 1046 if (!caps) 1047 continue; 1048 1049 for (guint structureIndex = 0; structureIndex < gst_caps_get_size(caps); structureIndex++) { 1050 GstStructure* structure = gst_caps_get_structure(caps, structureIndex); 1051 const gchar* name = gst_structure_get_name(structure); 1052 bool cached = false; 1053 1054 // These formats are supported by GStreamer, but not 1055 // correctly advertised. 1056 if (g_str_equal(name, "video/x-h264") 1057 || g_str_equal(name, "audio/x-m4a")) { 1058 cache.add(String("video/mp4")); 1059 cache.add(String("audio/aac")); 1060 cached = true; 1061 } 1062 1063 if (g_str_equal(name, "video/x-theora")) { 1064 cache.add(String("video/ogg")); 1065 cached = true; 1066 } 1067 1068 if (g_str_equal(name, "audio/x-vorbis")) { 1069 cache.add(String("audio/ogg")); 1070 cached = true; 1071 } 1072 1073 if (g_str_equal(name, "audio/x-wav")) { 1074 cache.add(String("audio/wav")); 1075 cached = true; 1076 } 1077 1078 if (g_str_equal(name, "audio/mpeg")) { 1079 cache.add(String(name)); 1080 cached = true; 1081 1082 // This is what we are handling: 1083 // mpegversion=(int)1, layer=(int)[ 1, 3 ] 1084 gint mpegVersion = 0; 1085 if (gst_structure_get_int(structure, "mpegversion", &mpegVersion) && (mpegVersion == 1)) { 1086 const GValue* layer = gst_structure_get_value(structure, "layer"); 1087 if (G_VALUE_TYPE(layer) == GST_TYPE_INT_RANGE) { 1088 gint minLayer = gst_value_get_int_range_min(layer); 1089 gint maxLayer = gst_value_get_int_range_max(layer); 1090 if (minLayer <= 1 && 1 <= maxLayer) 1091 cache.add(String("audio/mp1")); 1092 if (minLayer <= 2 && 2 <= maxLayer) 1093 cache.add(String("audio/mp2")); 1094 if (minLayer <= 3 && 3 <= maxLayer) 1095 cache.add(String("audio/mp3")); 1096 } 1097 } 1098 } 1099 1100 if (!cached) { 1101 // GStreamer plugins can be capable of supporting 1102 // types which WebKit supports by default. In that 1103 // case, we should not consider these types 1104 // supportable by GStreamer. Examples of what 1105 // GStreamer can support but should not be added: 1106 // text/plain, text/html, image/jpeg, 1107 // application/xml 1108 gchar** mimetype = g_strsplit(name, "/", 2); 1109 if (g_str_equal(mimetype[0], "audio") 1110 || g_str_equal(mimetype[0], "video") 1111 || (g_str_equal(mimetype[0], "application") 1112 && handledApplicationSubtypes.contains(String(mimetype[1])))) 1113 cache.add(String(name)); 1114 1115 g_strfreev(mimetype); 1116 } 1117 } 1118 } 1119 1120 gst_plugin_feature_list_free(factories); 1121 typeListInitialized = true; 1122 } 1123 1124 return cache; 1125} 1126 1127void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) 1128{ 1129 types = mimeTypeCache(); 1130} 1131 1132MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) 1133{ 1134 if (type.isNull() || type.isEmpty()) 1135 return MediaPlayer::IsNotSupported; 1136 1137 // spec says we should not return "probably" if the codecs string is empty 1138 if (mimeTypeCache().contains(type)) 1139 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported; 1140 return MediaPlayer::IsNotSupported; 1141} 1142 1143bool MediaPlayerPrivate::hasSingleSecurityOrigin() const 1144{ 1145 return true; 1146} 1147 1148bool MediaPlayerPrivate::supportsFullscreen() const 1149{ 1150 return true; 1151} 1152 1153void MediaPlayerPrivate::createGSTPlayBin(String url) 1154{ 1155 ASSERT(!m_playBin); 1156 m_playBin = gst_element_factory_make("playbin2", "play"); 1157 1158 GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(m_playBin)); 1159 gst_bus_add_signal_watch(bus); 1160 g_signal_connect(bus, "message", G_CALLBACK(mediaPlayerPrivateMessageCallback), this); 1161 gst_object_unref(bus); 1162 1163 g_object_set(m_playBin, "uri", url.utf8().data(), NULL); 1164 1165 g_signal_connect(m_playBin, "notify::volume", G_CALLBACK(mediaPlayerPrivateVolumeChangedCallback), this); 1166 g_signal_connect(m_playBin, "notify::source", G_CALLBACK(mediaPlayerPrivateSourceChangedCallback), this); 1167 g_signal_connect(m_playBin, "notify::mute", G_CALLBACK(mediaPlayerPrivateMuteChangedCallback), this); 1168 1169 m_videoSink = webkit_video_sink_new(); 1170 1171 g_object_ref_sink(m_videoSink); 1172 1173 WTFLogChannel* channel = getChannelFromName("Media"); 1174 if (channel->state == WTFLogChannelOn) { 1175 m_fpsSink = gst_element_factory_make("fpsdisplaysink", "sink"); 1176 if (g_object_class_find_property(G_OBJECT_GET_CLASS(m_fpsSink), "video-sink")) { 1177 g_object_set(m_fpsSink, "video-sink", m_videoSink, NULL); 1178 g_object_ref_sink(m_fpsSink); 1179 g_object_set(m_playBin, "video-sink", m_fpsSink, NULL); 1180 } else { 1181 m_fpsSink = 0; 1182 g_object_set(m_playBin, "video-sink", m_videoSink, NULL); 1183 LOG(Media, "Can't display FPS statistics, you need gst-plugins-bad >= 0.10.18"); 1184 } 1185 } else 1186 g_object_set(m_playBin, "video-sink", m_videoSink, NULL); 1187 1188 g_signal_connect(m_videoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this); 1189} 1190 1191} 1192 1193#endif 1194