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