/* 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 .
*/
#include // for QApplication::activeWindow
#include
#include
#include
#include // for CCoeEnv
#include
#include "mmf_videoplayer.h"
#include "utils.h"
#ifndef QT_NO_DEBUG
#include "objectdump.h"
#endif
QT_BEGIN_NAMESPACE
using namespace Phonon;
using namespace Phonon::MMF;
/*! \class MMF::VideoPlayer
\internal
*/
//-----------------------------------------------------------------------------
// Constructor / destructor
//-----------------------------------------------------------------------------
MMF::VideoPlayer::VideoPlayer()
: m_wsSession(CCoeEnv::Static()->WsSession())
, m_screenDevice(*CCoeEnv::Static()->ScreenDevice())
, m_window(0)
, m_totalTime(0)
, m_mmfOutputChangePending(false)
{
construct();
}
MMF::VideoPlayer::VideoPlayer(const AbstractPlayer& player)
: AbstractMediaPlayer(player)
, m_wsSession(CCoeEnv::Static()->WsSession())
, m_screenDevice(*CCoeEnv::Static()->ScreenDevice())
, m_window(0)
, m_totalTime(0)
, m_mmfOutputChangePending(false)
{
construct();
}
void MMF::VideoPlayer::construct()
{
TRACE_CONTEXT(VideoPlayer::VideoPlayer, EVideoApi);
TRACE_ENTRY_0();
if (m_videoOutput)
m_videoOutput->setObserver(this);
const TInt priority = 0;
const TMdaPriorityPreference preference = EMdaPriorityPreferenceNone;
// Ignore return value - first call must always return true
getNativeWindowSystemHandles();
// TODO: is this the correct way to handle errors which occur when
// creating a Symbian object in the constructor of a Qt object?
// TODO: check whether videoOutput is visible? If not, then the
// corresponding window will not be active, meaning that the
// clipping region will be set to empty and the video will not be
// visible. If this is the case, we should set m_mmfOutputChangePending
// and respond to future showEvents from the videoOutput widget.
TRAPD(err,
m_player.reset(CVideoPlayerUtility::NewL
(
*this,
priority, preference,
m_wsSession, m_screenDevice,
*m_window,
m_rect, m_rect
))
);
if (KErrNone != err)
changeState(ErrorState);
TRACE_EXIT_0();
}
MMF::VideoPlayer::~VideoPlayer()
{
TRACE_CONTEXT(VideoPlayer::~VideoPlayer, EVideoApi);
TRACE_ENTRY_0();
if (m_videoOutput)
m_videoOutput->setObserver(0);
TRACE_EXIT_0();
}
//-----------------------------------------------------------------------------
// Public API
//-----------------------------------------------------------------------------
void MMF::VideoPlayer::doPlay()
{
TRACE_CONTEXT(VideoPlayer::doPlay, EVideoApi);
// See comment in updateMmfOutput
if (m_mmfOutputChangePending) {
TRACE_0("MMF output change pending - pushing now");
updateMmfOutput();
}
m_player->Play();
}
void MMF::VideoPlayer::doPause()
{
TRACE_CONTEXT(VideoPlayer::doPause, EVideoApi);
TRAPD(err, m_player->PauseL());
if (KErrNone != err) {
TRACE("PauseL error %d", err);
setError(NormalError);
}
}
void MMF::VideoPlayer::doStop()
{
m_player->Stop();
}
void MMF::VideoPlayer::doSeek(qint64 ms)
{
TRACE_CONTEXT(VideoPlayer::doSeek, EVideoApi);
bool wasPlaying = false;
if (state() == PlayingState) {
// The call to SetPositionL does not have any effect if playback is
// ongoing, so we pause before seeking.
doPause();
wasPlaying = true;
}
TRAPD(err, m_player->SetPositionL(TTimeIntervalMicroSeconds(ms * 1000)));
if (KErrNone == err) {
if (wasPlaying)
doPlay();
}
else {
TRACE("SetPositionL error %d", err);
setError(NormalError);
}
}
int MMF::VideoPlayer::setDeviceVolume(int mmfVolume)
{
TRAPD(err, m_player->SetVolumeL(mmfVolume));
return err;
}
int MMF::VideoPlayer::openFile(RFile& file)
{
TRAPD(err, m_player->OpenFileL(file));
return err;
}
void MMF::VideoPlayer::close()
{
m_player->Close();
}
bool MMF::VideoPlayer::hasVideo() const
{
return true;
}
qint64 MMF::VideoPlayer::currentTime() const
{
TRACE_CONTEXT(VideoPlayer::currentTime, 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(this)->setError(NormalError);
}
return result;
}
qint64 MMF::VideoPlayer::totalTime() const
{
return m_totalTime;
}
//-----------------------------------------------------------------------------
// MVideoPlayerUtilityObserver callbacks
//-----------------------------------------------------------------------------
void MMF::VideoPlayer::MvpuoOpenComplete(TInt aError)
{
TRACE_CONTEXT(VideoPlayer::MvpuoOpenComplete, EVideoApi);
TRACE_ENTRY("state %d error %d", state(), aError);
__ASSERT_ALWAYS(LoadingState == state(), Utils::panic(InvalidStatePanic));
if (KErrNone == aError)
m_player->Prepare();
else
setError(NormalError);
TRACE_EXIT_0();
}
void MMF::VideoPlayer::MvpuoPrepareComplete(TInt aError)
{
TRACE_CONTEXT(VideoPlayer::MvpuoPrepareComplete, EVideoApi);
TRACE_ENTRY("state %d error %d", state(), aError);
__ASSERT_ALWAYS(LoadingState == state(), Utils::panic(InvalidStatePanic));
TRAPD(err, doPrepareCompleteL(aError));
if (KErrNone == err) {
maxVolumeChanged(m_player->MaxVolume());
if (m_videoOutput)
m_videoOutput->setFrameSize(m_frameSize);
// See comment in updateMmfOutput
if (m_mmfOutputChangePending) {
TRACE_0("MMF output change pending - pushing now");
updateMmfOutput();
}
emit totalTimeChanged(totalTime());
changeState(StoppedState);
} else {
setError(NormalError);
}
TRACE_EXIT_0();
}
void MMF::VideoPlayer::doPrepareCompleteL(TInt aError)
{
User::LeaveIfError(aError);
// Get frame size
TSize size;
m_player->VideoFrameSizeL(size);
m_frameSize = QSize(size.iWidth, size.iHeight);
// Get duration
m_totalTime = toMilliSeconds(m_player->DurationL());
}
void MMF::VideoPlayer::MvpuoFrameReady(CFbsBitmap &aFrame, TInt aError)
{
TRACE_CONTEXT(VideoPlayer::MvpuoFrameReady, EVideoApi);
TRACE_ENTRY("state %d error %d", state(), aError);
// TODO
Q_UNUSED(aFrame);
Q_UNUSED(aError); // suppress warnings in release builds
TRACE_EXIT_0();
}
void MMF::VideoPlayer::MvpuoPlayComplete(TInt aError)
{
TRACE_CONTEXT(VideoPlayer::MvpuoPlayComplete, EVideoApi)
TRACE_ENTRY("state %d error %d", state(), aError);
Q_UNUSED(aError); // suppress warnings in release builds
changeState(StoppedState);
TRACE_EXIT_0();
}
void MMF::VideoPlayer::MvpuoEvent(const TMMFEvent &aEvent)
{
TRACE_CONTEXT(VideoPlayer::MvpuoEvent, EVideoApi);
TRACE_ENTRY("state %d", state());
// TODO
Q_UNUSED(aEvent);
TRACE_EXIT_0();
}
//-----------------------------------------------------------------------------
// VideoOutputObserver
//-----------------------------------------------------------------------------
void MMF::VideoPlayer::videoOutputRegionChanged()
{
TRACE_CONTEXT(VideoPlayer::videoOutputRegionChanged, EVideoInternal);
TRACE_ENTRY("state %d", state());
const bool changed = getNativeWindowSystemHandles();
// See comment in updateMmfOutput
if (changed) {
if (state() == LoadingState)
m_mmfOutputChangePending = true;
else
updateMmfOutput();
}
TRACE_EXIT_0();
}
#ifndef QT_NO_DEBUG
// The following code is for debugging problems related to video visibility. It allows
// the VideoPlayer instance to query the window server in order to determine the
// DSA drawing region for the video window.
class CDummyAO : public CActive
{
public:
CDummyAO() : CActive(CActive::EPriorityStandard) { CActiveScheduler::Add(this); }
void RunL() { }
void DoCancel() { }
TRequestStatus& Status() { return iStatus; }
void SetActive() { CActive::SetActive(); }
};
void getDsaRegion(RWsSession &session, const RWindowBase &window)
{
RDirectScreenAccess dsa(session);
TInt err = dsa.Construct();
CDummyAO ao;
RRegion* region;
err = dsa.Request(region, ao.Status(), window);
ao.SetActive();
dsa.Close();
ao.Cancel();
if (region) {
qDebug() << "Phonon::MMF::getDsaRegion count" << region->Count();
for (int i=0; iCount(); ++i) {
const TRect& rect = region->RectangleList()[i];
qDebug() << "Phonon::MMF::getDsaRegion rect"
<< rect.iTl.iX << rect.iTl.iY << rect.iBr.iX << rect.iBr.iY;
}
region->Close();
}
}
#endif // _DEBUG
void MMF::VideoPlayer::updateMmfOutput()
{
TRACE_CONTEXT(VideoPlayer::updateMmfOutput, EVideoInternal);
TRACE_ENTRY_0();
// Calling SetDisplayWindowL is a no-op unless the MMF controller has
// been loaded, so we shouldn't do it. Instead, the
// m_mmfOutputChangePending flag is used to record the fact that we
// need to call SetDisplayWindowL, and this is checked in
// MvpuoPrepareComplete, at which point the MMF controller has been
// loaded.
#ifndef QT_NO_DEBUG
getDsaRegion(m_wsSession, *m_window);
#endif
TRAPD(err,
m_player->SetDisplayWindowL
(
m_wsSession, m_screenDevice,
*m_window,
m_rect, m_rect
)
);
if (KErrNone != err) {
TRACE("SetDisplayWindowL error %d", err);
setError(NormalError);
}
m_mmfOutputChangePending = false;
TRACE_EXIT_0();
}
//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------
void MMF::VideoPlayer::videoOutputChanged()
{
TRACE_CONTEXT(VideoPlayer::videoOutputChanged, EVideoInternal);
TRACE_ENTRY_0();
if (m_videoOutput) {
m_videoOutput->setObserver(this);
m_videoOutput->setFrameSize(m_frameSize);
}
videoOutputRegionChanged();
TRACE_EXIT_0();
}
bool MMF::VideoPlayer::getNativeWindowSystemHandles()
{
TRACE_CONTEXT(VideoPlayer::getNativeWindowSystemHandles, EVideoInternal);
TRACE_ENTRY_0();
CCoeControl *control = 0;
if (m_videoOutput)
// Create native window
control = m_videoOutput->winId();
else
// Get top-level window
control = QApplication::activeWindow()->effectiveWinId();
#ifndef QT_NO_DEBUG
if (m_videoOutput) {
QScopedPointer dumper(new ObjectDump::QDumper);
dumper->setPrefix("Phonon::MMF"); // to aid searchability of logs
ObjectDump::addDefaultAnnotators(*dumper);
TRACE_0("Dumping VideoOutput:");
dumper->dumpObject(*m_videoOutput);
}
else {
TRACE_0("m_videoOutput is null - dumping top-level control info:");
TRACE("control %08x", control);
TRACE("control.parent %08x", control->Parent());
TRACE("control.isVisible %d", control->IsVisible());
TRACE("control.rect %d,%d %dx%d",
control->Position().iX, control->Position().iY,
control->Size().iWidth, control->Size().iHeight);
TRACE("control.ownsWindow %d", control->OwnsWindow());
}
#endif
RWindowBase *const window = control->DrawableWindow();
const TRect rect(window->AbsPosition(), window->Size());
TRACE("rect %d %d - %d %d",
rect.iTl.iX, rect.iTl.iY,
rect.iBr.iX, rect.iBr.iY);
bool changed = false;
if (window != m_window || rect != m_rect) {
m_window = window;
m_rect = rect;
changed = true;
}
TRACE_RETURN("changed %d", changed);
}
int MMF::VideoPlayer::numberOfMetaDataEntries() const
{
int numberOfEntries = 0;
TRAP_IGNORE(numberOfEntries = m_player->NumberOfMetaDataEntriesL());
return numberOfEntries;
}
QPair MMF::VideoPlayer::metaDataEntry(int index) const
{
CMMFMetaDataEntry *entry = 0;
QT_TRAP_THROWING(entry = m_player->MetaDataEntryL(index));
return QPair(qt_TDesC2QString(entry->Name()), qt_TDesC2QString(entry->Value()));
}
QT_END_NAMESPACE