/* 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 #include #include #include // 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(); 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(RFile &file) { TRAPD(err, m_player->OpenFileL(file)); return err; } int MMF::AbstractVideoPlayer::openUrl(const QString &url) { TRAPD(err, m_player->OpenUrlL(qt_QString2TPtrC(url))); return err; } int MMF::AbstractVideoPlayer::bufferStatus() const { int result = 0; TRAP_IGNORE(m_player->GetVideoLoadingProgressL(result)); return result; } void MMF::AbstractVideoPlayer::close() { m_player->Close(); } bool MMF::AbstractVideoPlayer::hasVideo() const { return true; } qint64 MMF::AbstractVideoPlayer::currentTime() const { TRACE_CONTEXT(AbstractVideoPlayer::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(tr("Getting position failed"), err); } return result; } qint64 MMF::AbstractVideoPlayer::totalTime() const { return m_totalTime; } //----------------------------------------------------------------------------- // Public slots //----------------------------------------------------------------------------- void MMF::AbstractVideoPlayer::videoWindowChanged() { TRACE_CONTEXT(AbstractVideoPlayer::videoOutputRegionChanged, EVideoInternal); TRACE_ENTRY("state %d", state()); m_window = m_videoOutput ? m_videoOutput->videoWindow() : 0; handleVideoWindowChanged(); TRACE_EXIT_0(); } void MMF::AbstractVideoPlayer::aspectRatioChanged() { TRACE_CONTEXT(AbstractVideoPlayer::aspectRatioChanged, EVideoInternal); TRACE_ENTRY("state %d aspectRatio %d", state()); updateScaleFactors(m_videoOutput->videoWindowSize()); TRACE_EXIT_0(); } void MMF::AbstractVideoPlayer::scaleModeChanged() { TRACE_CONTEXT(AbstractVideoPlayer::scaleModeChanged, EVideoInternal); TRACE_ENTRY("state %d", state()); 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(), 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(), Utils::panic(InvalidStatePanic)); TRAPD(err, getVideoClipParametersL(aError)); if (KErrNone == err) { maxVolumeChanged(m_player->MaxVolume()); if (m_videoOutput) m_videoOutput->setVideoSize(m_videoFrameSize); prepareCompleted(); handlePendingParametersChanged(); emit totalTimeChanged(totalTime()); changeState(StoppedState); } else { setError(tr("Buffering clip failed"), err); } 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() { 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); // Do these after all connections are complete, to ensure // that any signals generated get to their destinations. m_videoOutput->winId(); 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) { 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) 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 MMF::AbstractVideoPlayer::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