| /* |
| Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) |
| Copyright (C) 2009 Apple Inc. All rights reserved. |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public License |
| along with this library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "MediaPlayerPrivatePhonon.h" |
| |
| #include <limits> |
| |
| #include "CString.h" |
| #include "FrameView.h" |
| #include "GraphicsContext.h" |
| #include "NotImplemented.h" |
| #include "TimeRanges.h" |
| #include "Widget.h" |
| #include <wtf/HashSet.h> |
| |
| #include <QDebug> |
| #include <QPainter> |
| #include <QWidget> |
| #include <QMetaEnum> |
| #include <QUrl> |
| #include <QEvent> |
| |
| #include <audiooutput.h> |
| #include <mediaobject.h> |
| #include <videowidget.h> |
| |
| using namespace Phonon; |
| |
| #define LOG_MEDIAOBJECT() (LOG(Media, "%s", debugMediaObject(this, *m_mediaObject).constData())) |
| |
| static QByteArray debugMediaObject(WebCore::MediaPlayerPrivate* mediaPlayer, const MediaObject& mediaObject) |
| { |
| QByteArray byteArray; |
| QTextStream stream(&byteArray); |
| |
| const QMetaObject* metaObj = mediaPlayer->metaObject(); |
| QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState")); |
| |
| stream << "debugMediaObject -> Phonon::MediaObject("; |
| stream << "State: " << phononStates.valueToKey(mediaObject.state()); |
| stream << " | Current time: " << mediaObject.currentTime(); |
| stream << " | Remaining time: " << mediaObject.remainingTime(); |
| stream << " | Total time: " << mediaObject.totalTime(); |
| stream << " | Meta-data: "; |
| QMultiMap<QString, QString> map = mediaObject.metaData(); |
| for (QMap<QString, QString>::const_iterator it = map.constBegin(); |
| it != map.constEnd(); ++it) { |
| stream << "(" << it.key() << ", " << it.value() << ")"; |
| } |
| stream << " | Has video: " << mediaObject.hasVideo(); |
| stream << " | Is seekable: " << mediaObject.isSeekable(); |
| stream << ")"; |
| |
| stream.flush(); |
| |
| return byteArray; |
| } |
| |
| using namespace WTF; |
| |
| namespace WebCore { |
| |
| MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) |
| : m_player(player) |
| , m_networkState(MediaPlayer::Empty) |
| , m_readyState(MediaPlayer::HaveNothing) |
| , m_mediaObject(new MediaObject()) |
| , m_videoWidget(new VideoWidget(0)) |
| , m_audioOutput(new AudioOutput()) |
| , m_isVisible(false) |
| { |
| // Hint to Phonon to disable overlay painting |
| m_videoWidget->setAttribute(Qt::WA_DontShowOnScreen); |
| #if QT_VERSION < 0x040500 |
| m_videoWidget->setAttribute(Qt::WA_QuitOnClose, false); |
| #endif |
| |
| createPath(m_mediaObject, m_videoWidget); |
| createPath(m_mediaObject, m_audioOutput); |
| |
| // Make sure we get updates for each frame |
| m_videoWidget->installEventFilter(this); |
| foreach (QWidget* widget, qFindChildren<QWidget*>(m_videoWidget)) |
| widget->installEventFilter(this); |
| |
| connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), |
| this, SLOT(stateChanged(Phonon::State, Phonon::State))); |
| connect(m_mediaObject, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged())); |
| connect(m_mediaObject, SIGNAL(seekableChanged(bool)), this, SLOT(seekableChanged(bool))); |
| connect(m_mediaObject, SIGNAL(hasVideoChanged(bool)), this, SLOT(hasVideoChanged(bool))); |
| connect(m_mediaObject, SIGNAL(bufferStatus(int)), this, SLOT(bufferStatus(int))); |
| connect(m_mediaObject, SIGNAL(finished()), this, SLOT(finished())); |
| connect(m_mediaObject, SIGNAL(currentSourceChanged(const Phonon::MediaSource&)), |
| this, SLOT(currentSourceChanged(const Phonon::MediaSource&))); |
| connect(m_mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish())); |
| connect(m_mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); |
| } |
| |
| MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) |
| { |
| return new MediaPlayerPrivate(player); |
| } |
| |
| void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) |
| { |
| if (isAvailable()) |
| registrar(create, getSupportedTypes, supportsType); |
| } |
| |
| |
| MediaPlayerPrivate::~MediaPlayerPrivate() |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting videowidget"); |
| m_videoWidget->close(); |
| delete m_videoWidget; |
| m_videoWidget = 0; |
| |
| LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting audiooutput"); |
| delete m_audioOutput; |
| m_audioOutput = 0; |
| |
| LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting mediaobject"); |
| delete m_mediaObject; |
| m_mediaObject = 0; |
| } |
| |
| void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&) |
| { |
| notImplemented(); |
| } |
| |
| MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String&, const String&) |
| { |
| // FIXME: do the real thing |
| notImplemented(); |
| return MediaPlayer::IsNotSupported; |
| } |
| |
| bool MediaPlayerPrivate::hasVideo() const |
| { |
| bool hasVideo = m_mediaObject->hasVideo(); |
| LOG(Media, "MediaPlayerPrivatePhonon::hasVideo() -> %s", hasVideo ? "true" : "false"); |
| return hasVideo; |
| } |
| |
| bool MediaPlayerPrivate::hasAudio() const |
| { |
| // FIXME: Phonon::MediaObject does not have such a hasAudio() function |
| bool hasAudio = true; |
| LOG(Media, "MediaPlayerPrivatePhonon::hasAudio() -> %s", hasAudio ? "true" : "false"); |
| return hasAudio; |
| } |
| |
| void MediaPlayerPrivate::load(const String& url) |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::load(\"%s\")", url.utf8().data()); |
| |
| // We are now loading |
| if (m_networkState != MediaPlayer::Loading) { |
| m_networkState = MediaPlayer::Loading; |
| m_player->networkStateChanged(); |
| } |
| |
| // And we don't have any data yet |
| if (m_readyState != MediaPlayer::HaveNothing) { |
| m_readyState = MediaPlayer::HaveNothing; |
| m_player->readyStateChanged(); |
| } |
| |
| m_mediaObject->setCurrentSource(QUrl(url)); |
| m_audioOutput->setVolume(m_player->volume()); |
| setVisible(m_player->visible()); |
| } |
| |
| void MediaPlayerPrivate::cancelLoad() |
| { |
| notImplemented(); |
| } |
| |
| |
| void MediaPlayerPrivate::play() |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::play()"); |
| m_mediaObject->play(); |
| } |
| |
| void MediaPlayerPrivate::pause() |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::pause()"); |
| m_mediaObject->pause(); |
| } |
| |
| |
| bool MediaPlayerPrivate::paused() const |
| { |
| bool paused = m_mediaObject->state() == Phonon::PausedState; |
| LOG(Media, "MediaPlayerPrivatePhonon::paused() --> %s", paused ? "true" : "false"); |
| return paused; |
| } |
| |
| void MediaPlayerPrivate::seek(float position) |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::seek(%f)", position); |
| |
| if (!m_mediaObject->isSeekable()) |
| return; |
| |
| if (position > duration()) |
| position = duration(); |
| |
| m_mediaObject->seek(position * 1000.0f); |
| } |
| |
| bool MediaPlayerPrivate::seeking() const |
| { |
| return false; |
| } |
| |
| float MediaPlayerPrivate::duration() const |
| { |
| if (m_readyState < MediaPlayer::HaveMetadata) |
| return 0.0f; |
| |
| float duration = m_mediaObject->totalTime() / 1000.0f; |
| |
| if (duration == 0.0f) // We are streaming |
| duration = std::numeric_limits<float>::infinity(); |
| |
| LOG(Media, "MediaPlayerPrivatePhonon::duration() --> %f", duration); |
| return duration; |
| } |
| |
| float MediaPlayerPrivate::currentTime() const |
| { |
| float currentTime = m_mediaObject->currentTime() / 1000.0f; |
| |
| LOG(Media, "MediaPlayerPrivatePhonon::currentTime() --> %f", currentTime); |
| return currentTime; |
| } |
| |
| void MediaPlayerPrivate::setEndTime(float) |
| { |
| notImplemented(); |
| } |
| |
| PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const |
| { |
| notImplemented(); |
| return TimeRanges::create(); |
| } |
| |
| float MediaPlayerPrivate::maxTimeSeekable() const |
| { |
| notImplemented(); |
| return 0.0f; |
| } |
| |
| unsigned MediaPlayerPrivate::bytesLoaded() const |
| { |
| notImplemented(); |
| return 0; |
| } |
| |
| bool MediaPlayerPrivate::totalBytesKnown() const |
| { |
| //notImplemented(); |
| return false; |
| } |
| |
| unsigned MediaPlayerPrivate::totalBytes() const |
| { |
| //notImplemented(); |
| return 0; |
| } |
| |
| void MediaPlayerPrivate::setRate(float) |
| { |
| notImplemented(); |
| } |
| |
| void MediaPlayerPrivate::setVolume(float volume) |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::setVolume()"); |
| m_audioOutput->setVolume(volume); |
| } |
| |
| void MediaPlayerPrivate::setMuted(bool muted) |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::setMuted()"); |
| m_audioOutput->setMuted(muted); |
| } |
| |
| |
| int MediaPlayerPrivate::dataRate() const |
| { |
| // This is not used at the moment |
| return 0; |
| } |
| |
| |
| MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const |
| { |
| const QMetaObject* metaObj = this->metaObject(); |
| QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState")); |
| LOG(Media, "MediaPlayerPrivatePhonon::networkState() --> %s", networkStates.valueToKey(m_networkState)); |
| return m_networkState; |
| } |
| |
| MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const |
| { |
| const QMetaObject* metaObj = this->metaObject(); |
| QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState")); |
| LOG(Media, "MediaPlayerPrivatePhonon::readyState() --> %s", readyStates.valueToKey(m_readyState)); |
| return m_readyState; |
| } |
| |
| void MediaPlayerPrivate::updateStates() |
| { |
| MediaPlayer::NetworkState oldNetworkState = m_networkState; |
| MediaPlayer::ReadyState oldReadyState = m_readyState; |
| |
| Phonon::State phononState = m_mediaObject->state(); |
| |
| if (phononState == Phonon::StoppedState) { |
| if (m_readyState < MediaPlayer::HaveMetadata) { |
| m_networkState = MediaPlayer::Loading; // FIXME: should this be MediaPlayer::Idle? |
| m_readyState = MediaPlayer::HaveMetadata; |
| m_mediaObject->pause(); |
| } |
| } else if (phononState == Phonon::PausedState) { |
| m_networkState = MediaPlayer::Loaded; |
| m_readyState = MediaPlayer::HaveEnoughData; |
| } else if (phononState == Phonon::ErrorState) { |
| if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) { |
| // FIXME: is it possile to differentiate between different types of errors |
| m_networkState = MediaPlayer::NetworkError; |
| m_readyState = MediaPlayer::HaveNothing; |
| cancelLoad(); |
| } else |
| m_mediaObject->pause(); |
| } |
| |
| if (seeking()) |
| m_readyState = MediaPlayer::HaveNothing; |
| |
| if (m_networkState != oldNetworkState) { |
| const QMetaObject* metaObj = this->metaObject(); |
| QMetaEnum networkStates = metaObj->enumerator(metaObj->indexOfEnumerator("NetworkState")); |
| LOG(Media, "Network state changed from '%s' to '%s'", |
| networkStates.valueToKey(oldNetworkState), |
| networkStates.valueToKey(m_networkState)); |
| m_player->networkStateChanged(); |
| } |
| |
| if (m_readyState != oldReadyState) { |
| const QMetaObject* metaObj = this->metaObject(); |
| QMetaEnum readyStates = metaObj->enumerator(metaObj->indexOfEnumerator("ReadyState")); |
| LOG(Media, "Ready state changed from '%s' to '%s'", |
| readyStates.valueToKey(oldReadyState), |
| readyStates.valueToKey(m_readyState)); |
| m_player->readyStateChanged(); |
| } |
| } |
| |
| void MediaPlayerPrivate::setVisible(bool visible) |
| { |
| m_isVisible = visible; |
| LOG(Media, "MediaPlayerPrivatePhonon::setVisible(%s)", visible ? "true" : "false"); |
| |
| m_videoWidget->setVisible(m_isVisible); |
| } |
| |
| void MediaPlayerPrivate::setSize(const IntSize& newSize) |
| { |
| if (!m_videoWidget) |
| return; |
| |
| LOG(Media, "MediaPlayerPrivatePhonon::setSize(%d,%d)", |
| newSize.width(), newSize.height()); |
| |
| QRect currentRect = m_videoWidget->rect(); |
| |
| if (newSize.width() != currentRect.width() || newSize.height() != currentRect.height()) |
| m_videoWidget->resize(newSize.width(), newSize.height()); |
| } |
| |
| IntSize MediaPlayerPrivate::naturalSize() const |
| { |
| if (!hasVideo()) { |
| LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", |
| 0, 0); |
| return IntSize(); |
| } |
| |
| if (m_readyState < MediaPlayer::HaveMetadata) { |
| LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", |
| 0, 0); |
| return IntSize(); |
| } |
| |
| QSize videoSize = m_videoWidget->sizeHint(); |
| IntSize naturalSize(videoSize.width(), videoSize.height()); |
| LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", |
| naturalSize.width(), naturalSize.height()); |
| return naturalSize; |
| } |
| |
| bool MediaPlayerPrivate::eventFilter(QObject* obj, QEvent* event) |
| { |
| if (event->type() == QEvent::UpdateRequest) |
| m_player->repaint(); |
| |
| return QObject::eventFilter(obj, event); |
| } |
| |
| void MediaPlayerPrivate::paint(GraphicsContext* graphicsContect, const IntRect& rect) |
| { |
| if (graphicsContect->paintingDisabled()) |
| return; |
| |
| if (!m_isVisible) |
| return; |
| |
| QPainter* painter = graphicsContect->platformContext(); |
| |
| painter->fillRect(rect, Qt::black); |
| |
| m_videoWidget->render(painter, QPoint(rect.x(), rect.y()), |
| QRegion(0, 0, rect.width(), rect.height())); |
| } |
| |
| // ====================== Phonon::MediaObject signals ====================== |
| |
| void MediaPlayerPrivate::stateChanged(Phonon::State newState, Phonon::State oldState) |
| { |
| const QMetaObject* metaObj = this->metaObject(); |
| QMetaEnum phononStates = metaObj->enumerator(metaObj->indexOfEnumerator("PhononState")); |
| LOG(Media, "MediaPlayerPrivatePhonon::stateChanged(newState=%s, oldState=%s)", |
| phononStates.valueToKey(newState), phononStates.valueToKey(oldState)); |
| |
| updateStates(); |
| } |
| |
| void MediaPlayerPrivate::metaDataChanged() |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::metaDataChanged()"); |
| LOG_MEDIAOBJECT(); |
| } |
| |
| void MediaPlayerPrivate::seekableChanged(bool) |
| { |
| notImplemented(); |
| LOG_MEDIAOBJECT(); |
| } |
| |
| void MediaPlayerPrivate::hasVideoChanged(bool hasVideo) |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::hasVideoChanged(%s)", hasVideo ? "true" : "false"); |
| } |
| |
| void MediaPlayerPrivate::bufferStatus(int) |
| { |
| notImplemented(); |
| LOG_MEDIAOBJECT(); |
| } |
| |
| void MediaPlayerPrivate::finished() |
| { |
| notImplemented(); |
| LOG_MEDIAOBJECT(); |
| } |
| |
| void MediaPlayerPrivate::currentSourceChanged(const Phonon::MediaSource&) |
| { |
| notImplemented(); |
| LOG_MEDIAOBJECT(); |
| } |
| |
| void MediaPlayerPrivate::aboutToFinish() |
| { |
| notImplemented(); |
| LOG_MEDIAOBJECT(); |
| } |
| |
| void MediaPlayerPrivate::totalTimeChanged(qint64 totalTime) |
| { |
| LOG(Media, "MediaPlayerPrivatePhonon::totalTimeChanged(%d)", totalTime); |
| LOG_MEDIAOBJECT(); |
| } |
| |
| } // namespace WebCore |
| |
| #include "moc_MediaPlayerPrivatePhonon.cpp" |