source: trunk/src/3rdparty/phonon/qt7/mediaobject.mm@ 757

Last change on this file since 757 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 28.2 KB
Line 
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
30QT_BEGIN_NAMESPACE
31
32namespace Phonon
33{
34namespace QT7
35{
36
37MediaObject::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
78MediaObject::~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
91bool 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
106void 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
117void 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
128void 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
148void 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
220void 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
284void 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
293void 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
343static 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
355void 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
370void 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;
387fail:
388 stopDisplayLink();
389}
390
391void 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
401void 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
429void 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
445void 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
465void 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
497void 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
515void 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
530void 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
557QStringList MediaObject::availableAudioStreams() const
558{
559 NOT_IMPLEMENTED;
560 return QStringList();
561}
562
563QStringList MediaObject::availableVideoStreams() const
564{
565 NOT_IMPLEMENTED;
566 return QStringList();
567}
568
569QStringList MediaObject::availableSubtitleStreams() const
570{
571 NOT_IMPLEMENTED;
572 return QStringList();
573}
574
575QString MediaObject::currentAudioStream(const QObject */*audioPath*/) const
576{
577 NOT_IMPLEMENTED;
578 return QString();
579}
580
581QString MediaObject::currentVideoStream(const QObject */*videoPath*/) const
582{
583 NOT_IMPLEMENTED;
584 return QString();
585}
586
587QString MediaObject::currentSubtitleStream(const QObject */*videoPath*/) const
588{
589 NOT_IMPLEMENTED;
590 return QString();
591}
592
593void MediaObject::setCurrentAudioStream(const QString &/*streamName*/,const QObject */*audioPath*/)
594{
595 NOT_IMPLEMENTED;
596}
597
598void MediaObject::setCurrentVideoStream(const QString &/*streamName*/,const QObject */*videoPath*/)
599{
600 NOT_IMPLEMENTED;
601}
602
603void MediaObject::setCurrentSubtitleStream(const QString &/*streamName*/,const QObject */*videoPath*/)
604{
605 NOT_IMPLEMENTED;
606}
607
608int MediaObject::videoOutputCount()
609{
610 return m_videoOutputCount;
611}
612
613void 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
624qint32 MediaObject::tickInterval() const
625{
626 IMPLEMENTED;
627 return m_tickInterval;
628}
629
630void 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
642bool MediaObject::hasVideo() const
643{
644 IMPLEMENTED;
645 return m_videoPlayer ? m_videoPlayer->hasVideo() : false;
646}
647
648bool MediaObject::isSeekable() const
649{
650 IMPLEMENTED;
651 return m_videoPlayer ? m_videoPlayer->isSeekable() : false;
652}
653
654qint64 MediaObject::currentTime() const
655{
656 IMPLEMENTED_SILENT;
657 const_cast<MediaObject *>(this)->updateCurrentTime();
658 return m_currentTime;
659}
660
661void 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
710qint64 MediaObject::totalTime() const
711{
712 IMPLEMENTED_SILENT;
713 return m_videoPlayer->duration();
714}
715
716Phonon::State MediaObject::state() const
717{
718 IMPLEMENTED;
719 return m_state;
720}
721
722QString MediaObject::errorString() const
723{
724 IMPLEMENTED;
725 return m_errorString;
726}
727
728Phonon::ErrorType MediaObject::errorType() const
729{
730 IMPLEMENTED;
731 return m_errorType;
732}
733
734bool 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
748QuickTimeVideoPlayer* MediaObject::videoPlayer() const
749{
750 return m_videoPlayer;
751}
752
753MediaSource MediaObject::source() const
754{
755 IMPLEMENTED;
756 return m_videoPlayer->mediaSource();
757}
758
759qint32 MediaObject::prefinishMark() const
760{
761 IMPLEMENTED;
762 return m_prefinishMark;
763}
764
765void MediaObject::setPrefinishMark(qint32 mark)
766{
767 IMPLEMENTED;
768 m_prefinishMark = mark;
769}
770
771qint32 MediaObject::transitionTime() const
772{
773 IMPLEMENTED;
774 return m_transitionTime;
775}
776
777void MediaObject::setTransitionTime(qint32 transitionTime)
778{
779 IMPLEMENTED;
780 m_transitionTime = transitionTime;
781}
782
783void MediaObject::setVolumeOnMovie(float volume)
784{
785 m_videoPlayer->setMasterVolume(volume);
786 m_nextVideoPlayer->setMasterVolume(volume);
787}
788
789bool MediaObject::setAudioDeviceOnMovie(int id)
790{
791 m_nextVideoPlayer->setAudioDevice(id);
792 return m_videoPlayer->setAudioDevice(id);
793}
794
795void 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
807void 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
816void MediaObject::updateAudioBuffers()
817{
818 // Schedule audio slices:
819 m_audioPlayer->scheduleAudioToGraph();
820 m_nextAudioPlayer->scheduleAudioToGraph();
821}
822
823bool MediaObject::isCrossFading()
824{
825 return m_mediaObjectAudioNode->isCrossFading();
826}
827
828void 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
848void 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
868void MediaObject::updateRapidly()
869{
870 updateCurrentTime();
871 updateCrossFade();
872 updateBufferStatus();
873}
874
875void MediaObject::setMute(bool mute)
876{
877 m_mediaObjectAudioNode->setMute(mute);
878 m_videoPlayer->setMute(mute);
879 m_nextVideoPlayer->setMute(mute);
880}
881
882void 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
910bool 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
938void 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
948bool MediaObject::hasInterface(Interface iface) const
949{
950 return iface == AddonInterface::TitleInterface;
951}
952
953QVariant 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
979QT_END_NAMESPACE
980
981#include "moc_mediaobject.cpp"
982
Note: See TracBrowser for help on using the repository browser.