1 /******************************************************************************
2     QtAV:  Multimedia framework based on Qt and FFmpeg
3     Copyright (C) 2012-2017 Wang Bin <wbsecg1@gmail.com>
4 
5 *   This file is part of QtAV (from 2013)
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 ******************************************************************************/
21 
22 #include "QmlAV/QmlAVPlayer.h"
23 #include <QtAV/AVPlayer.h>
24 #include <QtAV/AudioOutput.h>
25 #include <QtAV/VideoCapture.h>
26 
27 template<typename ID, typename T>
idsToNames(QVector<ID> ids)28 static QStringList idsToNames(QVector<ID> ids) {
29     QStringList decs;
30     if (!ids.isEmpty()) {
31         decs.reserve(ids.size());
32         foreach (ID id, ids) {
33             decs.append(QString::fromLatin1(T::name(id)));
34         }
35     }
36     return decs;
37 }
38 
VideoDecodersToNames(QVector<QtAV::VideoDecoderId> ids)39 static inline QStringList VideoDecodersToNames(QVector<QtAV::VideoDecoderId> ids) {
40     return idsToNames<QtAV::VideoDecoderId, VideoDecoder>(ids);
41 }
42 
QmlAVPlayer(QObject * parent)43 QmlAVPlayer::QmlAVPlayer(QObject *parent) :
44     QObject(parent)
45   , mUseWallclockAsTimestamps(false)
46   , m_complete(false)
47   , m_mute(false)
48   , mAutoPlay(false)
49   , mAutoLoad(false)
50   , mHasAudio(false)
51   , mHasVideo(false)
52   , m_fastSeek(false)
53   , m_loading(false)
54   , mLoopCount(1)
55   , mStart(0)
56   , mStop(PositionMax)
57   , mPlaybackRate(1.0)
58   , mVolume(1.0)
59   , mPlaybackState(StoppedState)
60   , mError(NoError)
61   , mpPlayer(0)
62   , mChannelLayout(ChannelLayoutAuto)
63   , m_timeout(30000)
64   , m_abort_timeout(true)
65   , m_audio_track(0)
66   , m_video_track(0)
67   , m_sub_track(0)
68   , m_ao(AudioOutput::backendsAvailable())
69 {
70     classBegin();
71 }
72 
classBegin()73 void QmlAVPlayer::classBegin()
74 {
75     if (mpPlayer)
76         return;
77     mpPlayer = new AVPlayer(this);
78     connect(mpPlayer, SIGNAL(internalSubtitleTracksChanged(QVariantList)), SIGNAL(internalSubtitleTracksChanged()));
79     connect(mpPlayer, SIGNAL(internalAudioTracksChanged(QVariantList)), SIGNAL(internalAudioTracksChanged()));
80     connect(mpPlayer, SIGNAL(internalVideoTracksChanged(QVariantList)), SIGNAL(internalVideoTracksChanged()));
81     connect(mpPlayer, SIGNAL(externalAudioTracksChanged(QVariantList)), SIGNAL(externalAudioTracksChanged()));
82     connect(mpPlayer, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged()));
83     connect(mpPlayer, SIGNAL(mediaStatusChanged(QtAV::MediaStatus)), SLOT(_q_statusChanged()));
84     connect(mpPlayer, SIGNAL(error(QtAV::AVError)), SLOT(_q_error(QtAV::AVError)));
85     connect(mpPlayer, SIGNAL(paused(bool)), SLOT(_q_paused(bool)));
86     connect(mpPlayer, SIGNAL(started()), SLOT(_q_started()));
87     connect(mpPlayer, SIGNAL(stopped()), SLOT(_q_stopped()));
88     connect(mpPlayer, SIGNAL(positionChanged(qint64)), SIGNAL(positionChanged()));
89     connect(mpPlayer, SIGNAL(seekableChanged()), SIGNAL(seekableChanged()));
90     connect(mpPlayer, SIGNAL(seekFinished(qint64)), this, SIGNAL(seekFinished()), Qt::DirectConnection);
91     connect(mpPlayer, SIGNAL(bufferProgressChanged(qreal)), SIGNAL(bufferProgressChanged()));
92     connect(this, SIGNAL(channelLayoutChanged()), SLOT(applyChannelLayout()));
93     // direct connection to ensure volume() in slots is correct
94     connect(mpPlayer->audio(), SIGNAL(volumeChanged(qreal)), SLOT(applyVolume()), Qt::DirectConnection);
95     connect(mpPlayer->audio(), SIGNAL(muteChanged(bool)), SLOT(applyVolume()), Qt::DirectConnection);
96 
97     mVideoCodecs << QStringLiteral("FFmpeg");
98 
99     m_metaData.reset(new MediaMetaData());
100 
101     Q_EMIT mediaObjectChanged();
102 }
103 
componentComplete()104 void QmlAVPlayer::componentComplete()
105 {
106     // TODO: set player parameters
107     if (mSource.isValid() && (mAutoLoad || mAutoPlay)) {
108         if (mAutoLoad)
109             mpPlayer->load();
110         if (mAutoPlay)
111             mpPlayer->play();
112     }
113 
114     m_complete = true;
115 }
116 
hasAudio() const117 bool QmlAVPlayer::hasAudio() const
118 {
119     if (!m_complete)
120         return false;
121     return mHasAudio;
122 }
123 
hasVideo() const124 bool QmlAVPlayer::hasVideo() const
125 {
126     if (!m_complete)
127         return false;
128     return mHasVideo;
129 }
130 
source() const131 QUrl QmlAVPlayer::source() const
132 {
133     return mSource;
134 }
135 
setSource(const QUrl & url)136 void QmlAVPlayer::setSource(const QUrl &url)
137 {
138     if (mSource == url)
139         return;
140     mSource = url;
141     if (url.isLocalFile() || url.scheme().isEmpty()
142             || url.scheme().startsWith("qrc")
143             || url.scheme().startsWith("avdevice")
144             // TODO: what about custom io?
145             )
146         mpPlayer->setFile(QUrl::fromPercentEncoding(url.toEncoded()));
147     else
148         mpPlayer->setFile(url.toEncoded());
149     Q_EMIT sourceChanged(); //TODO: Q_EMIT only when player loaded a new source
150 
151     if (mHasAudio) {
152         mHasAudio = false;
153         Q_EMIT hasAudioChanged();
154     }
155     if (mHasVideo) {
156         mHasVideo = false;
157         Q_EMIT hasVideoChanged();
158     }
159 
160     // TODO: in componentComplete()?
161     if (m_complete && (mAutoLoad || mAutoPlay)) {
162         mError = NoError;
163         mErrorString = tr("No error");
164         Q_EMIT error(mError, mErrorString);
165         Q_EMIT errorChanged();
166         stop(); // TODO: no stop for autoLoad?
167         if (mAutoLoad)
168             mpPlayer->load();
169         if (mAutoPlay) {
170             //mPlaybackState is actually changed in slots. But if set to a new source the state may not change before call play()
171             mPlaybackState = StoppedState;
172             play();
173         }
174     }
175 }
176 
isAutoLoad() const177 bool QmlAVPlayer::isAutoLoad() const
178 {
179     return mAutoLoad;
180 }
181 
setAutoLoad(bool autoLoad)182 void QmlAVPlayer::setAutoLoad(bool autoLoad)
183 {
184     if (mAutoLoad == autoLoad)
185         return;
186 
187     mAutoLoad = autoLoad;
188     Q_EMIT autoLoadChanged();
189 }
190 
autoPlay() const191 bool QmlAVPlayer::autoPlay() const
192 {
193     return mAutoPlay;
194 }
195 
setAutoPlay(bool autoplay)196 void QmlAVPlayer::setAutoPlay(bool autoplay)
197 {
198     if (mAutoPlay == autoplay)
199         return;
200 
201     mAutoPlay = autoplay;
202     Q_EMIT autoPlayChanged();
203 }
204 
metaData() const205 MediaMetaData* QmlAVPlayer::metaData() const
206 {
207     return m_metaData.data();
208 }
209 
mediaObject() const210 QObject* QmlAVPlayer::mediaObject() const
211 {
212     return mpPlayer;
213 }
214 
videoCapture() const215 VideoCapture *QmlAVPlayer::videoCapture() const
216 {
217     return mpPlayer->videoCapture();
218 }
219 
videoCodecs() const220 QStringList QmlAVPlayer::videoCodecs() const
221 {
222     return VideoDecodersToNames(VideoDecoder::registered());
223 }
224 
setVideoCodecPriority(const QStringList & p)225 void QmlAVPlayer::setVideoCodecPriority(const QStringList &p)
226 {
227     if (mVideoCodecs == p)
228         return;
229     mVideoCodecs = p;
230     Q_EMIT videoCodecPriorityChanged();
231     if (!mpPlayer) {
232         qWarning("player not ready");
233         return;
234     }
235     QVariantHash vcopt;
236     for (QVariantMap::const_iterator cit = vcodec_opt.cbegin(); cit != vcodec_opt.cend(); ++cit) {
237         vcopt[cit.key()] = cit.value();
238     }
239     if (!vcopt.isEmpty())
240         mpPlayer->setOptionsForVideoCodec(vcopt);
241     mpPlayer->setVideoDecoderPriority(p);
242 }
243 
videoCodecPriority() const244 QStringList QmlAVPlayer::videoCodecPriority() const
245 {
246     return mVideoCodecs;
247 }
248 
videoCodecOptions() const249 QVariantMap QmlAVPlayer::videoCodecOptions() const
250 {
251     return vcodec_opt;
252 }
253 
setVideoCodecOptions(const QVariantMap & value)254 void QmlAVPlayer::setVideoCodecOptions(const QVariantMap &value)
255 {
256     if (value == vcodec_opt)
257         return;
258     vcodec_opt = value;
259     Q_EMIT videoCodecOptionsChanged();
260     if (!mpPlayer) {
261         qWarning("player not ready");
262         return;
263     }
264     QVariantHash vcopt;
265     for (QVariantMap::const_iterator cit = vcodec_opt.cbegin(); cit != vcodec_opt.cend(); ++cit) {
266         vcopt[cit.key()] = cit.value();
267     }
268     if (!vcopt.isEmpty())
269         mpPlayer->setOptionsForVideoCodec(vcopt);
270     mpPlayer->setVideoDecoderPriority(videoCodecPriority());
271 }
272 
avFormatOptions() const273 QVariantMap QmlAVPlayer::avFormatOptions() const
274 {
275     return avfmt_opt;
276 }
277 
setAVFormatOptions(const QVariantMap & value)278 void QmlAVPlayer::setAVFormatOptions(const QVariantMap &value)
279 {
280     if (value == avfmt_opt)
281         return;
282     avfmt_opt = value;
283     Q_EMIT avFormatOptionsChanged();
284     if (!mpPlayer) {
285         qWarning("player not ready");
286         return;
287     }
288     QVariantHash avfopt;
289     for (QVariantMap::const_iterator cit = avfmt_opt.cbegin(); cit != avfmt_opt.cend(); ++cit) {
290         avfopt[cit.key()] = cit.value();
291     }
292     if (!avfopt.isEmpty())
293         mpPlayer->setOptionsForFormat(avfopt);
294 }
295 
useWallclockAsTimestamps() const296 bool QmlAVPlayer::useWallclockAsTimestamps() const
297 {
298     return mUseWallclockAsTimestamps;
299 }
300 
setWallclockAsTimestamps(bool use_wallclock_as_timestamps)301 void QmlAVPlayer::setWallclockAsTimestamps(bool use_wallclock_as_timestamps)
302 {
303     if (mUseWallclockAsTimestamps == use_wallclock_as_timestamps)
304         return;
305 
306     mUseWallclockAsTimestamps = use_wallclock_as_timestamps;
307 
308     QVariantHash opt = mpPlayer->optionsForFormat();
309 
310     if (use_wallclock_as_timestamps) {
311         opt[QStringLiteral("use_wallclock_as_timestamps")] = 1;
312         mpPlayer->setBufferValue(1);
313     } else {
314         opt.remove(QStringLiteral("use_wallclock_as_timestamps"));
315         mpPlayer->setBufferValue(-1);
316     }
317     mpPlayer->setOptionsForFormat(opt);
318     Q_EMIT useWallclockAsTimestampsChanged();
319 }
320 
toAudioFormatChannelLayout(QmlAVPlayer::ChannelLayout ch)321 static AudioFormat::ChannelLayout toAudioFormatChannelLayout(QmlAVPlayer::ChannelLayout ch)
322 {
323     struct {
324         QmlAVPlayer::ChannelLayout ch;
325         AudioFormat::ChannelLayout a;
326     } map[] = {
327     { QmlAVPlayer::Left, AudioFormat::ChannelLayout_Left },
328     { QmlAVPlayer::Right, AudioFormat::ChannelLayout_Right },
329     { QmlAVPlayer::Mono, AudioFormat::ChannelLayout_Mono },
330     { QmlAVPlayer::Stereo, AudioFormat::ChannelLayout_Stereo },
331     };
332     for (uint i = 0; i < sizeof(map)/sizeof(map[0]); ++i) {
333         if (map[i].ch == ch)
334             return map[i].a;
335     }
336     return AudioFormat::ChannelLayout_Unsupported;
337 }
338 
339 
setChannelLayout(ChannelLayout channel)340 void QmlAVPlayer::setChannelLayout(ChannelLayout channel)
341 {
342     if (mChannelLayout == channel)
343         return;
344     mChannelLayout = channel;
345     Q_EMIT channelLayoutChanged();
346 }
347 
channelLayout() const348 QmlAVPlayer::ChannelLayout QmlAVPlayer::channelLayout() const
349 {
350     return mChannelLayout;
351 }
352 
setTimeout(int value)353 void QmlAVPlayer::setTimeout(int value)
354 {
355     if (m_timeout == value)
356         return;
357     m_timeout = value;
358     Q_EMIT timeoutChanged();
359     if (mpPlayer)
360         mpPlayer->setInterruptTimeout(m_timeout);
361 }
362 
timeout() const363 int QmlAVPlayer::timeout() const
364 {
365     return m_timeout;
366 }
367 
setAbortOnTimeout(bool value)368 void QmlAVPlayer::setAbortOnTimeout(bool value)
369 {
370     if (m_abort_timeout == value)
371         return;
372     m_abort_timeout = value;
373     Q_EMIT abortOnTimeoutChanged();
374     if (mpPlayer)
375         mpPlayer->setInterruptOnTimeout(value);
376 }
377 
abortOnTimeout() const378 bool QmlAVPlayer::abortOnTimeout() const
379 {
380     return m_abort_timeout;
381 }
382 
audioTrack() const383 int QmlAVPlayer::audioTrack() const
384 {
385     return m_audio_track;
386 }
387 
setAudioTrack(int value)388 void QmlAVPlayer::setAudioTrack(int value)
389 {
390     if (m_audio_track == value)
391         return;
392     m_audio_track = value;
393     Q_EMIT audioTrackChanged();
394     if (mpPlayer)
395         mpPlayer->setAudioStream(value);
396 }
397 
videoTrack() const398 int QmlAVPlayer::videoTrack() const
399 {
400     return m_video_track;
401 }
402 
setVideoTrack(int value)403 void QmlAVPlayer::setVideoTrack(int value)
404 {
405     if (m_video_track == value)
406         return;
407     m_video_track = value;
408     Q_EMIT videoTrackChanged();
409     if (mpPlayer)
410         mpPlayer->setVideoStream(value);
411 }
412 
bufferSize() const413 int QmlAVPlayer::bufferSize() const
414 {
415     return mpPlayer->bufferValue();
416 }
417 
setBufferSize(int value)418 void QmlAVPlayer::setBufferSize(int value)
419 {
420     if (mpPlayer->bufferValue() == value)
421         return;
422     if (mpPlayer) {
423         mpPlayer->setBufferValue(value);
424         Q_EMIT bufferSizeChanged();
425     }
426 }
427 
bufferMode() const428 QmlAVPlayer::BufferMode QmlAVPlayer::bufferMode() const
429 {
430     return static_cast<BufferMode>(mpPlayer->bufferMode());
431 }
432 
setBufferMode(QmlAVPlayer::BufferMode value)433 void QmlAVPlayer::setBufferMode(QmlAVPlayer::BufferMode value)
434 {
435     QtAV::BufferMode mode = static_cast<QtAV::BufferMode>(value);
436     if(mpPlayer->bufferMode() == mode)
437         return;
438     if(mpPlayer) {
439         mpPlayer->setBufferMode(mode);
440         Q_EMIT bufferModeChanged();
441     }
442 }
443 
frameRate() const444 qreal QmlAVPlayer::frameRate() const
445 {
446     return mpPlayer->forcedFrameRate();
447 }
448 
setFrameRate(qreal value)449 void QmlAVPlayer::setFrameRate(qreal value)
450 {
451     if(mpPlayer) {
452         mpPlayer->setFrameRate(value);
453         Q_EMIT frameRateChanged();
454     }
455 }
456 
mediaEndAction() const457 QmlAVPlayer::MediaEndAction QmlAVPlayer::mediaEndAction() const
458 {
459     return  static_cast<MediaEndAction>(int(mpPlayer->mediaEndAction()));
460 }
461 
setMediaEndAction(QmlAVPlayer::MediaEndAction value)462 void QmlAVPlayer::setMediaEndAction(QmlAVPlayer::MediaEndAction value)
463 {
464     QtAV::MediaEndAction action = static_cast<QtAV::MediaEndAction>(value);
465     if (mpPlayer->mediaEndAction() == action)
466         return;
467     if (mpPlayer) {
468         mpPlayer->setMediaEndAction(action);
469         Q_EMIT mediaEndActionChanged();
470     }
471 }
472 
externalAudio() const473 QUrl QmlAVPlayer::externalAudio() const
474 {
475     return m_audio;
476 }
477 
setExternalAudio(const QUrl & url)478 void QmlAVPlayer::setExternalAudio(const QUrl &url)
479 {
480     if (m_audio == url)
481         return;
482     m_audio = url;
483     mpPlayer->setExternalAudio(QUrl::fromPercentEncoding(m_audio.toEncoded()));
484     Q_EMIT externalAudioChanged();
485 }
486 
externalAudioTracks() const487 QVariantList QmlAVPlayer::externalAudioTracks() const
488 {
489     return mpPlayer ? mpPlayer->externalAudioTracks() : QVariantList();
490 }
491 
internalAudioTracks() const492 QVariantList QmlAVPlayer::internalAudioTracks() const
493 {
494     return mpPlayer ? mpPlayer->internalAudioTracks() : QVariantList();
495 }
496 
internalVideoTracks() const497 QVariantList QmlAVPlayer::internalVideoTracks() const
498 {
499     return mpPlayer ? mpPlayer->internalVideoTracks() : QVariantList();
500 }
501 
internalSubtitleTrack() const502 int QmlAVPlayer::internalSubtitleTrack() const
503 {
504     return m_sub_track;
505 }
506 
setInternalSubtitleTrack(int value)507 void QmlAVPlayer::setInternalSubtitleTrack(int value)
508 {
509     if (m_sub_track == value)
510         return;
511     m_sub_track = value;
512     Q_EMIT internalSubtitleTrackChanged();
513     if (mpPlayer)
514         mpPlayer->setSubtitleStream(value);
515 }
516 
internalSubtitleTracks() const517 QVariantList QmlAVPlayer::internalSubtitleTracks() const
518 {
519     return mpPlayer ? mpPlayer->internalSubtitleTracks() : QVariantList();
520 }
521 
audioFilters()522 QQmlListProperty<QuickAudioFilter> QmlAVPlayer::audioFilters()
523 {
524     return QQmlListProperty<QuickAudioFilter>(this, NULL, af_append, af_count, af_at, af_clear);
525 }
526 
videoFilters()527 QQmlListProperty<QuickVideoFilter> QmlAVPlayer::videoFilters()
528 {
529     return QQmlListProperty<QuickVideoFilter>(this, NULL, vf_append, vf_count, vf_at, vf_clear);
530 }
531 
af_append(QQmlListProperty<QuickAudioFilter> * property,QuickAudioFilter * value)532 void QmlAVPlayer::af_append(QQmlListProperty<QuickAudioFilter> *property, QuickAudioFilter *value)
533 {
534     QmlAVPlayer* self = static_cast<QmlAVPlayer*>(property->object);
535     self->m_afilters.append(value);
536     if (self->mpPlayer)
537         self->mpPlayer->installFilter(value);
538 }
539 
af_count(QQmlListProperty<QuickAudioFilter> * property)540 int QmlAVPlayer::af_count(QQmlListProperty<QuickAudioFilter> *property)
541 {
542     QmlAVPlayer* self = static_cast<QmlAVPlayer*>(property->object);
543     return self->m_afilters.size();
544 }
545 
af_at(QQmlListProperty<QuickAudioFilter> * property,int index)546 QuickAudioFilter* QmlAVPlayer::af_at(QQmlListProperty<QuickAudioFilter> *property, int index)
547 {
548     QmlAVPlayer* self = static_cast<QmlAVPlayer*>(property->object);
549     return self->m_afilters.at(index);
550 }
551 
af_clear(QQmlListProperty<QuickAudioFilter> * property)552 void QmlAVPlayer::af_clear(QQmlListProperty<QuickAudioFilter> *property)
553 {
554     QmlAVPlayer* self = static_cast<QmlAVPlayer*>(property->object);
555     if (self->mpPlayer) {
556         foreach (QuickAudioFilter *f, self->m_afilters) {
557             self->mpPlayer->uninstallFilter(f);
558         }
559     }
560     self->m_afilters.clear();
561 }
562 
vf_append(QQmlListProperty<QuickVideoFilter> * property,QuickVideoFilter * value)563 void QmlAVPlayer::vf_append(QQmlListProperty<QuickVideoFilter> *property, QuickVideoFilter *value)
564 {
565     QmlAVPlayer* self = static_cast<QmlAVPlayer*>(property->object);
566     self->m_vfilters.append(value);
567     if (self->mpPlayer)
568         self->mpPlayer->installFilter(value);
569 }
570 
vf_count(QQmlListProperty<QuickVideoFilter> * property)571 int QmlAVPlayer::vf_count(QQmlListProperty<QuickVideoFilter> *property)
572 {
573     QmlAVPlayer* self = static_cast<QmlAVPlayer*>(property->object);
574     return self->m_vfilters.size();
575 }
576 
vf_at(QQmlListProperty<QuickVideoFilter> * property,int index)577 QuickVideoFilter* QmlAVPlayer::vf_at(QQmlListProperty<QuickVideoFilter> *property, int index)
578 {
579     QmlAVPlayer* self = static_cast<QmlAVPlayer*>(property->object);
580     return self->m_vfilters.at(index);
581 }
582 
vf_clear(QQmlListProperty<QuickVideoFilter> * property)583 void QmlAVPlayer::vf_clear(QQmlListProperty<QuickVideoFilter> *property)
584 {
585     QmlAVPlayer* self = static_cast<QmlAVPlayer*>(property->object);
586     if (self->mpPlayer) {
587         foreach (QuickVideoFilter *f, self->m_vfilters) {
588             self->mpPlayer->uninstallFilter(f);
589         }
590     }
591     self->m_vfilters.clear();
592 }
593 
audioBackends() const594 QStringList QmlAVPlayer::audioBackends() const
595 {
596     return m_ao;
597 }
598 
setAudioBackends(const QStringList & value)599 void QmlAVPlayer::setAudioBackends(const QStringList &value)
600 {
601     if (m_ao == value)
602         return;
603     m_ao = value;
604     Q_EMIT audioBackendsChanged();
605 }
606 
supportedAudioBackends() const607 QStringList QmlAVPlayer::supportedAudioBackends() const
608 {
609     return AudioOutput::backendsAvailable();
610 }
611 
loopCount() const612 int QmlAVPlayer::loopCount() const
613 {
614     return mLoopCount;
615 }
616 
setLoopCount(int c)617 void QmlAVPlayer::setLoopCount(int c)
618 {
619     if (c == 0)
620         c = 1;
621     else if (c < -1)
622         c = -1;
623     if (mLoopCount == c) {
624         return;
625     }
626     mLoopCount = c;
627     Q_EMIT loopCountChanged();
628 }
629 
volume() const630 qreal QmlAVPlayer::volume() const
631 {
632     return mVolume;
633 }
634 
635 // mVolume, m_mute are required by qml properties. player.audio()->setXXX is not enought because player maybe not created
setVolume(qreal value)636 void QmlAVPlayer::setVolume(qreal value)
637 {
638     if (mVolume < 0) {
639         qWarning("volume must > 0");
640         return;
641     }
642     if (qFuzzyCompare(mVolume + 1.0, value + 1.0))
643         return;
644     mVolume = value;
645     Q_EMIT volumeChanged();
646     applyVolume();
647 }
648 
isMuted() const649 bool QmlAVPlayer::isMuted() const
650 {
651     return m_mute;
652 }
653 
setMuted(bool m)654 void QmlAVPlayer::setMuted(bool m)
655 {
656     if (isMuted() == m)
657         return;
658     m_mute = m;
659     Q_EMIT mutedChanged();
660     applyVolume();
661 }
662 
duration() const663 int QmlAVPlayer::duration() const
664 {
665     return mpPlayer ? mpPlayer->duration() : 0;
666 }
667 
position() const668 int QmlAVPlayer::position() const
669 {
670     return mpPlayer ? mpPlayer->position() : 0;
671 }
672 
isSeekable() const673 bool QmlAVPlayer::isSeekable() const
674 {
675     return mpPlayer && mpPlayer->isSeekable();
676 }
677 
startPosition() const678 int QmlAVPlayer::startPosition() const
679 {
680     return mStart;
681 }
682 
setStartPosition(int value)683 void QmlAVPlayer::setStartPosition(int value)
684 {
685     if (mStart == value)
686         return;
687     mStart = value;
688     Q_EMIT startPositionChanged();
689     if (mpPlayer) {
690         mpPlayer->setStartPosition(mStart);
691     }
692 }
693 
stopPosition() const694 int QmlAVPlayer::stopPosition() const
695 {
696     return mStop;
697 }
698 
setStopPosition(int value)699 void QmlAVPlayer::setStopPosition(int value)
700 {
701     if (mStop == value)
702         return;
703     mStop = value;
704     Q_EMIT stopPositionChanged();
705     if (mpPlayer) {
706         if (value == PositionMax)
707             mpPlayer->setStopPosition();
708         else
709             mpPlayer->setStopPosition(value);
710     }
711 }
712 
isFastSeek() const713 bool QmlAVPlayer::isFastSeek() const
714 {
715     return m_fastSeek;
716 }
717 
setFastSeek(bool value)718 void QmlAVPlayer::setFastSeek(bool value)
719 {
720     if (m_fastSeek == value)
721         return;
722     m_fastSeek = value;
723     Q_EMIT fastSeekChanged();
724 }
725 
bufferProgress() const726 qreal QmlAVPlayer::bufferProgress() const
727 {
728     if (!mpPlayer)
729         return 0;
730     return mpPlayer->bufferProgress();
731 }
732 
status() const733 QmlAVPlayer::Status QmlAVPlayer::status() const
734 {
735     if (!mpPlayer)
736         return NoMedia;
737     return (Status)mpPlayer->mediaStatus();
738 }
739 
error() const740 QmlAVPlayer::Error QmlAVPlayer::error() const
741 {
742     return mError;
743 }
744 
errorString() const745 QString QmlAVPlayer::errorString() const
746 {
747     return mErrorString;
748 }
749 
playbackState() const750 QmlAVPlayer::PlaybackState QmlAVPlayer::playbackState() const
751 {
752     return mPlaybackState;
753 }
754 
setPlaybackState(PlaybackState playbackState)755 void QmlAVPlayer::setPlaybackState(PlaybackState playbackState)
756 {
757     if (mPlaybackState == playbackState) {
758         return;
759     }
760     if (!m_complete || !mpPlayer)
761         return;
762     switch (playbackState) {
763     case PlayingState:
764         if (mpPlayer->isPaused()) {
765             mpPlayer->pause(false);
766         } else {
767             mpPlayer->setInterruptTimeout(m_timeout);
768             mpPlayer->setInterruptOnTimeout(m_abort_timeout);
769             mpPlayer->setRepeat(mLoopCount - 1);
770             mpPlayer->setAudioStream(m_audio_track);
771             mpPlayer->setVideoStream(m_video_track); // will check in case of error
772             mpPlayer->setSubtitleStream(m_sub_track);
773             if (!vcodec_opt.isEmpty()) {
774                 QVariantHash vcopt;
775                 for (QVariantMap::const_iterator cit = vcodec_opt.cbegin(); cit != vcodec_opt.cend(); ++cit) {
776                     vcopt[cit.key()] = cit.value();
777                 }
778                 if (!vcopt.isEmpty())
779                     mpPlayer->setOptionsForVideoCodec(vcopt);
780             }
781             if (!avfmt_opt.isEmpty()) {
782                 QVariantHash avfopt;
783                 for (QVariantMap::const_iterator cit = avfmt_opt.cbegin(); cit != avfmt_opt.cend(); ++cit) {
784                     avfopt[cit.key()] = cit.value();
785                 }
786                 if (!avfopt.isEmpty())
787                     mpPlayer->setOptionsForFormat(avfopt);
788             }
789 
790             mpPlayer->setStartPosition(startPosition());
791             if (stopPosition() == PositionMax)
792                 mpPlayer->setStopPosition();
793             else
794                 mpPlayer->setStopPosition(stopPosition());
795 
796             m_loading = true;
797             // TODO: change backends is not thread safe now, so change when stopped
798             mpPlayer->audio()->setBackends(m_ao);
799             mpPlayer->play();
800         }
801         break;
802     case PausedState:
803         mpPlayer->pause(true);
804         mPlaybackState = PausedState;
805         break;
806     case StoppedState:
807         mpPlayer->stop();
808         m_loading = false;
809         mPlaybackState = StoppedState;
810         break;
811     default:
812         break;
813     }
814 }
815 
playbackRate() const816 qreal QmlAVPlayer::playbackRate() const
817 {
818     return mPlaybackRate;
819 }
820 
setPlaybackRate(qreal s)821 void QmlAVPlayer::setPlaybackRate(qreal s)
822 {
823     if (playbackRate() == s)
824         return;
825     mPlaybackRate = s;
826     if (mpPlayer)
827         mpPlayer->setSpeed(s);
828     Q_EMIT playbackRateChanged();
829 }
830 
player()831 AVPlayer* QmlAVPlayer::player()
832 {
833     return mpPlayer;
834 }
835 
play(const QUrl & url)836 void QmlAVPlayer::play(const QUrl &url)
837 {
838     if (mSource == url && (playbackState() != StoppedState || m_loading))
839         return;
840     setSource(url);
841     if (!autoPlay())
842         play();
843 }
844 
play()845 void QmlAVPlayer::play()
846 {
847     // if not autoPlay, maybe a different source was set and play() was not called
848     if (isAutoLoad() && (playbackState() == PlayingState || m_loading))
849         return;
850     setPlaybackState(PlayingState);
851 }
852 
pause()853 void QmlAVPlayer::pause()
854 {
855     setPlaybackState(PausedState);
856 }
857 
stop()858 void QmlAVPlayer::stop()
859 {
860     setPlaybackState(StoppedState);
861 }
862 
stepForward()863 void QmlAVPlayer::stepForward()
864 {
865     if (!mpPlayer)
866         return;
867     mpPlayer->stepForward();
868 }
869 
stepBackward()870 void QmlAVPlayer::stepBackward()
871 {
872     if (!mpPlayer)
873         return;
874     return mpPlayer->stepBackward();
875 }
876 
seek(int offset)877 void QmlAVPlayer::seek(int offset)
878 {
879     if (!mpPlayer)
880         return;
881     mpPlayer->setSeekType(isFastSeek() ? KeyFrameSeek : AccurateSeek);
882     mpPlayer->seek(qint64(offset));
883 }
884 
seekForward()885 void QmlAVPlayer::seekForward()
886 {
887     if (!mpPlayer)
888         return;
889     mpPlayer->setSeekType(isFastSeek() ? KeyFrameSeek : AccurateSeek);
890     mpPlayer->seekForward();
891 }
892 
seekBackward()893 void QmlAVPlayer::seekBackward()
894 {
895     if (!mpPlayer)
896         return;
897     mpPlayer->setSeekType(isFastSeek() ? KeyFrameSeek : AccurateSeek);
898     mpPlayer->seekBackward();
899 }
900 
_q_error(const AVError & e)901 void QmlAVPlayer::_q_error(const AVError &e)
902 {
903     mError = NoError;
904     mErrorString = e.string();
905     const AVError::ErrorCode ec = e.error();
906     if (ec <= AVError::NoError)
907         mError = NoError;
908     else if (ec <= AVError::NetworkError)
909         mError = NetworkError;
910     else if (ec <= AVError::ResourceError)
911         mError = ResourceError;
912     else if (ec <= AVError::FormatError)
913         mError = FormatError;
914     else if (ec <= AVError::AccessDenied)
915         mError = AccessDenied;
916     //else
917       //  err = ServiceMissing;
918     if (ec != AVError::NoError)
919         m_loading = false;
920     Q_EMIT error(mError, mErrorString);
921     Q_EMIT errorChanged();
922 }
923 
_q_statusChanged()924 void QmlAVPlayer::_q_statusChanged()
925 {
926     Q_EMIT statusChanged();
927 }
928 
_q_paused(bool p)929 void QmlAVPlayer::_q_paused(bool p)
930 {
931     if (p) {
932         mPlaybackState = PausedState;
933         Q_EMIT paused();
934     } else {
935         mPlaybackState = PlayingState;
936         Q_EMIT playing();
937     }
938     Q_EMIT playbackStateChanged();
939 }
940 
_q_started()941 void QmlAVPlayer::_q_started()
942 {
943     m_loading = false;
944     mPlaybackState = PlayingState;
945     applyChannelLayout();
946     // applyChannelLayout() first because it may reopen audio device
947     applyVolume(); //sender is AVPlayer
948 
949     mpPlayer->audio()->setMute(isMuted());
950     mpPlayer->setSpeed(playbackRate());
951     // TODO: in load()?
952     m_metaData->setValuesFromStatistics(mpPlayer->statistics());
953     if (!mHasAudio) {
954         mHasAudio = !mpPlayer->internalAudioTracks().isEmpty();
955         if (mHasAudio)
956             Q_EMIT hasAudioChanged();
957     }
958     if (!mHasVideo) {
959         mHasVideo = mpPlayer->videoStreamCount() > 0;
960         if (mHasVideo)
961             Q_EMIT hasVideoChanged();
962     }
963     Q_EMIT playing();
964     Q_EMIT playbackStateChanged();
965 }
966 
_q_stopped()967 void QmlAVPlayer::_q_stopped()
968 {
969     mPlaybackState = StoppedState;
970     Q_EMIT stopped();
971     Q_EMIT playbackStateChanged();
972 }
973 
applyVolume()974 void QmlAVPlayer::applyVolume()
975 {
976     AudioOutput *ao = mpPlayer->audio();
977     if (!ao || !ao->isAvailable())
978         return;
979     if (!sender() || qobject_cast<AudioOutput*>(sender()) != ao) {
980         ao->setVolume(volume()); // will omit if value is not changed
981         ao->setMute(isMuted());
982         return;
983     }
984     // from ao.reportVolume() reportMute()
985     setVolume(ao->volume());// will omit if value is not changed
986     setMuted(ao->isMute());
987 }
988 
applyChannelLayout()989 void QmlAVPlayer::applyChannelLayout()
990 {
991     AudioOutput *ao = mpPlayer->audio();
992     if (!ao || !ao->isAvailable())
993         return;
994     AudioFormat af = ao->audioFormat();
995     AudioFormat::ChannelLayout ch = toAudioFormatChannelLayout(channelLayout());
996     if (channelLayout() == ChannelLayoutAuto || ch == af.channelLayout())
997         return;
998     af.setChannelLayout(ch);
999     if (!ao->close()) {
1000         qWarning("close audio failed");
1001         return;
1002     }
1003     ao->setAudioFormat(af);
1004     if (!ao->open()) {
1005         qWarning("open audio failed");
1006         return;
1007     }
1008 }
1009