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_metaData = new QuickTimeMetaData();
50
    m_audioGraph = new AudioGraph(this);
51
52
    m_tickInterval = 0;
53
    m_prefinishMark = 0;
54
    m_currentTime = 0;
55
    m_transitionTime = 0;
56
    m_percentageLoaded = 0;
57
    m_waitNextSwap = false;
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_bufferTimer = 0;
67
    m_rapidTimer = 0;
68
69
    checkForError();
70
}
71
72
MediaObject::~MediaObject()
73
{   
74
    // m_mediaObjectAudioNode is owned by super class.    
75
    m_audioPlayer->unsetVideoPlayer();
76
    m_nextAudioPlayer->unsetVideoPlayer();
77
    delete m_videoPlayer;
78
    delete m_nextVideoPlayer;
79
    delete m_metaData;
80
    checkForError();
81
}
82
83
bool MediaObject::setState(Phonon::State state)
84
{
85
    Phonon::State prevState = m_state;
86
    m_state = state;
87
    if (prevState != m_state){
88
        emit stateChanged(m_state, prevState);
89
        if (m_state != state){
90
            // End-application did something
91
            // upon  receiving the signal. 
92
            return false;
93
        }
94
    }
95
    return true;
96
}
97
98
void MediaObject::inspectAudioGraphRecursive(AudioConnection *connection, int &effectCount, int &outputCount)
99
{
100
    if ((connection->m_sink->m_description & (AudioSource | AudioSink)) == (AudioSource | AudioSink))
101
        ++effectCount;
102
	else if (connection->m_sink->m_description & AudioSink)
103
    	++outputCount;
104
105
    for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i)
106
        inspectAudioGraphRecursive(connection->m_sink->m_audioSinkList[i], effectCount, outputCount);
107
}
108
109
void MediaObject::inspectVideoGraphRecursive(MediaNode *node, int &effectCount, int &outputCount)
110
{
111
    if ((node->m_description & (VideoSource | VideoSink)) == (VideoSource | VideoSink))
112
        ++effectCount;
113
	else if (node->m_description & VideoSink)
114
    	++outputCount;
115
116
    for (int i=0; i<node->m_videoSinkList.size(); ++i)
117
        inspectVideoGraphRecursive(node->m_videoSinkList[i], effectCount, outputCount);
118
}
119
120
void MediaObject::inspectGraph()
121
{
122
    // Inspect the graph to check wether there are any
123
    // effects or outputs connected. This will have
124
    // influence on the audio system and video system that ends up beeing used:
125
    int prevVideoOutputCount = m_videoOutputCount;	
126
    m_audioEffectCount = 0;
127
    m_audioOutputCount = 0;
128
    m_videoEffectCount = 0;
129
    m_videoOutputCount = 0;
130
    AudioConnection rootConnection(this);
131
    inspectAudioGraphRecursive(&rootConnection, m_audioEffectCount, m_audioOutputCount);
132
    inspectVideoGraphRecursive(this, m_videoEffectCount, m_videoOutputCount);
133
134
	if (m_videoOutputCount != prevVideoOutputCount){
135
	    MediaNodeEvent e1(MediaNodeEvent::VideoOutputCountChanged, &m_videoOutputCount);
136
	    notify(&e1);
137
	}	
138
}
139
140
void MediaObject::setupAudioSystem()
141
{
142
    // Select which audio system to use:
143
    AudioSystem newAudioSystem = AS_Unset;
144
    if (!m_audioOutputCount || !m_videoPlayer->canPlayMedia()){
145
        newAudioSystem = AS_Silent;
146
    } else if (m_audioEffectCount == 0){
147
        newAudioSystem = AS_Video;
148
    } else if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_4){
149
        newAudioSystem = AS_Video;
150
        SET_ERROR("Audio effects are not supported for Mac OS 10.3 and below", NORMAL_ERROR);
151
    } else if (m_videoPlayer->isDrmProtected()){
152
        newAudioSystem = AS_Video;
153
        SET_ERROR("Audio effects are not supported for DRM protected media", NORMAL_ERROR);
154
    } else if (m_audioGraph->graphCannotPlay()){
155
        newAudioSystem = AS_Video;
156
        SET_ERROR("Audio effects are not supported for the current codec", NORMAL_ERROR);
157
#ifdef QUICKTIME_C_API_AVAILABLE
158
    } else {
159
        newAudioSystem = AS_Graph;
160
    }
