/* This file is part of the KDE project. | |
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). | |
This library is free software: you can redistribute it and/or modify | |
it under the terms of the GNU Lesser General Public License as published by | |
the Free Software Foundation, either version 2.1 or 3 of the License. | |
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 Lesser General Public License for more details. | |
You should have received a copy of the GNU Lesser General Public License | |
along with this library. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#include <QDir> | |
#include <QUrl> | |
#include <QTimer> | |
#include <QWidget> | |
#include <coemain.h> // for CCoeEnv | |
#include "abstractvideoplayer.h" | |
#include "utils.h" | |
#ifndef QT_NO_DEBUG | |
#include "objectdump.h" | |
#endif | |
QT_BEGIN_NAMESPACE | |
using namespace Phonon; | |
using namespace Phonon::MMF; | |
/*! \class MMF::AbstractVideoPlayer | |
\internal | |
*/ | |
//----------------------------------------------------------------------------- | |
// Constructor / destructor | |
//----------------------------------------------------------------------------- | |
MMF::AbstractVideoPlayer::AbstractVideoPlayer(MediaObject *parent, const AbstractPlayer *player) | |
: AbstractMediaPlayer(parent, player) | |
, m_wsSession(CCoeEnv::Static()->WsSession()) | |
, m_screenDevice(*CCoeEnv::Static()->ScreenDevice()) | |
, m_window(0) | |
, m_scaleWidth(1.0) | |
, m_scaleHeight(1.0) | |
, m_totalTime(0) | |
{ | |
} | |
void MMF::AbstractVideoPlayer::construct() | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::AbstractVideoPlayer, EVideoApi); | |
TRACE_ENTRY_0(); | |
if (m_videoOutput) { | |
initVideoOutput(); | |
m_window = m_videoOutput->videoWindow(); | |
} | |
createPlayer(); | |
m_player->RegisterForVideoLoadingNotification(*this); | |
TRACE_EXIT_0(); | |
} | |
MMF::AbstractVideoPlayer::~AbstractVideoPlayer() | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::~AbstractVideoPlayer, EVideoApi); | |
TRACE_ENTRY_0(); | |
// QObject destructor removes all signal-slot connections involving this | |
// object, so we do not need to disconnect from m_videoOutput here. | |
TRACE_EXIT_0(); | |
} | |
CVideoPlayerUtility* MMF::AbstractVideoPlayer::nativePlayer() const | |
{ | |
return m_player.data(); | |
} | |
//----------------------------------------------------------------------------- | |
// Public API | |
//----------------------------------------------------------------------------- | |
void MMF::AbstractVideoPlayer::doPlay() | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::doPlay, EVideoApi); | |
handlePendingParametersChanged(); | |
m_player->Play(); | |
} | |
void MMF::AbstractVideoPlayer::doPause() | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::doPause, EVideoApi); | |
TRAPD(err, m_player->PauseL()); | |
if (KErrNone != err && state() != ErrorState) { | |
TRACE("PauseL error %d", err); | |
setError(tr("Pause failed"), err); | |
} | |
} | |
void MMF::AbstractVideoPlayer::doStop() | |
{ | |
m_player->Stop(); | |
} | |
void MMF::AbstractVideoPlayer::doSeek(qint64 ms) | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::doSeek, EVideoApi); | |
TRAPD(err, m_player->SetPositionL(TTimeIntervalMicroSeconds(ms * 1000))); | |
if (KErrNone != err) | |
setError(tr("Seek failed"), err); | |
} | |
int MMF::AbstractVideoPlayer::setDeviceVolume(int mmfVolume) | |
{ | |
TRAPD(err, m_player->SetVolumeL(mmfVolume)); | |
return err; | |
} | |
int MMF::AbstractVideoPlayer::openFile(const QString &fileName) | |
{ | |
const QHBufC nativeFileName(QDir::toNativeSeparators(fileName)); | |
TRAPD(err, m_player->OpenFileL(*nativeFileName)); | |
return err; | |
} | |
int MMF::AbstractVideoPlayer::openFile(RFile &file) | |
{ | |
TRAPD(err, m_player->OpenFileL(file)); | |
return err; | |
} | |
int MMF::AbstractVideoPlayer::openUrl(const QString &url, int iap) | |
{ | |
TRAPD(err, m_player->OpenUrlL(qt_QString2TPtrC(url), iap)); | |
return err; | |
} | |
int MMF::AbstractVideoPlayer::openDescriptor(const TDesC8 &des) | |
{ | |
TRAPD(err, m_player->OpenDesL(des)); | |
return err; | |
} | |
int MMF::AbstractVideoPlayer::bufferStatus() const | |
{ | |
int result = 0; | |
TRAP_IGNORE(m_player->GetVideoLoadingProgressL(result)); | |
return result; | |
} | |
void MMF::AbstractVideoPlayer::doClose() | |
{ | |
m_player->Close(); | |
} | |
bool MMF::AbstractVideoPlayer::hasVideo() const | |
{ | |
return true; | |
} | |
qint64 MMF::AbstractVideoPlayer::getCurrentTime() const | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::getCurrentTime, EVideoApi); | |
TTimeIntervalMicroSeconds us; | |
TRAPD(err, us = m_player->PositionL()) | |
qint64 result = 0; | |
if (KErrNone == err) { | |
result = toMilliSeconds(us); | |
} else { | |
TRACE("PositionL error %d", err); | |
// If we don't cast away constness here, we simply have to ignore | |
// the error. | |
const_cast<AbstractVideoPlayer*>(this)->setError(tr("Getting position failed"), err); | |
} | |
return result; | |
} | |
qint64 MMF::AbstractVideoPlayer::totalTime() const | |
{ | |
return m_totalTime; | |
} | |
//----------------------------------------------------------------------------- | |
// Public slots | |
//----------------------------------------------------------------------------- | |
void MMF::AbstractVideoPlayer::videoWindowChanged() | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::videoWindowChanged, EVideoInternal); | |
TRACE_ENTRY("state %d", state()); | |
m_window = m_videoOutput ? m_videoOutput->videoWindow() : 0; | |
if (m_videoOutput) | |
m_videoOutput->dump(); | |
handleVideoWindowChanged(); | |
TRACE_EXIT_0(); | |
} | |
void MMF::AbstractVideoPlayer::aspectRatioChanged() | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::aspectRatioChanged, EVideoInternal); | |
TRACE_ENTRY("state %d aspectRatio %d", state()); | |
if (m_videoOutput) | |
updateScaleFactors(m_videoOutput->videoWindowSize()); | |
TRACE_EXIT_0(); | |
} | |
void MMF::AbstractVideoPlayer::scaleModeChanged() | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::scaleModeChanged, EVideoInternal); | |
TRACE_ENTRY("state %d", state()); | |
if (m_videoOutput) | |
updateScaleFactors(m_videoOutput->videoWindowSize()); | |
TRACE_EXIT_0(); | |
} | |
//----------------------------------------------------------------------------- | |
// MVideoPlayerUtilityObserver callbacks | |
//----------------------------------------------------------------------------- | |
void MMF::AbstractVideoPlayer::MvpuoOpenComplete(TInt aError) | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::MvpuoOpenComplete, EVideoApi); | |
TRACE_ENTRY("state %d error %d", state(), aError); | |
__ASSERT_ALWAYS(LoadingState == state() || | |
progressiveDownloadStalled() && BufferingState == state(), | |
Utils::panic(InvalidStatePanic)); | |
if (KErrNone == aError) | |
m_player->Prepare(); | |
else | |
setError(tr("Opening clip failed"), aError); | |
TRACE_EXIT_0(); | |
} | |
void MMF::AbstractVideoPlayer::MvpuoPrepareComplete(TInt aError) | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::MvpuoPrepareComplete, EVideoApi); | |
TRACE_ENTRY("state %d error %d", state(), aError); | |
__ASSERT_ALWAYS(LoadingState == state() || | |
progressiveDownloadStalled() && BufferingState == state(), | |
Utils::panic(InvalidStatePanic)); | |
TRAPD(err, getVideoClipParametersL(aError)); | |
if (KErrNone == err) { | |
if (m_videoOutput) | |
m_videoOutput->dump(); | |
maxVolumeChanged(m_player->MaxVolume()); | |
if (m_videoOutput) | |
m_videoOutput->setVideoSize(m_videoFrameSize); | |
prepareCompleted(); | |
handlePendingParametersChanged(); | |
emit totalTimeChanged(totalTime()); | |
} | |
loadingComplete(aError); | |
TRACE_EXIT_0(); | |
} | |
void MMF::AbstractVideoPlayer::getVideoClipParametersL(TInt aError) | |
{ | |
User::LeaveIfError(aError); | |
// Get frame size | |
TSize size; | |
m_player->VideoFrameSizeL(size); | |
m_videoFrameSize = QSize(size.iWidth, size.iHeight); | |
// Get duration | |
m_totalTime = toMilliSeconds(m_player->DurationL()); | |
} | |
void MMF::AbstractVideoPlayer::MvpuoFrameReady(CFbsBitmap &aFrame, TInt aError) | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::MvpuoFrameReady, EVideoApi); | |
TRACE_ENTRY("state %d error %d", state(), aError); | |
Q_UNUSED(aFrame); | |
Q_UNUSED(aError); // suppress warnings in release builds | |
TRACE_EXIT_0(); | |
} | |
void MMF::AbstractVideoPlayer::MvpuoPlayComplete(TInt aError) | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::MvpuoPlayComplete, EVideoApi) | |
TRACE_ENTRY("state %d error %d", state(), aError); | |
// Call base class function which handles end of playback for both | |
// audio and video clips. | |
playbackComplete(aError); | |
TRACE_EXIT_0(); | |
} | |
void MMF::AbstractVideoPlayer::MvpuoEvent(const TMMFEvent &aEvent) | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::MvpuoEvent, EVideoApi); | |
TRACE_ENTRY("state %d", state()); | |
Q_UNUSED(aEvent); | |
TRACE_EXIT_0(); | |
} | |
//----------------------------------------------------------------------------- | |
// MVideoLoadingObserver callbacks | |
//----------------------------------------------------------------------------- | |
void MMF::AbstractVideoPlayer::MvloLoadingStarted() | |
{ | |
bufferingStarted(); | |
} | |
void MMF::AbstractVideoPlayer::MvloLoadingComplete() | |
{ | |
bufferingComplete(); | |
} | |
//----------------------------------------------------------------------------- | |
// Video window updates | |
//----------------------------------------------------------------------------- | |
void MMF::AbstractVideoPlayer::videoOutputChanged() | |
{ | |
TRACE_CONTEXT(AbstractVideoPlayer::videoOutputChanged, EVideoInternal); | |
TRACE_ENTRY_0(); | |
if (m_videoOutput) | |
initVideoOutput(); | |
videoWindowChanged(); | |
TRACE_EXIT_0(); | |
} | |
void MMF::AbstractVideoPlayer::initVideoOutput() | |
{ | |
Q_ASSERT(m_videoOutput); | |
bool connected = connect( | |
m_videoOutput, SIGNAL(videoWindowChanged()), | |
this, SLOT(videoWindowChanged()) | |
); | |
Q_ASSERT(connected); | |
connected = connect( | |
m_videoOutput, SIGNAL(aspectRatioChanged()), | |
this, SLOT(aspectRatioChanged()) | |
); | |
Q_ASSERT(connected); | |
connected = connect( | |
m_videoOutput, SIGNAL(scaleModeChanged()), | |
this, SLOT(scaleModeChanged()) | |
); | |
Q_ASSERT(connected); | |
// Suppress warnings in release builds | |
Q_UNUSED(connected); | |
m_videoOutput->setVideoSize(m_videoFrameSize); | |
} | |
// Helper function for aspect ratio / scale mode handling | |
QSize scaleToAspect(const QSize &srcRect, int aspectWidth, int aspectHeight) | |
{ | |
const qreal aspectRatio = qreal(aspectWidth) / aspectHeight; | |
int width = srcRect.width(); | |
int height = srcRect.width() / aspectRatio; | |
if (height > srcRect.height()){ | |
height = srcRect.height(); | |
width = srcRect.height() * aspectRatio; | |
} | |
return QSize(width, height); | |
} | |
void MMF::AbstractVideoPlayer::updateScaleFactors(const QSize &windowSize, bool apply) | |
{ | |
Q_ASSERT(m_videoOutput); | |
if (m_videoFrameSize.isValid()) { | |
QRect videoRect; | |
// Calculate size of smallest rect which contains video frame size | |
// and conforms to aspect ratio | |
switch (m_videoOutput->aspectRatio()) { | |
case Phonon::VideoWidget::AspectRatioAuto: | |
videoRect.setSize(m_videoFrameSize); | |
break; | |
case Phonon::VideoWidget::AspectRatioWidget: | |
videoRect.setSize(windowSize); | |
break; | |
case Phonon::VideoWidget::AspectRatio4_3: | |
videoRect.setSize(scaleToAspect(m_videoFrameSize, 4, 3)); | |
break; | |
case Phonon::VideoWidget::AspectRatio16_9: | |
videoRect.setSize(scaleToAspect(m_videoFrameSize, 16, 9)); | |
break; | |
} | |
// Scale to fill the window width | |
const int windowWidth = windowSize.width(); | |
const int windowHeight = windowSize.height(); | |
const qreal windowScaleFactor = qreal(windowWidth) / videoRect.width(); | |
int videoWidth = windowWidth; | |
int videoHeight = videoRect.height() * windowScaleFactor; | |
const qreal windowToVideoHeightRatio = qreal(windowHeight) / videoHeight; | |
switch (m_videoOutput->scaleMode()) { | |
case Phonon::VideoWidget::ScaleAndCrop: | |
if (videoHeight < windowHeight) { | |
videoWidth *= windowToVideoHeightRatio; | |
videoHeight = windowHeight; | |
} | |
break; | |
case Phonon::VideoWidget::FitInView: | |
default: | |
if (videoHeight > windowHeight) { | |
videoWidth *= windowToVideoHeightRatio; | |
videoHeight = windowHeight; | |
} | |
break; | |
} | |
// Calculate scale factors | |
m_scaleWidth = 100.0f * videoWidth / m_videoFrameSize.width(); | |
m_scaleHeight = 100.0f * videoHeight / m_videoFrameSize.height(); | |
if (apply) | |
parametersChanged(ScaleFactors); | |
} | |
} | |
void MMF::AbstractVideoPlayer::parametersChanged(VideoParameters parameters) | |
{ | |
if (state() == LoadingState || progressiveDownloadStalled() && BufferingState == state()) | |
m_pendingChanges |= parameters; | |
else | |
handleParametersChanged(parameters); | |
} | |
void MMF::AbstractVideoPlayer::handlePendingParametersChanged() | |
{ | |
if (m_pendingChanges) | |
handleParametersChanged(m_pendingChanges); | |
m_pendingChanges = 0; | |
} | |
//----------------------------------------------------------------------------- | |
// Metadata | |
//----------------------------------------------------------------------------- | |
int MMF::AbstractVideoPlayer::numberOfMetaDataEntries() const | |
{ | |
int numberOfEntries = 0; | |
TRAP_IGNORE(numberOfEntries = m_player->NumberOfMetaDataEntriesL()); | |
return numberOfEntries; | |
} | |
QPair<QString, QString> MMF::AbstractVideoPlayer::metaDataEntry(int index) const | |
{ | |
CMMFMetaDataEntry *entry = 0; | |
QT_TRAP_THROWING(entry = m_player->MetaDataEntryL(index)); | |
return QPair<QString, QString>(qt_TDesC2QString(entry->Name()), qt_TDesC2QString(entry->Value())); | |
} | |
QT_END_NAMESPACE | |