2 Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
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.
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.
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.
21 #include "MediaPlayerPrivateQt.h"
23 #include "FrameLoaderClientQt.h"
24 #include "FrameView.h"
25 #include "GraphicsContext.h"
26 #include "HTMLMediaElement.h"
27 #include "HTMLVideoElement.h"
28 #include "QtNAMThreadSafeProxy.h"
29 #include "NetworkingContext.h"
30 #include "NotImplemented.h"
31 #include "RenderVideo.h"
32 #include "TimeRanges.h"
34 #include "qwebframe.h"
37 #include <QGraphicsScene>
38 #include <QGraphicsVideoItem>
39 #include <QMediaPlayerControl>
40 #include <QMediaService>
41 #include <QNetworkAccessManager>
42 #include <QNetworkCookieJar>
43 #include <QNetworkRequest>
47 #include <QStyleOptionGraphicsItem>
52 #include <wtf/HashSet.h>
53 #include <wtf/text/CString.h>
55 #if USE(ACCELERATED_COMPOSITING)
56 #include "texmap/TextureMapperPlatformLayer.h"
63 MediaPlayerPrivateInterface* MediaPlayerPrivateQt::create(MediaPlayer* player)
65 return new MediaPlayerPrivateQt(player);
68 void MediaPlayerPrivateQt::registerMediaEngine(MediaEngineRegistrar registrar)
70 registrar(create, getSupportedTypes, supportsType);
73 void MediaPlayerPrivateQt::getSupportedTypes(HashSet<String> &supported)
75 QStringList types = QMediaPlayer::supportedMimeTypes();
77 for (int i = 0; i < types.size(); i++) {
78 QString mime = types.at(i);
79 if (mime.startsWith("audio/") || mime.startsWith("video/"))
84 MediaPlayer::SupportsType MediaPlayerPrivateQt::supportsType(const String& mime, const String& codec)
86 if (!mime.startsWith("audio/") && !mime.startsWith("video/"))
87 return MediaPlayer::IsNotSupported;
89 if (QMediaPlayer::hasSupport(mime, QStringList(codec)) >= QtMultimediaKit::ProbablySupported)
90 return MediaPlayer::IsSupported;
92 return MediaPlayer::MayBeSupported;
95 MediaPlayerPrivateQt::MediaPlayerPrivateQt(MediaPlayer* player)
96 : m_webCorePlayer(player)
97 , m_mediaPlayer(new QMediaPlayer)
98 , m_mediaPlayerControl(0)
99 , m_videoItem(new QGraphicsVideoItem)
100 , m_videoScene(new QGraphicsScene)
101 , m_networkState(MediaPlayer::Empty)
102 , m_readyState(MediaPlayer::HaveNothing)
103 , m_currentSize(0, 0)
104 , m_naturalSize(RenderVideo::defaultSize())
107 , m_composited(false)
109 , m_preload(MediaPlayer::Auto)
111 m_mediaPlayer->setVideoOutput(m_videoItem);
112 m_videoScene->addItem(m_videoItem);
115 connect(m_mediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
116 this, SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus)));
117 connect(m_mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)),
118 this, SLOT(stateChanged(QMediaPlayer::State)));
119 connect(m_mediaPlayer, SIGNAL(error(QMediaPlayer::Error)),
120 this, SLOT(handleError(QMediaPlayer::Error)));
121 connect(m_mediaPlayer, SIGNAL(bufferStatusChanged(int)),
122 this, SLOT(bufferStatusChanged(int)));
123 connect(m_mediaPlayer, SIGNAL(durationChanged(qint64)),
124 this, SLOT(durationChanged(qint64)));
125 connect(m_mediaPlayer, SIGNAL(positionChanged(qint64)),
126 this, SLOT(positionChanged(qint64)));
127 connect(m_mediaPlayer, SIGNAL(volumeChanged(int)),
128 this, SLOT(volumeChanged(int)));
129 connect(m_mediaPlayer, SIGNAL(mutedChanged(bool)),
130 this, SLOT(mutedChanged(bool)));
131 connect(m_videoScene, SIGNAL(changed(QList<QRectF>)),
132 this, SLOT(repaint()));
133 connect(m_videoItem, SIGNAL(nativeSizeChanged(QSizeF)),
134 this, SLOT(nativeSizeChanged(QSizeF)));
136 // Grab the player control
137 if (QMediaService* service = m_mediaPlayer->service()) {
138 m_mediaPlayerControl = qobject_cast<QMediaPlayerControl *>(
139 service->requestControl(QMediaPlayerControl_iid));
143 MediaPlayerPrivateQt::~MediaPlayerPrivateQt()
145 m_mediaPlayer->disconnect(this);
146 m_mediaPlayer->stop();
147 m_mediaPlayer->setMedia(QMediaContent());
149 delete m_mediaPlayer;
153 bool MediaPlayerPrivateQt::hasVideo() const
155 return m_mediaPlayer->isVideoAvailable();
158 bool MediaPlayerPrivateQt::hasAudio() const
163 void MediaPlayerPrivateQt::load(const String& url)
167 // QtMultimedia does not have an API to throttle loading
168 // so we handle this ourselves by delaying the load
169 if (m_preload == MediaPlayer::None) {
170 m_delayingLoad = true;
177 void MediaPlayerPrivateQt::commitLoad(const String& url)
179 // We are now loading
180 if (m_networkState != MediaPlayer::Loading) {
181 m_networkState = MediaPlayer::Loading;
182 m_webCorePlayer->networkStateChanged();
185 // And we don't have any data yet
186 if (m_readyState != MediaPlayer::HaveNothing) {
187 m_readyState = MediaPlayer::HaveNothing;
188 m_webCorePlayer->readyStateChanged();
191 const QUrl rUrl = QUrl(QString(url));
192 const QString scheme = rUrl.scheme().toLower();
194 // Grab the client media element
195 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(m_webCorePlayer->mediaPlayerClient());
197 // Construct the media content with a network request if the resource is http[s]
198 if (scheme == "http" || scheme == "https") {
199 QNetworkRequest request = QNetworkRequest(rUrl);
201 // Grab the current document
202 Document* document = element->document();
204 document = element->ownerDocument();
206 // Grab the frame and network manager
207 Frame* frame = document ? document->frame() : 0;
208 QNetworkAccessManager* manager = frame ? frame->loader()->networkingContext()->networkAccessManager() : 0;
209 FrameLoaderClientQt* frameLoader = frame ? static_cast<FrameLoaderClientQt*>(frame->loader()->client()) : 0;
211 if (document && manager) {
213 QtNAMThreadSafeProxy managerProxy(manager);
214 QList<QNetworkCookie> cookies = managerProxy.cookiesForUrl(rUrl);
216 // Don't set the header if there are no cookies.
217 // This prevents a warning from being emitted.
218 if (!cookies.isEmpty())
219 request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies));
221 // Set the refferer, but not when requesting insecure content from a secure page
222 QUrl documentUrl = QUrl(QString(document->documentURI()));
223 if (documentUrl.scheme().toLower() == "http" || scheme == "https")
224 request.setRawHeader("Referer", documentUrl.toEncoded());
226 // Set the user agent
227 request.setRawHeader("User-Agent", frameLoader->userAgent(rUrl).utf8().data());
230 m_mediaPlayer->setMedia(QMediaContent(request));
232 // Otherwise, just use the URL
233 m_mediaPlayer->setMedia(QMediaContent(rUrl));
236 // Set the current volume and mute status
237 // We get these from the element, rather than the player, in case we have
238 // transitioned from a media engine which doesn't support muting, to a media
239 // engine which does.
240 m_mediaPlayer->setMuted(element->muted());
241 m_mediaPlayer->setVolume(static_cast<int>(element->volume() * 100.0));
243 // Setting a media source will start loading the media, but we need
244 // to pre-roll as well to get video size-hints and buffer-status
245 if (element->paused())
246 m_mediaPlayer->pause();
248 m_mediaPlayer->play();
251 void MediaPlayerPrivateQt::resumeLoad()
253 m_delayingLoad = false;
255 if (!m_mediaUrl.isNull())
256 commitLoad(m_mediaUrl);
259 void MediaPlayerPrivateQt::cancelLoad()
261 m_mediaPlayer->setMedia(QMediaContent());
265 void MediaPlayerPrivateQt::prepareToPlay()
267 if (m_mediaPlayer->media().isNull() || m_delayingLoad)
271 void MediaPlayerPrivateQt::play()
273 if (m_mediaPlayer->state() != QMediaPlayer::PlayingState)
274 m_mediaPlayer->play();
277 void MediaPlayerPrivateQt::pause()
279 if (m_mediaPlayer->state() == QMediaPlayer::PlayingState)
280 m_mediaPlayer->pause();
283 bool MediaPlayerPrivateQt::paused() const
285 return (m_mediaPlayer->state() != QMediaPlayer::PlayingState);
288 void MediaPlayerPrivateQt::seek(float position)
290 if (!m_mediaPlayer->isSeekable())
293 if (m_mediaPlayerControl && !m_mediaPlayerControl->availablePlaybackRanges().contains(position * 1000))
299 if (position > duration())
300 position = duration();
302 // Seeking is most reliable when we're paused.
303 // Webkit will try to pause before seeking, but due to the asynchronous nature
304 // of the backend, the player may not actually be paused yet.
305 // In this case, we should queue the seek and wait until pausing has completed
306 // before attempting to seek.
307 if (m_mediaPlayer->state() == QMediaPlayer::PlayingState) {
308 m_mediaPlayer->pause();
310 m_queuedSeek = static_cast<qint64>(position * 1000);
312 // Set a timeout, so that in the event that we don't get a state changed
313 // signal, we still attempt the seek.
314 QTimer::singleShot(1000, this, SLOT(queuedSeekTimeout()));
317 m_mediaPlayer->setPosition(static_cast<qint64>(position * 1000));
319 // Set a timeout, in case we don't get a position changed signal
320 QTimer::singleShot(10000, this, SLOT(seekTimeout()));
324 bool MediaPlayerPrivateQt::seeking() const
329 float MediaPlayerPrivateQt::duration() const
331 if (m_readyState < MediaPlayer::HaveMetadata)
334 float duration = m_mediaPlayer->duration() / 1000.0f;
337 if (duration <= 0.0f)
338 duration = std::numeric_limits<float>::infinity();
343 float MediaPlayerPrivateQt::currentTime() const
345 return m_mediaPlayer->position() / 1000.0f;
348 PassRefPtr<TimeRanges> MediaPlayerPrivateQt::buffered() const
350 RefPtr<TimeRanges> buffered = TimeRanges::create();
352 if (!m_mediaPlayerControl)
355 QMediaTimeRange playbackRanges = m_mediaPlayerControl->availablePlaybackRanges();
357 foreach (const QMediaTimeInterval interval, playbackRanges.intervals()) {
358 float rangeMin = static_cast<float>(interval.start()) / 1000.0f;
359 float rangeMax = static_cast<float>(interval.end()) / 1000.0f;
360 buffered->add(rangeMin, rangeMax);
363 return buffered.release();
366 float MediaPlayerPrivateQt::maxTimeSeekable() const
368 if (!m_mediaPlayerControl)
371 return static_cast<float>(m_mediaPlayerControl->availablePlaybackRanges().latestTime()) / 1000.0f;
374 unsigned MediaPlayerPrivateQt::bytesLoaded() const
376 QLatin1String bytesLoadedKey("bytes-loaded");
377 if (m_mediaPlayer->availableExtendedMetaData().contains(bytesLoadedKey))
378 return m_mediaPlayer->extendedMetaData(bytesLoadedKey).toInt();
380 return m_mediaPlayer->bufferStatus();
383 unsigned MediaPlayerPrivateQt::totalBytes() const
385 if (m_mediaPlayer->availableMetaData().contains(QtMultimediaKit::Size))
386 return m_mediaPlayer->metaData(QtMultimediaKit::Size).toInt();
391 void MediaPlayerPrivateQt::setPreload(MediaPlayer::Preload preload)
394 if (m_delayingLoad && m_preload != MediaPlayer::None)
398 void MediaPlayerPrivateQt::setRate(float rate)
400 m_mediaPlayer->setPlaybackRate(rate);
403 void MediaPlayerPrivateQt::setVolume(float volume)
405 m_mediaPlayer->setVolume(static_cast<int>(volume * 100.0));
408 bool MediaPlayerPrivateQt::supportsMuting() const
413 void MediaPlayerPrivateQt::setMuted(bool muted)
415 m_mediaPlayer->setMuted(muted);
418 MediaPlayer::NetworkState MediaPlayerPrivateQt::networkState() const
420 return m_networkState;
423 MediaPlayer::ReadyState MediaPlayerPrivateQt::readyState() const
428 void MediaPlayerPrivateQt::setVisible(bool visible)
430 m_isVisible = visible;
433 void MediaPlayerPrivateQt::mediaStatusChanged(QMediaPlayer::MediaStatus)
438 void MediaPlayerPrivateQt::handleError(QMediaPlayer::Error)
443 void MediaPlayerPrivateQt::stateChanged(QMediaPlayer::State state)
445 if (state != QMediaPlayer::PlayingState && m_isSeeking && m_queuedSeek >= 0) {
446 m_mediaPlayer->setPosition(m_queuedSeek);
451 void MediaPlayerPrivateQt::nativeSizeChanged(const QSizeF& size)
453 LOG(Media, "MediaPlayerPrivateQt::naturalSizeChanged(%dx%d)",
454 size.toSize().width(), size.toSize().height());
459 m_naturalSize = size.toSize();
460 m_webCorePlayer->sizeChanged();
463 void MediaPlayerPrivateQt::queuedSeekTimeout()
465 // If we haven't heard anything, assume the player is now paused
466 // and we can attempt the seek
467 if (m_isSeeking && m_queuedSeek >= 0) {
468 m_mediaPlayer->setPosition(m_queuedSeek);
471 // Set a timeout, in case we don't get a position changed signal
472 QTimer::singleShot(10000, this, SLOT(seekTimeout()));
476 void MediaPlayerPrivateQt::seekTimeout()
478 // If we haven't heard anything, assume the seek succeeded
480 m_webCorePlayer->timeChanged();
485 void MediaPlayerPrivateQt::positionChanged(qint64)
487 // Only propagate this event if we are seeking
488 if (m_isSeeking && m_queuedSeek == -1) {
489 m_webCorePlayer->timeChanged();
494 void MediaPlayerPrivateQt::bufferStatusChanged(int)
499 void MediaPlayerPrivateQt::durationChanged(qint64)
501 m_webCorePlayer->durationChanged();
504 void MediaPlayerPrivateQt::volumeChanged(int volume)
506 m_webCorePlayer->volumeChanged(static_cast<float>(volume) / 100.0);
509 void MediaPlayerPrivateQt::mutedChanged(bool muted)
511 m_webCorePlayer->muteChanged(muted);
514 void MediaPlayerPrivateQt::updateStates()
516 // Store the old states so that we can detect a change and raise change events
517 MediaPlayer::NetworkState oldNetworkState = m_networkState;
518 MediaPlayer::ReadyState oldReadyState = m_readyState;
520 QMediaPlayer::MediaStatus currentStatus = m_mediaPlayer->mediaStatus();
521 QMediaPlayer::Error currentError = m_mediaPlayer->error();
523 if (currentError != QMediaPlayer::NoError) {
524 m_readyState = MediaPlayer::HaveNothing;
525 if (currentError == QMediaPlayer::FormatError)
526 m_networkState = MediaPlayer::FormatError;
528 m_networkState = MediaPlayer::NetworkError;
529 } else if (currentStatus == QMediaPlayer::UnknownMediaStatus
530 || currentStatus == QMediaPlayer::NoMedia) {
531 m_networkState = MediaPlayer::Idle;
532 m_readyState = MediaPlayer::HaveNothing;
533 } else if (currentStatus == QMediaPlayer::LoadingMedia) {
534 m_networkState = MediaPlayer::Loading;
535 m_readyState = MediaPlayer::HaveNothing;
536 } else if (currentStatus == QMediaPlayer::LoadedMedia) {
537 m_networkState = MediaPlayer::Loading;
538 m_readyState = MediaPlayer::HaveMetadata;
539 } else if (currentStatus == QMediaPlayer::BufferingMedia) {
540 m_networkState = MediaPlayer::Loading;
541 m_readyState = MediaPlayer::HaveFutureData;
542 } else if (currentStatus == QMediaPlayer::StalledMedia) {
543 m_networkState = MediaPlayer::Loading;
544 m_readyState = MediaPlayer::HaveCurrentData;
545 } else if (currentStatus == QMediaPlayer::BufferedMedia
546 || currentStatus == QMediaPlayer::EndOfMedia) {
547 m_networkState = MediaPlayer::Idle;
548 m_readyState = MediaPlayer::HaveEnoughData;
549 } else if (currentStatus == QMediaPlayer::InvalidMedia) {
550 m_networkState = MediaPlayer::NetworkError;
551 m_readyState = MediaPlayer::HaveNothing;
554 // Log the state changes and raise the state change events
555 // NB: The readyStateChanged event must come before the networkStateChanged event.
556 // Breaking this invariant will cause the resource selection algorithm for multiple
558 if (m_readyState != oldReadyState)
559 m_webCorePlayer->readyStateChanged();
561 if (m_networkState != oldNetworkState)
562 m_webCorePlayer->networkStateChanged();
565 void MediaPlayerPrivateQt::setSize(const IntSize& size)
567 LOG(Media, "MediaPlayerPrivateQt::setSize(%dx%d)",
568 size.width(), size.height());
570 if (size == m_currentSize)
573 m_currentSize = size;
574 m_videoItem->setSize(QSizeF(QSize(size)));
577 IntSize MediaPlayerPrivateQt::naturalSize() const
579 if (!hasVideo() || m_readyState < MediaPlayer::HaveMetadata) {
580 LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> 0x0 (!hasVideo || !haveMetaData)");
584 LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> %dx%d (m_naturalSize)",
585 m_naturalSize.width(), m_naturalSize.height());
587 return m_naturalSize;
590 void MediaPlayerPrivateQt::removeVideoItem()
592 m_oldNaturalSize = m_naturalSize;
593 m_mediaPlayer->setVideoOutput(static_cast<QGraphicsVideoItem*>(0));
594 m_videoScene->removeItem(m_videoItem);
597 void MediaPlayerPrivateQt::restoreVideoItem()
599 m_mediaPlayer->setVideoOutput(m_videoItem);
600 m_videoScene->addItem(m_videoItem);
601 // FIXME: a qtmobility bug, need to reset the size when restore the videoitem, otherwise the size is 0
602 // http://bugreports.qt.nokia.com/browse/QTMOBILITY-971
603 nativeSizeChanged(QSize(m_oldNaturalSize));
606 void MediaPlayerPrivateQt::paint(GraphicsContext* context, const IntRect& rect)
608 #if USE(ACCELERATED_COMPOSITING)
612 if (context->paintingDisabled())
618 QPainter* painter = context->platformContext();
619 m_videoScene->render(painter, QRectF(QRect(rect)), m_videoItem->sceneBoundingRect());
622 void MediaPlayerPrivateQt::repaint()
624 m_webCorePlayer->repaint();
627 #if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
629 class TextureMapperVideoLayerQt : public virtual TextureMapperVideoLayer {
631 TextureMapperVideoLayerQt(QGraphicsVideoItem* videoItem)
632 : m_videoItem(videoItem)
636 virtual void setPlatformLayerClient(TextureMapperLayerClient* client)
641 virtual void paint(GraphicsContext* context)
646 QStyleOptionGraphicsItem opt;
647 opt.exposedRect = m_videoItem.data()->sceneBoundingRect();
648 opt.rect = opt.exposedRect.toRect();
649 m_videoItem.data()->paint(context->platformContext(), &opt);
652 virtual IntSize size() const
654 return m_videoItem ? IntSize(m_videoItem.data()->size().width(), m_videoItem.data()->size().height()) : IntSize();
657 QWeakPointer<QGraphicsVideoItem> m_videoItem;
658 TextureMapperLayerClient* m_client;
662 void MediaPlayerPrivateQt::acceleratedRenderingStateChanged()
664 MediaPlayerClient* client = m_webCorePlayer->mediaPlayerClient();
665 bool composited = client->mediaPlayerRenderingCanBeAccelerated(m_webCorePlayer);
666 if (composited == m_composited)
669 m_composited = composited;
671 m_platformLayer = new TextureMapperVideoLayerQt(m_videoItem);
674 PlatformLayer* MediaPlayerPrivateQt::platformLayer() const
676 return m_composited ? m_platformLayer.get() : 0;
680 PlatformMedia MediaPlayerPrivateQt::platformMedia() const
683 pm.type = PlatformMedia::QtMediaPlayerType;
684 pm.media.qtMediaPlayer = const_cast<MediaPlayerPrivateQt*>(this);
688 } // namespace WebCore
690 #include "moc_MediaPlayerPrivateQt.cpp"