161
#else
162
    } else {
163
        newAudioSystem = AS_Video;
164
        SET_ERROR("Audio effects are not supported for the 64-bit version of the Phonon QT7 backend", NORMAL_ERROR);
165
    }
166
#endif
167
168
    if (newAudioSystem == m_audioSystem)
169
        return;
170
  
171
    // Enable selected audio system:
172
    m_audioSystem = newAudioSystem; 
173
    switch (newAudioSystem){
174
        case AS_Silent:
175
            m_audioGraph->stop();
176
            m_videoPlayer->enableAudio(false);
177
            m_nextVideoPlayer->enableAudio(false);    
178
            m_audioPlayer->enableAudio(false);
179
            m_nextAudioPlayer->enableAudio(false);
180
        break;
181
        case AS_Graph:
182
            if (m_state == Phonon::PausedState)
183
                m_audioGraph->prepare();
184
            else
185
                m_audioGraph->start();
186
            // Starting the graph can lead to a recursive call
187
            // telling us that we must direct audio through
188
            // video. If that has happened, we must not proceed:
189
            if (m_audioSystem != AS_Graph)
190
                return;
191
            m_videoPlayer->enableAudio(false);
192
            m_nextVideoPlayer->enableAudio(false);
193
            m_audioPlayer->enableAudio(true);
194
            m_audioPlayer->seek(m_videoPlayer->currentTime());
195
            m_nextAudioPlayer->enableAudio(true);
196
            m_audioPlayer->seek(m_videoPlayer->currentTime());
197
            m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime());
198
        break;
199
        case AS_Video:
200
        case AS_Unset:
201
            m_audioGraph->stop();
202
            m_videoPlayer->enableAudio(true);
203
            m_nextVideoPlayer->enableAudio(true);
204
            m_audioPlayer->enableAudio(false);
205
            m_nextAudioPlayer->enableAudio(false);
206
            m_videoPlayer->seek(m_audioPlayer->currentTime());
207
            m_nextVideoPlayer->seek(m_nextAudioPlayer->currentTime());
208
        break;
209
    }
210
}
211
212
void MediaObject::setSource(const MediaSource &source)
213
{
214
    IMPLEMENTED;
215
	PhononAutoReleasePool pool;
216
    setState(Phonon::LoadingState);
217
    
218
    // Save current state for event/signal handling below:
219
    bool prevHasVideo = m_videoPlayer->hasVideo();
220
    qint64 prevTotalTime = totalTime();
221
    m_waitNextSwap = false;
222
        
223
    // Cancel cross-fade if any:
224
    m_nextVideoPlayer->pause();
225
    m_nextAudioPlayer->pause();
226
    m_mediaObjectAudioNode->cancelCrossFade();
227
    
228
    // Set new source:
229
    m_audioPlayer->unsetVideoPlayer();
230
    m_videoPlayer->setMediaSource(source);
231
    m_audioPlayer->setVideoPlayer(m_videoPlayer);
232
    m_metaData->setVideo(m_videoPlayer);        
233
234
    m_audioGraph->updateStreamSpecifications();        
235
    m_nextAudioPlayer->unsetVideoPlayer();
236
    m_nextVideoPlayer->unsetVideo();
237
    m_currentTime = 0;
238
        
239
    // Emit/notify information about the new source:
240
    QRect videoRect = m_videoPlayer->videoRect();
241
    MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect);
242
    notify(&e1);
243
244
    // Clear video widgets:
245
    VideoFrame emptyFrame;
246
    updateVideo(emptyFrame);
247
248
    emit currentSourceChanged(source);
249
    emit metaDataChanged(m_metaData->metaData());
250
251
    if (prevHasVideo != m_videoPlayer->hasVideo())
252
        emit hasVideoChanged(m_videoPlayer->hasVideo());        
