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