src/3rdparty/phonon/ds9/mediaobject.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
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/QVector>
       
    19 #include <QtCore/QTimerEvent>
       
    20 #include <QtCore/QTimer>
       
    21 #include <QtCore/QTime>
       
    22 #include <QtCore/QLibrary>
       
    23 
       
    24 #ifndef Q_CC_MSVC
       
    25 #include <dshow.h>
       
    26 #endif
       
    27 #include <objbase.h>
       
    28 #include <initguid.h>
       
    29 #include <qnetwork.h>
       
    30 #include <evcode.h>
       
    31 
       
    32 #include "mediaobject.h"
       
    33 #include "videowidget.h"
       
    34 #include "audiooutput.h"
       
    35 
       
    36 
       
    37 #include <QtCore/QDebug>
       
    38 
       
    39 #define TIMER_INTERVAL 16 //... ms for the timer that polls the current state (we use the multimedia timer)
       
    40 #define PRELOAD_TIME 2000 // 2 seconds to load a source
       
    41 
       
    42 QT_BEGIN_NAMESPACE
       
    43 
       
    44 namespace Phonon
       
    45 {
       
    46     namespace DS9
       
    47     {
       
    48         typedef BOOL (WINAPI* LPAMGETERRORTEXT)(HRESULT, WCHAR *, DWORD);
       
    49 
       
    50         //first the definition of the WorkerThread class
       
    51         WorkerThread::WorkerThread()
       
    52           : QThread(), m_finished(false), m_currentWorkId(1)
       
    53         {
       
    54         }
       
    55 
       
    56         WorkerThread::~WorkerThread()
       
    57         {
       
    58         }
       
    59 
       
    60         void WorkerThread::run()
       
    61         {
       
    62             while (m_finished == false) {
       
    63                 HANDLE handles[FILTER_COUNT +1];
       
    64                 handles[0] = m_waitCondition;
       
    65                 int count = 1;
       
    66                 for(int i = 0; i < FILTER_COUNT; ++i) {
       
    67                     if (m_graphHandle[i].graph) {
       
    68                         handles[count++] = m_graphHandle[i].handle;
       
    69                     }
       
    70                 }
       
    71                 DWORD result = ::WaitForMultipleObjects(count, handles, FALSE, INFINITE);
       
    72                 if (result == WAIT_OBJECT_0) {
       
    73                     handleTask();
       
    74                 } else {
       
    75                     //this is the event management
       
    76                     const Graph &graph = m_graphHandle[result - WAIT_OBJECT_0 - 1].graph;
       
    77                     long eventCode;
       
    78                     LONG_PTR param1, param2;
       
    79 
       
    80                     ComPointer<IMediaEvent> mediaEvent(graph, IID_IMediaEvent);
       
    81                     mediaEvent->GetEvent(&eventCode, &param1, &param2, 0);
       
    82                     emit eventReady(graph, eventCode, param1);
       
    83                     mediaEvent->FreeEventParams(eventCode, param1, param2);
       
    84                 }
       
    85             }
       
    86         }
       
    87 
       
    88         //wants to know as soon as the state is set
       
    89         void WorkerThread::addStateChangeRequest(Graph graph, OAFilterState state, QList<Filter> decoders)
       
    90         {
       
    91             QMutexLocker locker(&m_mutex);
       
    92             bool found = false;
       
    93             //we try to see if there is already an attempt to change the state and we remove it
       
    94             for(int i = 0; !found && i < m_queue.size(); ++i) {
       
    95                 const Work &w = m_queue.at(i);
       
    96                 if (w.graph == graph && w.task == ChangeState) {
       
    97                     found = true;
       
    98                     m_queue.removeAt(i);
       
    99                 }
       
   100             }
       
   101 
       
   102             //now let's create the new task
       
   103             Work w;
       
   104             w.task = ChangeState;
       
   105             w.id = m_currentWorkId++;
       
   106             w.graph = graph;
       
   107             w.state = state;
       
   108             w.decoders = decoders;
       
   109             m_queue.enqueue(w);
       
   110             m_waitCondition.set();
       
   111         }
       
   112 
       
   113         quint16 WorkerThread::addSeekRequest(Graph graph, qint64 time)
       
   114         {
       
   115             QMutexLocker locker(&m_mutex);
       
   116             bool found = false;
       
   117             //we try to see if there is already an attempt to seek and we remove it
       
   118             for(int i = 0; !found && i < m_queue.size(); ++i) {
       
   119                 const Work &w = m_queue.at(i);
       
   120                 if (w.graph == graph && w.task == Seek) {
       
   121                     found = true;
       
   122                     m_queue.removeAt(i);
       
   123                 }
       
   124             }
       
   125 
       
   126             Work w;
       
   127             w.task = Seek;
       
   128             //we create a new graph
       
   129             w.graph = graph;
       
   130             w.id = m_currentWorkId++;
       
   131             w.time = time;
       
   132             m_queue.enqueue(w);
       
   133             m_waitCondition.set();
       
   134             return w.id;
       
   135         }
       
   136 
       
   137         quint16 WorkerThread::addUrlToRender(const QString &url)
       
   138         {
       
   139             QMutexLocker locker(&m_mutex);
       
   140             Work w;
       
   141             w.task = Render;
       
   142             //we create a new graph
       
   143             w.graph = Graph(CLSID_FilterGraph, IID_IGraphBuilder);
       
   144             w.url = url;
       
   145             w.url.detach();
       
   146             w.id = m_currentWorkId++;
       
   147             m_queue.enqueue(w);
       
   148             m_waitCondition.set();
       
   149             return w.id;
       
   150         }
       
   151 
       
   152         quint16 WorkerThread::addFilterToRender(const Filter &filter)
       
   153         {
       
   154             QMutexLocker locker(&m_mutex);
       
   155             Work w;
       
   156             w.task = Render;
       
   157             //we create a new graph
       
   158             w.graph = Graph(CLSID_FilterGraph, IID_IGraphBuilder);
       
   159             w.filter = filter;
       
   160             w.graph->AddFilter(filter, 0);
       
   161             w.id = m_currentWorkId++;
       
   162             m_queue.enqueue(w);
       
   163             m_waitCondition.set();
       
   164             return w.id;
       
   165         }
       
   166 
       
   167         void WorkerThread::replaceGraphForEventManagement(Graph newGraph, Graph oldGraph)
       
   168         {
       
   169             QMutexLocker locker(&m_mutex);
       
   170             Work w;
       
   171             w.task = ReplaceGraph;
       
   172             w.graph = newGraph;
       
   173             w.oldGraph = oldGraph;
       
   174             m_queue.enqueue(w);
       
   175             m_waitCondition.set();
       
   176         }
       
   177 
       
   178         void WorkerThread::handleTask()
       
   179         {
       
   180             QMutexLocker locker(Backend::directShowMutex());
       
   181             {
       
   182                 QMutexLocker locker(&m_mutex);
       
   183                 if (m_finished || m_queue.isEmpty()) {
       
   184                     return;
       
   185                 }
       
   186 
       
   187                 m_currentWork = m_queue.dequeue();
       
   188 
       
   189                 //we ensure to have the wait condition in the right state
       
   190                 if (m_queue.isEmpty()) {
       
   191                     m_waitCondition.reset();
       
   192                 } else {
       
   193                     m_waitCondition.set();
       
   194                 }
       
   195             }
       
   196 
       
   197             HRESULT hr = S_OK;
       
   198 
       
   199             if (m_currentWork.task == ReplaceGraph) {
       
   200                 int index = -1;
       
   201                 for(int i = 0; i < FILTER_COUNT; ++i) {
       
   202                     if (m_graphHandle[i].graph == m_currentWork.oldGraph) {
       
   203                         m_graphHandle[i].graph = Graph();
       
   204                         index = i;
       
   205                         break;
       
   206                     } else if (index == -1 && m_graphHandle[i].graph == 0) {
       
   207                         //this is the first available slot
       
   208                         index = i;
       
   209                     }
       
   210                 }
       
   211 
       
   212                 Q_ASSERT(index != -1);
       
   213 
       
   214                 //add the new graph
       
   215                 HANDLE h;
       
   216                 if (SUCCEEDED(ComPointer<IMediaEvent>(m_currentWork.graph, IID_IMediaEvent)
       
   217                     ->GetEventHandle(reinterpret_cast<OAEVENT*>(&h)))) {
       
   218                     m_graphHandle[index].graph = m_currentWork.graph;
       
   219                     m_graphHandle[index].handle = h;
       
   220                 }
       
   221             } else if (m_currentWork.task == Render) {
       
   222                 if (m_currentWork.filter) {
       
   223                     //let's render pins
       
   224                     const QList<OutputPin> outputs = BackendNode::pins(m_currentWork.filter, PINDIR_OUTPUT);
       
   225                     for (int i = 0; SUCCEEDED(hr) && i < outputs.count(); ++i) {
       
   226                         hr = m_currentWork.graph->Render(outputs.at(i));
       
   227                     }
       
   228                 } else if (!m_currentWork.url.isEmpty()) {
       
   229                     //let's render a url (blocking call)
       
   230                     hr = m_currentWork.graph->RenderFile(reinterpret_cast<const wchar_t *>(m_currentWork.url.utf16()), 0);
       
   231                 }
       
   232                 if (hr != E_ABORT) {
       
   233                     emit asyncRenderFinished(m_currentWork.id, hr, m_currentWork.graph);
       
   234                 }
       
   235             } else if (m_currentWork.task == Seek) {
       
   236                 //that's a seekrequest
       
   237                 ComPointer<IMediaSeeking> mediaSeeking(m_currentWork.graph, IID_IMediaSeeking);
       
   238                 qint64 newtime = m_currentWork.time * 10000;
       
   239                 hr = mediaSeeking->SetPositions(&newtime, AM_SEEKING_AbsolutePositioning,
       
   240                     0, AM_SEEKING_NoPositioning);
       
   241                 emit asyncSeekingFinished(m_currentWork.id, newtime / 10000);
       
   242                 hr = E_ABORT; //to avoid emitting asyncRenderFinished
       
   243             } else if (m_currentWork.task == ChangeState) {
       
   244 
       
   245                 //remove useless decoders
       
   246                 QList<Filter> unused;
       
   247                 for (int i = 0; i < m_currentWork.decoders.count(); ++i) {
       
   248                     const Filter &filter = m_currentWork.decoders.at(i);
       
   249                     bool used = false;
       
   250                     const QList<OutputPin> pins = BackendNode::pins(filter, PINDIR_OUTPUT);
       
   251                     for( int i = 0; i < pins.count(); ++i) {
       
   252                         InputPin input;
       
   253                         if (pins.at(i)->ConnectedTo(input.pparam()) == S_OK) {
       
   254                             used = true;
       
   255                         }
       
   256                     }
       
   257                     if (!used) {
       
   258                         unused += filter;
       
   259                     }
       
   260                 }
       
   261 
       
   262                 //we can get the state
       
   263                 for (int i = 0; i < unused.count(); ++i) {
       
   264                     //we should remove this filter from the graph
       
   265                     m_currentWork.graph->RemoveFilter(unused.at(i));
       
   266                 }
       
   267 
       
   268 
       
   269                 //we can get the state
       
   270                 ComPointer<IMediaControl> mc(m_currentWork.graph, IID_IMediaControl);
       
   271 
       
   272                 //we change the state here
       
   273                 switch(m_currentWork.state)
       
   274                 {
       
   275                 case State_Stopped:
       
   276                     mc->Stop();
       
   277                     break;
       
   278                 case State_Paused:
       
   279                     mc->Pause();
       
   280                     break;
       
   281                 case State_Running:
       
   282                     mc->Run();
       
   283                     break;
       
   284                 }
       
   285                 OAFilterState s;
       
   286                 //blocking call
       
   287                 HRESULT hr = mc->GetState(INFINITE, &s);
       
   288 
       
   289                 if (SUCCEEDED(hr)) {
       
   290                     if (s == State_Stopped) {
       
   291                         emit stateReady(m_currentWork.graph, Phonon::StoppedState);
       
   292                     } else if (s == State_Paused) {
       
   293                         emit stateReady(m_currentWork.graph, Phonon::PausedState);
       
   294                     } else /*if (s == State_Running)*/ {
       
   295                         emit stateReady(m_currentWork.graph, Phonon::PlayingState);
       
   296                     }
       
   297                 }
       
   298             }
       
   299 
       
   300             {
       
   301                 QMutexLocker locker(&m_mutex);
       
   302                 m_currentWork = Work(); //reinitialize
       
   303             }
       
   304         }
       
   305 
       
   306         void WorkerThread::abortCurrentRender(qint16 renderId)
       
   307         {
       
   308             QMutexLocker locker(&m_mutex);
       
   309             if (m_currentWork.id == renderId) {
       
   310                 m_currentWork.graph->Abort();
       
   311             }
       
   312             bool found = false;
       
   313             for(int i = 0; !found && i < m_queue.size(); ++i) {
       
   314                 const Work &w = m_queue.at(i);
       
   315                 if (w.id == renderId) {
       
   316                     found = true;
       
   317                     m_queue.removeAt(i);
       
   318                     if (m_queue.isEmpty()) {
       
   319                         m_waitCondition.reset();
       
   320                     }
       
   321                 }
       
   322             }
       
   323         }
       
   324 
       
   325         //tells the thread to stop processing
       
   326         void WorkerThread::signalStop()
       
   327         {
       
   328             QMutexLocker locker(&m_mutex);
       
   329             m_queue.clear();
       
   330             if (m_currentWork.graph) {
       
   331                 //in case we're currently rendering something
       
   332                 m_currentWork.graph->Abort();
       
   333 
       
   334             }
       
   335 
       
   336             m_finished = true;
       
   337             m_waitCondition.set();
       
   338         }
       
   339 
       
   340 
       
   341         MediaObject::MediaObject(QObject *parent) : BackendNode(parent),
       
   342             transactionState(Phonon::StoppedState),
       
   343             m_errorType(Phonon::NoError),
       
   344             m_state(Phonon::LoadingState),
       
   345             m_nextState(Phonon::StoppedState),
       
   346             m_prefinishMark(0),
       
   347             m_tickInterval(0),
       
   348             m_buffering(false),
       
   349             m_oldHasVideo(false),
       
   350             m_prefinishMarkSent(false),
       
   351             m_aboutToFinishSent(false),
       
   352             m_nextSourceReadyToStart(false),
       
   353 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
   354             m_autoplayTitles(true),
       
   355             m_currentTitle(0),
       
   356 #endif //QT_NO_PHONON_MEDIACONTROLLER
       
   357             m_targetTick(INFINITE)
       
   358         {
       
   359 
       
   360             for(int i = 0; i < FILTER_COUNT; ++i) {
       
   361                 m_graphs[i] = new MediaGraph(this, i);
       
   362             }
       
   363 
       
   364             connect(&m_thread, SIGNAL(stateReady(Graph, Phonon::State)),
       
   365                                SLOT(slotStateReady(Graph, Phonon::State)));
       
   366 
       
   367             connect(&m_thread, SIGNAL(eventReady(Graph, long, long)),
       
   368                                SLOT(handleEvents(Graph, long, long)));
       
   369 
       
   370             connect(&m_thread, SIGNAL(asyncRenderFinished(quint16, HRESULT, Graph)),
       
   371                 SLOT(finishLoading(quint16, HRESULT, Graph)));
       
   372 
       
   373             connect(&m_thread, SIGNAL(asyncSeekingFinished(quint16, qint64)),
       
   374                 SLOT(finishSeeking(quint16, qint64)));
       
   375             //really special case
       
   376             m_mediaObject = this;
       
   377             m_thread.start();
       
   378         }
       
   379 
       
   380         MediaObject::~MediaObject()
       
   381         {
       
   382             //be sure to finish the timer first
       
   383             m_tickTimer.stop();
       
   384 
       
   385             //we finish the worker thread here
       
   386             m_thread.signalStop();
       
   387             m_thread.wait();
       
   388 
       
   389             //and then we delete the graphs
       
   390             for (int i = 0; i < FILTER_COUNT; ++i) {
       
   391                 delete m_graphs[i];
       
   392             }
       
   393         }
       
   394 
       
   395         WorkerThread *MediaObject::workerThread()
       
   396         {
       
   397             return &m_thread;
       
   398         }
       
   399 
       
   400         MediaGraph *MediaObject::currentGraph() const
       
   401         {
       
   402             return m_graphs[0];
       
   403         }
       
   404 
       
   405         MediaGraph *MediaObject::nextGraph() const
       
   406         {
       
   407             return m_graphs[FILTER_COUNT - 1];
       
   408         }
       
   409 
       
   410         //utility function to save the graph to a file
       
   411         void MediaObject::timerEvent(QTimerEvent *e)
       
   412         {
       
   413             if (e->timerId() == m_tickTimer.timerId()) {
       
   414 
       
   415                 const qint64 current = currentTime();
       
   416                 const qint64 total = totalTime();
       
   417 
       
   418                 if ( m_tickInterval != 0 && current > m_targetTick) {
       
   419                     updateTargetTick();
       
   420                     emit tick(current);
       
   421                 }
       
   422 
       
   423                 //check that the title hasn't changed
       
   424 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
   425                 if (m_autoplayTitles && m_currentTitle < _iface_availableTitles() - 1) {
       
   426 
       
   427                     if (current >= total) {
       
   428                         //we go to the next title
       
   429                         _iface_setCurrentTitle(m_currentTitle + 1, false);
       
   430                         emit tick(current);
       
   431                     }
       
   432                     return;
       
   433                 }
       
   434 #endif //QT_NO_PHONON_MEDIACONTROLLER
       
   435 
       
   436                 if (total) {
       
   437                     const qint64 remaining = total - current;
       
   438 
       
   439                     if (m_transitionTime < 0 && m_nextSourceReadyToStart) {
       
   440                         if (remaining < -m_transitionTime + TIMER_INTERVAL/2) {
       
   441                             //we need to switch graphs to run the next source in the queue (with cross-fading)
       
   442                             switchToNextSource();
       
   443                             return;
       
   444                         } else if (current < -m_transitionTime) {
       
   445                             //we are currently crossfading
       
   446                             for (int i = 0; i < m_audioOutputs.count(); ++i) {
       
   447                                 m_audioOutputs.at(i)->setCrossFadingProgress( currentGraph()->index(), qMin( qreal(1.), qreal(current) / qreal(-m_transitionTime)));
       
   448                             }
       
   449                         }
       
   450                     }
       
   451 
       
   452                     if (m_prefinishMark > 0 && !m_prefinishMarkSent && remaining < m_prefinishMark + TIMER_INTERVAL/2) {
       
   453 #ifdef GRAPH_DEBUG
       
   454                         qDebug() << "DS9: emit prefinishMarkReached" << remaining << QTime::currentTime().toString();
       
   455 #endif
       
   456                         m_prefinishMarkSent = true;
       
   457 
       
   458                         emit prefinishMarkReached( remaining );
       
   459                     }
       
   460 
       
   461                     if (!m_aboutToFinishSent && remaining < PRELOAD_TIME - m_transitionTime  + TIMER_INTERVAL/2) {
       
   462                         //let's take a 2 seconds time to actually load the next file
       
   463 #ifdef GRAPH_DEBUG
       
   464                         qDebug() << "DS9: emit aboutToFinish" << remaining << QTime::currentTime().toString();
       
   465 #endif
       
   466                         m_aboutToFinishSent = true;
       
   467                         emit aboutToFinish();
       
   468                     }
       
   469                 } else {
       
   470                     //total is 0: the stream is probably live (endless)
       
   471                 }
       
   472 
       
   473                 if (m_buffering) {
       
   474                     ComPointer<IAMNetworkStatus> status(currentGraph()->realSource(), IID_IAMNetworkStatus);
       
   475                     if (status) {
       
   476                         long l;
       
   477                         status->get_BufferingProgress(&l);
       
   478                         emit bufferStatus(l);
       
   479 #ifdef GRAPH_DEBUG
       
   480                         qDebug() << "emit bufferStatus(" << l << ")";
       
   481 #endif
       
   482                     }
       
   483                 }
       
   484             }
       
   485         }
       
   486 
       
   487         void MediaObject::switchToNextSource()
       
   488         {
       
   489             m_prefinishMarkSent = false;
       
   490             m_aboutToFinishSent = false;
       
   491             m_nextSourceReadyToStart = false;
       
   492 
       
   493             m_oldHasVideo = currentGraph()->hasVideo();
       
   494 
       
   495             qSwap(m_graphs[0], m_graphs[1]); //swap the graphs
       
   496 
       
   497             if (m_transitionTime >= 0)
       
   498                 m_graphs[1]->stop(); //make sure we stop the previous graph
       
   499 
       
   500             if (currentGraph()->mediaSource().type() != Phonon::MediaSource::Invalid &&
       
   501                 catchComError(currentGraph()->renderResult())) {
       
   502                     setState(Phonon::ErrorState);
       
   503                     return;
       
   504             }
       
   505 
       
   506             //we need to play the next media
       
   507             play();
       
   508 
       
   509             //we tell the video widgets to switch now to the new source
       
   510 #ifndef QT_NO_PHONON_VIDEO
       
   511             for (int i = 0; i < m_videoWidgets.count(); ++i) {
       
   512                 m_videoWidgets.at(i)->setCurrentGraph(currentGraph()->index());
       
   513             }
       
   514 #endif //QT_NO_PHONON_VIDEO
       
   515 
       
   516             emit currentSourceChanged(currentGraph()->mediaSource());
       
   517             emit metaDataChanged(currentGraph()->metadata());
       
   518 
       
   519             if (nextGraph()->hasVideo() != currentGraph()->hasVideo()) {
       
   520                 emit hasVideoChanged(currentGraph()->hasVideo());
       
   521             }
       
   522 
       
   523             emit tick(0);
       
   524             emit totalTimeChanged(totalTime());
       
   525 
       
   526 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
   527             setTitles(currentGraph()->titles());
       
   528 #endif //QT_NO_PHONON_MEDIACONTROLLER
       
   529         }
       
   530 
       
   531         Phonon::State MediaObject::state() const
       
   532         {
       
   533             if (m_buffering) {
       
   534                 return Phonon::BufferingState;
       
   535             } else {
       
   536                 return m_state;
       
   537             }
       
   538         }
       
   539 
       
   540         bool MediaObject::hasVideo() const
       
   541         {
       
   542             return currentGraph()->hasVideo();
       
   543         }
       
   544 
       
   545         bool MediaObject::isSeekable() const
       
   546         {
       
   547             return currentGraph()->isSeekable();
       
   548         }
       
   549 
       
   550         qint64 MediaObject::totalTime() const
       
   551         {
       
   552 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
   553             //1st, check if there is more titles after
       
   554             const qint64 ret = (m_currentTitle < _iface_availableTitles() - 1) ?
       
   555                 titleAbsolutePosition(m_currentTitle+1) : currentGraph()->absoluteTotalTime();
       
   556 
       
   557             //this is the duration of the current title
       
   558             return ret - titleAbsolutePosition(m_currentTitle);
       
   559 #else
       
   560             return currentGraph()->absoluteTotalTime();
       
   561 #endif //QT_NO_PHONON_MEDIACONTROLLER
       
   562         }
       
   563 
       
   564         qint64 MediaObject::currentTime() const
       
   565         {
       
   566             //this handles inaccuracy when stopping on a title
       
   567             return currentGraph()->absoluteCurrentTime()
       
   568 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
   569                 - titleAbsolutePosition(m_currentTitle)
       
   570 #endif //QT_NO_PHONON_MEDIACONTROLLER
       
   571                 ;
       
   572         }
       
   573 
       
   574         qint32 MediaObject::tickInterval() const
       
   575         {
       
   576             return m_tickInterval;
       
   577         }
       
   578 
       
   579         void MediaObject::setTickInterval(qint32 newTickInterval)
       
   580         {
       
   581             m_tickInterval = newTickInterval;
       
   582             updateTargetTick();
       
   583         }
       
   584 
       
   585         void MediaObject::pause()
       
   586         {
       
   587             if (currentGraph()->isLoading()) {
       
   588                 m_nextState = Phonon::PausedState;
       
   589             } else {
       
   590                 currentGraph()->pause();
       
   591             }
       
   592         }
       
   593 
       
   594         void MediaObject::stop()
       
   595         {
       
   596             if (currentGraph()->isLoading()) {
       
   597                 m_nextState = Phonon::StoppedState;
       
   598             } else {
       
   599                 currentGraph()->stop();
       
   600             }
       
   601         }
       
   602 
       
   603         void MediaObject::ensureStopped()
       
   604         {
       
   605             currentGraph()->ensureStopped();
       
   606             if (m_state == Phonon::ErrorState) {
       
   607                 //we reset the state here
       
   608                 m_state = Phonon::StoppedState;
       
   609             }
       
   610         }
       
   611 
       
   612         void MediaObject::play()
       
   613         {
       
   614             if (currentGraph()->isLoading()) {
       
   615                 m_nextState = Phonon::PlayingState;
       
   616             } else {
       
   617                 currentGraph()->play();
       
   618             }
       
   619         }
       
   620 
       
   621         QString MediaObject::errorString() const
       
   622         {
       
   623             return m_errorString;
       
   624         }
       
   625 
       
   626         Phonon::ErrorType MediaObject::errorType() const
       
   627         {
       
   628             return m_errorType;
       
   629         }
       
   630 
       
   631 
       
   632         void MediaObject::updateTargetTick()
       
   633         {
       
   634             if (m_tickInterval) {
       
   635                 const qint64 current = currentTime();
       
   636                 m_targetTick = current / m_tickInterval * m_tickInterval;
       
   637                 if (current == 0 || m_targetTick < current) {
       
   638                     m_targetTick += m_tickInterval;
       
   639                 }
       
   640             }
       
   641         }
       
   642 
       
   643         void MediaObject::setState(Phonon::State newstate)
       
   644         {
       
   645             if (newstate == Phonon::PlayingState) {
       
   646                 updateTargetTick();
       
   647             }
       
   648 
       
   649             if (newstate == m_state) {
       
   650                 return;
       
   651             }
       
   652 
       
   653             //manage the timer
       
   654             if (newstate == Phonon::PlayingState) {
       
   655                 m_tickTimer.start(TIMER_INTERVAL, this);
       
   656             } else {
       
   657                 m_tickTimer.stop();
       
   658             }
       
   659 
       
   660             Phonon::State oldstate = state();
       
   661             m_state = newstate;
       
   662             emit stateChanged(newstate, oldstate);
       
   663         }
       
   664 
       
   665 
       
   666         qint32 MediaObject::prefinishMark() const
       
   667         {
       
   668             return m_prefinishMark;
       
   669         }
       
   670 
       
   671         void MediaObject::setPrefinishMark(qint32 newPrefinishMark)
       
   672         {
       
   673             m_prefinishMark = newPrefinishMark;
       
   674         }
       
   675 
       
   676         qint32 MediaObject::transitionTime() const
       
   677         {
       
   678             return m_transitionTime;
       
   679         }
       
   680 
       
   681         void MediaObject::setTransitionTime(qint32 time)
       
   682         {
       
   683             m_transitionTime = time;
       
   684         }
       
   685 
       
   686         qint64 MediaObject::remainingTime() const
       
   687         {
       
   688             return totalTime() - currentTime();
       
   689         }
       
   690 
       
   691 
       
   692         Phonon::MediaSource MediaObject::source() const
       
   693         {
       
   694             return currentGraph()->mediaSource();
       
   695         }
       
   696 
       
   697         void MediaObject::setNextSource(const Phonon::MediaSource &source)
       
   698         {
       
   699             m_nextSourceReadyToStart = true;
       
   700             const bool shouldSwitch = (m_state == Phonon::StoppedState || m_state == Phonon::ErrorState);
       
   701             nextGraph()->loadSource(source); //let's preload the source
       
   702 
       
   703             if (shouldSwitch) {
       
   704                 switchToNextSource();
       
   705             }
       
   706         }
       
   707 
       
   708         void MediaObject::setSource(const Phonon::MediaSource &source)
       
   709         {
       
   710             m_nextSourceReadyToStart = false;
       
   711             m_prefinishMarkSent = false;
       
   712             m_aboutToFinishSent = false;
       
   713 
       
   714             m_oldHasVideo = currentGraph()->hasVideo();
       
   715             setState(Phonon::LoadingState);
       
   716             //After loading we go into stopped state
       
   717             m_nextState = Phonon::StoppedState;
       
   718             catchComError(currentGraph()->loadSource(source));
       
   719             emit currentSourceChanged(source);
       
   720         }
       
   721 
       
   722         void MediaObject::slotStateReady(Graph graph, Phonon::State newState)
       
   723         {
       
   724             if (graph == currentGraph()->graph() && !currentGraph()->isLoading()) {
       
   725                 setState(newState);
       
   726             }
       
   727         }
       
   728 
       
   729         void MediaObject::loadingFinished(MediaGraph *mg)
       
   730         {
       
   731             if (mg == currentGraph()) {
       
   732 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
   733                 //Title interface
       
   734                 m_currentTitle = 0;
       
   735                 setTitles(currentGraph()->titles());
       
   736 #endif //QT_NO_PHONON_MEDIACONTROLLER
       
   737 
       
   738                 HRESULT hr = mg->renderResult();
       
   739 
       
   740                 if (catchComError(hr)) {
       
   741                     return;
       
   742                 }
       
   743 
       
   744                 if (m_oldHasVideo != currentGraph()->hasVideo()) {
       
   745                     emit hasVideoChanged(currentGraph()->hasVideo());
       
   746                 }
       
   747 
       
   748 #ifndef QT_NO_PHONON_VIDEO
       
   749                 if (currentGraph()->hasVideo()) {
       
   750                     updateVideoGeometry();
       
   751                 }
       
   752 #endif //QT_NO_PHONON_VIDEO
       
   753 
       
   754                 emit metaDataChanged(currentGraph()->metadata());
       
   755                 emit totalTimeChanged(totalTime());
       
   756 
       
   757                 //let's put the next state
       
   758                 switch(m_nextState)
       
   759                 {
       
   760                 case Phonon::PausedState:
       
   761                     pause();
       
   762                     break;
       
   763                 case Phonon::PlayingState:
       
   764                     play();
       
   765                     break;
       
   766                 case Phonon::ErrorState:
       
   767                     setState(Phonon::ErrorState);
       
   768                     break;
       
   769                 case Phonon::StoppedState:
       
   770                 default:
       
   771                     stop();
       
   772                     break;
       
   773                 }
       
   774             }
       
   775         }
       
   776 
       
   777         void MediaObject::seek(qint64 time)
       
   778         {
       
   779             //we seek into the current title
       
   780             currentGraph()->absoluteSeek(time
       
   781 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
   782                 + titleAbsolutePosition(m_currentTitle)
       
   783 #endif //QT_NO_PHONON_MEDIACONTROLLER
       
   784                 );
       
   785         }
       
   786 
       
   787         void MediaObject::seekingFinished(MediaGraph *mg)
       
   788         {
       
   789             if (mg == currentGraph()) {
       
   790 
       
   791                 updateTargetTick();
       
   792                 if (currentTime() < totalTime() - m_prefinishMark) {
       
   793                     m_prefinishMarkSent = false;
       
   794                 }
       
   795 
       
   796                 if (currentTime() < totalTime() - PRELOAD_TIME + m_transitionTime) {
       
   797                     m_aboutToFinishSent = false;
       
   798                 }
       
   799 
       
   800                 //this helps the update of the application (seekslider for example)
       
   801                 if (m_state == PausedState || m_state == PlayingState) {
       
   802                     emit tick(currentTime());
       
   803                 }
       
   804             }
       
   805         }
       
   806 
       
   807 
       
   808         bool MediaObject::catchComError(HRESULT hr)
       
   809         {
       
   810 
       
   811             m_errorString.clear();
       
   812             m_errorType = Phonon::NoError;
       
   813 
       
   814             if (hr != S_OK) {
       
   815 #ifdef GRAPH_DEBUG
       
   816                 qWarning("an error occurred 0x%x",hr);
       
   817 #endif
       
   818                 LPAMGETERRORTEXT getErrorText = (LPAMGETERRORTEXT)QLibrary::resolve(QLatin1String("quartz"), "AMGetErrorTextW");
       
   819 
       
   820                 WCHAR buffer[MAX_ERROR_TEXT_LEN];
       
   821                 if (getErrorText && getErrorText(hr, buffer, MAX_ERROR_TEXT_LEN)) {
       
   822                     m_errorString = QString::fromWCharArray(buffer);
       
   823                 } else {
       
   824                     m_errorString = QString::fromLatin1("Unknown error");
       
   825                 }
       
   826                 const QString comError = QString::number(uint(hr), 16);
       
   827                 if (!m_errorString.toLower().contains(comError.toLower())) {
       
   828                     m_errorString += QString::fromLatin1(" (0x%1)").arg(comError);
       
   829                 }
       
   830                 if (FAILED(hr)) {
       
   831                     m_errorType = Phonon::FatalError;
       
   832                     setState(Phonon::ErrorState);
       
   833                 } else {
       
   834                     m_errorType = Phonon::NormalError;
       
   835                     m_nextState = Phonon::ErrorState;
       
   836                 }
       
   837             } else {
       
   838                 m_errorType = Phonon::NoError;
       
   839 
       
   840             }
       
   841 
       
   842             return m_errorType == Phonon::FatalError;
       
   843         }
       
   844 
       
   845 
       
   846         void MediaObject::grabNode(BackendNode *node)
       
   847         {
       
   848             for (int i = 0; i < FILTER_COUNT; ++i) {
       
   849                 m_graphs[i]->grabNode(node);
       
   850             }
       
   851             node->setMediaObject(this);
       
   852         }
       
   853 
       
   854         bool MediaObject::connectNodes(BackendNode *source, BackendNode *sink)
       
   855         {
       
   856             bool ret = true;
       
   857             for (int i = 0; i < FILTER_COUNT; ++i) {
       
   858                 ret = ret && m_graphs[i]->connectNodes(source, sink);
       
   859             }
       
   860             if (ret) {
       
   861 #ifndef QT_NO_PHONON_VIDEO
       
   862                 if (VideoWidget *video = qobject_cast<VideoWidget*>(sink)) {
       
   863                     m_videoWidgets += video;
       
   864                 } else
       
   865 #endif //QT_NO_PHONON_VIDEO
       
   866                     if (AudioOutput *audio = qobject_cast<AudioOutput*>(sink)) {
       
   867                     m_audioOutputs += audio;
       
   868                 }
       
   869             }
       
   870             return ret;
       
   871         }
       
   872 
       
   873         bool MediaObject::disconnectNodes(BackendNode *source, BackendNode *sink)
       
   874         {
       
   875             bool ret = true;
       
   876             for (int i = 0; i < FILTER_COUNT; ++i) {
       
   877                 ret = ret && m_graphs[i]->disconnectNodes(source, sink);
       
   878             }
       
   879             if (ret) {
       
   880 #ifndef QT_NO_PHONON_VIDEO
       
   881                 if (VideoWidget *video = qobject_cast<VideoWidget*>(sink)) {
       
   882                     m_videoWidgets.removeOne(video);
       
   883                 } else
       
   884 #endif //QT_NO_PHONON_VIDEO
       
   885                     if (AudioOutput *audio = qobject_cast<AudioOutput*>(sink)) {
       
   886                         m_audioOutputs.removeOne(audio);
       
   887                 }
       
   888             }
       
   889             return ret;
       
   890         }
       
   891 
       
   892 #ifndef QT_NO_PHONON_VIDEO
       
   893         void MediaObject::updateVideoGeometry()
       
   894         {
       
   895             for (int i = 0; i < m_videoWidgets.count(); ++i) {
       
   896                 m_videoWidgets.at(i)->notifyVideoLoaded();
       
   897             }
       
   898         }
       
   899 #endif //QT_NO_PHONON_VIDEO
       
   900 
       
   901         void MediaObject::handleComplete(IGraphBuilder *graph)
       
   902         {
       
   903             if (graph == currentGraph()->graph()) {
       
   904                 if (m_transitionTime >= PRELOAD_TIME || m_aboutToFinishSent == false) {
       
   905                     emit aboutToFinish(); //give a chance to the frontend to give a next source
       
   906                     m_aboutToFinishSent = true;
       
   907                 }
       
   908 
       
   909                 if (!m_nextSourceReadyToStart) {
       
   910                     //this is the last source, we simply finish
       
   911                     const qint64 current = currentTime();
       
   912                     const OAFilterState currentState = currentGraph()->syncGetRealState();
       
   913 
       
   914                     emit tick(current); //this ensures that the end of the seek slider is reached
       
   915                     emit finished();
       
   916 
       
   917                     if (currentTime() == current && currentGraph()->syncGetRealState() == currentState) {
       
   918                         //no seek operation in-between
       
   919                         pause();
       
   920                         setState(Phonon::PausedState); //we set it here
       
   921                     }
       
   922 
       
   923                 } else if (m_transitionTime == 0) {
       
   924                     //gapless transition
       
   925                     switchToNextSource(); //let's call the function immediately
       
   926                 } else if (m_transitionTime > 0) {
       
   927                     //management of the transition (if it is >= 0)
       
   928                     QTimer::singleShot(m_transitionTime, this, SLOT(switchToNextSource()));
       
   929                 }
       
   930             } else {
       
   931                 //it is just the end of the previous source (in case of cross-fading)
       
   932                 nextGraph()->cleanup();
       
   933             }
       
   934             for (int i = 0; i < m_audioOutputs.count(); ++i) {
       
   935                 m_audioOutputs.at(i)->setCrossFadingProgress( currentGraph()->index(), 1.); //cross-fading is in any case finished
       
   936             }
       
   937         }
       
   938 
       
   939         void MediaObject::finishLoading(quint16 workId, HRESULT hr, Graph graph)
       
   940         {
       
   941             for(int i = 0; i < FILTER_COUNT; ++i) {
       
   942                 m_graphs[i]->finishLoading(workId, hr, graph);
       
   943             }
       
   944         }
       
   945 
       
   946         void MediaObject::finishSeeking(quint16 workId, qint64 time)
       
   947         {
       
   948             for(int i = 0; i < FILTER_COUNT; ++i) {
       
   949                 m_graphs[i]->finishSeeking(workId, time);
       
   950             }
       
   951         }
       
   952 
       
   953 
       
   954         void MediaObject::handleEvents(Graph graph, long eventCode, long param1)
       
   955         {
       
   956             QString eventDescription;
       
   957             switch (eventCode)
       
   958             {
       
   959             case EC_BUFFERING_DATA:
       
   960                 if (graph == currentGraph()->graph()) {
       
   961                     m_buffering = param1;
       
   962                     emit stateChanged(state(), m_state);
       
   963                 }
       
   964                 break;
       
   965             case EC_LENGTH_CHANGED:
       
   966                 if (graph == currentGraph()->graph()) {
       
   967                     emit totalTimeChanged( totalTime() );
       
   968                 }
       
   969                 break;
       
   970 
       
   971             case EC_COMPLETE:
       
   972                 handleComplete(graph);
       
   973                 break;
       
   974 
       
   975 #ifndef QT_NO_PHONON_VIDEO
       
   976             case EC_VIDEO_SIZE_CHANGED:
       
   977                 if (graph == currentGraph()->graph()) {
       
   978                     updateVideoGeometry();
       
   979                 }
       
   980                 break;
       
   981 #endif //QT_NO_PHONON_VIDEO
       
   982 
       
   983 #ifdef GRAPH_DEBUG
       
   984             case EC_ACTIVATE: qDebug() << "EC_ACTIVATE: A video window is being " << (param1 ? "ACTIVATED" : "DEACTIVATED"); break;
       
   985             case EC_BUILT: qDebug() << "EC_BUILT: Send by the Video Control when a graph has been built. Not forwarded to applications."; break;
       
   986             case EC_CLOCK_CHANGED: qDebug() << "EC_CLOCK_CHANGED"; break;
       
   987             case EC_CLOCK_UNSET: qDebug() << "EC_CLOCK_UNSET: The clock provider was disconnected."; break;
       
   988             case EC_CODECAPI_EVENT: qDebug() << "EC_CODECAPI_EVENT: Sent by an encoder to signal an encoding event."; break;
       
   989             case EC_DEVICE_LOST: qDebug() << "EC_DEVICE_LOST: A Plug and Play device was removed or has become available again."; break;
       
   990             case EC_DISPLAY_CHANGED: qDebug() << "EC_DISPLAY_CHANGED: The display mode has changed."; break;
       
   991             case EC_END_OF_SEGMENT: qDebug() << "EC_END_OF_SEGMENT: The end of a segment has been reached."; break;
       
   992             case EC_ERROR_STILLPLAYING: qDebug() << "EC_ERROR_STILLPLAYING: An asynchronous command to run the graph has failed."; break;
       
   993             case EC_ERRORABORT: qDebug() << "EC_ERRORABORT: An operation was aborted because of an error."; break;
       
   994             case EC_EXTDEVICE_MODE_CHANGE: qDebug() << "EC_EXTDEVICE_MODE_CHANGE: Not supported."; break;
       
   995             case EC_FULLSCREEN_LOST: qDebug() << "EC_FULLSCREEN_LOST: The video renderer is switching out of full-screen mode."; break;
       
   996             case EC_GRAPH_CHANGED: qDebug() << "EC_GRAPH_CHANGED: The filter graph has changed."; break;
       
   997             case EC_NEED_RESTART: qDebug() << "EC_NEED_RESTART: A filter is requesting that the graph be restarted."; break;
       
   998             case EC_NOTIFY_WINDOW: qDebug() << "EC_NOTIFY_WINDOW: Notifies a filter of the video renderer's window."; break;
       
   999             case EC_OLE_EVENT: qDebug() << "EC_OLE_EVENT: A filter is passing a text string to the application."; break;
       
  1000             case EC_OPENING_FILE: qDebug() << "EC_OPENING_FILE: The graph is opening a file, or has finished opening a file."; break;
       
  1001             case EC_PALETTE_CHANGED: qDebug() << "EC_PALETTE_CHANGED: The video palette has changed."; break;
       
  1002             case EC_PAUSED: qDebug() << "EC_PAUSED: A pause request has completed."; break;
       
  1003             case EC_PREPROCESS_COMPLETE: qDebug() << "EC_PREPROCESS_COMPLETE: Sent by the WM ASF Writer filter when it completes the pre-processing for multipass encoding."; break;
       
  1004             case EC_QUALITY_CHANGE: qDebug() << "EC_QUALITY_CHANGE: The graph is dropping samples, for quality control."; break;
       
  1005             case EC_REPAINT: qDebug() << "EC_REPAINT: A video renderer requires a repaint."; break;
       
  1006             case EC_SEGMENT_STARTED: qDebug() << "EC_SEGMENT_STARTED: A new segment has started."; break;
       
  1007             case EC_SHUTTING_DOWN: qDebug() << "EC_SHUTTING_DOWN: The filter graph is shutting down, prior to being destroyed."; break;
       
  1008             case EC_SNDDEV_IN_ERROR: qDebug() << "EC_SNDDEV_IN_ERROR: A device error has occurred in an audio capture filter."; break;
       
  1009             case EC_SNDDEV_OUT_ERROR: qDebug() << "EC_SNDDEV_OUT_ERROR: A device error has occurred in an audio renderer filter."; break;
       
  1010             case EC_STARVATION: qDebug() << "EC_STARVATION: A filter is not receiving enough data."; break;
       
  1011             case EC_STATE_CHANGE: qDebug() << "EC_STATE_CHANGE: The filter graph has changed state."; break;
       
  1012             case EC_STEP_COMPLETE: qDebug() << "EC_STEP_COMPLETE: A filter performing frame stepping has stepped the specified number of frames."; break;
       
  1013             case EC_STREAM_CONTROL_STARTED: qDebug() << "EC_STREAM_CONTROL_STARTED: A stream-control start command has taken effect."; break;
       
  1014             case EC_STREAM_CONTROL_STOPPED: qDebug() << "EC_STREAM_CONTROL_STOPPED: A stream-control stop command has taken effect."; break;
       
  1015             case EC_STREAM_ERROR_STILLPLAYING: qDebug() << "EC_STREAM_ERROR_STILLPLAYING: An error has occurred in a stream. The stream is still playing."; break;
       
  1016             case EC_STREAM_ERROR_STOPPED: qDebug() << "EC_STREAM_ERROR_STOPPED: A stream has stopped because of an error."; break;
       
  1017             case EC_TIMECODE_AVAILABLE: qDebug() << "EC_TIMECODE_AVAILABLE: Not supported."; break;
       
  1018             case EC_UNBUILT: qDebug() << "Sent by the Video Control when a graph has been torn down. Not forwarded to applications."; break;
       
  1019             case EC_USERABORT: qDebug() << "EC_USERABORT: Send by the Video Control when a graph has been torn down. Not forwarded to applications."; break;
       
  1020             case EC_VMR_RECONNECTION_FAILED: qDebug() << "EC_VMR_RECONNECTION_FAILED: Sent by the VMR-7 and the VMR-9 when it was unable to accept a dynamic format change request from the upstream decoder."; break;
       
  1021             case EC_VMR_RENDERDEVICE_SET: qDebug() << "EC_VMR_RENDERDEVICE_SET: Sent when the VMR has selected its rendering mechanism."; break;
       
  1022             case EC_VMR_SURFACE_FLIPPED: qDebug() << "EC_VMR_SURFACE_FLIPPED: Sent when the VMR-7's allocator presenter has called the DirectDraw Flip method on the surface being presented."; break;
       
  1023             case EC_WINDOW_DESTROYED: qDebug() << "EC_WINDOW_DESTROYED: The video renderer was destroyed or removed from the graph"; break;
       
  1024             case EC_WMT_EVENT: qDebug() << "EC_WMT_EVENT: Sent by the Windows Media Format SDK when an application uses the ASF Reader filter to play ASF files protected by digital rights management (DRM)."; break;
       
  1025             case EC_WMT_INDEX_EVENT: qDebug() << "EC_WMT_INDEX_EVENT: Sent by the Windows Media Format SDK when an application uses the ASF Writer to index Windows Media Video files."; break;
       
  1026 
       
  1027                 //documented by Microsoft but not supported in the Platform SDK
       
  1028                 //              case EC_BANDWIDTHCHANGE : qDebug() << "EC_BANDWIDTHCHANGE: not supported"; break;
       
  1029                 //              case EC_CONTENTPROPERTY_CHANGED: qDebug() << "EC_CONTENTPROPERTY_CHANGED: not supported."; break;
       
  1030                 //              case EC_EOS_SOON: qDebug() << "EC_EOS_SOON: not supported"; break;
       
  1031                 //              case EC_ERRORABORTEX: qDebug() << "EC_ERRORABORTEX: An operation was aborted because of an error."; break;
       
  1032                 //              case EC_FILE_CLOSED: qDebug() << "EC_FILE_CLOSED: The source file was closed because of an unexpected event."; break;
       
  1033                 //              case EC_LOADSTATUS: qDebug() << "EC_LOADSTATUS: Notifies the application of progress when opening a network file."; break;
       
  1034                 //              case EC_MARKER_HIT: qDebug() << "EC_MARKER_HIT: not supported."; break;
       
  1035                 //              case EC_NEW_PIN: qDebug() << "EC_NEW_PIN: not supported."; break;
       
  1036                 //              case EC_PLEASE_REOPEN: qDebug() << "EC_PLEASE_REOPEN: The source file has changed."; break;
       
  1037                 //              case EC_PROCESSING_LATENCY: qDebug() << "EC_PROCESSING_LATENCY: Indicates the amount of time that a component is taking to process each sample."; break;
       
  1038                 //              case EC_RENDER_FINISHED: qDebug() << "EC_RENDER_FINISHED: Not supported."; break;
       
  1039                 //              case EC_SAMPLE_LATENCY: qDebug() << "EC_SAMPLE_LATENCY: Specifies how far behind schedule a component is for processing samples."; break;
       
  1040                 //              case EC_SAMPLE_NEEDED: qDebug() << "EC_SAMPLE_NEEDED: Requests a new input sample from the Enhanced Video Renderer (EVR) filter."; break;
       
  1041                 //              case EC_SCRUB_TIME: qDebug() << "EC_SCRUB_TIME: Specifies the time stamp for the most recent frame step."; break;
       
  1042                 //              case EC_STATUS: qDebug() << "EC_STATUS: Contains two arbitrary status strings."; break;
       
  1043                 //              case EC_VIDEOFRAMEREADY: qDebug() << "EC_VIDEOFRAMEREADY: A video frame is ready for display."; break;
       
  1044 
       
  1045             default:
       
  1046                 qDebug() << "Unknown event" << eventCode << "(" << param1 << ")";
       
  1047                 break;
       
  1048 #else
       
  1049             default:
       
  1050                 break;
       
  1051 #endif
       
  1052             }
       
  1053         }
       
  1054 
       
  1055 
       
  1056 #ifndef QT_NO_PHONON_MEDIACONTROLLER
       
  1057         //interface management
       
  1058         bool MediaObject::hasInterface(Interface iface) const
       
  1059         {
       
  1060             return iface == AddonInterface::TitleInterface;
       
  1061         }
       
  1062 
       
  1063         QVariant MediaObject::interfaceCall(Interface iface, int command, const QList<QVariant> &params)
       
  1064         {
       
  1065             if (hasInterface(iface)) {
       
  1066 
       
  1067                 switch (iface)
       
  1068                 {
       
  1069                 case TitleInterface:
       
  1070                     switch (command)
       
  1071                     {
       
  1072                     case availableTitles:
       
  1073                         return _iface_availableTitles();
       
  1074                     case title:
       
  1075                         return _iface_currentTitle();
       
  1076                     case setTitle:
       
  1077                         _iface_setCurrentTitle(params.first().toInt());
       
  1078                         break;
       
  1079                     case autoplayTitles:
       
  1080                         return m_autoplayTitles;
       
  1081                     case setAutoplayTitles:
       
  1082                         m_autoplayTitles = params.first().toBool();
       
  1083                         updateStopPosition();
       
  1084                         break;
       
  1085                     }
       
  1086                     break;
       
  1087                 default:
       
  1088                     break;
       
  1089                 }
       
  1090             }
       
  1091             return QVariant();
       
  1092         }
       
  1093 
       
  1094 
       
  1095         //TitleInterface
       
  1096 
       
  1097         //this is called to set the time for the different titles
       
  1098         qint64 MediaObject::titleAbsolutePosition(int title) const
       
  1099         {
       
  1100             if (title >= 0 && title < m_titles.count()) {
       
  1101                 return m_titles.at(title);
       
  1102             } else {
       
  1103                 return 0;
       
  1104             }
       
  1105         }
       
  1106 
       
  1107         void MediaObject::setTitles(const QList<qint64> &titles)
       
  1108         {
       
  1109             //this is called when the source is loaded
       
  1110             const bool emitSignal = m_titles.count() != titles.count();
       
  1111             m_titles = titles;
       
  1112             if (emitSignal) {
       
  1113                 emit availableTitlesChanged(titles.count());
       
  1114             }
       
  1115             updateStopPosition();
       
  1116         }
       
  1117 
       
  1118 
       
  1119         int MediaObject::_iface_availableTitles() const
       
  1120         {
       
  1121             return m_titles.count() - 1;
       
  1122         }
       
  1123 
       
  1124         int MediaObject::_iface_currentTitle() const
       
  1125         {
       
  1126             return m_currentTitle;
       
  1127         }
       
  1128 
       
  1129         void MediaObject::_iface_setCurrentTitle(int title, bool bseek)
       
  1130         {
       
  1131 #ifdef GRAPH_DEBUG
       
  1132             qDebug() << "_iface_setCurrentTitle" << title;
       
  1133 #endif
       
  1134             const int oldTitle = m_currentTitle;
       
  1135             m_currentTitle = title;
       
  1136             updateStopPosition();
       
  1137             if (bseek) {
       
  1138                 //let's seek to the beginning of the song
       
  1139                 seek(0);
       
  1140             } else {
       
  1141                 updateTargetTick();
       
  1142             }
       
  1143             if (oldTitle != title) {
       
  1144                 emit titleChanged(title);
       
  1145                 emit totalTimeChanged(totalTime());
       
  1146             }
       
  1147 
       
  1148         }
       
  1149 
       
  1150         void MediaObject::updateStopPosition()
       
  1151         {
       
  1152             if (!m_autoplayTitles && m_currentTitle < _iface_availableTitles() - 1) {
       
  1153                 //stop position is set to the end of the track
       
  1154                 currentGraph()->setStopPosition(titleAbsolutePosition(m_currentTitle+1));
       
  1155             } else {
       
  1156                 //stop position is set to the end
       
  1157                 currentGraph()->setStopPosition(-1);
       
  1158             }
       
  1159         }
       
  1160 #endif //QT_NO_PHONON_QT_NO_PHONON_MEDIACONTROLLER
       
  1161 
       
  1162         void MediaObject::switchFilters(int index, Filter oldFilter, Filter newFilter)
       
  1163         {
       
  1164             if (currentGraph()->index() == index) {
       
  1165                 currentGraph()->switchFilters(oldFilter, newFilter);
       
  1166             } else {
       
  1167                 nextGraph()->switchFilters(oldFilter, newFilter);
       
  1168             }
       
  1169 
       
  1170         }
       
  1171 
       
  1172 
       
  1173     }
       
  1174 }
       
  1175 
       
  1176 QT_END_NAMESPACE
       
  1177 
       
  1178 #include "moc_mediaobject.cpp"