253
    if (prevTotalTime != totalTime())
254
        emit totalTimeChanged(totalTime());        
255
    if (checkForError())
256
        return;
257
    if (!m_videoPlayer->isDrmAuthorized())
258
        SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR)
259
    if (checkForError())
260
        return;
261
    if (!m_videoPlayer->canPlayMedia())
262
        SET_ERROR("Cannot play media.", FATAL_ERROR)
263
        
264
    // The state might have changed from LoadingState
265
    // as a response to an error state change. So we
266
    // need to check it before stopping: 
267
    if (m_state == Phonon::LoadingState)
268
        stop();
269
270
    setupAudioSystem();
271
    checkForError();
272
}
273
274
void MediaObject::setNextSource(const MediaSource &source)
275
{
276
    IMPLEMENTED;
277
    m_nextAudioPlayer->unsetVideoPlayer();
278
    m_nextVideoPlayer->setMediaSource(source);
279
    m_nextAudioPlayer->setVideoPlayer(m_nextVideoPlayer);
280
    checkForError();
281
}
282
283
void MediaObject::swapCurrentWithNext(qint32 transitionTime)
284
{
285
	PhononAutoReleasePool pool;
286
    setState(Phonon::LoadingState);
287
    // Save current state for event/signal handling below:
288
    bool prevHasVideo = m_videoPlayer->hasVideo();
289
    qint64 prevTotalTime = totalTime();
290
291
    qSwap(m_audioPlayer, m_nextAudioPlayer);
292
    qSwap(m_videoPlayer, m_nextVideoPlayer);
293
    m_mediaObjectAudioNode->startCrossFade(transitionTime);
294
    m_audioGraph->updateStreamSpecifications();
295
    m_metaData->setVideo(m_videoPlayer);
296
297
    m_waitNextSwap = false;
298
    m_currentTime = 0;
299
        
300
    // Emit/notify information about the new source:
301
    QRect videoRect = m_videoPlayer->videoRect();
302
    MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect);
303
    notify(&e1);
304
305
    emit currentSourceChanged(m_videoPlayer->mediaSource());
306
    emit metaDataChanged(m_metaData->metaData());
307
308
    if (prevHasVideo != m_videoPlayer->hasVideo())
309
        emit hasVideoChanged(m_videoPlayer->hasVideo());        
310
    if (prevTotalTime != totalTime())
311
        emit totalTimeChanged(totalTime());
312
    if (checkForError())
313
        return;
314
    if (!m_videoPlayer->isDrmAuthorized())
315
        SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR)
316
    if (checkForError())
317
        return;
318
    if (!m_videoPlayer->canPlayMedia())
319
        SET_ERROR("Cannot play next media.", FATAL_ERROR)
320
321
    setupAudioSystem();
322
    checkForError();
323
    if (m_state == Phonon::LoadingState){
324
        if (setState(Phonon::PlayingState))
325
            play_internal();
326
        checkForError();
327
    }
328
}
329
330
void MediaObject::updateTimer(int &timer, int interval)
331
{
332
    if (timer)
333
        killTimer(timer);
334
    timer = 0;
335
    if (interval >= 0)    
336
        timer = startTimer(interval); 
337
}
338
339
void MediaObject::play_internal()
340
{
341
    // Play main audio/video:
342
    m_videoPlayer->play();
343
    m_audioPlayer->play();     
344
    updateLipSynch(0);
345
    // Play old audio/video to finish cross-fade:
346
    if (m_nextVideoPlayer->currentTime() > 0){
347
        m_nextVideoPlayer->play();
348
        m_nextAudioPlayer->play();
349
    }
350
    bufferAudioVideo();
351
    updateTimer(m_rapidTimer, 100);
352
}
353
354
void MediaObject::pause_internal()
355
{
356
    m_audioGraph->stop();
357
    m_audioPlayer->pause();
358
    m_nextAudioPlayer->pause();
359
    m_videoPlayer->pause();
360
    m_nextVideoPlayer->pause();
361
    updateTimer(m_rapidTimer, -1);
362
    updateTimer(m_bufferTimer, -1);
363
364
    if (m_waitNextSwap)
365
        m_swapTimeLeft = m_swapTime.msecsTo(QTime::currentTime());
366
}
367
368
void MediaObject::play()
369
{
370
    IMPLEMENTED;
371
    if (m_state == Phonon::PlayingState)
372
        return;
373
    if (m_waitNextSwap){
374
        // update swap time after pause:
375
        m_swapTime = QTime::currentTime();
376
        m_swapTime.addMSecs(m_swapTimeLeft);
377
        setState(Phonon::PlayingState);
378
        return;
379
    }
380
    if (m_currentTime == m_videoPlayer->duration())
381
        return;
382
    if (!m_videoPlayer->canPlayMedia())
383
        return;
384
    if (!setState(Phonon::PlayingState))
385
        return;        
386
    if (m_audioSystem == AS_Graph){
387
        m_audioGraph->start();
388
        m_mediaObjectAudioNode->setMute(true);
389
    }
390
	// Inform the graph that we are about to play:
391
	bool playing = true;
392
    MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing);
