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