Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidmediaplayer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
9#include "qaudiooutput.h"
10
11#include <private/qplatformvideosink_p.h>
12#include <qloggingcategory.h>
13
15
16Q_STATIC_LOGGING_CATEGORY(lcMediaPlayer, "qt.multimedia.mediaplayer.android");
17
19{
20public:
22 : mControl(mp)
25 {
26 ++mControl->mActiveStateChangeNotifiers;
27 }
28
30 {
31 if (--mControl->mActiveStateChangeNotifiers)
32 return;
33
34 if (mPreviousMediaStatus != mControl->mediaStatus())
35 Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus());
36
37 if (mPreviousState != mControl->state())
38 Q_EMIT mControl->stateChanged(mControl->state());
39 }
40
41private:
42 QAndroidMediaPlayer *mControl;
43 QMediaPlayer::PlaybackState mPreviousState;
44 QMediaPlayer::MediaStatus mPreviousMediaStatus;
45};
46
47QAndroidMediaPlayer::QAndroidMediaPlayer(QMediaPlayer *parent)
48 : QPlatformMediaPlayer(parent),
49 mMediaPlayer(new AndroidMediaPlayer),
51{
52 // Set seekable to True by default. It changes if MEDIA_INFO_NOT_SEEKABLE is received
53 seekableChanged(true);
54 connect(mMediaPlayer, &AndroidMediaPlayer::bufferingChanged, this,
55 &QAndroidMediaPlayer::onBufferingChanged);
56 connect(mMediaPlayer, &AndroidMediaPlayer::info, this, &QAndroidMediaPlayer::onInfo);
57 connect(mMediaPlayer, &AndroidMediaPlayer::error, this, &QAndroidMediaPlayer::onError);
58 connect(mMediaPlayer, &AndroidMediaPlayer::stateChanged, this,
59 &QAndroidMediaPlayer::onStateChanged);
60 connect(mMediaPlayer, &AndroidMediaPlayer::videoSizeChanged, this,
61 &QAndroidMediaPlayer::onVideoSizeChanged);
62 connect(mMediaPlayer, &AndroidMediaPlayer::progressChanged, this,
63 &QAndroidMediaPlayer::positionChanged);
64 connect(mMediaPlayer, &AndroidMediaPlayer::durationChanged, this,
65 &QAndroidMediaPlayer::durationChanged);
66 connect(mMediaPlayer, &AndroidMediaPlayer::tracksInfoChanged, this,
67 &QAndroidMediaPlayer::updateTrackInfo);
68}
69
71{
72 if (m_videoSink)
73 disconnect(m_videoSink->platformVideoSink(), nullptr, this, nullptr);
74
75 mMediaPlayer->disconnect();
76 mMediaPlayer->release();
77 delete mMediaPlayer;
78}
79
81{
82 if (mediaStatus() == QMediaPlayer::NoMedia)
83 return 0;
84
85 if ((mState & (AndroidMediaPlayer::Prepared
90 return 0;
91 }
92
93 return mMediaPlayer->getDuration();
94}
95
97{
98 if (mediaStatus() == QMediaPlayer::EndOfMedia)
99 return duration();
100
101 if ((mState & (AndroidMediaPlayer::Prepared
105 return mMediaPlayer->getCurrentPosition();
106 }
107
108 return (mPendingPosition == -1) ? 0 : mPendingPosition;
109}
110
111void QAndroidMediaPlayer::setPosition(qint64 position)
112{
113 if (!isSeekable())
114 return;
115
116 const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
117
118 qint64 currentPosition = mMediaPlayer->getCurrentPosition();
119 if (seekPosition == currentPosition) {
120 // update position - will send a new frame of this position
121 // for consistency with other platforms
122 mMediaPlayer->seekTo(seekPosition);
123 return;
124 }
125 StateChangeNotifier notifier(this);
126
127 if (mediaStatus() == QMediaPlayer::EndOfMedia)
128 setMediaStatus(QMediaPlayer::LoadedMedia);
129
130 if ((mState & (AndroidMediaPlayer::Prepared
134 mPendingPosition = seekPosition;
135 } else {
136 mMediaPlayer->seekTo(seekPosition);
137
138 if (mPendingPosition != -1) {
139 mPendingPosition = -1;
140 }
141 }
142
143 Q_EMIT positionChanged(seekPosition);
144}
145
146void QAndroidMediaPlayer::setVolume(float volume)
147{
148 if ((mState & (AndroidMediaPlayer::Idle
155 mPendingVolume = volume;
156 return;
157 }
158
159 mMediaPlayer->setVolume(qRound(volume*100.));
160 mPendingVolume = -1;
161}
162
163void QAndroidMediaPlayer::setMuted(bool muted)
164{
165 if ((mState & (AndroidMediaPlayer::Idle
172 mPendingMute = muted;
173 return;
174 }
175
176 mMediaPlayer->setMuted(muted);
177 mPendingMute = -1;
178}
179
181{
182 return QAndroidMetaData::extractMetadata(mMediaContent);
183}
184
186{
187 return mBufferFilled ? 1. : mBufferPercent;
188}
189
191{
192 return mAudioAvailable;
193}
194
196{
197 return mVideoAvailable;
198}
199
201{
202 return mAvailablePlaybackRange;
203}
204
205void QAndroidMediaPlayer::updateAvailablePlaybackRanges()
206{
207 if (mBuffering) {
208 const qint64 pos = position();
209 const qint64 end = (duration() / 100) * mBufferPercent;
210 mAvailablePlaybackRange.addInterval(pos, end);
211 } else if (isSeekable()) {
212 mAvailablePlaybackRange = QMediaTimeRange(0, duration());
213 } else {
214 mAvailablePlaybackRange = QMediaTimeRange();
215 }
216
217// #### Q_EMIT availablePlaybackRangesChanged(mAvailablePlaybackRange);
218}
219
221{
222 return mCurrentPlaybackRate;
223}
224
226{
227 if (mState != AndroidMediaPlayer::Started) {
228 // If video isn't playing, changing speed rate may start it automatically
229 // It need to be postponed
230 if (mCurrentPlaybackRate != rate) {
231 mCurrentPlaybackRate = rate;
232 mHasPendingPlaybackRate = true;
233 Q_EMIT playbackRateChanged(rate);
234 }
235 return;
236 }
237
238 if (mMediaPlayer->setPlaybackRate(rate)) {
239 mCurrentPlaybackRate = rate;
240 Q_EMIT playbackRateChanged(rate);
241 }
242}
243
245{
246 return mMediaContent;
247}
248
250{
251 return mMediaStream;
252}
253
254void QAndroidMediaPlayer::setMedia(const QUrl &mediaContent,
255 QIODevice *stream)
256{
257 StateChangeNotifier notifier(this);
258
259 mReloadingMedia = (mMediaContent == mediaContent) && !mPendingSetMedia;
260
261 if (!mReloadingMedia) {
262 mMediaContent = mediaContent;
263 mMediaStream = stream;
264 }
265
266 if (mediaContent.isEmpty()) {
267 setMediaStatus(QMediaPlayer::NoMedia);
268 } else {
269 if (mVideoOutput && !mVideoOutput->isReady()) {
270 // if a video output is set but the video texture is not ready, delay loading the media
271 // since it can cause problems on some hardware
272 mPendingSetMedia = true;
273 return;
274 }
275
276 if (mVideoSize.isValid() && mVideoOutput)
277 mVideoOutput->setVideoSize(mVideoSize);
278
279 if (mVideoOutput &&
280 (mMediaPlayer->display() == 0 || mVideoOutput->shouldTextureBeUpdated()))
281 mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
282 mMediaPlayer->setDataSource(QNetworkRequest(mediaContent));
283 mMediaPlayer->prepareAsync();
284
285 if (!mReloadingMedia)
286 setMediaStatus(QMediaPlayer::LoadingMedia);
287 }
288
289 resetBufferingProgress();
290
291 mReloadingMedia = false;
292}
293
294void QAndroidMediaPlayer::setVideoSink(QVideoSink *sink)
295{
296 if (m_videoSink == sink)
297 return;
298
299 if (m_videoSink)
300 disconnect(m_videoSink->platformVideoSink(), nullptr, this, nullptr);
301
302 m_videoSink = sink;
303
304 if (!m_videoSink) {
305 return;
306 }
307
308 if (mVideoOutput) {
309 delete mVideoOutput;
310 mVideoOutput = nullptr;
311 mMediaPlayer->setDisplay(nullptr);
312 }
313
314 mVideoOutput = new QAndroidTextureVideoOutput(sink, this);
315 connect(mVideoOutput, &QAndroidTextureVideoOutput::readyChanged, this,
316 &QAndroidMediaPlayer::onVideoOutputReady);
317 connect(mMediaPlayer, &AndroidMediaPlayer::timedTextChanged, mVideoOutput,
318 &QAndroidTextureVideoOutput::setSubtitle);
319
320 if (mVideoOutput->isReady())
321 mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
322
323 connect(m_videoSink->platformVideoSink(), &QPlatformVideoSink::rhiChanged, this, [&]()
324 { mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); });
325}
326
327void QAndroidMediaPlayer::setAudioOutput(QPlatformAudioOutput *output)
328{
329 if (m_audioOutput == output)
330 return;
331 if (m_audioOutput)
332 m_audioOutput->q->disconnect(this);
333 m_audioOutput = static_cast<QAndroidAudioOutput *>(output);
334 if (m_audioOutput) {
335 connect(m_audioOutput->q, &QAudioOutput::deviceChanged, this, &QAndroidMediaPlayer::updateAudioDevice);
336 connect(m_audioOutput->q, &QAudioOutput::volumeChanged, this, &QAndroidMediaPlayer::setVolume);
337 connect(m_audioOutput->q, &QAudioOutput::mutedChanged, this, &QAndroidMediaPlayer::setMuted);
339 }
340}
341
343{
344 if (m_audioOutput)
345 mMediaPlayer->setAudioOutput(m_audioOutput->device.id());
346}
347
349{
350 StateChangeNotifier notifier(this);
351
352 resetCurrentLoop();
353
354 // We need to prepare the mediaplayer again.
355 if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isEmpty()) {
356 setMedia(mMediaContent, mMediaStream);
357 }
358
359 if (!mMediaContent.isEmpty())
360 stateChanged(QMediaPlayer::PlayingState);
361
362 if ((mState & (AndroidMediaPlayer::Prepared
366 mPendingState = QMediaPlayer::PlayingState;
367 return;
368 }
369
370 if (mVideoOutput)
371 mVideoOutput->start();
372
374
375 if (mHasPendingPlaybackRate) {
376 mHasPendingPlaybackRate = false;
377 if (mMediaPlayer->setPlaybackRate(mCurrentPlaybackRate))
378 return;
379 mCurrentPlaybackRate = mMediaPlayer->playbackRate();
380 Q_EMIT playbackRateChanged(mCurrentPlaybackRate);
381 }
382
383 mMediaPlayer->play();
384}
385
387{
388 // cannot pause without media
389 if (mediaStatus() == QMediaPlayer::NoMedia)
390 return;
391
392 StateChangeNotifier notifier(this);
393
394 stateChanged(QMediaPlayer::PausedState);
395
396 if ((mState & (AndroidMediaPlayer::Started
401 mPendingState = QMediaPlayer::PausedState;
402 return;
403 }
404
405 const qint64 currentPosition = mMediaPlayer->getCurrentPosition();
406 setPosition(currentPosition);
407
408 mMediaPlayer->pause();
409}
410
412{
413 StateChangeNotifier notifier(this);
414
415 stateChanged(QMediaPlayer::StoppedState);
416
417 if ((mState & (AndroidMediaPlayer::Prepared
422 if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized | AndroidMediaPlayer::Error)) == 0)
423 mPendingState = QMediaPlayer::StoppedState;
424 return;
425 }
426
427 if (mCurrentPlaybackRate != 1.)
428 // Playback rate need to by reapplied
429 mHasPendingPlaybackRate = true;
430
431 if (mVideoOutput)
432 mVideoOutput->stop();
433
434 mMediaPlayer->stop();
435}
436
437void QAndroidMediaPlayer::onInfo(qint32 what, qint32 extra)
438{
439 StateChangeNotifier notifier(this);
440
441 Q_UNUSED(extra);
442 switch (what) {
444 break;
446 // IGNORE
447 break;
449 break;
451 mPendingState = state();
452 stateChanged(QMediaPlayer::PausedState);
453 setMediaStatus(QMediaPlayer::StalledMedia);
454 break;
456 if (state() != QMediaPlayer::StoppedState)
457 flushPendingStates();
458 break;
460 break;
462 seekableChanged(false);
463 break;
465 Q_EMIT metaDataChanged();
466 break;
467 }
468}
469
470void QAndroidMediaPlayer::onError(qint32 what, qint32 extra)
471{
472 StateChangeNotifier notifier(this);
473
474 QString errorString;
475 QMediaPlayer::Error error = QMediaPlayer::ResourceError;
476
477 switch (what) {
479 errorString = QLatin1String("Error:");
480 break;
482 errorString = QLatin1String("Error: Server died");
483 error = QMediaPlayer::ResourceError;
484 break;
486 errorString = QLatin1String("Error: Invalid state");
487 error = QMediaPlayer::ResourceError;
488 break;
489 }
490
491 switch (extra) {
492 case AndroidMediaPlayer::MEDIA_ERROR_IO: // Network OR file error
493 errorString += QLatin1String(" (I/O operation failed)");
494 error = QMediaPlayer::NetworkError;
495 setMediaStatus(QMediaPlayer::InvalidMedia);
496 break;
498 errorString += QLatin1String(" (Malformed bitstream)");
499 error = QMediaPlayer::FormatError;
500 setMediaStatus(QMediaPlayer::InvalidMedia);
501 break;
503 errorString += QLatin1String(" (Unsupported media)");
504 error = QMediaPlayer::FormatError;
505 setMediaStatus(QMediaPlayer::InvalidMedia);
506 break;
508 errorString += QLatin1String(" (Timed out)");
509 break;
511 errorString += QLatin1String(" (Unable to start progressive playback')");
512 error = QMediaPlayer::FormatError;
513 setMediaStatus(QMediaPlayer::InvalidMedia);
514 break;
515 case AndroidMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN:
516 errorString += mMediaContent.scheme() == QLatin1String("rtsp")
517 ? QLatin1String(" (Unknown error/Insufficient resources or RTSP may not be supported)")
518 : QLatin1String(" (Unknown error/Insufficient resources)");
519 error = QMediaPlayer::ResourceError;
520 break;
521 }
522
523 Q_EMIT QPlatformMediaPlayer::error(error, errorString);
524}
525
526void QAndroidMediaPlayer::onBufferingChanged(qint32 percent)
527{
528 StateChangeNotifier notifier(this);
529
530 mBuffering = percent != 100;
531 mBufferPercent = percent;
532
533 updateAvailablePlaybackRanges();
534
535 if (state() != QMediaPlayer::StoppedState)
536 setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
537
538 updateBufferStatus();
539}
540
541void QAndroidMediaPlayer::onVideoSizeChanged(qint32 width, qint32 height)
542{
543 QSize newSize(width, height);
544
545 if (width == 0 || height == 0 || newSize == mVideoSize)
546 return;
547
548 setVideoAvailable(true);
549 mVideoSize = newSize;
550
551 if (mVideoOutput)
552 mVideoOutput->setVideoSize(mVideoSize);
553}
554
555void QAndroidMediaPlayer::onStateChanged(qint32 state)
556{
557 // If reloading, don't report state changes unless the new state is Prepared or Error.
558 if ((mState & AndroidMediaPlayer::Stopped)
560 return;
561 }
562
563 StateChangeNotifier notifier(this);
564
565 mState = state;
566 switch (mState) {
568 break;
570 break;
571 case AndroidMediaPlayer::Preparing:
572 if (!mReloadingMedia)
573 setMediaStatus(QMediaPlayer::LoadingMedia);
574 break;
575 case AndroidMediaPlayer::Prepared:
576 setMediaStatus(QMediaPlayer::LoadedMedia);
577 if (mBuffering) {
578 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
579 : QMediaPlayer::BufferingMedia);
580 } else {
581 onBufferingChanged(100);
582 }
583 setPosition(0);
584 Q_EMIT metaDataChanged();
585 setAudioAvailable(true);
586 flushPendingStates();
587 break;
588 case AndroidMediaPlayer::Started:
589 stateChanged(QMediaPlayer::PlayingState);
590 if (mBuffering) {
591 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
592 : QMediaPlayer::BufferingMedia);
593 } else {
594 setMediaStatus(QMediaPlayer::BufferedMedia);
595 }
596 Q_EMIT positionChanged(position());
597 break;
598 case AndroidMediaPlayer::Paused:
599 stateChanged(QMediaPlayer::PausedState);
600 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
601 setPosition(0);
602 setMediaStatus(QMediaPlayer::BufferedMedia);
603 } else {
604 Q_EMIT positionChanged(position());
605 }
606 break;
607 case AndroidMediaPlayer::Error:
608 stateChanged(QMediaPlayer::StoppedState);
609 setMediaStatus(QMediaPlayer::InvalidMedia);
610 mMediaPlayer->release();
611 Q_EMIT positionChanged(0);
612 break;
613 case AndroidMediaPlayer::Stopped:
614 stateChanged(QMediaPlayer::StoppedState);
615 setMediaStatus(QMediaPlayer::LoadedMedia);
616 Q_EMIT positionChanged(0);
617 break;
619 if (doLoop()) {
620 setPosition(0);
621 mMediaPlayer->play();
622 break;
623 }
624 stateChanged(QMediaPlayer::StoppedState);
625 setMediaStatus(QMediaPlayer::EndOfMedia);
626 break;
628 // reset some properties (unless we reload the same media)
629 if (!mReloadingMedia) {
630 resetBufferingProgress();
631 mPendingPosition = -1;
632 mPendingSetMedia = false;
633 mPendingState = -1;
634
635 Q_EMIT durationChanged(0);
636 Q_EMIT positionChanged(0);
637
638 setAudioAvailable(false);
639 setVideoAvailable(false);
640 seekableChanged(true);
641 }
642 break;
643 default:
644 break;
645 }
646
648 mMediaPlayer->setDisplay(0);
649 if (mVideoOutput) {
650 mVideoOutput->stop();
651 }
652 }
653}
654
655int QAndroidMediaPlayer::trackCount(TrackType trackType)
656{
657 if (!mTracksMetadata.contains(trackType))
658 return -1;
659
660 auto tracks = mTracksMetadata.value(trackType);
661 return tracks.count();
662}
663
664QMediaMetaData QAndroidMediaPlayer::trackMetaData(TrackType trackType, int streamNumber)
665{
666 if (!mTracksMetadata.contains(trackType))
667 return QMediaMetaData();
668
669 auto tracks = mTracksMetadata.value(trackType);
670 if (tracks.count() < streamNumber)
671 return QMediaMetaData();
672
673 QAndroidMetaData trackInfo = tracks.at(streamNumber);
674 return static_cast<QMediaMetaData>(trackInfo);
675}
676
678{
679 switch (type) {
680 case AndroidMediaPlayer::TrackType::Video:
681 return QPlatformMediaPlayer::TrackType::VideoStream;
682 case AndroidMediaPlayer::TrackType::Audio:
683 return QPlatformMediaPlayer::TrackType::AudioStream;
684 case AndroidMediaPlayer::TrackType::TimedText:
685 return QPlatformMediaPlayer::TrackType::SubtitleStream;
686 case AndroidMediaPlayer::TrackType::Subtitle:
687 return QPlatformMediaPlayer::TrackType::SubtitleStream;
688 case AndroidMediaPlayer::TrackType::Unknown:
689 case AndroidMediaPlayer::TrackType::Metadata:
690 return QPlatformMediaPlayer::TrackType::NTrackTypes;
691 }
692
693 return QPlatformMediaPlayer::TrackType::NTrackTypes;
694}
695
696int QAndroidMediaPlayer::convertTrackNumber(int androidTrackNumber)
697{
698 int trackNumber = androidTrackNumber;
699
700 int videoTrackCount = trackCount(QPlatformMediaPlayer::TrackType::VideoStream);
701 if (trackNumber <= videoTrackCount)
702 return trackNumber;
703
704 trackNumber = trackNumber - videoTrackCount;
705
706 int audioTrackCount = trackCount(QPlatformMediaPlayer::TrackType::AudioStream);
707 if (trackNumber <= audioTrackCount)
708 return trackNumber;
709
710 trackNumber = trackNumber - audioTrackCount;
711
712 auto subtitleTracks = mTracksMetadata.value(QPlatformMediaPlayer::TrackType::SubtitleStream);
713 int timedTextCount = 0;
714 int subtitleTextCount = 0;
715 for (const auto &track : subtitleTracks) {
716 if (track.androidTrackType() == 3) // 3 == TimedText
717 timedTextCount++;
718
719 if (track.androidTrackType() == 4) // 4 == Subtitle
720 subtitleTextCount++;
721 }
722
723 if (trackNumber <= timedTextCount)
724 return trackNumber;
725
726 trackNumber = trackNumber - timedTextCount;
727
728 if (trackNumber <= subtitleTextCount)
729 return trackNumber;
730
731 return -1;
732}
733
734int QAndroidMediaPlayer::activeTrack(TrackType trackType)
735{
736 int androidTrackNumber = -1;
737
738 switch (trackType) {
739 case QPlatformMediaPlayer::TrackType::VideoStream: {
740 if (!mIsVideoTrackEnabled)
741 return -1;
742 androidTrackNumber = mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Video);
743 }
744 case QPlatformMediaPlayer::TrackType::AudioStream: {
745 if (!mIsAudioTrackEnabled)
746 return -1;
747
748 androidTrackNumber = mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Audio);
749 }
750 case QPlatformMediaPlayer::TrackType::SubtitleStream: {
751 int timedTextSelectedTrack =
753
754 if (timedTextSelectedTrack > -1) {
755 androidTrackNumber = timedTextSelectedTrack;
756 break;
757 }
758
759 int subtitleSelectedTrack =
761 if (subtitleSelectedTrack > -1) {
762 androidTrackNumber = subtitleSelectedTrack;
763 break;
764 }
765
766 return -1;
767 }
768 case QPlatformMediaPlayer::TrackType::NTrackTypes:
769 return -1;
770 }
771
772 return convertTrackNumber(androidTrackNumber);
773}
774
775void QAndroidMediaPlayer::disableTrack(TrackType trackType)
776{
777 const auto track = activeTrack(trackType);
778
779 switch (trackType) {
780 case VideoStream: {
781 if (track > -1) {
782 mMediaPlayer->setDisplay(nullptr);
783 mIsVideoTrackEnabled = false;
784 }
785 break;
786 }
787 case AudioStream: {
788 if (track > -1) {
789 mMediaPlayer->setMuted(true);
790 mMediaPlayer->blockAudio();
791 mIsAudioTrackEnabled = false;
792 }
793 break;
794 }
795 case SubtitleStream: {
796 // subtitles and timedtext tracks can be selected at the same time so deselect both
797 int subtitleSelectedTrack =
799 if (subtitleSelectedTrack > -1)
800 mMediaPlayer->deselectTrack(subtitleSelectedTrack);
801
802 int timedTextSelectedTrack =
804 if (timedTextSelectedTrack > -1)
805 mMediaPlayer->deselectTrack(timedTextSelectedTrack);
806
807 break;
808 }
809 case NTrackTypes:
810 break;
811 }
812}
813
814void QAndroidMediaPlayer::setActiveTrack(TrackType trackType, int streamNumber)
815{
816
817 if (!mTracksMetadata.contains(trackType)) {
818 qCWarning(lcMediaPlayer)
819 << "Trying to set a active track which type has no available tracks.";
820 return;
821 }
822
823 const auto &tracks = mTracksMetadata.value(trackType);
824 if (streamNumber > tracks.count()) {
825 qCWarning(lcMediaPlayer) << "Trying to set a active track that does not exist.";
826 return;
827 }
828
829 // in case of < 0 deselect tracktype
830 if (streamNumber < 0) {
831 disableTrack(trackType);
832 return;
833 }
834
835 const auto currentTrack = activeTrack(trackType);
836 if (streamNumber == currentTrack) {
837 return;
838 }
839
840 if (trackType == TrackType::VideoStream && !mIsVideoTrackEnabled) {
841 // enable video stream
842 mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
843 mIsVideoTrackEnabled = true;
844 }
845
846 if (trackType == TrackType::AudioStream && !mIsAudioTrackEnabled) {
847 // enable audio stream
848 mMediaPlayer->unblockAudio();
849 mMediaPlayer->setMuted(false);
850 mIsAudioTrackEnabled = true;
851 }
852
853 if (trackType == TrackType::SubtitleStream) {
854 // subtitles and timedtext tracks can be selected at the same time so deselect both before
855 // selecting a new one
856 disableTrack(TrackType::SubtitleStream);
857 }
858
859 const auto &trackInfo = tracks.at(streamNumber);
860 const auto &trackNumber = trackInfo.androidTrackNumber();
861 mMediaPlayer->selectTrack(trackNumber);
862
863 emit activeTracksChanged();
864}
865
866void QAndroidMediaPlayer::positionChanged(qint64 position)
867{
868 QPlatformMediaPlayer::positionChanged(position);
869}
870
871void QAndroidMediaPlayer::durationChanged(qint64 duration)
872{
873 QPlatformMediaPlayer::durationChanged(duration);
874}
875
876void QAndroidMediaPlayer::onVideoOutputReady(bool ready)
877{
878 if ((mMediaPlayer->display() == 0) && mVideoOutput && ready)
879 mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
880
881 flushPendingStates();
882}
883
884void QAndroidMediaPlayer::setMediaStatus(QMediaPlayer::MediaStatus status)
885{
886 mediaStatusChanged(status);
887
888 if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) {
889 Q_EMIT durationChanged(0);
890 Q_EMIT metaDataChanged();
891 setAudioAvailable(false);
892 setVideoAvailable(false);
893 }
894
895 if (status == QMediaPlayer::EndOfMedia)
896 Q_EMIT positionChanged(position());
897
898 updateBufferStatus();
899}
900
901void QAndroidMediaPlayer::setAudioAvailable(bool available)
902{
903 if (mAudioAvailable == available)
904 return;
905
906 mAudioAvailable = available;
907 Q_EMIT audioAvailableChanged(mAudioAvailable);
908}
909
910void QAndroidMediaPlayer::setVideoAvailable(bool available)
911{
912 if (mVideoAvailable == available)
913 return;
914
915 if (!available)
916 mVideoSize = QSize();
917
918 mVideoAvailable = available;
919 Q_EMIT videoAvailableChanged(mVideoAvailable);
920}
921
922void QAndroidMediaPlayer::resetBufferingProgress()
923{
924 mBuffering = false;
925 mBufferPercent = 0;
926 mAvailablePlaybackRange = QMediaTimeRange();
927}
928
929void QAndroidMediaPlayer::flushPendingStates()
930{
931 if (mPendingSetMedia) {
932 setMedia(mMediaContent, 0);
933 mPendingSetMedia = false;
934 return;
935 }
936
937 const int newState = mPendingState;
938 mPendingState = -1;
939
940 if (mPendingPosition != -1)
941 setPosition(mPendingPosition);
942 if (mPendingVolume >= 0)
943 setVolume(mPendingVolume);
944 if (mPendingMute != -1)
945 setMuted((mPendingMute == 1));
946
947 switch (newState) {
948 case QMediaPlayer::PlayingState:
949 play();
950 break;
951 case QMediaPlayer::PausedState:
952 pause();
953 break;
954 case QMediaPlayer::StoppedState:
955 stop();
956 break;
957 default:
958 break;
959 }
960}
961
962void QAndroidMediaPlayer::updateBufferStatus()
963{
964 const auto &status = mediaStatus();
965 bool bufferFilled = (status == QMediaPlayer::BufferedMedia || status == QMediaPlayer::BufferingMedia);
966
967 if (mBufferFilled != bufferFilled)
968 mBufferFilled = bufferFilled;
969
970 emit bufferProgressChanged(bufferProgress());
971}
972
973void QAndroidMediaPlayer::updateTrackInfo()
974{
975 const auto &androidTracksInfo = mMediaPlayer->tracksInfo();
976
977 // prepare mTracksMetadata
978 mTracksMetadata[TrackType::VideoStream] = QList<QAndroidMetaData>();
979 mTracksMetadata[TrackType::AudioStream] = QList<QAndroidMetaData>();
980 mTracksMetadata[TrackType::SubtitleStream] = QList<QAndroidMetaData>();
981 mTracksMetadata[TrackType::NTrackTypes] = QList<QAndroidMetaData>();
982
983 for (const auto &androidTrackInfo : androidTracksInfo) {
984
985 const auto &mediaPlayerType = convertTrackType(androidTrackInfo.trackType);
986 auto &tracks = mTracksMetadata[mediaPlayerType];
987
988 const QAndroidMetaData metadata(mediaPlayerType, androidTrackInfo.trackType,
989 androidTrackInfo.trackNumber, androidTrackInfo.mimeType,
990 androidTrackInfo.language);
991 tracks.append(metadata);
992 }
993
994 emit tracksChanged();
995}
996
997QT_END_NAMESPACE
998
999#include "moc_qandroidmediaplayer_p.cpp"
void deselectTrack(int trackNumber)
void selectTrack(int trackNumber)
void setDisplay(AndroidSurfaceTexture *surfaceTexture)
int activeTrack(TrackType trackType)
void setVolume(int volume)
bool isAudioAvailable() const override
void setPlaybackRate(qreal rate) override
const QIODevice * mediaStream() const override
qreal playbackRate() const override
qint64 duration() const override
QMediaMetaData metaData() const override
int activeTrack(TrackType trackType) override
bool isVideoAvailable() const override
int trackCount(TrackType trackType) override
qint64 position() const override
float bufferProgress() const override
void setMedia(const QUrl &mediaContent, QIODevice *stream) override
QUrl media() const override
void setActiveTrack(TrackType trackType, int streamNumber) override
void setPosition(qint64 position) override
QMediaMetaData trackMetaData(TrackType trackType, int streamNumber) override
QMediaTimeRange availablePlaybackRanges() const override
AndroidSurfaceTexture * surfaceTexture() override
\inmodule QtCore
Definition qsize.h:26
StateChangeNotifier(QAndroidMediaPlayer *mp)
QPlatformMediaPlayer::TrackType convertTrackType(AndroidMediaPlayer::TrackType type)
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)