393
    notify(&e1);
394
	// Start to play:
395
    play_internal();
396
    m_mediaObjectAudioNode->setMute(false);
397
    checkForError();
398
}
399
400
void MediaObject::pause()
401
{
402
    IMPLEMENTED;
403
    if (m_state == Phonon::PausedState)
404
        return;
405
    if (!setState(Phonon::PausedState))
406
        return;
407
    pause_internal();
408
	// Inform the graph that we are no longer playing:
409
	bool playing = false;
410
    MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing);
411
    notify(&e1);
412
	// But be prepared:
413
    if (m_audioSystem == AS_Graph)
414
        m_audioGraph->prepare();
415
    checkForError();
416
}
417
418
void MediaObject::stop()
419
{
420
    IMPLEMENTED;
421
    if (m_state == Phonon::StoppedState)
422
        return;
423
    if (!setState(Phonon::StoppedState))
424
        return;
425
    m_waitNextSwap = false;
426
    m_nextVideoPlayer->unsetVideo();
427
    m_nextAudioPlayer->unsetVideoPlayer();
428
    pause_internal();
429
    seek(0);
430
    checkForError();
431
}
432
433
void MediaObject::seek(qint64 milliseconds)
434
{
435
    IMPLEMENTED;
436
    if (m_state == Phonon::ErrorState)
437
        return;
438
        
439
    // Stop cross-fade if any:
440
    m_nextVideoPlayer->unsetVideo();
441
    m_nextAudioPlayer->unsetVideoPlayer();
442
    m_mediaObjectAudioNode->cancelCrossFade();
443
444
    // Seek to new position:
445
    m_mediaObjectAudioNode->setMute(true);
446
    m_videoPlayer->seek(milliseconds);
447
    m_audioPlayer->seek(m_videoPlayer->currentTime());
448
    m_mediaObjectAudioNode->setMute(false);
449
    
450
    // Update time and cancel pending swap:
451
    if (m_currentTime < m_videoPlayer->duration())
452
        m_waitNextSwap = false;
453
454
    updateCurrentTime();
455
	if (m_state != Phonon::PlayingState)
456
		updateVideoFrames();
457
    checkForError();
458
}
459
460
QStringList MediaObject::availableAudioStreams() const
461
{
462
    NOT_IMPLEMENTED;
463
    return QStringList();
464
}
465
466
QStringList MediaObject::availableVideoStreams() const
467
{
468
    NOT_IMPLEMENTED;
469
    return QStringList();
470
}
471
472
QStringList MediaObject::availableSubtitleStreams() const
473
{
474
    NOT_IMPLEMENTED;
475
    return QStringList();
476
}
477
478
QString MediaObject::currentAudioStream(const QObject */*audioPath*/) const
479
{
480
    NOT_IMPLEMENTED;
481
    return QString();
482
}
483
484
QString MediaObject::currentVideoStream(const QObject */*videoPath*/) const
485
{
486
    NOT_IMPLEMENTED;
487
    return QString();
488
}
489
490
QString MediaObject::currentSubtitleStream(const QObject */*videoPath*/) const
491
{
492
    NOT_IMPLEMENTED;
493
    return QString();
494
}
495
496
void MediaObject::setCurrentAudioStream(const QString &/*streamName*/,const QObject */*audioPath*/)
497
{
498
    NOT_IMPLEMENTED;
499
}
500
501
void MediaObject::setCurrentVideoStream(const QString &/*streamName*/,const QObject */*videoPath*/)
502
{
503
    NOT_IMPLEMENTED;
504
}
505
506
void MediaObject::setCurrentSubtitleStream(const QString &/*streamName*/,const QObject */*videoPath*/)
507
{
508
    NOT_IMPLEMENTED;
509
}
510
511
int MediaObject::videoOutputCount()
512
{
513
	return m_videoOutputCount;
514
}
515
516
void MediaObject::synchAudioVideo()
517
{
518
    if (m_state != Phonon::PlayingState)
519
        return;
520
    if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty())
