src/3rdparty/phonon/qt7/mediaobject.mm
changeset 0 1918ee327afb
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /*  This file is part of the KDE project.
       
     2 
       
     3     Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 
       
     5     This library is free software: you can redistribute it and/or modify
       
     6     it under the terms of the GNU Lesser General Public License as published by
       
     7     the Free Software Foundation, either version 2.1 or 3 of the License.
       
     8 
       
     9     This library is distributed in the hope that it will be useful,
       
    10     but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12     GNU Lesser General Public License for more details.
       
    13 
       
    14     You should have received a copy of the GNU Lesser General Public License
       
    15     along with this library.  If not, see <http://www.gnu.org/licenses/>.
       
    16 */
       
    17 
       
    18 #include <QtCore/QEvent>
       
    19 #include "mediaobject.h"
       
    20 #include "backendheader.h"
       
    21 #include "videowidget.h"
       
    22 #include "videoframe.h"
       
    23 #include "audiooutput.h"
       
    24 #include "quicktimevideoplayer.h"
       
    25 #include "quicktimemetadata.h"
       
    26 #include "audiograph.h"
       
    27 #include "mediaobjectaudionode.h"
       
    28 #include "quicktimeaudioplayer.h"
       
    29 
       
    30 QT_BEGIN_NAMESPACE
       
    31 
       
    32 namespace Phonon
       
    33 {
       
    34 namespace QT7
       
    35 {
       
    36 
       
    37 MediaObject::MediaObject(QObject *parent) : MediaNode(AudioSource | VideoSource, parent)
       
    38 {
       
    39     m_owningMediaObject = this;
       
    40     m_state = Phonon::LoadingState;
       
    41 
       
    42     m_videoPlayer = new QuickTimeVideoPlayer();
       
    43     m_audioPlayer = new QuickTimeAudioPlayer();
       
    44     m_nextVideoPlayer = new QuickTimeVideoPlayer();
       
    45     m_nextAudioPlayer = new QuickTimeAudioPlayer();
       
    46     m_mediaObjectAudioNode = new MediaObjectAudioNode(m_audioPlayer, m_nextAudioPlayer);
       
    47     setAudioNode(m_mediaObjectAudioNode);
       
    48 
       
    49     m_audioGraph = new AudioGraph(this);
       
    50 
       
    51     m_tickInterval = 0;
       
    52     m_prefinishMark = 0;
       
    53     m_currentTime = 0;
       
    54     m_transitionTime = 0;
       
    55     m_percentageLoaded = 0;
       
    56     m_waitNextSwap = false;
       
    57     m_autoplayTitles = true;
       
    58     m_audioEffectCount = 0;
       
    59     m_audioOutputCount = 0;
       
    60     m_videoEffectCount = 0;
       
    61     m_videoOutputCount = 0;
       
    62     m_audioSystem = AS_Unset;
       
    63     m_errorType = Phonon::NoError;
       
    64 
       
    65     m_tickTimer = 0;
       
    66     m_videoTimer = 0;
       
    67     m_audioTimer = 0;
       
    68     m_rapidTimer = 0;
       
    69 
       
    70 #if QT_ALLOW_QUICKTIME
       
    71     m_displayLink = 0;
       
    72     m_pendingDisplayLinkEvent = false;
       
    73 #endif
       
    74 
       
    75     checkForError();
       
    76 }
       
    77 
       
    78 MediaObject::~MediaObject()
       
    79 {
       
    80     // m_mediaObjectAudioNode is owned by super class.
       
    81 #if QT_ALLOW_QUICKTIME
       
    82     stopDisplayLink();
       
    83 #endif
       
    84     m_audioPlayer->unsetVideoPlayer();
       
    85     m_nextAudioPlayer->unsetVideoPlayer();
       
    86     delete m_videoPlayer;
       
    87     delete m_nextVideoPlayer;
       
    88     checkForError();
       
    89 }
       
    90 
       
    91 bool MediaObject::setState(Phonon::State state)
       
    92 {
       
    93     Phonon::State prevState = m_state;
       
    94     m_state = state;
       
    95     if (prevState != m_state){
       
    96         emit stateChanged(m_state, prevState);
       
    97         if (m_state != state){
       
    98             // End-application did something
       
    99             // upon  receiving the signal.
       
   100             return false;
       
   101         }
       
   102     }
       
   103     return true;
       
   104 }
       
   105 
       
   106 void MediaObject::inspectAudioGraphRecursive(AudioConnection *connection, int &effectCount, int &outputCount)
       
   107 {
       
   108     if ((connection->m_sink->m_description & (AudioSource | AudioSink)) == (AudioSource | AudioSink))
       
   109         ++effectCount;
       
   110 	else if (connection->m_sink->m_description & AudioSink)
       
   111     	++outputCount;
       
   112 
       
   113     for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i)
       
   114         inspectAudioGraphRecursive(connection->m_sink->m_audioSinkList[i], effectCount, outputCount);
       
   115 }
       
   116 
       
   117 void MediaObject::inspectVideoGraphRecursive(MediaNode *node, int &effectCount, int &outputCount)
       
   118 {
       
   119     if ((node->m_description & (VideoSource | VideoSink)) == (VideoSource | VideoSink))
       
   120         ++effectCount;
       
   121 	else if (node->m_description & VideoSink)
       
   122     	++outputCount;
       
   123 
       
   124     for (int i=0; i<node->m_videoSinkList.size(); ++i)
       
   125         inspectVideoGraphRecursive(node->m_videoSinkList[i], effectCount, outputCount);
       
   126 }
       
   127 
       
   128 void MediaObject::inspectGraph()
       
   129 {
       
   130     // Inspect the graph to check wether there are any
       
   131     // effects or outputs connected. This will have
       
   132     // influence on the audio system and video system that ends up beeing used:
       
   133     int prevVideoOutputCount = m_videoOutputCount;
       
   134     m_audioEffectCount = 0;
       
   135     m_audioOutputCount = 0;
       
   136     m_videoEffectCount = 0;
       
   137     m_videoOutputCount = 0;
       
   138     AudioConnection rootConnection(this);
       
   139     inspectAudioGraphRecursive(&rootConnection, m_audioEffectCount, m_audioOutputCount);
       
   140     inspectVideoGraphRecursive(this, m_videoEffectCount, m_videoOutputCount);
       
   141 
       
   142 	if (m_videoOutputCount != prevVideoOutputCount){
       
   143 	    MediaNodeEvent e1(MediaNodeEvent::VideoOutputCountChanged, &m_videoOutputCount);
       
   144 	    notify(&e1);
       
   145     }
       
   146 }
       
   147 
       
   148 void MediaObject::setupAudioSystem()
       
   149 {
       
   150     // Select which audio system to use:
       
   151     AudioSystem newAudioSystem = AS_Unset;
       
   152     if (!m_audioOutputCount || !m_videoPlayer->canPlayMedia()){
       
   153         newAudioSystem = AS_Silent;
       
   154     } else if (m_audioEffectCount == 0){
       
   155         newAudioSystem = AS_Video;
       
   156     } else if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_4){
       
   157         newAudioSystem = AS_Video;
       
   158         SET_ERROR("Audio effects are not supported for Mac OS 10.3 and below", NORMAL_ERROR);
       
   159     } else if (m_videoPlayer->isDrmProtected()){
       
   160         newAudioSystem = AS_Video;
       
   161         SET_ERROR("Audio effects are not supported for DRM protected media", NORMAL_ERROR);
       
   162     } else if (m_audioGraph->graphCannotPlay()){
       
   163         newAudioSystem = AS_Video;
       
   164         SET_ERROR("Audio effects are not supported for the current codec", NORMAL_ERROR);
       
   165 #ifdef QUICKTIME_C_API_AVAILABLE
       
   166     } else {
       
   167         newAudioSystem = AS_Graph;
       
   168     }
       
   169 #else
       
   170     } else {
       
   171         newAudioSystem = AS_Video;
       
   172         SET_ERROR("Audio effects are not supported for the 64-bit version of the Phonon QT7 backend", NORMAL_ERROR);
       
   173     }
       
   174 #endif
       
   175 
       
   176     if (newAudioSystem == m_audioSystem)
       
   177         return;
       
   178 
       
   179     // Enable selected audio system:
       
   180     m_audioSystem = newAudioSystem;
       
   181     switch (newAudioSystem){
       
   182         case AS_Silent:
       
   183             m_audioGraph->stop();
       
   184             m_videoPlayer->enableAudio(false);
       
   185             m_nextVideoPlayer->enableAudio(false);
       
   186             m_audioPlayer->enableAudio(false);
       
   187             m_nextAudioPlayer->enableAudio(false);
       
   188         break;
       
   189         case AS_Graph:
       
   190             if (m_state == Phonon::PausedState)
       
   191                 m_audioGraph->prepare();
       
   192             else
       
   193                 m_audioGraph->start();
       
   194             // Starting the graph can lead to a recursive call
       
   195             // telling us that we must direct audio through
       
   196             // video. If that has happened, we must not proceed:
       
   197             if (m_audioSystem != AS_Graph)
       
   198                 return;
       
   199             m_videoPlayer->enableAudio(false);
       
   200             m_nextVideoPlayer->enableAudio(false);
       
   201             m_audioPlayer->enableAudio(true);
       
   202             m_audioPlayer->seek(m_videoPlayer->currentTime());
       
   203             m_nextAudioPlayer->enableAudio(true);
       
   204             m_audioPlayer->seek(m_videoPlayer->currentTime());
       
   205             m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime());
       
   206         break;
       
   207         case AS_Video:
       
   208         case AS_Unset:
       
   209             m_audioGraph->stop();
       
   210             m_videoPlayer->enableAudio(true);
       
   211             m_nextVideoPlayer->enableAudio(true);
       
   212             m_audioPlayer->enableAudio(false);
       
   213             m_nextAudioPlayer->enableAudio(false);
       
   214             m_videoPlayer->seek(m_audioPlayer->currentTime());
       
   215             m_nextVideoPlayer->seek(m_nextAudioPlayer->currentTime());
       
   216         break;
       
   217     }
       
   218 }
       
   219 
       
   220 void MediaObject::setSource(const MediaSource &source)
       
   221 {
       
   222     IMPLEMENTED;
       
   223 	PhononAutoReleasePool pool;
       
   224     setState(Phonon::LoadingState);
       
   225 
       
   226     // Save current state for event/signal handling below:
       
   227     bool prevHasVideo = m_videoPlayer->hasVideo();
       
   228     qint64 prevTotalTime = totalTime();
       
   229     int prevTrackCount = m_videoPlayer->trackCount();
       
   230     m_waitNextSwap = false;
       
   231 
       
   232     // Cancel cross-fade if any:
       
   233     m_nextVideoPlayer->pause();
       
   234     m_nextAudioPlayer->pause();
       
   235     m_mediaObjectAudioNode->cancelCrossFade();
       
   236 
       
   237     // Set new source:
       
   238     m_audioPlayer->unsetVideoPlayer();
       
   239     m_videoPlayer->setMediaSource(source);
       
   240     m_audioPlayer->setVideoPlayer(m_videoPlayer);
       
   241 
       
   242     m_audioGraph->updateStreamSpecifications();
       
   243     m_nextAudioPlayer->unsetVideoPlayer();
       
   244     m_nextVideoPlayer->unsetCurrentMediaSource();
       
   245     m_currentTime = 0;
       
   246 
       
   247     // Emit/notify information about the new source:
       
   248     QRect videoRect = m_videoPlayer->videoRect();
       
   249     MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect);
       
   250     notify(&e1);
       
   251 
       
   252     // Clear video widgets:
       
   253     VideoFrame emptyFrame;
       
   254     updateVideo(emptyFrame);
       
   255 
       
   256     emit currentSourceChanged(source);
       
   257     emit metaDataChanged(m_videoPlayer->metaData());
       
   258 
       
   259     if (prevHasVideo != m_videoPlayer->hasVideo())
       
   260         emit hasVideoChanged(m_videoPlayer->hasVideo());
       
   261     if (prevTotalTime != totalTime())
       
   262         emit totalTimeChanged(totalTime());
       
   263     if (prevTrackCount != m_videoPlayer->trackCount())
       
   264         emit availableTitlesChanged(m_videoPlayer->trackCount());
       
   265     if (checkForError())
       
   266         return;
       
   267     if (!m_videoPlayer->isDrmAuthorized())
       
   268         SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR)
       
   269     if (checkForError())
       
   270         return;
       
   271     if (!m_videoPlayer->canPlayMedia())
       
   272         SET_ERROR("Cannot play media.", FATAL_ERROR)
       
   273 
       
   274     // The state might have changed from LoadingState
       
   275     // as a response to an error state change. So we
       
   276     // need to check it before stopping: 
       
   277     if (m_state == Phonon::LoadingState)
       
   278         stop();
       
   279 
       
   280     setupAudioSystem();
       
   281     checkForError();
       
   282 }
       
   283 
       
   284 void MediaObject::setNextSource(const MediaSource &source)
       
   285 {
       
   286     IMPLEMENTED;
       
   287     m_nextAudioPlayer->unsetVideoPlayer();
       
   288     m_nextVideoPlayer->setMediaSource(source);
       
   289     m_nextAudioPlayer->setVideoPlayer(m_nextVideoPlayer);
       
   290     checkForError();
       
   291 }
       
   292 
       
   293 void MediaObject::swapCurrentWithNext(qint32 transitionTime)
       
   294 {
       
   295 	PhononAutoReleasePool pool;
       
   296     setState(Phonon::LoadingState);
       
   297     // Save current state for event/signal handling below:
       
   298     bool prevHasVideo = m_videoPlayer->hasVideo();
       
   299     qint64 prevTotalTime = totalTime();
       
   300     int prevTrackCount = m_videoPlayer->trackCount();
       
   301 
       
   302     qSwap(m_audioPlayer, m_nextAudioPlayer);
       
   303     qSwap(m_videoPlayer, m_nextVideoPlayer);
       
   304     m_mediaObjectAudioNode->startCrossFade(transitionTime);
       
   305     m_audioGraph->updateStreamSpecifications();
       
   306 
       
   307     m_waitNextSwap = false;
       
   308     m_currentTime = 0;
       
   309 
       
   310     // Emit/notify information about the new source:
       
   311     QRect videoRect = m_videoPlayer->videoRect();
       
   312     MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect);
       
   313     notify(&e1);
       
   314 
       
   315     emit currentSourceChanged(m_videoPlayer->mediaSource());
       
   316     emit metaDataChanged(m_videoPlayer->metaData());
       
   317 
       
   318     if (prevHasVideo != m_videoPlayer->hasVideo())
       
   319         emit hasVideoChanged(m_videoPlayer->hasVideo());
       
   320     if (prevTotalTime != totalTime())
       
   321         emit totalTimeChanged(totalTime());
       
   322     if (prevTrackCount != m_videoPlayer->trackCount())
       
   323         emit availableTitlesChanged(m_videoPlayer->trackCount());
       
   324     if (checkForError())
       
   325         return;
       
   326     if (!m_videoPlayer->isDrmAuthorized())
       
   327         SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR)
       
   328     if (checkForError())
       
   329         return;
       
   330     if (!m_videoPlayer->canPlayMedia())
       
   331         SET_ERROR("Cannot play next media.", FATAL_ERROR)
       
   332 
       
   333     setupAudioSystem();
       
   334     checkForError();
       
   335     if (m_state == Phonon::LoadingState){
       
   336         if (setState(Phonon::PlayingState))
       
   337             play_internal();
       
   338         checkForError();
       
   339     }
       
   340 }
       
   341 
       
   342 #if QT_ALLOW_QUICKTIME
       
   343 static CVReturn displayLinkCallback(CVDisplayLinkRef /*displayLink*/,
       
   344 								 const CVTimeStamp */*inNow*/,
       
   345 								 const CVTimeStamp */*inOutputTime*/,
       
   346 								 CVOptionFlags /*flagsIn*/,
       
   347 								 CVOptionFlags */*flagsOut*/,
       
   348                                  void *userData)
       
   349 {
       
   350     MediaObject *mediaObject = static_cast<MediaObject *>(userData);
       
   351     mediaObject->displayLinkEvent();
       
   352     return kCVReturnSuccess;
       
   353 }
       
   354 
       
   355 void MediaObject::displayLinkEvent()
       
   356 {
       
   357     // This function is called from a
       
   358     // thread != gui thread. So we post the event.
       
   359     // But we need to make sure that we don't post faster
       
   360     // than the event loop can eat:
       
   361     m_displayLinkMutex.lock();
       
   362     bool pending = m_pendingDisplayLinkEvent;
       
   363     m_pendingDisplayLinkEvent = true;
       
   364     m_displayLinkMutex.unlock();
       
   365 
       
   366     if (!pending)
       
   367         qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority);
       
   368 }
       
   369 
       
   370 void MediaObject::startDisplayLink()
       
   371 {
       
   372     if (m_displayLink)
       
   373         return;
       
   374     OSStatus err = CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink);
       
   375     if (err != noErr)
       
   376         goto fail;
       
   377     err = CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay);
       
   378     if (err != noErr)
       
   379         goto fail;
       
   380     err = CVDisplayLinkSetOutputCallback(m_displayLink, displayLinkCallback, this);
       
   381     if (err != noErr)
       
   382         goto fail;
       
   383     err = CVDisplayLinkStart(m_displayLink);
       
   384     if (err != noErr)
       
   385         goto fail;
       
   386     return;
       
   387 fail:
       
   388     stopDisplayLink();
       
   389 }
       
   390 
       
   391 void MediaObject::stopDisplayLink()
       
   392 {
       
   393     if (!m_displayLink)
       
   394         return;
       
   395     CVDisplayLinkStop(m_displayLink);
       
   396     CFRelease(m_displayLink);
       
   397     m_displayLink = 0;
       
   398 }
       
   399 #endif
       
   400 
       
   401 void MediaObject::restartAudioVideoTimers()
       
   402 {
       
   403     if (m_videoTimer)
       
   404         killTimer(m_videoTimer);
       
   405     if (m_audioTimer)
       
   406         killTimer(m_audioTimer);
       
   407 
       
   408 #if QT_ALLOW_QUICKTIME
       
   409     // We prefer to use a display link as timer if available, since
       
   410     // it is more steady, and results in better and smoother frame drawing:
       
   411     startDisplayLink();
       
   412     if (!m_displayLink){
       
   413         float fps = m_videoPlayer->staticFps();
       
   414         long videoUpdateFrequency = fps ? long(1000.0f / fps) : 0.001;
       
   415         m_videoTimer = startTimer(videoUpdateFrequency);
       
   416     }
       
   417 #else
       
   418     float fps = m_videoPlayer->staticFps();
       
   419     long videoUpdateFrequency = fps ? long(1000.0f / fps) : 0.001;
       
   420     m_videoTimer = startTimer(videoUpdateFrequency);
       
   421 #endif
       
   422 
       
   423     long audioUpdateFrequency = m_audioPlayer->regularTaskFrequency();
       
   424     m_audioTimer = startTimer(audioUpdateFrequency);
       
   425     updateVideoFrames();
       
   426     updateAudioBuffers();
       
   427 }
       
   428 
       
   429 void MediaObject::play_internal()
       
   430 {
       
   431     // Play main audio/video:
       
   432     m_videoPlayer->play();
       
   433     m_audioPlayer->play();
       
   434     updateLipSynch(0);
       
   435     // Play old audio/video to finish cross-fade:
       
   436     if (m_nextVideoPlayer->currentTime() > 0){
       
   437         m_nextVideoPlayer->play();
       
   438         m_nextAudioPlayer->play();
       
   439     }
       
   440     restartAudioVideoTimers();
       
   441     if (!m_rapidTimer)
       
   442         m_rapidTimer = startTimer(100);
       
   443 }
       
   444 
       
   445 void MediaObject::pause_internal()
       
   446 {
       
   447     m_audioGraph->stop();
       
   448     m_audioPlayer->pause();
       
   449     m_nextAudioPlayer->pause();
       
   450     m_videoPlayer->pause();
       
   451     m_nextVideoPlayer->pause();
       
   452     killTimer(m_rapidTimer);
       
   453     killTimer(m_videoTimer);
       
   454     killTimer(m_audioTimer);
       
   455     m_rapidTimer = 0;
       
   456     m_videoTimer = 0;
       
   457     m_audioTimer = 0;
       
   458 #if QT_ALLOW_QUICKTIME
       
   459     stopDisplayLink();
       
   460 #endif
       
   461     if (m_waitNextSwap)
       
   462         m_swapTimeLeft = m_swapTime.msecsTo(QTime::currentTime());
       
   463 }
       
   464 
       
   465 void MediaObject::play()
       
   466 {
       
   467     IMPLEMENTED;
       
   468     if (m_state == Phonon::PlayingState)
       
   469         return;
       
   470     if (m_waitNextSwap){
       
   471         // update swap time after pause:
       
   472         m_swapTime = QTime::currentTime();
       
   473         m_swapTime.addMSecs(m_swapTimeLeft);
       
   474         setState(Phonon::PlayingState);
       
   475         return;
       
   476     }
       
   477     if (m_currentTime == m_videoPlayer->duration())
       
   478         return;
       
   479     if (!m_videoPlayer->canPlayMedia())
       
   480         return;
       
   481     if (!setState(Phonon::PlayingState))
       
   482         return;
       
   483     if (m_audioSystem == AS_Graph){
       
   484         m_audioGraph->start();
       
   485         m_mediaObjectAudioNode->setMute(true);
       
   486     }
       
   487 	// Inform the graph that we are about to play:
       
   488 	bool playing = true;
       
   489     MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing);
       
   490     notify(&e1);
       
   491 	// Start to play:
       
   492     play_internal();
       
   493     m_mediaObjectAudioNode->setMute(false);
       
   494     checkForError();
       
   495 }
       
   496 
       
   497 void MediaObject::pause()
       
   498 {
       
   499     IMPLEMENTED;
       
   500     if (m_state == Phonon::PausedState)
       
   501         return;
       
   502     if (!setState(Phonon::PausedState))
       
   503         return;
       
   504     pause_internal();
       
   505 	// Inform the graph that we are no longer playing:
       
   506 	bool playing = false;
       
   507     MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing);
       
   508     notify(&e1);
       
   509 	// But be prepared:
       
   510     if (m_audioSystem == AS_Graph)
       
   511         m_audioGraph->prepare();
       
   512     checkForError();
       
   513 }
       
   514 
       
   515 void MediaObject::stop()
       
   516 {
       
   517     IMPLEMENTED;
       
   518     if (m_state == Phonon::StoppedState)
       
   519         return;
       
   520     if (!setState(Phonon::StoppedState))
       
   521         return;
       
   522     m_waitNextSwap = false;
       
   523     m_nextVideoPlayer->unsetCurrentMediaSource();
       
   524     m_nextAudioPlayer->unsetVideoPlayer();
       
   525     pause_internal();
       
   526     seek(0);
       
   527     checkForError();
       
   528 }
       
   529 
       
   530 void MediaObject::seek(qint64 milliseconds)
       
   531 {
       
   532     IMPLEMENTED;
       
   533     if (m_state == Phonon::ErrorState)
       
   534         return;
       
   535 
       
   536     // Stop cross-fade if any:
       
   537     m_nextVideoPlayer->unsetCurrentMediaSource();
       
   538     m_nextAudioPlayer->unsetVideoPlayer();
       
   539     m_mediaObjectAudioNode->cancelCrossFade();
       
   540 
       
   541     // Seek to new position:
       
   542     m_mediaObjectAudioNode->setMute(true);
       
   543     m_videoPlayer->seek(milliseconds);
       
   544     m_audioPlayer->seek(m_videoPlayer->currentTime());
       
   545     m_mediaObjectAudioNode->setMute(false);
       
   546 
       
   547     // Update time and cancel pending swap:
       
   548     if (m_currentTime < m_videoPlayer->duration())
       
   549         m_waitNextSwap = false;
       
   550 
       
   551     updateCurrentTime();
       
   552 	if (m_state != Phonon::PlayingState)
       
   553 		updateVideoFrames();
       
   554     checkForError();
       
   555 }
       
   556 
       
   557 QStringList MediaObject::availableAudioStreams() const
       
   558 {
       
   559     NOT_IMPLEMENTED;
       
   560     return QStringList();
       
   561 }
       
   562 
       
   563 QStringList MediaObject::availableVideoStreams() const
       
   564 {
       
   565     NOT_IMPLEMENTED;
       
   566     return QStringList();
       
   567 }
       
   568 
       
   569 QStringList MediaObject::availableSubtitleStreams() const
       
   570 {
       
   571     NOT_IMPLEMENTED;
       
   572     return QStringList();
       
   573 }
       
   574 
       
   575 QString MediaObject::currentAudioStream(const QObject */*audioPath*/) const
       
   576 {
       
   577     NOT_IMPLEMENTED;
       
   578     return QString();
       
   579 }
       
   580 
       
   581 QString MediaObject::currentVideoStream(const QObject */*videoPath*/) const
       
   582 {
       
   583     NOT_IMPLEMENTED;
       
   584     return QString();
       
   585 }
       
   586 
       
   587 QString MediaObject::currentSubtitleStream(const QObject */*videoPath*/) const
       
   588 {
       
   589     NOT_IMPLEMENTED;
       
   590     return QString();
       
   591 }
       
   592 
       
   593 void MediaObject::setCurrentAudioStream(const QString &/*streamName*/,const QObject */*audioPath*/)
       
   594 {
       
   595     NOT_IMPLEMENTED;
       
   596 }
       
   597 
       
   598 void MediaObject::setCurrentVideoStream(const QString &/*streamName*/,const QObject */*videoPath*/)
       
   599 {
       
   600     NOT_IMPLEMENTED;
       
   601 }
       
   602 
       
   603 void MediaObject::setCurrentSubtitleStream(const QString &/*streamName*/,const QObject */*videoPath*/)
       
   604 {
       
   605     NOT_IMPLEMENTED;
       
   606 }
       
   607 
       
   608 int MediaObject::videoOutputCount()
       
   609 {
       
   610 	return m_videoOutputCount;
       
   611 }
       
   612 
       
   613 void MediaObject::synchAudioVideo()
       
   614 {
       
   615     if (m_state != Phonon::PlayingState)
       
   616         return;
       
   617     if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty())
       
   618         return;
       
   619 
       
   620     seek(m_currentTime);
       
   621     checkForError();
       
   622 }
       
   623 
       
   624 qint32 MediaObject::tickInterval() const
       
   625 {
       
   626     IMPLEMENTED;
       
   627     return m_tickInterval;
       
   628 }
       
   629 
       
   630 void MediaObject::setTickInterval(qint32 interval)
       
   631 {
       
   632     IMPLEMENTED;
       
   633     m_tickInterval = interval;
       
   634     if (m_tickInterval > 0)
       
   635         m_tickTimer = startTimer(m_tickInterval);
       
   636     else{
       
   637         killTimer(m_tickTimer);
       
   638         m_tickTimer = 0;
       
   639     }
       
   640 }
       
   641 
       
   642 bool MediaObject::hasVideo() const
       
   643 {
       
   644     IMPLEMENTED;
       
   645     return m_videoPlayer ? m_videoPlayer->hasVideo() : false;
       
   646 }
       
   647 
       
   648 bool MediaObject::isSeekable() const
       
   649 {
       
   650     IMPLEMENTED;
       
   651     return m_videoPlayer ? m_videoPlayer->isSeekable() : false;
       
   652 }
       
   653 
       
   654 qint64 MediaObject::currentTime() const
       
   655 {
       
   656     IMPLEMENTED_SILENT;
       
   657     const_cast<MediaObject *>(this)->updateCurrentTime();
       
   658     return m_currentTime;
       
   659 }
       
   660 
       
   661 void MediaObject::updateCurrentTime()
       
   662 {
       
   663     quint64 lastUpdateTime = m_currentTime;
       
   664     m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime();
       
   665     quint64 total = m_videoPlayer->duration();
       
   666 
       
   667     if (m_videoPlayer->currentTrack() < m_videoPlayer->trackCount() - 1){
       
   668         // There are still more tracks to play after the current track.
       
   669         if (m_autoplayTitles) {
       
   670             if (lastUpdateTime < m_currentTime && m_currentTime == total)
       
   671                 setCurrentTrack(m_videoPlayer->currentTrack() + 1);
       
   672         }
       
   673     } else if (m_nextVideoPlayer->state() == QuickTimeVideoPlayer::NoMedia){
       
   674         // There is no more sources or tracks to play after the current source.
       
   675         // Check if it's time to emit aboutToFinish:
       
   676         quint32 mark = qMax(quint64(0), qMin(total, total + m_transitionTime - 2000));
       
   677         if (lastUpdateTime < mark && mark <= m_currentTime)
       
   678             emit aboutToFinish();
       
   679 
       
   680         // Check if it's time to emit prefinishMarkReached:
       
   681         mark = qMax(quint64(0), total - m_prefinishMark);
       
   682         if (lastUpdateTime < mark && mark <= m_currentTime)
       
   683             emit prefinishMarkReached(total - m_currentTime);
       
   684 
       
   685         if (lastUpdateTime < m_currentTime && m_currentTime == total){
       
   686             emit finished();
       
   687             m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime();
       
   688             if (m_state == Phonon::PlayingState && m_currentTime == total)
       
   689                 pause();
       
   690         }
       
   691     } else {
       
   692         // We have a next source.
       
   693         // Check if it's time to swap to next source:
       
   694         quint32 mark = qMax(quint64(0), total + m_transitionTime);
       
   695         if (m_waitNextSwap && m_state == Phonon::PlayingState &&
       
   696             m_transitionTime < m_swapTime.msecsTo(QTime::currentTime())){
       
   697             swapCurrentWithNext(0);
       
   698         } else if (mark >= total){
       
   699             if (lastUpdateTime < total && total == m_currentTime){
       
   700                 m_swapTime = QTime::currentTime();
       
   701                 m_swapTime.addMSecs(mark - total);
       
   702                 m_waitNextSwap = true;
       
   703             }
       
   704         } else if (lastUpdateTime < mark && mark <= m_currentTime){
       
   705             swapCurrentWithNext(total - m_currentTime);
       
   706         }
       
   707     }
       
   708 }
       
   709 
       
   710 qint64 MediaObject::totalTime() const
       
   711 {
       
   712     IMPLEMENTED_SILENT;
       
   713     return m_videoPlayer->duration();
       
   714 }
       
   715 
       
   716 Phonon::State MediaObject::state() const
       
   717 {
       
   718     IMPLEMENTED;
       
   719     return m_state;
       
   720 }
       
   721 
       
   722 QString MediaObject::errorString() const
       
   723 {
       
   724     IMPLEMENTED;
       
   725     return m_errorString;
       
   726 }
       
   727 
       
   728 Phonon::ErrorType MediaObject::errorType() const
       
   729 {
       
   730     IMPLEMENTED;
       
   731     return m_errorType;
       
   732 }
       
   733 
       
   734 bool MediaObject::checkForError()
       
   735 {
       
   736     int type = gGetErrorType();
       
   737     if (type == NO_ERROR)
       
   738         return false;
       
   739 
       
   740     m_errorType = (type == NORMAL_ERROR) ? Phonon::NormalError : Phonon::FatalError;
       
   741     m_errorString = gGetErrorString();
       
   742     pause_internal();
       
   743     gClearError();
       
   744     setState(Phonon::ErrorState);
       
   745     return true;
       
   746 }
       
   747 
       
   748 QuickTimeVideoPlayer* MediaObject::videoPlayer() const
       
   749 {
       
   750     return m_videoPlayer;
       
   751 }
       
   752 
       
   753 MediaSource MediaObject::source() const
       
   754 {
       
   755     IMPLEMENTED;
       
   756     return m_videoPlayer->mediaSource();
       
   757 }
       
   758 
       
   759 qint32 MediaObject::prefinishMark() const
       
   760 {
       
   761     IMPLEMENTED;
       
   762     return m_prefinishMark;
       
   763 }
       
   764 
       
   765 void MediaObject::setPrefinishMark(qint32 mark)
       
   766 {
       
   767     IMPLEMENTED;
       
   768     m_prefinishMark = mark;
       
   769 }
       
   770 
       
   771 qint32 MediaObject::transitionTime() const
       
   772 {
       
   773     IMPLEMENTED;
       
   774     return m_transitionTime;
       
   775 }
       
   776 
       
   777 void MediaObject::setTransitionTime(qint32 transitionTime)
       
   778 {
       
   779     IMPLEMENTED;
       
   780     m_transitionTime = transitionTime;
       
   781 }
       
   782 
       
   783 void MediaObject::setVolumeOnMovie(float volume)
       
   784 {
       
   785     m_videoPlayer->setMasterVolume(volume);
       
   786     m_nextVideoPlayer->setMasterVolume(volume);
       
   787 }
       
   788 
       
   789 bool MediaObject::setAudioDeviceOnMovie(int id)
       
   790 {
       
   791     m_nextVideoPlayer->setAudioDevice(id);
       
   792     return m_videoPlayer->setAudioDevice(id);
       
   793 }
       
   794 
       
   795 void MediaObject::updateCrossFade()
       
   796 {
       
   797     m_mediaObjectAudioNode->updateCrossFade(m_currentTime);
       
   798     // Clean-up previous movie if done fading:
       
   799     if (m_mediaObjectAudioNode->m_fadeDuration == 0){
       
   800         if (m_nextVideoPlayer->isPlaying() || m_nextAudioPlayer->isPlaying()){
       
   801             m_nextVideoPlayer->unsetCurrentMediaSource();
       
   802             m_nextAudioPlayer->unsetVideoPlayer();
       
   803         }
       
   804     }
       
   805 }
       
   806 
       
   807 void MediaObject::updateBufferStatus()
       
   808 {
       
   809     float percent = m_videoPlayer->percentageLoaded();
       
   810     if (percent != m_percentageLoaded){
       
   811         m_percentageLoaded = percent;
       
   812         emit bufferStatus(m_percentageLoaded * 100);
       
   813     }
       
   814 }
       
   815 
       
   816 void MediaObject::updateAudioBuffers()
       
   817 {
       
   818     // Schedule audio slices:
       
   819     m_audioPlayer->scheduleAudioToGraph();
       
   820     m_nextAudioPlayer->scheduleAudioToGraph();
       
   821 }
       
   822 
       
   823 bool MediaObject::isCrossFading()
       
   824 {
       
   825     return m_mediaObjectAudioNode->isCrossFading();
       
   826 }
       
   827 
       
   828 void MediaObject::updateVideoFrames()
       
   829 {
       
   830     // Draw next frame if awailable:
       
   831     if (m_videoPlayer->videoFrameChanged()){
       
   832         updateLipSynch(50);
       
   833         VideoFrame frame(m_videoPlayer);
       
   834         if (m_nextVideoPlayer->isPlaying()
       
   835             && m_nextVideoPlayer->hasVideo()
       
   836             && isCrossFading()){
       
   837             VideoFrame bgFrame(m_nextVideoPlayer);
       
   838             frame.setBackgroundFrame(bgFrame);
       
   839             frame.setBaseOpacity(m_mediaObjectAudioNode->m_volume1);
       
   840         }
       
   841 
       
   842         // Send the frame through the graph:
       
   843         updateVideo(frame);
       
   844         checkForError();
       
   845     }
       
   846 }
       
   847 
       
   848 void MediaObject::updateLipSynch(int allowedOffset)
       
   849 {
       
   850     if (m_audioSystem != AS_Graph || !m_audioGraph->isRunning())
       
   851         return;
       
   852     if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty())
       
   853         return;
       
   854 
       
   855     if (m_videoPlayer->hasVideo()){
       
   856         qint64 diff = m_audioPlayer->currentTime() - m_videoPlayer->currentTime();
       
   857         if (-allowedOffset > diff || diff > allowedOffset)
       
   858             m_audioPlayer->seek(m_videoPlayer->currentTime());
       
   859     }
       
   860 
       
   861     if (isCrossFading() && m_nextVideoPlayer->hasVideo()){
       
   862         qint64 diff = m_nextAudioPlayer->currentTime() - m_nextVideoPlayer->currentTime();
       
   863         if (-(allowedOffset*2) > diff || diff > (allowedOffset*2))
       
   864             m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime());
       
   865     }
       
   866 }
       
   867 
       
   868 void MediaObject::updateRapidly()
       
   869 {
       
   870     updateCurrentTime();
       
   871     updateCrossFade();
       
   872     updateBufferStatus();
       
   873 }
       
   874 
       
   875 void MediaObject::setMute(bool mute)
       
   876 {
       
   877     m_mediaObjectAudioNode->setMute(mute);
       
   878     m_videoPlayer->setMute(mute);
       
   879     m_nextVideoPlayer->setMute(mute);
       
   880 }
       
   881 
       
   882 void MediaObject::mediaNodeEvent(const MediaNodeEvent *event)
       
   883 {
       
   884     switch (event->type()){
       
   885         case MediaNodeEvent::EndConnectionChange:
       
   886             m_mediaObjectAudioNode->setMute(true);
       
   887             inspectGraph();
       
   888             setupAudioSystem();
       
   889             synchAudioVideo();
       
   890             checkForError();
       
   891             m_mediaObjectAudioNode->setMute(false);
       
   892             if (m_state == Phonon::PlayingState)
       
   893                 restartAudioVideoTimers();
       
   894             break;
       
   895         case MediaNodeEvent::AudioGraphCannotPlay:
       
   896         case MediaNodeEvent::AudioGraphInitialized:
       
   897             if (m_state != Phonon::LoadingState){
       
   898                 m_mediaObjectAudioNode->setMute(true);
       
   899                 setupAudioSystem();
       
   900                 updateLipSynch(0);
       
   901                 checkForError();
       
   902                 m_mediaObjectAudioNode->setMute(false);
       
   903             }
       
   904             break;
       
   905         default:
       
   906             break;
       
   907     }
       
   908 }
       
   909 
       
   910 bool MediaObject::event(QEvent *event)
       
   911 {
       
   912     switch (event->type()){
       
   913 #if QT_ALLOW_QUICKTIME
       
   914         case QEvent::User:{
       
   915             m_displayLinkMutex.lock();
       
   916             m_pendingDisplayLinkEvent = false;
       
   917             m_displayLinkMutex.unlock();
       
   918             updateVideoFrames();
       
   919             break; }
       
   920 #endif
       
   921         case QEvent::Timer:{
       
   922             int timerId = static_cast<QTimerEvent *>(event)->timerId();
       
   923             if (timerId == m_rapidTimer)
       
   924                 updateRapidly();
       
   925             else if (timerId == m_tickTimer)
       
   926                 emit tick(currentTime());
       
   927             else if (timerId == m_videoTimer)
       
   928                 updateVideoFrames();
       
   929             else if (timerId == m_audioTimer)
       
   930                 updateAudioBuffers();
       
   931             break; }
       
   932         default:
       
   933             break;
       
   934     }
       
   935     return QObject::event(event);
       
   936 }
       
   937 
       
   938 void MediaObject::setCurrentTrack(int track)
       
   939 {
       
   940     if (track == m_videoPlayer->currentTrack() || track < 0 || track >= m_videoPlayer->trackCount())
       
   941         return;
       
   942 
       
   943     m_videoPlayer->setCurrentTrack(track);
       
   944     emit titleChanged(track);
       
   945     emit metaDataChanged(m_videoPlayer->metaData());
       
   946 }
       
   947 
       
   948 bool MediaObject::hasInterface(Interface iface) const
       
   949 {
       
   950     return iface == AddonInterface::TitleInterface;
       
   951 }
       
   952 
       
   953 QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QVariant> &params)
       
   954 {
       
   955     switch (iface) {
       
   956         case TitleInterface:
       
   957             switch (command) {
       
   958                 case availableTitles:
       
   959                     return m_videoPlayer->trackCount();
       
   960                 case title:
       
   961                     return m_videoPlayer->currentTrack();
       
   962                 case setTitle:
       
   963                     setCurrentTrack(params.first().toInt());
       
   964                     break;
       
   965                 case autoplayTitles:
       
   966                     return m_autoplayTitles;
       
   967                 case setAutoplayTitles:
       
   968                     m_autoplayTitles = params.first().toBool();
       
   969                     break;
       
   970             }
       
   971         default:
       
   972             break;
       
   973     }
       
   974     return QVariant();
       
   975 }
       
   976 
       
   977 }} // namespace Phonon::QT7
       
   978 
       
   979 QT_END_NAMESPACE
       
   980 
       
   981 #include "moc_mediaobject.cpp"
       
   982