src/3rdparty/phonon/mmf/mmf_videoplayer.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/3rdparty/phonon/mmf/mmf_videoplayer.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,492 @@
+/*  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 <QApplication>    // for QApplication::activeWindow
+#include <QUrl>
+#include <QTimer>
+#include <QWidget>
+
+#include <coemain.h>    // for CCoeEnv
+#include <coecntrl.h>
+
+#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();
+
+    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<VideoPlayer*>(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; i<region->Count(); ++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<ObjectDump::QDumper> 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);
+}
+
+
+QT_END_NAMESPACE
+