521
        return;
522
523
    seek(m_currentTime);
524
    checkForError();
525
}
526
527
qint32 MediaObject::tickInterval() const
528
{
529
    IMPLEMENTED;
530
    return m_tickInterval;
531
}
532
533
void MediaObject::setTickInterval(qint32 interval)
534
{
535
    IMPLEMENTED;
536
    m_tickInterval = interval;
537
    if (m_tickInterval > 0)
538
        m_tickTimer = startTimer(m_tickInterval);
539
    else{
540
        killTimer(m_tickTimer);
541
        m_tickTimer = 0;
542
    }
543
}
544
545
bool MediaObject::hasVideo() const
546
{
547
    IMPLEMENTED;
548
    return m_videoPlayer ? m_videoPlayer->hasVideo() : false;
549
}
550
551
bool MediaObject::isSeekable() const
552
{
553
    IMPLEMENTED;
554
    return m_videoPlayer ? m_videoPlayer->isSeekable() : false;
555
}
556
557
qint64 MediaObject::currentTime() const
558
{
559
    IMPLEMENTED_SILENT;
560
    const_cast<MediaObject *>(this)->updateCurrentTime(); 
561
    return m_currentTime;
562
}
563
564
void MediaObject::updateCurrentTime()
565
{
566
    quint64 lastUpdateTime = m_currentTime;
567
    m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime();
568
    quint64 total = m_videoPlayer->duration();
569
570
    // Check if it's time to emit aboutToFinish:
571
    quint32 mark = qMax(quint64(0), qMin(total, total + m_transitionTime - 2000));
572
    if (lastUpdateTime < mark && mark <= m_currentTime)
573
        emit aboutToFinish();
574
575
    // Check if it's time to emit prefinishMarkReached:
576
    mark = qMax(quint64(0), total - m_prefinishMark);
577
    if (lastUpdateTime < mark && mark <= m_currentTime)
578
        emit prefinishMarkReached(total - m_currentTime);
579
580
    if (m_nextVideoPlayer->state() == QuickTimeVideoPlayer::NoMedia){
581
        // There is no next source in que.
582
        // Check if it's time to emit finished:
583
        if (lastUpdateTime < m_currentTime && m_currentTime == total){
584
            emit finished();
585
            m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime();
586
            if (m_state == Phonon::PlayingState && m_currentTime == total)
587
                pause();
588
        }
589
    } else {
590
        // We have a next source.
591
        // Check if it's time to swap to next source:
592
        mark = qMax(quint64(0), total + m_transitionTime);
593
        if (m_waitNextSwap && m_state == Phonon::PlayingState &&
594
            m_transitionTime < m_swapTime.msecsTo(QTime::currentTime())){
595
            swapCurrentWithNext(0);
596
        } else if (mark >= total){
597
            if (lastUpdateTime < total && total == m_currentTime){
598
                m_swapTime = QTime::currentTime();
599
                m_swapTime.addMSecs(mark - total);
600
                m_waitNextSwap = true;
601
            }
602
        } else if (lastUpdateTime < mark && mark <= m_currentTime){
603
            swapCurrentWithNext(total - m_currentTime);
604
        }
605
    }
606
}
607
608
qint64 MediaObject::totalTime() const
609
{
610
    IMPLEMENTED_SILENT;
611
    return m_videoPlayer->duration();
612
}
613
614
Phonon::State MediaObject::state() const
615
{
616
    IMPLEMENTED;
617
    return m_state;
618
}
619
620
QString MediaObject::errorString() const
621
{
622
    IMPLEMENTED;
623
    return m_errorString;
624
}
625
626
Phonon::ErrorType MediaObject::errorType() const
627
{
628
    IMPLEMENTED;
629
    return m_errorType;
630
}
631
632
bool MediaObject::checkForError()
633
{
634
    int type = gGetErrorType();
635
    if (type == NO_ERROR)
636
        return false;
637
638
    m_errorType = (type == NORMAL_ERROR) ? Phonon::NormalError : Phonon::FatalError;
639
    m_errorString = gGetErrorString();
640
    pause_internal();
641
    gClearError();
642
    setState(Phonon::ErrorState);
643
    return true;
644
}
645
646
QuickTimeVideoPlayer* MediaObject::videoPlayer() const
647
{
648
    return m_videoPlayer;
649
}
650
651
MediaSource MediaObject::source() const
652
{
653
    IMPLEMENTED;
654
    return m_videoPlayer->mediaSource();
655
}
656
657
qint32 MediaObject::prefinishMark() const
658
{
659
    IMPLEMENTED;
660
    return m_prefinishMark;
661
}
662
663
void MediaObject::setPrefinishMark(qint32 mark)
664
{
665
    IMPLEMENTED;
666
    m_prefinishMark = mark;
667
}
668
669
qint32 MediaObject::transitionTime() const
670
{
671
    IMPLEMENTED;
672
    return m_transitionTime;
673
}
674
675
void MediaObject::setTransitionTime(qint32 transitionTime)
676
{
677
    IMPLEMENTED;
678
    m_transitionTime = transitionTime;
679
}
680
681
void MediaObject::setVolumeOnMovie(float volume)
682
{
683
    m_videoPlayer->setMasterVolume(volume);
684
    m_nextVideoPlayer->setMasterVolume(volume);
685
}
686
687
bool MediaObject::setAudioDeviceOnMovie(int id)
688
{
689
    m_nextVideoPlayer->setAudioDevice(id);
690
    return m_videoPlayer->setAudioDevice(id);
691
}
692
693
void MediaObject::updateCrossFade()
694
{
695
    m_mediaObjectAudioNode->updateCrossFade(m_currentTime);   
696
    // Clean-up previous movie if done fading:
697
    if (m_mediaObjectAudioNode->m_fadeDuration == 0){
698
        if (m_nextVideoPlayer->isPlaying() || m_nextAudioPlayer->isPlaying()){
699
            m_nextVideoPlayer->unsetVideo();
700
            m_nextAudioPlayer->unsetVideoPlayer();
701
        }
702
    }        
703
}
704
705
void MediaObject::updateBufferStatus()
706
{
707
    float percent = m_videoPlayer->percentageLoaded();
708
    if (percent != m_percentageLoaded){
709
        m_percentageLoaded = percent;
710
        emit bufferStatus(m_percentageLoaded * 100);
711
    }
712
}
713
714
void MediaObject::updateAudioBuffers()
715
{
716
    // Schedule audio slices:
717
    m_audioPlayer->scheduleAudioToGraph();
718
    m_nextAudioPlayer->scheduleAudioToGraph();
719
}
720
721
bool MediaObject::isCrossFading()
722
{
723
    return m_mediaObjectAudioNode->isCrossFading();
724
}
725
726
void MediaObject::updateVideoFrames()
727
{
728
    // Draw next frame if awailable:
729
    if (m_videoPlayer->videoFrameChanged()){
730
        updateLipSynch(50);
731
        VideoFrame frame(m_videoPlayer);           
732
        if (m_nextVideoPlayer->isPlaying()
733
            && m_nextVideoPlayer->hasVideo()
734
            && isCrossFading()){
735
            VideoFrame bgFrame(m_nextVideoPlayer);
736
            frame.setBackgroundFrame(bgFrame);
737
            frame.setBaseOpacity(m_mediaObjectAudioNode->m_volume1);
738
        }
739
        
740
        // Send the frame through the graph:
741
        updateVideo(frame);    
742
        checkForError();
743
    }
744
}
745
746
void MediaObject::updateLipSynch(int allowedOffset)
747
{
748
    if (m_audioSystem != AS_Graph || !m_audioGraph->isRunning())
749
        return;
750
    if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty())
