Home | History | Annotate | Download | only in qt
      1 /*
      2     Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      3     Copyright (C) 2009 Apple Inc. All rights reserved.
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Library General Public
      7     License as published by the Free Software Foundation; either
      8     version 2 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Library General Public License for more details.
     14 
     15     You should have received a copy of the GNU Library General Public License
     16     along with this library; see the file COPYING.LIB.  If not, write to
     17     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18     Boston, MA 02110-1301, USA.
     19 */
     20 
     21 #include "config.h"
     22 #include "MediaPlayerPrivatePhonon.h"
     23 
     24 #include <limits>
     25 
     26 #include "FrameView.h"
     27 #include "GraphicsContext.h"
     28 #include "Logging.h"
     29 #include "MIMETypeRegistry.h"
     30 #include "NotImplemented.h"
     31 #include "TimeRanges.h"
     32 #include "Widget.h"
     33 #include <wtf/HashSet.h>
     34 #include <wtf/text/CString.h>
     35 
     36 #include <QDebug>
     37 #include <QEvent>
     38 #include <QMetaEnum>
     39 #include <QPainter>
     40 #include <QWidget>
     41 #include <QUrl>
     42 
     43 #include <phonon/audiooutput.h>
     44 #include <phonon/backendcapabilities.h>
     45 #include <phonon/path.h>
     46 #include <phonon/mediaobject.h>
     47 #include <phonon/videowidget.h>
     48 
     49 using namespace Phonon;
     50 
     51 #define LOG_MEDIAOBJECT() (LOG(Media, "%s", debugMediaObject(this, *m_mediaObject).constData()))
     52 
     53 #if !LOG_DISABLED
     54 static QByteArray debugMediaObject(WebCore::MediaPlayerPrivatePhonon* mediaPlayer, const MediaObject& mediaObject)
     55 {
     56     QByteArray byteArray;
     57     QTextStream stream(&byteArray);
     58 
     59     const QMetaObject* metaObj = mediaPlayer->metaObject();
     60     QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState"));
     61 
     62     stream << "debugMediaObject -> Phonon::MediaObject(";
     63     stream << "State: " << phononStates.valueToKey(mediaObject.state());
     64     stream << " | Current time: " << mediaObject.currentTime();
     65     stream << " | Remaining time: " << mediaObject.remainingTime();
     66     stream << " | Total time: " << mediaObject.totalTime();
     67     stream << " | Meta-data: ";
     68     QMultiMap<QString, QString> map = mediaObject.metaData();
     69     for (QMap<QString, QString>::const_iterator it = map.constBegin();
     70         it != map.constEnd(); ++it) {
     71         stream << "(" << it.key() << ", " << it.value() << ")";
     72     }
     73     stream << " | Has video: " << mediaObject.hasVideo();
     74     stream << " | Is seekable: " << mediaObject.isSeekable();
     75     stream << ")";
     76 
     77     stream.flush();
     78 
     79     return byteArray;
     80 }
     81 #endif
     82 
     83 using namespace WTF;
     84 
     85 namespace WebCore {
     86 
     87 MediaPlayerPrivatePhonon::MediaPlayerPrivatePhonon(MediaPlayer* player)
     88     : m_player(player)
     89     , m_networkState(MediaPlayer::Empty)
     90     , m_readyState(MediaPlayer::HaveNothing)
     91     , m_mediaObject(new MediaObject())
     92     , m_videoWidget(new VideoWidget(0))
     93     , m_audioOutput(new AudioOutput())
     94     , m_isVisible(false)
     95 {
     96     // Hint to Phonon to disable overlay painting
     97     m_videoWidget->setAttribute(Qt::WA_DontShowOnScreen);
     98     m_videoWidget->setAttribute(Qt::WA_QuitOnClose, false);
     99 
    100     createPath(m_mediaObject, m_videoWidget);
    101     createPath(m_mediaObject, m_audioOutput);
    102 
    103     // Make sure we get updates for each frame
    104     m_videoWidget->installEventFilter(this);
    105     foreach (QWidget* widget, m_videoWidget->findChildren<QWidget*>())
    106         widget->installEventFilter(this);
    107 
    108     connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
    109             this, SLOT(stateChanged(Phonon::State,Phonon::State)));
    110     connect(m_mediaObject, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
    111     connect(m_mediaObject, SIGNAL(seekableChanged(bool)), this, SLOT(seekableChanged(bool)));
    112     connect(m_mediaObject, SIGNAL(hasVideoChanged(bool)), this, SLOT(hasVideoChanged(bool)));
    113     connect(m_mediaObject, SIGNAL(bufferStatus(int)), this, SLOT(bufferStatus(int)));
    114     connect(m_mediaObject, SIGNAL(finished()), this, SLOT(finished()));
    115     connect(m_mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
    116             this, SLOT(currentSourceChanged(Phonon::MediaSource)));
    117     connect(m_mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish()));
    118     connect(m_mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
    119 }
    120 
    121 MediaPlayerPrivateInterface* MediaPlayerPrivatePhonon::create(MediaPlayer* player)
    122 {
    123     return new MediaPlayerPrivatePhonon(player);
    124 }
    125 
    126 void MediaPlayerPrivatePhonon::registerMediaEngine(MediaEngineRegistrar registrar)
    127 {
    128     if (isAvailable())
    129         registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
    130 }
    131 
    132 
    133 MediaPlayerPrivatePhonon::~MediaPlayerPrivatePhonon()
    134 {
    135     LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting videowidget");
    136     m_videoWidget->close();
    137     delete m_videoWidget;
    138     m_videoWidget = 0;
    139 
    140     LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting audiooutput");
    141     delete m_audioOutput;
    142     m_audioOutput = 0;
    143 
    144     LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting mediaobject");
    145     delete m_mediaObject;
    146     m_mediaObject = 0;
    147 }
    148 
    149 HashSet<String>& MediaPlayerPrivatePhonon::supportedTypesCache()
    150 {
    151     static HashSet<String> supportedTypes;
    152     if (!supportedTypes.isEmpty())
    153         return supportedTypes;
    154 
    155     // FIXME: we should rebuild the MIME type cache every time the backend is changed,
    156     // however, this would have no effect on MIMETypeRegistry anyway, because it
    157     // pulls this data only once.
    158 
    159     QStringList types = Phonon::BackendCapabilities::availableMimeTypes();
    160     foreach (const QString& type, types) {
    161         QString first = type.split(QLatin1Char('/')).at(0);
    162 
    163         // We're only interested in types which are not supported by WebCore itself.
    164         if (first != QLatin1String("video")
    165             && first != QLatin1String("audio")
    166             && first != QLatin1String("application"))
    167             continue;
    168         if (MIMETypeRegistry::isSupportedNonImageMIMEType(type))
    169             continue;
    170 
    171         supportedTypes.add(String(type));
    172     }
    173 
    174     // These formats are supported by GStreamer, but not correctly advertised.
    175     if (supportedTypes.contains(String("video/x-h264"))
    176         || supportedTypes.contains(String("audio/x-m4a"))) {
    177         supportedTypes.add(String("video/mp4"));
    178         supportedTypes.add(String("audio/aac"));
    179     }
    180 
    181     if (supportedTypes.contains(String("video/x-theora")))
    182         supportedTypes.add(String("video/ogg"));
    183 
    184     if (supportedTypes.contains(String("audio/x-vorbis")))
    185         supportedTypes.add(String("audio/ogg"));
    186 
    187     if (supportedTypes.contains(String("audio/x-wav")))
    188         supportedTypes.add(String("audio/wav"));
    189 
    190     return supportedTypes;
    191 }
    192 
    193 void MediaPlayerPrivatePhonon::getSupportedTypes(HashSet<String>& types)
    194 {
    195     types = supportedTypesCache();
    196 }
    197 
    198 MediaPlayer::SupportsType MediaPlayerPrivatePhonon::supportsType(const String& type, const String& codecs)
    199 {
    200     if (type.isEmpty())
    201         return MediaPlayer::IsNotSupported;
    202 
    203     if (supportedTypesCache().contains(type))
    204         return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
    205     return MediaPlayer::IsNotSupported;
    206 }
    207 
    208 bool MediaPlayerPrivatePhonon::hasVideo() const
    209 {
    210     bool hasVideo = m_mediaObject->hasVideo();
    211     LOG(Media, "MediaPlayerPrivatePhonon::hasVideo() -> %s", hasVideo ? "true" : "false");
    212     return hasVideo;
    213 }
    214 
    215 bool MediaPlayerPrivatePhonon::hasAudio() const
    216 {
    217     // FIXME: Phonon::MediaObject does not have such a hasAudio() function
    218     bool hasAudio = true;
    219     LOG(Media, "MediaPlayerPrivatePhonon::hasAudio() -> %s", hasAudio ? "true" : "false");
    220     return hasAudio;
    221 }
    222 
    223 void MediaPlayerPrivatePhonon::load(const String& url)
    224 {
    225     LOG(Media, "MediaPlayerPrivatePhonon::load(\"%s\")", url.utf8().data());
    226 
    227     // We are now loading
    228     if (m_networkState != MediaPlayer::Loading) {
    229         m_networkState = MediaPlayer::Loading;
    230         m_player->networkStateChanged();
    231     }
    232 
    233     // And we don't have any data yet
    234     if (m_readyState != MediaPlayer::HaveNothing) {
    235         m_readyState = MediaPlayer::HaveNothing;
    236         m_player->readyStateChanged();
    237     }
    238 
    239     m_mediaObject->setCurrentSource(QUrl(url));
    240     m_audioOutput->setVolume(m_player->volume());
    241     setVisible(m_player->visible());
    242 }
    243 
    244 void MediaPlayerPrivatePhonon::cancelLoad()
    245 {
    246     notImplemented();
    247 }
    248 
    249 
    250 void MediaPlayerPrivatePhonon::play()
    251 {
    252     LOG(Media, "MediaPlayerPrivatePhonon::play()");
    253     m_mediaObject->play();
    254 }
    255 
    256 void MediaPlayerPrivatePhonon::pause()
    257 {
    258     LOG(Media, "MediaPlayerPrivatePhonon::pause()");
    259     m_mediaObject->pause();
    260 }
    261 
    262 
    263 bool MediaPlayerPrivatePhonon::paused() const
    264 {
    265     bool paused = m_mediaObject->state() == Phonon::PausedState;
    266     LOG(Media, "MediaPlayerPrivatePhonon::paused() --> %s", paused ? "true" : "false");
    267     return paused;
    268 }
    269 
    270 void MediaPlayerPrivatePhonon::seek(float position)
    271 {
    272     LOG(Media, "MediaPlayerPrivatePhonon::seek(%f)", position);
    273 
    274     if (!m_mediaObject->isSeekable())
    275         return;
    276 
    277     if (position > duration())
    278         position = duration();
    279 
    280     m_mediaObject->seek(position * 1000.0f);
    281 }
    282 
    283 bool MediaPlayerPrivatePhonon::seeking() const
    284 {
    285     return false;
    286 }
    287 
    288 float MediaPlayerPrivatePhonon::duration() const
    289 {
    290     if (m_readyState < MediaPlayer::HaveMetadata)
    291         return 0.0f;
    292 
    293     float duration = m_mediaObject->totalTime() / 1000.0f;
    294 
    295     if (duration == 0.0f) // We are streaming
    296         duration = std::numeric_limits<float>::infinity();
    297 
    298     LOG(Media, "MediaPlayerPrivatePhonon::duration() --> %f", duration);
    299     return duration;
    300 }
    301 
    302 float MediaPlayerPrivatePhonon::currentTime() const
    303 {
    304     float currentTime = m_mediaObject->currentTime() / 1000.0f;
    305 
    306     LOG(Media, "MediaPlayerPrivatePhonon::currentTime() --> %f", currentTime);
    307     return currentTime;
    308 }
    309 
    310 PassRefPtr<TimeRanges> MediaPlayerPrivatePhonon::buffered() const
    311 {
    312     notImplemented();
    313     return TimeRanges::create();
    314 }
    315 
    316 float MediaPlayerPrivatePhonon::maxTimeSeekable() const
    317 {
    318     notImplemented();
    319     return 0.0f;
    320 }
    321 
    322 unsigned MediaPlayerPrivatePhonon::bytesLoaded() const
    323 {
    324     notImplemented();
    325     return 0;
    326 }
    327 
    328 unsigned MediaPlayerPrivatePhonon::totalBytes() const
    329 {
    330     //notImplemented();
    331     return 0;
    332 }
    333 
    334 void MediaPlayerPrivatePhonon::setRate(float)
    335 {
    336     notImplemented();
    337 }
    338 
    339 void MediaPlayerPrivatePhonon::setVolume(float volume)
    340 {
    341     LOG(Media, "MediaPlayerPrivatePhonon::setVolume()");
    342     m_audioOutput->setVolume(volume);
    343 }
    344 
    345 void MediaPlayerPrivatePhonon::setMuted(bool muted)
    346 {
    347     LOG(Media, "MediaPlayerPrivatePhonon::setMuted()");
    348     m_audioOutput->setMuted(muted);
    349 }
    350 
    351 MediaPlayer::NetworkState MediaPlayerPrivatePhonon::networkState() const
    352 {
    353     const QMetaObject* metaObj = this->metaObject();
    354     QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState"));
    355     LOG(Media, "MediaPlayerPrivatePhonon::networkState() --> %s", networkStates.valueToKey(m_networkState));
    356     return m_networkState;
    357 }
    358 
    359 MediaPlayer::ReadyState MediaPlayerPrivatePhonon::readyState() const
    360 {
    361     const QMetaObject* metaObj = this->metaObject();
    362     QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState"));
    363     LOG(Media, "MediaPlayerPrivatePhonon::readyState() --> %s", readyStates.valueToKey(m_readyState));
    364     return m_readyState;
    365 }
    366 
    367 void MediaPlayerPrivatePhonon::updateStates()
    368 {
    369     MediaPlayer::NetworkState oldNetworkState = m_networkState;
    370     MediaPlayer::ReadyState oldReadyState = m_readyState;
    371 
    372     Phonon::State phononState = m_mediaObject->state();
    373 
    374     if (phononState == Phonon::StoppedState) {
    375         if (m_readyState < MediaPlayer::HaveMetadata) {
    376             m_networkState = MediaPlayer::Loading; // FIXME: should this be MediaPlayer::Idle?
    377             m_readyState = MediaPlayer::HaveMetadata;
    378             m_mediaObject->pause();
    379         }
    380     } else if (phononState == Phonon::PausedState) {
    381         m_networkState = MediaPlayer::Loaded;
    382         m_readyState = MediaPlayer::HaveEnoughData;
    383     } else if (phononState == Phonon::ErrorState) {
    384          if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) {
    385              // FIXME: is it possile to differentiate between different types of errors
    386              m_networkState = MediaPlayer::NetworkError;
    387              m_readyState = MediaPlayer::HaveNothing;
    388              cancelLoad();
    389          } else
    390              m_mediaObject->pause();
    391     }
    392 
    393     if (seeking())
    394         m_readyState = MediaPlayer::HaveNothing;
    395 
    396     if (m_networkState != oldNetworkState) {
    397         const QMetaObject* metaObj = this->metaObject();
    398         QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState"));
    399         LOG(Media, "Network state changed from '%s' to '%s'",
    400                 networkStates.valueToKey(oldNetworkState),
    401                 networkStates.valueToKey(m_networkState));
    402         m_player->networkStateChanged();
    403     }
    404 
    405     if (m_readyState != oldReadyState) {
    406         const QMetaObject* metaObj = this->metaObject();
    407         QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState"));
    408         LOG(Media, "Ready state changed from '%s' to '%s'",
    409                 readyStates.valueToKey(oldReadyState),
    410                 readyStates.valueToKey(m_readyState));
    411         m_player->readyStateChanged();
    412     }
    413 }
    414 
    415 void MediaPlayerPrivatePhonon::setVisible(bool visible)
    416 {
    417     m_isVisible = visible;
    418     LOG(Media, "MediaPlayerPrivatePhonon::setVisible(%s)", visible ? "true" : "false");
    419 
    420     m_videoWidget->setVisible(m_isVisible);
    421 }
    422 
    423 void MediaPlayerPrivatePhonon::setSize(const IntSize& newSize)
    424 {
    425     if (!m_videoWidget)
    426         return;
    427 
    428     LOG(Media, "MediaPlayerPrivatePhonon::setSize(%d,%d)",
    429                 newSize.width(), newSize.height());
    430 
    431     QRect currentRect = m_videoWidget->rect();
    432 
    433     if (newSize.width() != currentRect.width() || newSize.height() != currentRect.height())
    434         m_videoWidget->resize(newSize.width(), newSize.height());
    435 }
    436 
    437 IntSize MediaPlayerPrivatePhonon::naturalSize() const
    438 {
    439     if (!hasVideo()) {
    440         LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
    441                     0, 0);
    442         return IntSize();
    443     }
    444 
    445     if (m_readyState < MediaPlayer::HaveMetadata) {
    446         LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
    447                            0, 0);
    448         return IntSize();
    449     }
    450 
    451     QSize videoSize = m_videoWidget->sizeHint();
    452     IntSize naturalSize(videoSize.width(), videoSize.height());
    453     LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d",
    454             naturalSize.width(), naturalSize.height());
    455     return naturalSize;
    456 }
    457 
    458 bool MediaPlayerPrivatePhonon::eventFilter(QObject* obj, QEvent* event)
    459 {
    460     if (event->type() == QEvent::UpdateRequest)
    461         m_player->repaint();
    462 
    463     return QObject::eventFilter(obj, event);
    464 }
    465 
    466 void MediaPlayerPrivatePhonon::paint(GraphicsContext* graphicsContect, const IntRect& rect)
    467 {
    468     if (graphicsContect->paintingDisabled())
    469         return;
    470 
    471     if (!m_isVisible)
    472         return;
    473 
    474     QPainter* painter = graphicsContect->platformContext();
    475 
    476     painter->fillRect(rect, Qt::black);
    477 
    478     m_videoWidget->render(painter, QPoint(rect.x(), rect.y()),
    479             QRegion(0, 0, rect.width(), rect.height()));
    480 }
    481 
    482 // ====================== Phonon::MediaObject signals ======================
    483 
    484 void MediaPlayerPrivatePhonon::stateChanged(Phonon::State newState, Phonon::State oldState)
    485 {
    486     const QMetaObject* metaObj = this->metaObject();
    487     QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState"));
    488     LOG(Media, "MediaPlayerPrivatePhonon::stateChanged(newState=%s, oldState=%s)",
    489             phononStates.valueToKey(newState), phononStates.valueToKey(oldState));
    490 
    491     updateStates();
    492 }
    493 
    494 void MediaPlayerPrivatePhonon::metaDataChanged()
    495 {
    496     LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()");
    497     LOG_MEDIAOBJECT();
    498 }
    499 
    500 void MediaPlayerPrivatePhonon::seekableChanged(bool)
    501 {
    502     notImplemented();
    503     LOG_MEDIAOBJECT();
    504 }
    505 
    506 void MediaPlayerPrivatePhonon::hasVideoChanged(bool hasVideo)
    507 {
    508     LOG(Media, "MediaPlayerPrivatePhonon::hasVideoChanged(%s)", hasVideo ? "true" : "false");
    509 }
    510 
    511 void MediaPlayerPrivatePhonon::bufferStatus(int)
    512 {
    513     notImplemented();
    514     LOG_MEDIAOBJECT();
    515 }
    516 
    517 void MediaPlayerPrivatePhonon::finished()
    518 {
    519     notImplemented();
    520     LOG_MEDIAOBJECT();
    521 }
    522 
    523 void MediaPlayerPrivatePhonon::currentSourceChanged(const Phonon::MediaSource&)
    524 {
    525     notImplemented();
    526     LOG_MEDIAOBJECT();
    527 }
    528 
    529 void MediaPlayerPrivatePhonon::aboutToFinish()
    530 {
    531     notImplemented();
    532     LOG_MEDIAOBJECT();
    533 }
    534 
    535 void MediaPlayerPrivatePhonon::totalTimeChanged(qint64 totalTime)
    536 {
    537 #if OS(WINDOWS)
    538     LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%I64d)", totalTime);
    539 #else
    540     LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%lld)", totalTime);
    541 #endif
    542     LOG_MEDIAOBJECT();
    543 }
    544 
    545 } // namespace WebCore
    546 
    547 #include "moc_MediaPlayerPrivatePhonon.cpp"
    548