1/* 2 Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) 3 4 This library is free software; you can redistribute it and/or 5 modify it under the terms of the GNU Library General Public 6 License as published by the Free Software Foundation; either 7 version 2 of the License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Library General Public License for more details. 13 14 You should have received a copy of the GNU Library General Public License 15 along with this library; see the file COPYING.LIB. If not, write to 16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 Boston, MA 02110-1301, USA. 18*/ 19 20#include "config.h" 21#include "MediaPlayerPrivateQt.h" 22 23#include "FrameView.h" 24#include "GraphicsContext.h" 25#include "HTMLMediaElement.h" 26#include "HTMLVideoElement.h" 27#include "NetworkingContext.h" 28#include "NotImplemented.h" 29#include "RenderVideo.h" 30#include "TimeRanges.h" 31#include "Widget.h" 32#include "qwebframe.h" 33#include "qwebpage.h" 34 35#include <QGraphicsScene> 36#include <QGraphicsVideoItem> 37#include <QMediaPlayerControl> 38#include <QMediaService> 39#include <QNetworkAccessManager> 40#include <QNetworkCookieJar> 41#include <QNetworkRequest> 42#include <QPainter> 43#include <QPoint> 44#include <QRect> 45#include <QStyleOptionGraphicsItem> 46#include <QTime> 47#include <QTimer> 48#include <QUrl> 49#include <limits> 50#include <wtf/HashSet.h> 51#include <wtf/text/CString.h> 52 53#if USE(ACCELERATED_COMPOSITING) 54#include "texmap/TextureMapperPlatformLayer.h" 55#endif 56 57using namespace WTF; 58 59namespace WebCore { 60 61MediaPlayerPrivateInterface* MediaPlayerPrivateQt::create(MediaPlayer* player) 62{ 63 return new MediaPlayerPrivateQt(player); 64} 65 66void MediaPlayerPrivateQt::registerMediaEngine(MediaEngineRegistrar registrar) 67{ 68 registrar(create, getSupportedTypes, supportsType, 0, 0, 0); 69} 70 71void MediaPlayerPrivateQt::getSupportedTypes(HashSet<String> &supported) 72{ 73 QStringList types = QMediaPlayer::supportedMimeTypes(); 74 75 for (int i = 0; i < types.size(); i++) { 76 QString mime = types.at(i); 77 if (mime.startsWith(QString::fromLatin1("audio/")) || mime.startsWith(QString::fromLatin1("video/"))) 78 supported.add(mime); 79 } 80} 81 82MediaPlayer::SupportsType MediaPlayerPrivateQt::supportsType(const String& mime, const String& codec) 83{ 84 if (!mime.startsWith("audio/") && !mime.startsWith("video/")) 85 return MediaPlayer::IsNotSupported; 86 87 // Parse and trim codecs. 88 QString codecStr = codec; 89 QStringList codecList = codecStr.split(QLatin1Char(','), QString::SkipEmptyParts); 90 QStringList codecListTrimmed; 91 foreach (const QString& codecStrNotTrimmed, codecList) { 92 QString codecStrTrimmed = codecStrNotTrimmed.trimmed(); 93 if (!codecStrTrimmed.isEmpty()) 94 codecListTrimmed.append(codecStrTrimmed); 95 } 96 97 if (QMediaPlayer::hasSupport(mime, codecListTrimmed) >= QtMultimediaKit::ProbablySupported) 98 return MediaPlayer::IsSupported; 99 100 return MediaPlayer::MayBeSupported; 101} 102 103MediaPlayerPrivateQt::MediaPlayerPrivateQt(MediaPlayer* player) 104 : m_webCorePlayer(player) 105 , m_mediaPlayer(new QMediaPlayer) 106 , m_mediaPlayerControl(0) 107 , m_videoItem(new QGraphicsVideoItem) 108 , m_videoScene(new QGraphicsScene) 109 , m_networkState(MediaPlayer::Empty) 110 , m_readyState(MediaPlayer::HaveNothing) 111 , m_currentSize(0, 0) 112 , m_naturalSize(RenderVideo::defaultSize()) 113 , m_isVisible(false) 114 , m_isSeeking(false) 115 , m_composited(false) 116 , m_preload(MediaPlayer::Auto) 117 , m_suppressNextPlaybackChanged(false) 118{ 119 m_mediaPlayer->setVideoOutput(m_videoItem); 120 m_videoScene->addItem(m_videoItem); 121 122 // Signal Handlers 123 connect(m_mediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), 124 this, SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus))); 125 connect(m_mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), 126 this, SLOT(stateChanged(QMediaPlayer::State))); 127 connect(m_mediaPlayer, SIGNAL(error(QMediaPlayer::Error)), 128 this, SLOT(handleError(QMediaPlayer::Error))); 129 connect(m_mediaPlayer, SIGNAL(bufferStatusChanged(int)), 130 this, SLOT(bufferStatusChanged(int))); 131 connect(m_mediaPlayer, SIGNAL(durationChanged(qint64)), 132 this, SLOT(durationChanged(qint64))); 133 connect(m_mediaPlayer, SIGNAL(positionChanged(qint64)), 134 this, SLOT(positionChanged(qint64))); 135 connect(m_mediaPlayer, SIGNAL(volumeChanged(int)), 136 this, SLOT(volumeChanged(int))); 137 connect(m_mediaPlayer, SIGNAL(mutedChanged(bool)), 138 this, SLOT(mutedChanged(bool))); 139 connect(m_videoScene, SIGNAL(changed(QList<QRectF>)), 140 this, SLOT(repaint())); 141 connect(m_videoItem, SIGNAL(nativeSizeChanged(QSizeF)), 142 this, SLOT(nativeSizeChanged(QSizeF))); 143 144 // Grab the player control 145 if (QMediaService* service = m_mediaPlayer->service()) { 146 m_mediaPlayerControl = qobject_cast<QMediaPlayerControl *>( 147 service->requestControl(QMediaPlayerControl_iid)); 148 } 149} 150 151MediaPlayerPrivateQt::~MediaPlayerPrivateQt() 152{ 153 m_mediaPlayer->disconnect(this); 154 m_mediaPlayer->stop(); 155 m_mediaPlayer->setMedia(QMediaContent()); 156 157 delete m_mediaPlayer; 158 delete m_videoScene; 159} 160 161bool MediaPlayerPrivateQt::hasVideo() const 162{ 163 return m_mediaPlayer->isVideoAvailable(); 164} 165 166bool MediaPlayerPrivateQt::hasAudio() const 167{ 168 return true; 169} 170 171void MediaPlayerPrivateQt::load(const String& url) 172{ 173 m_mediaUrl = url; 174 175 // QtMultimedia does not have an API to throttle loading 176 // so we handle this ourselves by delaying the load 177 if (m_preload == MediaPlayer::None) { 178 m_delayingLoad = true; 179 return; 180 } 181 182 commitLoad(url); 183} 184 185void MediaPlayerPrivateQt::commitLoad(const String& url) 186{ 187 // We are now loading 188 if (m_networkState != MediaPlayer::Loading) { 189 m_networkState = MediaPlayer::Loading; 190 m_webCorePlayer->networkStateChanged(); 191 } 192 193 // And we don't have any data yet 194 if (m_readyState != MediaPlayer::HaveNothing) { 195 m_readyState = MediaPlayer::HaveNothing; 196 m_webCorePlayer->readyStateChanged(); 197 } 198 199 KURL kUrl(ParsedURLString, url); 200 const QUrl rUrl = kUrl; 201 const QString scheme = rUrl.scheme().toLower(); 202 203 // Grab the client media element 204 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(m_webCorePlayer->mediaPlayerClient()); 205 206 // Construct the media content with a network request if the resource is http[s] 207 if (scheme == QString::fromLatin1("http") || scheme == QString::fromLatin1("https")) { 208 QNetworkRequest request = QNetworkRequest(rUrl); 209 210 // Grab the current document 211 Document* document = element->document(); 212 if (!document) 213 document = element->ownerDocument(); 214 215 // Grab the frame and network manager 216 Frame* frame = document ? document->frame() : 0; 217 FrameLoader* frameLoader = frame ? frame->loader() : 0; 218 QNetworkAccessManager* manager = frameLoader ? frameLoader->networkingContext()->networkAccessManager() : 0; 219 220 if (manager) { 221 // Set the cookies 222 QNetworkCookieJar* jar = manager->cookieJar(); 223 QList<QNetworkCookie> cookies = jar->cookiesForUrl(rUrl); 224 225 // Don't set the header if there are no cookies. 226 // This prevents a warning from being emitted. 227 if (!cookies.isEmpty()) 228 request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies)); 229 230 // Set the refferer, but not when requesting insecure content from a secure page 231 QUrl documentUrl = QUrl(QString(document->documentURI())); 232 if (documentUrl.scheme().toLower() == QString::fromLatin1("http") || scheme == QString::fromLatin1("https")) 233 request.setRawHeader("Referer", documentUrl.toEncoded()); 234 235 // Set the user agent 236 request.setRawHeader("User-Agent", frameLoader->userAgent(rUrl).utf8().data()); 237 } 238 239 m_mediaPlayer->setMedia(QMediaContent(request)); 240 } else { 241 // Otherwise, just use the URL 242 m_mediaPlayer->setMedia(QMediaContent(rUrl)); 243 } 244 245 // Set the current volume and mute status 246 // We get these from the element, rather than the player, in case we have 247 // transitioned from a media engine which doesn't support muting, to a media 248 // engine which does. 249 m_mediaPlayer->setMuted(element->muted()); 250 m_mediaPlayer->setVolume(static_cast<int>(element->volume() * 100.0)); 251 252 // Don't send PlaybackChanged notification for pre-roll. 253 m_suppressNextPlaybackChanged = true; 254 255 // Setting a media source will start loading the media, but we need 256 // to pre-roll as well to get video size-hints and buffer-status 257 if (element->paused()) 258 m_mediaPlayer->pause(); 259 else 260 m_mediaPlayer->play(); 261} 262 263void MediaPlayerPrivateQt::resumeLoad() 264{ 265 m_delayingLoad = false; 266 267 if (!m_mediaUrl.isNull()) 268 commitLoad(m_mediaUrl); 269} 270 271void MediaPlayerPrivateQt::cancelLoad() 272{ 273 m_mediaPlayer->setMedia(QMediaContent()); 274 updateStates(); 275} 276 277void MediaPlayerPrivateQt::prepareToPlay() 278{ 279 if (m_mediaPlayer->media().isNull() || m_delayingLoad) 280 resumeLoad(); 281} 282 283void MediaPlayerPrivateQt::play() 284{ 285 if (m_mediaPlayer->state() != QMediaPlayer::PlayingState) 286 m_mediaPlayer->play(); 287} 288 289void MediaPlayerPrivateQt::pause() 290{ 291 if (m_mediaPlayer->state() == QMediaPlayer::PlayingState) 292 m_mediaPlayer->pause(); 293} 294 295bool MediaPlayerPrivateQt::paused() const 296{ 297 return (m_mediaPlayer->state() != QMediaPlayer::PlayingState); 298} 299 300void MediaPlayerPrivateQt::seek(float position) 301{ 302 if (!m_mediaPlayer->isSeekable()) 303 return; 304 305 if (m_mediaPlayerControl && !m_mediaPlayerControl->availablePlaybackRanges().contains(position * 1000)) 306 return; 307 308 m_isSeeking = true; 309 m_mediaPlayer->setPosition(static_cast<qint64>(position * 1000)); 310} 311 312bool MediaPlayerPrivateQt::seeking() const 313{ 314 return m_isSeeking; 315} 316 317float MediaPlayerPrivateQt::duration() const 318{ 319 if (m_readyState < MediaPlayer::HaveMetadata) 320 return 0.0f; 321 322 float duration = m_mediaPlayer->duration() / 1000.0f; 323 324 // We are streaming 325 if (duration <= 0.0f) 326 duration = std::numeric_limits<float>::infinity(); 327 328 return duration; 329} 330 331float MediaPlayerPrivateQt::currentTime() const 332{ 333 return m_mediaPlayer->position() / 1000.0f; 334} 335 336PassRefPtr<TimeRanges> MediaPlayerPrivateQt::buffered() const 337{ 338 RefPtr<TimeRanges> buffered = TimeRanges::create(); 339 340 if (!m_mediaPlayerControl) 341 return buffered; 342 343 QMediaTimeRange playbackRanges = m_mediaPlayerControl->availablePlaybackRanges(); 344 345 foreach (const QMediaTimeInterval interval, playbackRanges.intervals()) { 346 float rangeMin = static_cast<float>(interval.start()) / 1000.0f; 347 float rangeMax = static_cast<float>(interval.end()) / 1000.0f; 348 buffered->add(rangeMin, rangeMax); 349 } 350 351 return buffered.release(); 352} 353 354float MediaPlayerPrivateQt::maxTimeSeekable() const 355{ 356 if (!m_mediaPlayerControl) 357 return 0; 358 359 return static_cast<float>(m_mediaPlayerControl->availablePlaybackRanges().latestTime()) / 1000.0f; 360} 361 362unsigned MediaPlayerPrivateQt::bytesLoaded() const 363{ 364 QLatin1String bytesLoadedKey("bytes-loaded"); 365 if (m_mediaPlayer->availableExtendedMetaData().contains(bytesLoadedKey)) 366 return m_mediaPlayer->extendedMetaData(bytesLoadedKey).toInt(); 367 368 return m_mediaPlayer->bufferStatus(); 369} 370 371unsigned MediaPlayerPrivateQt::totalBytes() const 372{ 373 if (m_mediaPlayer->availableMetaData().contains(QtMultimediaKit::Size)) 374 return m_mediaPlayer->metaData(QtMultimediaKit::Size).toInt(); 375 376 return 100; 377} 378 379void MediaPlayerPrivateQt::setPreload(MediaPlayer::Preload preload) 380{ 381 m_preload = preload; 382 if (m_delayingLoad && m_preload != MediaPlayer::None) 383 resumeLoad(); 384} 385 386void MediaPlayerPrivateQt::setRate(float rate) 387{ 388 m_mediaPlayer->setPlaybackRate(rate); 389} 390 391void MediaPlayerPrivateQt::setVolume(float volume) 392{ 393 m_mediaPlayer->setVolume(static_cast<int>(volume * 100.0)); 394} 395 396bool MediaPlayerPrivateQt::supportsMuting() const 397{ 398 return true; 399} 400 401void MediaPlayerPrivateQt::setMuted(bool muted) 402{ 403 m_mediaPlayer->setMuted(muted); 404} 405 406MediaPlayer::NetworkState MediaPlayerPrivateQt::networkState() const 407{ 408 return m_networkState; 409} 410 411MediaPlayer::ReadyState MediaPlayerPrivateQt::readyState() const 412{ 413 return m_readyState; 414} 415 416void MediaPlayerPrivateQt::setVisible(bool visible) 417{ 418 m_isVisible = visible; 419} 420 421void MediaPlayerPrivateQt::mediaStatusChanged(QMediaPlayer::MediaStatus) 422{ 423 updateStates(); 424} 425 426void MediaPlayerPrivateQt::handleError(QMediaPlayer::Error) 427{ 428 updateStates(); 429} 430 431void MediaPlayerPrivateQt::stateChanged(QMediaPlayer::State) 432{ 433 if (!m_suppressNextPlaybackChanged) 434 m_webCorePlayer->playbackStateChanged(); 435 else 436 m_suppressNextPlaybackChanged = false; 437} 438 439void MediaPlayerPrivateQt::nativeSizeChanged(const QSizeF& size) 440{ 441 LOG(Media, "MediaPlayerPrivateQt::naturalSizeChanged(%dx%d)", 442 size.toSize().width(), size.toSize().height()); 443 444 if (!size.isValid()) 445 return; 446 447 m_naturalSize = size.toSize(); 448 m_webCorePlayer->sizeChanged(); 449} 450 451void MediaPlayerPrivateQt::positionChanged(qint64) 452{ 453 // Only propagate this event if we are seeking 454 if (m_isSeeking) { 455 m_isSeeking = false; 456 m_webCorePlayer->timeChanged(); 457 } 458} 459 460void MediaPlayerPrivateQt::bufferStatusChanged(int) 461{ 462 notImplemented(); 463} 464 465void MediaPlayerPrivateQt::durationChanged(qint64) 466{ 467 m_webCorePlayer->durationChanged(); 468} 469 470void MediaPlayerPrivateQt::volumeChanged(int volume) 471{ 472 m_webCorePlayer->volumeChanged(static_cast<float>(volume) / 100.0); 473} 474 475void MediaPlayerPrivateQt::mutedChanged(bool muted) 476{ 477 m_webCorePlayer->muteChanged(muted); 478} 479 480void MediaPlayerPrivateQt::updateStates() 481{ 482 // Store the old states so that we can detect a change and raise change events 483 MediaPlayer::NetworkState oldNetworkState = m_networkState; 484 MediaPlayer::ReadyState oldReadyState = m_readyState; 485 486 QMediaPlayer::MediaStatus currentStatus = m_mediaPlayer->mediaStatus(); 487 QMediaPlayer::Error currentError = m_mediaPlayer->error(); 488 489 if (currentError != QMediaPlayer::NoError) { 490 m_readyState = MediaPlayer::HaveNothing; 491 if (currentError == QMediaPlayer::FormatError) 492 m_networkState = MediaPlayer::FormatError; 493 else 494 m_networkState = MediaPlayer::NetworkError; 495 } else if (currentStatus == QMediaPlayer::UnknownMediaStatus 496 || currentStatus == QMediaPlayer::NoMedia) { 497 m_networkState = MediaPlayer::Idle; 498 m_readyState = MediaPlayer::HaveNothing; 499 } else if (currentStatus == QMediaPlayer::LoadingMedia) { 500 m_networkState = MediaPlayer::Loading; 501 m_readyState = MediaPlayer::HaveNothing; 502 } else if (currentStatus == QMediaPlayer::LoadedMedia) { 503 m_networkState = MediaPlayer::Loading; 504 m_readyState = MediaPlayer::HaveMetadata; 505 } else if (currentStatus == QMediaPlayer::BufferingMedia) { 506 m_networkState = MediaPlayer::Loading; 507 m_readyState = MediaPlayer::HaveFutureData; 508 } else if (currentStatus == QMediaPlayer::StalledMedia) { 509 m_networkState = MediaPlayer::Loading; 510 m_readyState = MediaPlayer::HaveCurrentData; 511 } else if (currentStatus == QMediaPlayer::BufferedMedia 512 || currentStatus == QMediaPlayer::EndOfMedia) { 513 m_networkState = MediaPlayer::Loaded; 514 m_readyState = MediaPlayer::HaveEnoughData; 515 } else if (currentStatus == QMediaPlayer::InvalidMedia) { 516 m_networkState = MediaPlayer::NetworkError; 517 m_readyState = MediaPlayer::HaveNothing; 518 } 519 520 // Log the state changes and raise the state change events 521 // NB: The readyStateChanged event must come before the networkStateChanged event. 522 // Breaking this invariant will cause the resource selection algorithm for multiple 523 // sources to fail. 524 if (m_readyState != oldReadyState) 525 m_webCorePlayer->readyStateChanged(); 526 527 if (m_networkState != oldNetworkState) 528 m_webCorePlayer->networkStateChanged(); 529} 530 531void MediaPlayerPrivateQt::setSize(const IntSize& size) 532{ 533 LOG(Media, "MediaPlayerPrivateQt::setSize(%dx%d)", 534 size.width(), size.height()); 535 536 if (size == m_currentSize) 537 return; 538 539 m_currentSize = size; 540 m_videoItem->setSize(QSizeF(QSize(size))); 541} 542 543IntSize MediaPlayerPrivateQt::naturalSize() const 544{ 545 if (!hasVideo() || m_readyState < MediaPlayer::HaveMetadata) { 546 LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> 0x0 (!hasVideo || !haveMetaData)"); 547 return IntSize(); 548 } 549 550 LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> %dx%d (m_naturalSize)", 551 m_naturalSize.width(), m_naturalSize.height()); 552 553 return m_naturalSize; 554} 555 556void MediaPlayerPrivateQt::removeVideoItem() 557{ 558 m_oldNaturalSize = m_naturalSize; 559 m_mediaPlayer->setVideoOutput(static_cast<QGraphicsVideoItem*>(0)); 560 m_videoScene->removeItem(m_videoItem); 561} 562 563void MediaPlayerPrivateQt::restoreVideoItem() 564{ 565 m_mediaPlayer->setVideoOutput(m_videoItem); 566 m_videoScene->addItem(m_videoItem); 567 // FIXME: a qtmobility bug, need to reset the size when restore the videoitem, otherwise the size is 0 568 // http://bugreports.qt.nokia.com/browse/QTMOBILITY-971 569 nativeSizeChanged(QSize(m_oldNaturalSize)); 570} 571 572void MediaPlayerPrivateQt::paint(GraphicsContext* context, const IntRect& rect) 573{ 574#if USE(ACCELERATED_COMPOSITING) 575 if (m_composited) 576 return; 577#endif 578 if (context->paintingDisabled()) 579 return; 580 581 if (!m_isVisible) 582 return; 583 584 QPainter* painter = context->platformContext(); 585 m_videoScene->render(painter, QRectF(QRect(rect)), m_videoItem->sceneBoundingRect()); 586} 587 588void MediaPlayerPrivateQt::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect) 589{ 590 if (context->paintingDisabled()) 591 return; 592 593 if (!m_isVisible) 594 return; 595 596 // Grab the painter and widget 597 QPainter* painter = context->platformContext(); 598 599 // Render the video, using the item as it might not be in the scene 600 m_videoItem->paint(painter, 0, 0); 601} 602 603void MediaPlayerPrivateQt::repaint() 604{ 605 m_webCorePlayer->repaint(); 606} 607 608#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) 609 610class TextureMapperVideoLayerQt : public virtual TextureMapperMediaLayer { 611public: 612 TextureMapperVideoLayerQt(QGraphicsVideoItem* videoItem) 613 : m_videoItem(videoItem) 614 { 615 } 616 617 virtual void setPlatformLayerClient(TextureMapperLayerClient* client) 618 { 619 m_client = client; 620 } 621 622 virtual void paint(GraphicsContext* context) 623 { 624 if (!m_videoItem) 625 return; 626 627 QStyleOptionGraphicsItem opt; 628 opt.exposedRect = m_videoItem.data()->sceneBoundingRect(); 629 opt.rect = opt.exposedRect.toRect(); 630 m_videoItem.data()->paint(context->platformContext(), &opt); 631 } 632 633 virtual IntSize size() const 634 { 635 return m_videoItem ? IntSize(m_videoItem.data()->size().width(), m_videoItem.data()->size().height()) : IntSize(); 636 } 637 638 QWeakPointer<QGraphicsVideoItem> m_videoItem; 639 TextureMapperLayerClient* m_client; 640}; 641 642 643void MediaPlayerPrivateQt::acceleratedRenderingStateChanged() 644{ 645 MediaPlayerClient* client = m_webCorePlayer->mediaPlayerClient(); 646 bool composited = client->mediaPlayerRenderingCanBeAccelerated(m_webCorePlayer); 647 if (composited == m_composited) 648 return; 649 650 m_composited = composited; 651 if (composited) 652 m_platformLayer = new TextureMapperVideoLayerQt(m_videoItem); 653} 654 655PlatformLayer* MediaPlayerPrivateQt::platformLayer() const 656{ 657 return m_composited ? m_platformLayer.get() : 0; 658} 659#endif 660 661PlatformMedia MediaPlayerPrivateQt::platformMedia() const 662{ 663 PlatformMedia pm; 664 pm.type = PlatformMedia::QtMediaPlayerType; 665 pm.media.qtMediaPlayer = const_cast<MediaPlayerPrivateQt*>(this); 666 return pm; 667} 668 669} // namespace WebCore 670 671#include "moc_MediaPlayerPrivateQt.cpp" 672