751
        return;
752
        
753
    if (m_videoPlayer->hasVideo()){
754
        qint64 diff = m_audioPlayer->currentTime() - m_videoPlayer->currentTime();
755
        if (-allowedOffset > diff || diff > allowedOffset)
756
            m_audioPlayer->seek(m_videoPlayer->currentTime());
757
    }
758
759
    if (isCrossFading() && m_nextVideoPlayer->hasVideo()){
760
        qint64 diff = m_nextAudioPlayer->currentTime() - m_nextVideoPlayer->currentTime();
761
        if (-(allowedOffset*2) > diff || diff > (allowedOffset*2))
762
            m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime());
763
    }
764
}
765
766
void MediaObject::bufferAudioVideo()
767
{
768
    long nextVideoUpdate = m_videoPlayer->hasVideo() ? 30 : INT_MAX;
769
    long nextAudioUpdate = m_audioPlayer->regularTaskFrequency();
770
    updateAudioBuffers();
771
    updateVideoFrames();
772
    if (m_state == Phonon::PlayingState)
773
        updateTimer(m_bufferTimer, qMin(nextVideoUpdate, nextAudioUpdate));
774
}
775
776
void MediaObject::updateRapidly()
777
{
778
    updateCurrentTime();
779
    updateCrossFade();
780
    updateBufferStatus();
781
}
782
783
void MediaObject::setMute(bool mute)
784
{
785
    m_mediaObjectAudioNode->setMute(mute);
786
    m_videoPlayer->setMute(mute);
787
    m_nextVideoPlayer->setMute(mute);
788
}
789
790
void MediaObject::mediaNodeEvent(const MediaNodeEvent *event)
791
{
792
    switch (event->type()){
793
        case MediaNodeEvent::EndConnectionChange:
794
            m_mediaObjectAudioNode->setMute(true);
795
            inspectGraph();
796
            setupAudioSystem();
797
            synchAudioVideo();
798
            checkForError();
799
            m_mediaObjectAudioNode->setMute(false);
800
             if (m_state == Phonon::PlayingState)
801
                bufferAudioVideo();
802
            break;
803
        case MediaNodeEvent::AudioGraphCannotPlay:
804
        case MediaNodeEvent::AudioGraphInitialized:
805
            if (m_state != Phonon::LoadingState){
806
                m_mediaObjectAudioNode->setMute(true);
807
                setupAudioSystem();
808
                updateLipSynch(0);
809
                checkForError();
810
                m_mediaObjectAudioNode->setMute(false);
811
            }
812
            break; 
813
        default:
814
            break;
815
    }
816
}
817
818
bool MediaObject::event(QEvent *event)
819
{
820
    switch (event->type()){
821
        case QEvent::Timer: {
822
            QTimerEvent *timerEvent = static_cast<QTimerEvent *>(event);
823
            if (timerEvent->timerId() == m_rapidTimer)
824
                updateRapidly();
825
            else if (timerEvent->timerId() == m_tickTimer)
826
                emit tick(currentTime());
827
            else if (timerEvent->timerId() == m_bufferTimer)
828
                bufferAudioVideo();
829
            }
830
            break;
831
        default:
832
            break;
833
    }
834
    return QObject::event(event);
835
}
836
837
bool MediaObject::hasInterface(Interface /*interface*/) const
838
{
839
    return false;
840
}
841
842
QVariant MediaObject::interfaceCall(Interface /*interface*/, int /*command*/, const QList<QVariant> &/*arguments*/)
843
{
844
    return QVariant();
845
}
846
847
}} // namespace Phonon::QT7
848
849
QT_END_NAMESPACE
850
851
#include "moc_mediaobject.cpp"