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"
24 #include "FrameView.h"
25 #include "GraphicsContext.h"
26 #include "GraphicsLayer.h"
27 #include "HTMLMediaElement.h"
28 #include "HTMLVideoElement.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) && USE(TEXTURE_MAPPER)
56 #include "texmap/TextureMapper.h"
63 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateQt::create(MediaPlayer* player)
65 return adoptPtr(new MediaPlayerPrivateQt(player));
68 void MediaPlayerPrivateQt::registerMediaEngine(MediaEngineRegistrar registrar)
70 registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
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(QString::fromLatin1("audio/")) || mime.startsWith(QString::fromLatin1("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 // Parse and trim codecs.
90 QString codecStr = codec;
91 QStringList codecList = codecStr.split(QLatin1Char(','), QString::SkipEmptyParts);
92 QStringList codecListTrimmed;
93 foreach (const QString& codecStrNotTrimmed, codecList) {
94 QString codecStrTrimmed = codecStrNotTrimmed.trimmed();
95 if (!codecStrTrimmed.isEmpty())
96 codecListTrimmed.append(codecStrTrimmed);
99 if (QMediaPlayer::hasSupport(mime, codecListTrimmed) >= QtMultimediaKit::ProbablySupported)
100 return MediaPlayer::IsSupported;
102 return MediaPlayer::MayBeSupported;
105 MediaPlayerPrivateQt::MediaPlayerPrivateQt(MediaPlayer* player)
106 : m_webCorePlayer(player)
107 , m_mediaPlayer(new QMediaPlayer)
108 , m_mediaPlayerControl(0)
109 , m_videoItem(new QGraphicsVideoItem)
110 , m_videoScene(new QGraphicsScene)
111 , m_networkState(MediaPlayer::Empty)
112 , m_readyState(MediaPlayer::HaveNothing)
113 , m_currentSize(0, 0)
114 , m_naturalSize(RenderVideo::defaultSize())
117 , m_composited(false)
118 , m_preload(MediaPlayer::Auto)
119 , m_suppressNextPlaybackChanged(false)
121 m_mediaPlayer->setVideoOutput(m_videoItem);
122 m_videoScene->addItem(m_videoItem);
125 connect(m_mediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)),
126 this, SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus)));
127 connect(m_mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)),
128 this, SLOT(stateChanged(QMediaPlayer::State)));
129 connect(m_mediaPlayer, SIGNAL(error(QMediaPlayer::Error)),
130 this, SLOT(handleError(QMediaPlayer::Error)));
131 connect(m_mediaPlayer, SIGNAL(bufferStatusChanged(int)),
132 this, SLOT(bufferStatusChanged(int)));
133 connect(m_mediaPlayer, SIGNAL(durationChanged(qint64)),
134 this, SLOT(durationChanged(qint64)));
135 connect(m_mediaPlayer, SIGNAL(positionChanged(qint64)),
136 this, SLOT(positionChanged(qint64)));
137 connect(m_mediaPlayer, SIGNAL(volumeChanged(int)),
138 this, SLOT(volumeChanged(int)));
139 connect(m_mediaPlayer, SIGNAL(mutedChanged(bool)),
140 this, SLOT(mutedChanged(bool)));
141 connect(m_videoScene, SIGNAL(changed(QList<QRectF>)),
142 this, SLOT(repaint()));
143 connect(m_videoItem, SIGNAL(nativeSizeChanged(QSizeF)),
144 this, SLOT(nativeSizeChanged(QSizeF)));
146 // Grab the player control
147 if (QMediaService* service = m_mediaPlayer->service()) {
148 m_mediaPlayerControl = qobject_cast<QMediaPlayerControl *>(
149 service->requestControl(QMediaPlayerControl_iid));
153 MediaPlayerPrivateQt::~MediaPlayerPrivateQt()
155 m_mediaPlayer->disconnect(this);
156 m_mediaPlayer->stop();
157 m_mediaPlayer->setMedia(QMediaContent());
159 delete m_mediaPlayer;
163 bool MediaPlayerPrivateQt::hasVideo() const
165 return m_mediaPlayer->isVideoAvailable();
168 bool MediaPlayerPrivateQt::hasAudio() const
173 void MediaPlayerPrivateQt::load(const String& url)
177 // QtMultimedia does not have an API to throttle loading
178 // so we handle this ourselves by delaying the load
179 if (m_preload == MediaPlayer::None) {
180 m_delayingLoad = true;
187 void MediaPlayerPrivateQt::commitLoad(const String& url)
189 // We are now loading
190 if (m_networkState != MediaPlayer::Loading) {
191 m_networkState = MediaPlayer::Loading;
192 m_webCorePlayer->networkStateChanged();
195 // And we don't have any data yet
196 if (m_readyState != MediaPlayer::HaveNothing) {
197 m_readyState = MediaPlayer::HaveNothing;
198 m_webCorePlayer->readyStateChanged();
201 KURL kUrl(ParsedURLString, url);
202 const QUrl rUrl = kUrl;
203 const QString scheme = rUrl.scheme().toLower();
205 // Grab the client media element
206 HTMLMediaElement* element = static_cast<HTMLMediaElement*>(m_webCorePlayer->mediaPlayerClient());
208 // Construct the media content with a network request if the resource is http[s]
209 if (scheme == QString::fromLatin1("http") || scheme == QString::fromLatin1("https")) {
210 QNetworkRequest request = QNetworkRequest(rUrl);
212 // Grab the current document
213 Document* document = element->document();
215 document = element->ownerDocument();
217 // Grab the frame and network manager
218 Frame* frame = document ? document->frame() : 0;
219 FrameLoader* frameLoader = frame ? frame->loader() : 0;
220 QNetworkAccessManager* manager = frameLoader ? frameLoader->networkingContext()->networkAccessManager() : 0;
224 QNetworkCookieJar* jar = manager->cookieJar();
225 QList<QNetworkCookie> cookies = jar->cookiesForUrl(rUrl);
227 // Don't set the header if there are no cookies.
228 // This prevents a warning from being emitted.
229 if (!cookies.isEmpty())
230 request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
232 // Set the refferer, but not when requesting insecure content from a secure page
233 QUrl documentUrl = QUrl(QString(document->documentURI()));
234 if (documentUrl.scheme().toLower() == QString::fromLatin1("http") || scheme == QString::fromLatin1("https"))
235 request.setRawHeader("Referer", documentUrl.toEncoded());
237 // Set the user agent
238 request.setRawHeader("User-Agent", frameLoader->userAgent(rUrl).utf8().data());
241 m_mediaPlayer->setMedia(QMediaContent(request));
243 // Otherwise, just use the URL
244 m_mediaPlayer->setMedia(QMediaContent(rUrl));
247 // Set the current volume and mute status
248 // We get these from the element, rather than the player, in case we have
249 // transitioned from a media engine which doesn't support muting, to a media
250 // engine which does.
251 m_mediaPlayer->setMuted(element->muted());
252 m_mediaPlayer->setVolume(static_cast<int>(element->volume() * 100.0));
254 // Don't send PlaybackChanged notification for pre-roll.
255 m_suppressNextPlaybackChanged = true;
257 // Setting a media source will start loading the media, but we need
258 // to pre-roll as well to get video size-hints and buffer-status
259 if (element->paused())
260 m_mediaPlayer->pause();
262 m_mediaPlayer->play();
265 void MediaPlayerPrivateQt::resumeLoad()
267 m_delayingLoad = false;
269 if (!m_mediaUrl.isNull())
270 commitLoad(m_mediaUrl);
273 void MediaPlayerPrivateQt::cancelLoad()
275 m_mediaPlayer->setMedia(QMediaContent());
279 void MediaPlayerPrivateQt::prepareToPlay()
281 if (m_mediaPlayer->media().isNull() || m_delayingLoad)
285 void MediaPlayerPrivateQt::play()
287 if (m_mediaPlayer->state() != QMediaPlayer::PlayingState)
288 m_mediaPlayer->play();
291 void MediaPlayerPrivateQt::pause()
293 if (m_mediaPlayer->state() == QMediaPlayer::PlayingState)
294 m_mediaPlayer->pause();
297 bool MediaPlayerPrivateQt::paused() const
299 return (m_mediaPlayer->state() != QMediaPlayer::PlayingState);
302 void MediaPlayerPrivateQt::seek(float position)
304 if (!m_mediaPlayer->isSeekable())
307 if (m_mediaPlayerControl && !m_mediaPlayerControl->availablePlaybackRanges().contains(position * 1000))
311 m_mediaPlayer->setPosition(static_cast<qint64>(position * 1000));
314 bool MediaPlayerPrivateQt::seeking() const
319 float MediaPlayerPrivateQt::duration() const
321 if (m_readyState < MediaPlayer::HaveMetadata)
324 float duration = m_mediaPlayer->duration() / 1000.0f;
327 if (duration <= 0.0f)
328 duration = std::numeric_limits<float>::infinity();
333 float MediaPlayerPrivateQt::currentTime() const
335 return m_mediaPlayer->position() / 1000.0f;
338 PassRefPtr<TimeRanges> MediaPlayerPrivateQt::buffered() const
340 RefPtr<TimeRanges> buffered = TimeRanges::create();
342 if (!m_mediaPlayerControl)
345 QMediaTimeRange playbackRanges = m_mediaPlayerControl->availablePlaybackRanges();
347 foreach (const QMediaTimeInterval interval, playbackRanges.intervals()) {
348 float rangeMin = static_cast<float>(interval.start()) / 1000.0f;
349 float rangeMax = static_cast<float>(interval.end()) / 1000.0f;
350 buffered->add(rangeMin, rangeMax);
353 return buffered.release();
356 float MediaPlayerPrivateQt::maxTimeSeekable() const
358 if (!m_mediaPlayerControl)
361 return static_cast<float>(m_mediaPlayerControl->availablePlaybackRanges().latestTime()) / 1000.0f;
364 unsigned MediaPlayerPrivateQt::bytesLoaded() const
366 QLatin1String bytesLoadedKey("bytes-loaded");
367 if (m_mediaPlayer->availableExtendedMetaData().contains(bytesLoadedKey))
368 return m_mediaPlayer->extendedMetaData(bytesLoadedKey).toInt();
370 return m_mediaPlayer->bufferStatus();
373 unsigned MediaPlayerPrivateQt::totalBytes() const
375 if (m_mediaPlayer->availableMetaData().contains(QtMultimediaKit::Size))
376 return m_mediaPlayer->metaData(QtMultimediaKit::Size).toInt();
381 void MediaPlayerPrivateQt::setPreload(MediaPlayer::Preload preload)
384 if (m_delayingLoad && m_preload != MediaPlayer::None)
388 void MediaPlayerPrivateQt::setRate(float rate)
390 m_mediaPlayer->setPlaybackRate(rate);
393 void MediaPlayerPrivateQt::setVolume(float volume)
395 m_mediaPlayer->setVolume(static_cast<int>(volume * 100.0));
398 bool MediaPlayerPrivateQt::supportsMuting() const
403 void MediaPlayerPrivateQt::setMuted(bool muted)
405 m_mediaPlayer->setMuted(muted);
408 MediaPlayer::NetworkState MediaPlayerPrivateQt::networkState() const
410 return m_networkState;
413 MediaPlayer::ReadyState MediaPlayerPrivateQt::readyState() const
418 void MediaPlayerPrivateQt::setVisible(bool visible)
420 m_isVisible = visible;
423 void MediaPlayerPrivateQt::mediaStatusChanged(QMediaPlayer::MediaStatus)
428 void MediaPlayerPrivateQt::handleError(QMediaPlayer::Error)
433 void MediaPlayerPrivateQt::stateChanged(QMediaPlayer::State)
435 if (!m_suppressNextPlaybackChanged)
436 m_webCorePlayer->playbackStateChanged();
438 m_suppressNextPlaybackChanged = false;
441 void MediaPlayerPrivateQt::nativeSizeChanged(const QSizeF& size)
443 LOG(Media, "MediaPlayerPrivateQt::naturalSizeChanged(%dx%d)",
444 size.toSize().width(), size.toSize().height());
449 m_naturalSize = size.toSize();
450 m_webCorePlayer->sizeChanged();
453 void MediaPlayerPrivateQt::positionChanged(qint64)
455 // Only propagate this event if we are seeking
458 m_webCorePlayer->timeChanged();
462 void MediaPlayerPrivateQt::bufferStatusChanged(int)
467 void MediaPlayerPrivateQt::durationChanged(qint64)
469 m_webCorePlayer->durationChanged();
472 void MediaPlayerPrivateQt::volumeChanged(int volume)
474 m_webCorePlayer->volumeChanged(static_cast<float>(volume) / 100.0);
477 void MediaPlayerPrivateQt::mutedChanged(bool muted)
479 m_webCorePlayer->muteChanged(muted);
482 void MediaPlayerPrivateQt::updateStates()
484 // Store the old states so that we can detect a change and raise change events
485 MediaPlayer::NetworkState oldNetworkState = m_networkState;
486 MediaPlayer::ReadyState oldReadyState = m_readyState;
488 QMediaPlayer::MediaStatus currentStatus = m_mediaPlayer->mediaStatus();
489 QMediaPlayer::Error currentError = m_mediaPlayer->error();
491 if (currentError != QMediaPlayer::NoError) {
492 m_readyState = MediaPlayer::HaveNothing;
493 if (currentError == QMediaPlayer::FormatError || currentError == QMediaPlayer::ResourceError)
494 m_networkState = MediaPlayer::FormatError;
496 m_networkState = MediaPlayer::NetworkError;
497 } else if (currentStatus == QMediaPlayer::UnknownMediaStatus
498 || currentStatus == QMediaPlayer::NoMedia) {
499 m_networkState = MediaPlayer::Idle;
500 m_readyState = MediaPlayer::HaveNothing;
501 } else if (currentStatus == QMediaPlayer::LoadingMedia) {
502 m_networkState = MediaPlayer::Loading;
503 m_readyState = MediaPlayer::HaveNothing;
504 } else if (currentStatus == QMediaPlayer::LoadedMedia) {
505 m_networkState = MediaPlayer::Loading;
506 m_readyState = MediaPlayer::HaveMetadata;
507 } else if (currentStatus == QMediaPlayer::BufferingMedia) {
508 m_networkState = MediaPlayer::Loading;
509 m_readyState = MediaPlayer::HaveFutureData;
510 } else if (currentStatus == QMediaPlayer::StalledMedia) {
511 m_networkState = MediaPlayer::Loading;
512 m_readyState = MediaPlayer::HaveCurrentData;
513 } else if (currentStatus == QMediaPlayer::BufferedMedia
514 || currentStatus == QMediaPlayer::EndOfMedia) {
515 m_networkState = MediaPlayer::Loaded;
516 m_readyState = MediaPlayer::HaveEnoughData;
517 } else if (currentStatus == QMediaPlayer::InvalidMedia) {
518 m_networkState = MediaPlayer::FormatError;
519 m_readyState = MediaPlayer::HaveNothing;
522 // Log the state changes and raise the state change events
523 // NB: The readyStateChanged event must come before the networkStateChanged event.
524 // Breaking this invariant will cause the resource selection algorithm for multiple
526 if (m_readyState != oldReadyState)
527 m_webCorePlayer->readyStateChanged();
529 if (m_networkState != oldNetworkState)
530 m_webCorePlayer->networkStateChanged();
533 void MediaPlayerPrivateQt::setSize(const IntSize& size)
535 LOG(Media, "MediaPlayerPrivateQt::setSize(%dx%d)",
536 size.width(), size.height());
538 if (size == m_currentSize)
541 m_currentSize = size;
542 m_videoItem->setSize(QSizeF(QSize(size)));
545 IntSize MediaPlayerPrivateQt::naturalSize() const
547 if (!hasVideo() || m_readyState < MediaPlayer::HaveMetadata) {
548 LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> 0x0 (!hasVideo || !haveMetaData)");
552 LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> %dx%d (m_naturalSize)",
553 m_naturalSize.width(), m_naturalSize.height());
555 return m_naturalSize;
558 void MediaPlayerPrivateQt::removeVideoItem()
560 m_oldNaturalSize = m_naturalSize;
561 m_mediaPlayer->setVideoOutput(static_cast<QGraphicsVideoItem*>(0));
562 m_videoScene->removeItem(m_videoItem);
565 void MediaPlayerPrivateQt::restoreVideoItem()
567 m_mediaPlayer->setVideoOutput(m_videoItem);
568 m_videoScene->addItem(m_videoItem);
569 // FIXME: a qtmobility bug, need to reset the size when restore the videoitem, otherwise the size is 0
570 // http://bugreports.qt.nokia.com/browse/QTMOBILITY-971
571 nativeSizeChanged(QSize(m_oldNaturalSize));
574 void MediaPlayerPrivateQt::paint(GraphicsContext* context, const IntRect& rect)
576 #if USE(ACCELERATED_COMPOSITING)
580 if (context->paintingDisabled())
586 QPainter* painter = context->platformContext();
587 m_videoScene->render(painter, QRectF(QRect(rect)), m_videoItem->sceneBoundingRect());
590 void MediaPlayerPrivateQt::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
592 if (context->paintingDisabled())
598 // Grab the painter and widget
599 QPainter* painter = context->platformContext();
601 // Render the video, using the item as it might not be in the scene
602 m_videoItem->paint(painter, 0, 0);
605 void MediaPlayerPrivateQt::repaint()
607 m_webCorePlayer->repaint();
611 #if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
612 void MediaPlayerPrivateQt::paintToTextureMapper(TextureMapper* textureMapper, const FloatRect& targetRect, const TransformationMatrix& matrix, float opacity, BitmapTexture*) const
614 GraphicsContext* context = textureMapper->graphicsContext();
615 QPainter* painter = context->platformContext();
617 painter->setTransform(matrix);
618 painter->setOpacity(opacity);
619 m_videoScene->render(painter, QRectF(targetRect), m_videoItem->sceneBoundingRect());
624 PlatformMedia MediaPlayerPrivateQt::platformMedia() const
627 pm.type = PlatformMedia::QtMediaPlayerType;
628 pm.media.qtMediaPlayer = const_cast<MediaPlayerPrivateQt*>(this);
632 } // namespace WebCore
634 #include "moc_MediaPlayerPrivateQt.cpp"