diff -r 000000000000 -r 1918ee327afb src/3rdparty/phonon/qt7/mediaobject.mm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/3rdparty/phonon/qt7/mediaobject.mm Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,982 @@ +/* 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 "mediaobject.h" +#include "backendheader.h" +#include "videowidget.h" +#include "videoframe.h" +#include "audiooutput.h" +#include "quicktimevideoplayer.h" +#include "quicktimemetadata.h" +#include "audiograph.h" +#include "mediaobjectaudionode.h" +#include "quicktimeaudioplayer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaObject::MediaObject(QObject *parent) : MediaNode(AudioSource | VideoSource, parent) +{ + m_owningMediaObject = this; + m_state = Phonon::LoadingState; + + m_videoPlayer = new QuickTimeVideoPlayer(); + m_audioPlayer = new QuickTimeAudioPlayer(); + m_nextVideoPlayer = new QuickTimeVideoPlayer(); + m_nextAudioPlayer = new QuickTimeAudioPlayer(); + m_mediaObjectAudioNode = new MediaObjectAudioNode(m_audioPlayer, m_nextAudioPlayer); + setAudioNode(m_mediaObjectAudioNode); + + m_audioGraph = new AudioGraph(this); + + m_tickInterval = 0; + m_prefinishMark = 0; + m_currentTime = 0; + m_transitionTime = 0; + m_percentageLoaded = 0; + m_waitNextSwap = false; + m_autoplayTitles = true; + m_audioEffectCount = 0; + m_audioOutputCount = 0; + m_videoEffectCount = 0; + m_videoOutputCount = 0; + m_audioSystem = AS_Unset; + m_errorType = Phonon::NoError; + + m_tickTimer = 0; + m_videoTimer = 0; + m_audioTimer = 0; + m_rapidTimer = 0; + +#if QT_ALLOW_QUICKTIME + m_displayLink = 0; + m_pendingDisplayLinkEvent = false; +#endif + + checkForError(); +} + +MediaObject::~MediaObject() +{ + // m_mediaObjectAudioNode is owned by super class. +#if QT_ALLOW_QUICKTIME + stopDisplayLink(); +#endif + m_audioPlayer->unsetVideoPlayer(); + m_nextAudioPlayer->unsetVideoPlayer(); + delete m_videoPlayer; + delete m_nextVideoPlayer; + checkForError(); +} + +bool MediaObject::setState(Phonon::State state) +{ + Phonon::State prevState = m_state; + m_state = state; + if (prevState != m_state){ + emit stateChanged(m_state, prevState); + if (m_state != state){ + // End-application did something + // upon receiving the signal. + return false; + } + } + return true; +} + +void MediaObject::inspectAudioGraphRecursive(AudioConnection *connection, int &effectCount, int &outputCount) +{ + if ((connection->m_sink->m_description & (AudioSource | AudioSink)) == (AudioSource | AudioSink)) + ++effectCount; + else if (connection->m_sink->m_description & AudioSink) + ++outputCount; + + for (int i=0; im_sink->m_audioSinkList.size(); ++i) + inspectAudioGraphRecursive(connection->m_sink->m_audioSinkList[i], effectCount, outputCount); +} + +void MediaObject::inspectVideoGraphRecursive(MediaNode *node, int &effectCount, int &outputCount) +{ + if ((node->m_description & (VideoSource | VideoSink)) == (VideoSource | VideoSink)) + ++effectCount; + else if (node->m_description & VideoSink) + ++outputCount; + + for (int i=0; im_videoSinkList.size(); ++i) + inspectVideoGraphRecursive(node->m_videoSinkList[i], effectCount, outputCount); +} + +void MediaObject::inspectGraph() +{ + // Inspect the graph to check wether there are any + // effects or outputs connected. This will have + // influence on the audio system and video system that ends up beeing used: + int prevVideoOutputCount = m_videoOutputCount; + m_audioEffectCount = 0; + m_audioOutputCount = 0; + m_videoEffectCount = 0; + m_videoOutputCount = 0; + AudioConnection rootConnection(this); + inspectAudioGraphRecursive(&rootConnection, m_audioEffectCount, m_audioOutputCount); + inspectVideoGraphRecursive(this, m_videoEffectCount, m_videoOutputCount); + + if (m_videoOutputCount != prevVideoOutputCount){ + MediaNodeEvent e1(MediaNodeEvent::VideoOutputCountChanged, &m_videoOutputCount); + notify(&e1); + } +} + +void MediaObject::setupAudioSystem() +{ + // Select which audio system to use: + AudioSystem newAudioSystem = AS_Unset; + if (!m_audioOutputCount || !m_videoPlayer->canPlayMedia()){ + newAudioSystem = AS_Silent; + } else if (m_audioEffectCount == 0){ + newAudioSystem = AS_Video; + } else if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_4){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for Mac OS 10.3 and below", NORMAL_ERROR); + } else if (m_videoPlayer->isDrmProtected()){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for DRM protected media", NORMAL_ERROR); + } else if (m_audioGraph->graphCannotPlay()){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for the current codec", NORMAL_ERROR); +#ifdef QUICKTIME_C_API_AVAILABLE + } else { + newAudioSystem = AS_Graph; + } +#else + } else { + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for the 64-bit version of the Phonon QT7 backend", NORMAL_ERROR); + } +#endif + + if (newAudioSystem == m_audioSystem) + return; + + // Enable selected audio system: + m_audioSystem = newAudioSystem; + switch (newAudioSystem){ + case AS_Silent: + m_audioGraph->stop(); + m_videoPlayer->enableAudio(false); + m_nextVideoPlayer->enableAudio(false); + m_audioPlayer->enableAudio(false); + m_nextAudioPlayer->enableAudio(false); + break; + case AS_Graph: + if (m_state == Phonon::PausedState) + m_audioGraph->prepare(); + else + m_audioGraph->start(); + // Starting the graph can lead to a recursive call + // telling us that we must direct audio through + // video. If that has happened, we must not proceed: + if (m_audioSystem != AS_Graph) + return; + m_videoPlayer->enableAudio(false); + m_nextVideoPlayer->enableAudio(false); + m_audioPlayer->enableAudio(true); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_nextAudioPlayer->enableAudio(true); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime()); + break; + case AS_Video: + case AS_Unset: + m_audioGraph->stop(); + m_videoPlayer->enableAudio(true); + m_nextVideoPlayer->enableAudio(true); + m_audioPlayer->enableAudio(false); + m_nextAudioPlayer->enableAudio(false); + m_videoPlayer->seek(m_audioPlayer->currentTime()); + m_nextVideoPlayer->seek(m_nextAudioPlayer->currentTime()); + break; + } +} + +void MediaObject::setSource(const MediaSource &source) +{ + IMPLEMENTED; + PhononAutoReleasePool pool; + setState(Phonon::LoadingState); + + // Save current state for event/signal handling below: + bool prevHasVideo = m_videoPlayer->hasVideo(); + qint64 prevTotalTime = totalTime(); + int prevTrackCount = m_videoPlayer->trackCount(); + m_waitNextSwap = false; + + // Cancel cross-fade if any: + m_nextVideoPlayer->pause(); + m_nextAudioPlayer->pause(); + m_mediaObjectAudioNode->cancelCrossFade(); + + // Set new source: + m_audioPlayer->unsetVideoPlayer(); + m_videoPlayer->setMediaSource(source); + m_audioPlayer->setVideoPlayer(m_videoPlayer); + + m_audioGraph->updateStreamSpecifications(); + m_nextAudioPlayer->unsetVideoPlayer(); + m_nextVideoPlayer->unsetCurrentMediaSource(); + m_currentTime = 0; + + // Emit/notify information about the new source: + QRect videoRect = m_videoPlayer->videoRect(); + MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); + notify(&e1); + + // Clear video widgets: + VideoFrame emptyFrame; + updateVideo(emptyFrame); + + emit currentSourceChanged(source); + emit metaDataChanged(m_videoPlayer->metaData()); + + if (prevHasVideo != m_videoPlayer->hasVideo()) + emit hasVideoChanged(m_videoPlayer->hasVideo()); + if (prevTotalTime != totalTime()) + emit totalTimeChanged(totalTime()); + if (prevTrackCount != m_videoPlayer->trackCount()) + emit availableTitlesChanged(m_videoPlayer->trackCount()); + if (checkForError()) + return; + if (!m_videoPlayer->isDrmAuthorized()) + SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR) + if (checkForError()) + return; + if (!m_videoPlayer->canPlayMedia()) + SET_ERROR("Cannot play media.", FATAL_ERROR) + + // The state might have changed from LoadingState + // as a response to an error state change. So we + // need to check it before stopping: + if (m_state == Phonon::LoadingState) + stop(); + + setupAudioSystem(); + checkForError(); +} + +void MediaObject::setNextSource(const MediaSource &source) +{ + IMPLEMENTED; + m_nextAudioPlayer->unsetVideoPlayer(); + m_nextVideoPlayer->setMediaSource(source); + m_nextAudioPlayer->setVideoPlayer(m_nextVideoPlayer); + checkForError(); +} + +void MediaObject::swapCurrentWithNext(qint32 transitionTime) +{ + PhononAutoReleasePool pool; + setState(Phonon::LoadingState); + // Save current state for event/signal handling below: + bool prevHasVideo = m_videoPlayer->hasVideo(); + qint64 prevTotalTime = totalTime(); + int prevTrackCount = m_videoPlayer->trackCount(); + + qSwap(m_audioPlayer, m_nextAudioPlayer); + qSwap(m_videoPlayer, m_nextVideoPlayer); + m_mediaObjectAudioNode->startCrossFade(transitionTime); + m_audioGraph->updateStreamSpecifications(); + + m_waitNextSwap = false; + m_currentTime = 0; + + // Emit/notify information about the new source: + QRect videoRect = m_videoPlayer->videoRect(); + MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); + notify(&e1); + + emit currentSourceChanged(m_videoPlayer->mediaSource()); + emit metaDataChanged(m_videoPlayer->metaData()); + + if (prevHasVideo != m_videoPlayer->hasVideo()) + emit hasVideoChanged(m_videoPlayer->hasVideo()); + if (prevTotalTime != totalTime()) + emit totalTimeChanged(totalTime()); + if (prevTrackCount != m_videoPlayer->trackCount()) + emit availableTitlesChanged(m_videoPlayer->trackCount()); + if (checkForError()) + return; + if (!m_videoPlayer->isDrmAuthorized()) + SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR) + if (checkForError()) + return; + if (!m_videoPlayer->canPlayMedia()) + SET_ERROR("Cannot play next media.", FATAL_ERROR) + + setupAudioSystem(); + checkForError(); + if (m_state == Phonon::LoadingState){ + if (setState(Phonon::PlayingState)) + play_internal(); + checkForError(); + } +} + +#if QT_ALLOW_QUICKTIME +static CVReturn displayLinkCallback(CVDisplayLinkRef /*displayLink*/, + const CVTimeStamp */*inNow*/, + const CVTimeStamp */*inOutputTime*/, + CVOptionFlags /*flagsIn*/, + CVOptionFlags */*flagsOut*/, + void *userData) +{ + MediaObject *mediaObject = static_cast(userData); + mediaObject->displayLinkEvent(); + return kCVReturnSuccess; +} + +void MediaObject::displayLinkEvent() +{ + // This function is called from a + // thread != gui thread. So we post the event. + // But we need to make sure that we don't post faster + // than the event loop can eat: + m_displayLinkMutex.lock(); + bool pending = m_pendingDisplayLinkEvent; + m_pendingDisplayLinkEvent = true; + m_displayLinkMutex.unlock(); + + if (!pending) + qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); +} + +void MediaObject::startDisplayLink() +{ + if (m_displayLink) + return; + OSStatus err = CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink); + if (err != noErr) + goto fail; + err = CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay); + if (err != noErr) + goto fail; + err = CVDisplayLinkSetOutputCallback(m_displayLink, displayLinkCallback, this); + if (err != noErr) + goto fail; + err = CVDisplayLinkStart(m_displayLink); + if (err != noErr) + goto fail; + return; +fail: + stopDisplayLink(); +} + +void MediaObject::stopDisplayLink() +{ + if (!m_displayLink) + return; + CVDisplayLinkStop(m_displayLink); + CFRelease(m_displayLink); + m_displayLink = 0; +} +#endif + +void MediaObject::restartAudioVideoTimers() +{ + if (m_videoTimer) + killTimer(m_videoTimer); + if (m_audioTimer) + killTimer(m_audioTimer); + +#if QT_ALLOW_QUICKTIME + // We prefer to use a display link as timer if available, since + // it is more steady, and results in better and smoother frame drawing: + startDisplayLink(); + if (!m_displayLink){ + float fps = m_videoPlayer->staticFps(); + long videoUpdateFrequency = fps ? long(1000.0f / fps) : 0.001; + m_videoTimer = startTimer(videoUpdateFrequency); + } +#else + float fps = m_videoPlayer->staticFps(); + long videoUpdateFrequency = fps ? long(1000.0f / fps) : 0.001; + m_videoTimer = startTimer(videoUpdateFrequency); +#endif + + long audioUpdateFrequency = m_audioPlayer->regularTaskFrequency(); + m_audioTimer = startTimer(audioUpdateFrequency); + updateVideoFrames(); + updateAudioBuffers(); +} + +void MediaObject::play_internal() +{ + // Play main audio/video: + m_videoPlayer->play(); + m_audioPlayer->play(); + updateLipSynch(0); + // Play old audio/video to finish cross-fade: + if (m_nextVideoPlayer->currentTime() > 0){ + m_nextVideoPlayer->play(); + m_nextAudioPlayer->play(); + } + restartAudioVideoTimers(); + if (!m_rapidTimer) + m_rapidTimer = startTimer(100); +} + +void MediaObject::pause_internal() +{ + m_audioGraph->stop(); + m_audioPlayer->pause(); + m_nextAudioPlayer->pause(); + m_videoPlayer->pause(); + m_nextVideoPlayer->pause(); + killTimer(m_rapidTimer); + killTimer(m_videoTimer); + killTimer(m_audioTimer); + m_rapidTimer = 0; + m_videoTimer = 0; + m_audioTimer = 0; +#if QT_ALLOW_QUICKTIME + stopDisplayLink(); +#endif + if (m_waitNextSwap) + m_swapTimeLeft = m_swapTime.msecsTo(QTime::currentTime()); +} + +void MediaObject::play() +{ + IMPLEMENTED; + if (m_state == Phonon::PlayingState) + return; + if (m_waitNextSwap){ + // update swap time after pause: + m_swapTime = QTime::currentTime(); + m_swapTime.addMSecs(m_swapTimeLeft); + setState(Phonon::PlayingState); + return; + } + if (m_currentTime == m_videoPlayer->duration()) + return; + if (!m_videoPlayer->canPlayMedia()) + return; + if (!setState(Phonon::PlayingState)) + return; + if (m_audioSystem == AS_Graph){ + m_audioGraph->start(); + m_mediaObjectAudioNode->setMute(true); + } + // Inform the graph that we are about to play: + bool playing = true; + MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing); + notify(&e1); + // Start to play: + play_internal(); + m_mediaObjectAudioNode->setMute(false); + checkForError(); +} + +void MediaObject::pause() +{ + IMPLEMENTED; + if (m_state == Phonon::PausedState) + return; + if (!setState(Phonon::PausedState)) + return; + pause_internal(); + // Inform the graph that we are no longer playing: + bool playing = false; + MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing); + notify(&e1); + // But be prepared: + if (m_audioSystem == AS_Graph) + m_audioGraph->prepare(); + checkForError(); +} + +void MediaObject::stop() +{ + IMPLEMENTED; + if (m_state == Phonon::StoppedState) + return; + if (!setState(Phonon::StoppedState)) + return; + m_waitNextSwap = false; + m_nextVideoPlayer->unsetCurrentMediaSource(); + m_nextAudioPlayer->unsetVideoPlayer(); + pause_internal(); + seek(0); + checkForError(); +} + +void MediaObject::seek(qint64 milliseconds) +{ + IMPLEMENTED; + if (m_state == Phonon::ErrorState) + return; + + // Stop cross-fade if any: + m_nextVideoPlayer->unsetCurrentMediaSource(); + m_nextAudioPlayer->unsetVideoPlayer(); + m_mediaObjectAudioNode->cancelCrossFade(); + + // Seek to new position: + m_mediaObjectAudioNode->setMute(true); + m_videoPlayer->seek(milliseconds); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_mediaObjectAudioNode->setMute(false); + + // Update time and cancel pending swap: + if (m_currentTime < m_videoPlayer->duration()) + m_waitNextSwap = false; + + updateCurrentTime(); + if (m_state != Phonon::PlayingState) + updateVideoFrames(); + checkForError(); +} + +QStringList MediaObject::availableAudioStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QStringList MediaObject::availableVideoStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QStringList MediaObject::availableSubtitleStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QString MediaObject::currentAudioStream(const QObject */*audioPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +QString MediaObject::currentVideoStream(const QObject */*videoPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +QString MediaObject::currentSubtitleStream(const QObject */*videoPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +void MediaObject::setCurrentAudioStream(const QString &/*streamName*/,const QObject */*audioPath*/) +{ + NOT_IMPLEMENTED; +} + +void MediaObject::setCurrentVideoStream(const QString &/*streamName*/,const QObject */*videoPath*/) +{ + NOT_IMPLEMENTED; +} + +void MediaObject::setCurrentSubtitleStream(const QString &/*streamName*/,const QObject */*videoPath*/) +{ + NOT_IMPLEMENTED; +} + +int MediaObject::videoOutputCount() +{ + return m_videoOutputCount; +} + +void MediaObject::synchAudioVideo() +{ + if (m_state != Phonon::PlayingState) + return; + if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty()) + return; + + seek(m_currentTime); + checkForError(); +} + +qint32 MediaObject::tickInterval() const +{ + IMPLEMENTED; + return m_tickInterval; +} + +void MediaObject::setTickInterval(qint32 interval) +{ + IMPLEMENTED; + m_tickInterval = interval; + if (m_tickInterval > 0) + m_tickTimer = startTimer(m_tickInterval); + else{ + killTimer(m_tickTimer); + m_tickTimer = 0; + } +} + +bool MediaObject::hasVideo() const +{ + IMPLEMENTED; + return m_videoPlayer ? m_videoPlayer->hasVideo() : false; +} + +bool MediaObject::isSeekable() const +{ + IMPLEMENTED; + return m_videoPlayer ? m_videoPlayer->isSeekable() : false; +} + +qint64 MediaObject::currentTime() const +{ + IMPLEMENTED_SILENT; + const_cast(this)->updateCurrentTime(); + return m_currentTime; +} + +void MediaObject::updateCurrentTime() +{ + quint64 lastUpdateTime = m_currentTime; + m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime(); + quint64 total = m_videoPlayer->duration(); + + if (m_videoPlayer->currentTrack() < m_videoPlayer->trackCount() - 1){ + // There are still more tracks to play after the current track. + if (m_autoplayTitles) { + if (lastUpdateTime < m_currentTime && m_currentTime == total) + setCurrentTrack(m_videoPlayer->currentTrack() + 1); + } + } else if (m_nextVideoPlayer->state() == QuickTimeVideoPlayer::NoMedia){ + // There is no more sources or tracks to play after the current source. + // Check if it's time to emit aboutToFinish: + quint32 mark = qMax(quint64(0), qMin(total, total + m_transitionTime - 2000)); + if (lastUpdateTime < mark && mark <= m_currentTime) + emit aboutToFinish(); + + // Check if it's time to emit prefinishMarkReached: + mark = qMax(quint64(0), total - m_prefinishMark); + if (lastUpdateTime < mark && mark <= m_currentTime) + emit prefinishMarkReached(total - m_currentTime); + + if (lastUpdateTime < m_currentTime && m_currentTime == total){ + emit finished(); + m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime(); + if (m_state == Phonon::PlayingState && m_currentTime == total) + pause(); + } + } else { + // We have a next source. + // Check if it's time to swap to next source: + quint32 mark = qMax(quint64(0), total + m_transitionTime); + if (m_waitNextSwap && m_state == Phonon::PlayingState && + m_transitionTime < m_swapTime.msecsTo(QTime::currentTime())){ + swapCurrentWithNext(0); + } else if (mark >= total){ + if (lastUpdateTime < total && total == m_currentTime){ + m_swapTime = QTime::currentTime(); + m_swapTime.addMSecs(mark - total); + m_waitNextSwap = true; + } + } else if (lastUpdateTime < mark && mark <= m_currentTime){ + swapCurrentWithNext(total - m_currentTime); + } + } +} + +qint64 MediaObject::totalTime() const +{ + IMPLEMENTED_SILENT; + return m_videoPlayer->duration(); +} + +Phonon::State MediaObject::state() const +{ + IMPLEMENTED; + return m_state; +} + +QString MediaObject::errorString() const +{ + IMPLEMENTED; + return m_errorString; +} + +Phonon::ErrorType MediaObject::errorType() const +{ + IMPLEMENTED; + return m_errorType; +} + +bool MediaObject::checkForError() +{ + int type = gGetErrorType(); + if (type == NO_ERROR) + return false; + + m_errorType = (type == NORMAL_ERROR) ? Phonon::NormalError : Phonon::FatalError; + m_errorString = gGetErrorString(); + pause_internal(); + gClearError(); + setState(Phonon::ErrorState); + return true; +} + +QuickTimeVideoPlayer* MediaObject::videoPlayer() const +{ + return m_videoPlayer; +} + +MediaSource MediaObject::source() const +{ + IMPLEMENTED; + return m_videoPlayer->mediaSource(); +} + +qint32 MediaObject::prefinishMark() const +{ + IMPLEMENTED; + return m_prefinishMark; +} + +void MediaObject::setPrefinishMark(qint32 mark) +{ + IMPLEMENTED; + m_prefinishMark = mark; +} + +qint32 MediaObject::transitionTime() const +{ + IMPLEMENTED; + return m_transitionTime; +} + +void MediaObject::setTransitionTime(qint32 transitionTime) +{ + IMPLEMENTED; + m_transitionTime = transitionTime; +} + +void MediaObject::setVolumeOnMovie(float volume) +{ + m_videoPlayer->setMasterVolume(volume); + m_nextVideoPlayer->setMasterVolume(volume); +} + +bool MediaObject::setAudioDeviceOnMovie(int id) +{ + m_nextVideoPlayer->setAudioDevice(id); + return m_videoPlayer->setAudioDevice(id); +} + +void MediaObject::updateCrossFade() +{ + m_mediaObjectAudioNode->updateCrossFade(m_currentTime); + // Clean-up previous movie if done fading: + if (m_mediaObjectAudioNode->m_fadeDuration == 0){ + if (m_nextVideoPlayer->isPlaying() || m_nextAudioPlayer->isPlaying()){ + m_nextVideoPlayer->unsetCurrentMediaSource(); + m_nextAudioPlayer->unsetVideoPlayer(); + } + } +} + +void MediaObject::updateBufferStatus() +{ + float percent = m_videoPlayer->percentageLoaded(); + if (percent != m_percentageLoaded){ + m_percentageLoaded = percent; + emit bufferStatus(m_percentageLoaded * 100); + } +} + +void MediaObject::updateAudioBuffers() +{ + // Schedule audio slices: + m_audioPlayer->scheduleAudioToGraph(); + m_nextAudioPlayer->scheduleAudioToGraph(); +} + +bool MediaObject::isCrossFading() +{ + return m_mediaObjectAudioNode->isCrossFading(); +} + +void MediaObject::updateVideoFrames() +{ + // Draw next frame if awailable: + if (m_videoPlayer->videoFrameChanged()){ + updateLipSynch(50); + VideoFrame frame(m_videoPlayer); + if (m_nextVideoPlayer->isPlaying() + && m_nextVideoPlayer->hasVideo() + && isCrossFading()){ + VideoFrame bgFrame(m_nextVideoPlayer); + frame.setBackgroundFrame(bgFrame); + frame.setBaseOpacity(m_mediaObjectAudioNode->m_volume1); + } + + // Send the frame through the graph: + updateVideo(frame); + checkForError(); + } +} + +void MediaObject::updateLipSynch(int allowedOffset) +{ + if (m_audioSystem != AS_Graph || !m_audioGraph->isRunning()) + return; + if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty()) + return; + + if (m_videoPlayer->hasVideo()){ + qint64 diff = m_audioPlayer->currentTime() - m_videoPlayer->currentTime(); + if (-allowedOffset > diff || diff > allowedOffset) + m_audioPlayer->seek(m_videoPlayer->currentTime()); + } + + if (isCrossFading() && m_nextVideoPlayer->hasVideo()){ + qint64 diff = m_nextAudioPlayer->currentTime() - m_nextVideoPlayer->currentTime(); + if (-(allowedOffset*2) > diff || diff > (allowedOffset*2)) + m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime()); + } +} + +void MediaObject::updateRapidly() +{ + updateCurrentTime(); + updateCrossFade(); + updateBufferStatus(); +} + +void MediaObject::setMute(bool mute) +{ + m_mediaObjectAudioNode->setMute(mute); + m_videoPlayer->setMute(mute); + m_nextVideoPlayer->setMute(mute); +} + +void MediaObject::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::EndConnectionChange: + m_mediaObjectAudioNode->setMute(true); + inspectGraph(); + setupAudioSystem(); + synchAudioVideo(); + checkForError(); + m_mediaObjectAudioNode->setMute(false); + if (m_state == Phonon::PlayingState) + restartAudioVideoTimers(); + break; + case MediaNodeEvent::AudioGraphCannotPlay: + case MediaNodeEvent::AudioGraphInitialized: + if (m_state != Phonon::LoadingState){ + m_mediaObjectAudioNode->setMute(true); + setupAudioSystem(); + updateLipSynch(0); + checkForError(); + m_mediaObjectAudioNode->setMute(false); + } + break; + default: + break; + } +} + +bool MediaObject::event(QEvent *event) +{ + switch (event->type()){ +#if QT_ALLOW_QUICKTIME + case QEvent::User:{ + m_displayLinkMutex.lock(); + m_pendingDisplayLinkEvent = false; + m_displayLinkMutex.unlock(); + updateVideoFrames(); + break; } +#endif + case QEvent::Timer:{ + int timerId = static_cast(event)->timerId(); + if (timerId == m_rapidTimer) + updateRapidly(); + else if (timerId == m_tickTimer) + emit tick(currentTime()); + else if (timerId == m_videoTimer) + updateVideoFrames(); + else if (timerId == m_audioTimer) + updateAudioBuffers(); + break; } + default: + break; + } + return QObject::event(event); +} + +void MediaObject::setCurrentTrack(int track) +{ + if (track == m_videoPlayer->currentTrack() || track < 0 || track >= m_videoPlayer->trackCount()) + return; + + m_videoPlayer->setCurrentTrack(track); + emit titleChanged(track); + emit metaDataChanged(m_videoPlayer->metaData()); +} + +bool MediaObject::hasInterface(Interface iface) const +{ + return iface == AddonInterface::TitleInterface; +} + +QVariant MediaObject::interfaceCall(Interface iface, int command, const QList ¶ms) +{ + switch (iface) { + case TitleInterface: + switch (command) { + case availableTitles: + return m_videoPlayer->trackCount(); + case title: + return m_videoPlayer->currentTrack(); + case setTitle: + setCurrentTrack(params.first().toInt()); + break; + case autoplayTitles: + return m_autoplayTitles; + case setAutoplayTitles: + m_autoplayTitles = params.first().toBool(); + break; + } + default: + break; + } + return QVariant(); +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_mediaobject.cpp" +