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 setInvalidMediaWithError(QMediaPlayer::NetworkError, errorString);
495 return;
497 errorString += QLatin1String(" (Malformed bitstream)");
498 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
499 return;
501 errorString += QLatin1String(" (Unsupported media)");
502 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
503 return;
505 errorString += QLatin1String(" (Timed out)");
506 break;
508 errorString += QLatin1String(" (Unable to start progressive playback')");
509 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
510 return;
511 case AndroidMediaPlayer::MEDIA_ERROR_BAD_THINGS_ARE_GOING_TO_HAPPEN:
512 errorString += mMediaContent.scheme() == QLatin1String("rtsp")
513 ? QLatin1String(" (Unknown error/Insufficient resources or RTSP may not be supported)")
514 : QLatin1String(" (Unknown error/Insufficient resources)");
515 error = QMediaPlayer::ResourceError;
516 break;
517 }
518
519 Q_EMIT QPlatformMediaPlayer::error(error, errorString);
520}
521
522void QAndroidMediaPlayer::onBufferingChanged(qint32 percent)
523{
524 StateChangeNotifier notifier(this);
525
526 mBuffering = percent != 100;
527 mBufferPercent = percent;
528
529 updateAvailablePlaybackRanges();
530
531 if (state() != QMediaPlayer::StoppedState)
532 setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
533
534 updateBufferStatus();
535}
536
537void QAndroidMediaPlayer::onVideoSizeChanged(qint32 width, qint32 height)
538{
539 QSize newSize(width, height);
540
541 if (width == 0 || height == 0 || newSize == mVideoSize)
542 return;
543
544 setVideoAvailable(true);
545 mVideoSize = newSize;
546
547 if (mVideoOutput)
548 mVideoOutput->setVideoSize(mVideoSize);
549}
550
551void QAndroidMediaPlayer::onStateChanged(qint32 state)
552{
553 // If reloading, don't report state changes unless the new state is Prepared or Error.
554 if ((mState & AndroidMediaPlayer::Stopped)
556 return;
557 }
558
559 StateChangeNotifier notifier(this);
560
561 mState = state;
562 switch (mState) {
564 break;
566 break;
567 case AndroidMediaPlayer::Preparing:
568 if (!mReloadingMedia)
569 setMediaStatus(QMediaPlayer::LoadingMedia);
570 break;
571 case AndroidMediaPlayer::Prepared:
572 setMediaStatus(QMediaPlayer::LoadedMedia);
573 if (mBuffering) {
574 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
575 : QMediaPlayer::BufferingMedia);
576 } else {
577 onBufferingChanged(100);
578 }
579 setPosition(0);
580 Q_EMIT metaDataChanged();
581 setAudioAvailable(true);
582 flushPendingStates();
583 break;
585 stateChanged(QMediaPlayer::PlayingState);
586 if (mBuffering) {
587 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
588 : QMediaPlayer::BufferingMedia);
589 } else {
590 setMediaStatus(QMediaPlayer::BufferedMedia);
591 }
592 Q_EMIT positionChanged(position());
593 break;
595 stateChanged(QMediaPlayer::PausedState);
596 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
597 setPosition(0);
598 setMediaStatus(QMediaPlayer::BufferedMedia);
599 } else {
600 Q_EMIT positionChanged(position());
601 }
602 break;
604 stateChanged(QMediaPlayer::StoppedState);
605 setMediaStatus(QMediaPlayer::InvalidMedia);
606 mMediaPlayer->release();
607 Q_EMIT positionChanged(0);
608 break;
610 stateChanged(QMediaPlayer::StoppedState);
611 setMediaStatus(QMediaPlayer::LoadedMedia);
612 Q_EMIT positionChanged(0);
613 break;
615 if (doLoop()) {
616 setPosition(0);
617 mMediaPlayer->play();
618 break;
619 }
620 stateChanged(QMediaPlayer::StoppedState);
621 setMediaStatus(QMediaPlayer::EndOfMedia);
622 break;
624 // reset some properties (unless we reload the same media)
625 if (!mReloadingMedia) {
626 resetBufferingProgress();
627 mPendingPosition = -1;
628 mPendingSetMedia = false;
629 mPendingState = -1;
630
631 Q_EMIT durationChanged(0);
632 Q_EMIT positionChanged(0);
633
634 setAudioAvailable(false);
635 setVideoAvailable(false);
636 seekableChanged(true);
637 }
638 break;
639 default:
640 break;
641 }
642
644 mMediaPlayer->setDisplay(0);
645 if (mVideoOutput) {
646 mVideoOutput->stop();
647 }
648 }
649}
650
651int QAndroidMediaPlayer::trackCount(TrackType trackType)
652{
653 if (!mTracksMetadata.contains(trackType))
654 return -1;
655
656 auto tracks = mTracksMetadata.value(trackType);
657 return tracks.count();
658}
659
660QMediaMetaData QAndroidMediaPlayer::trackMetaData(TrackType trackType, int streamNumber)
661{
662 if (!mTracksMetadata.contains(trackType))
663 return QMediaMetaData();
664
665 auto tracks = mTracksMetadata.value(trackType);
666 if (tracks.count() < streamNumber)
667 return QMediaMetaData();
668
669 QAndroidMetaData trackInfo = tracks.at(streamNumber);
670 return static_cast<QMediaMetaData>(trackInfo);
671}
672
674{
675 switch (type) {
676 case AndroidMediaPlayer::TrackType::Video:
677 return QPlatformMediaPlayer::TrackType::VideoStream;
678 case AndroidMediaPlayer::TrackType::Audio:
679 return QPlatformMediaPlayer::TrackType::AudioStream;
680 case AndroidMediaPlayer::TrackType::TimedText:
681 return QPlatformMediaPlayer::TrackType::SubtitleStream;
682 case AndroidMediaPlayer::TrackType::Subtitle:
683 return QPlatformMediaPlayer::TrackType::SubtitleStream;
684 case AndroidMediaPlayer::TrackType::Unknown:
685 case AndroidMediaPlayer::TrackType::Metadata:
686 return QPlatformMediaPlayer::TrackType::NTrackTypes;
687 }
688
689 return QPlatformMediaPlayer::TrackType::NTrackTypes;
690}
691
692int QAndroidMediaPlayer::convertTrackNumber(int androidTrackNumber)
693{
694 int trackNumber = androidTrackNumber;
695
696 int videoTrackCount = trackCount(QPlatformMediaPlayer::TrackType::VideoStream);
697 if (trackNumber <= videoTrackCount)
698 return trackNumber;
699
700 trackNumber = trackNumber - videoTrackCount;
701
702 int audioTrackCount = trackCount(QPlatformMediaPlayer::TrackType::AudioStream);
703 if (trackNumber <= audioTrackCount)
704 return trackNumber;
705
706 trackNumber = trackNumber - audioTrackCount;
707
708 auto subtitleTracks = mTracksMetadata.value(QPlatformMediaPlayer::TrackType::SubtitleStream);
709 int timedTextCount = 0;
710 int subtitleTextCount = 0;
711 for (const auto &track : subtitleTracks) {
712 if (track.androidTrackType() == 3) // 3 == TimedText
713 timedTextCount++;
714
715 if (track.androidTrackType() == 4) // 4 == Subtitle
716 subtitleTextCount++;
717 }
718
719 if (trackNumber <= timedTextCount)
720 return trackNumber;
721
722 trackNumber = trackNumber - timedTextCount;
723
724 if (trackNumber <= subtitleTextCount)
725 return trackNumber;
726
727 return -1;
728}
729
730int QAndroidMediaPlayer::activeTrack(TrackType trackType)
731{
732 int androidTrackNumber = -1;
733
734 switch (trackType) {
735 case QPlatformMediaPlayer::TrackType::VideoStream: {
736 if (!mIsVideoTrackEnabled)
737 return -1;
738 androidTrackNumber = mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Video);
739 }
740 case QPlatformMediaPlayer::TrackType::AudioStream: {
741 if (!mIsAudioTrackEnabled)
742 return -1;
743
744 androidTrackNumber = mMediaPlayer->activeTrack(AndroidMediaPlayer::TrackType::Audio);
745 }
746 case QPlatformMediaPlayer::TrackType::SubtitleStream: {
747 int timedTextSelectedTrack =
749
750 if (timedTextSelectedTrack > -1) {
751 androidTrackNumber = timedTextSelectedTrack;
752 break;
753 }
754
755 int subtitleSelectedTrack =
757 if (subtitleSelectedTrack > -1) {
758 androidTrackNumber = subtitleSelectedTrack;
759 break;
760 }
761
762 return -1;
763 }
764 case QPlatformMediaPlayer::TrackType::NTrackTypes:
765 return -1;
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 VideoStream: {
777 if (track > -1) {
778 mMediaPlayer->setDisplay(nullptr);
779 mIsVideoTrackEnabled = false;
780 }
781 break;
782 }
783 case AudioStream: {
784 if (track > -1) {
785 mMediaPlayer->setMuted(true);
786 mMediaPlayer->blockAudio();
787 mIsAudioTrackEnabled = false;
788 }
789 break;
790 }
791 case 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 case NTrackTypes:
806 break;
807 }
808}
809
810void QAndroidMediaPlayer::setActiveTrack(TrackType trackType, int streamNumber)
811{
812
813 if (!mTracksMetadata.contains(trackType)) {
814 qCWarning(lcMediaPlayer)
815 << "Trying to set a active track which type has no available tracks.";
816 return;
817 }
818
819 const auto &tracks = mTracksMetadata.value(trackType);
820 if (streamNumber > tracks.count()) {
821 qCWarning(lcMediaPlayer) << "Trying to set a active track that does not exist.";
822 return;
823 }
824
825 // in case of < 0 deselect tracktype
826 if (streamNumber < 0) {
827 disableTrack(trackType);
828 return;
829 }
830
831 const auto currentTrack = activeTrack(trackType);
832 if (streamNumber == currentTrack) {
833 return;
834 }
835
836 if (trackType == TrackType::VideoStream && !mIsVideoTrackEnabled) {
837 // enable video stream
838 mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
839 mIsVideoTrackEnabled = true;
840 }
841
842 if (trackType == TrackType::AudioStream && !mIsAudioTrackEnabled) {
843 // enable audio stream
844 mMediaPlayer->unblockAudio();
845 mMediaPlayer->setMuted(false);
846 mIsAudioTrackEnabled = true;
847 }
848
849 if (trackType == TrackType::SubtitleStream) {
850 // subtitles and timedtext tracks can be selected at the same time so deselect both before
851 // selecting a new one
852 disableTrack(TrackType::SubtitleStream);
853 }
854
855 const auto &trackInfo = tracks.at(streamNumber);
856 const auto &trackNumber = trackInfo.androidTrackNumber();
857 mMediaPlayer->selectTrack(trackNumber);
858
859 emit activeTracksChanged();
860}
861
862void QAndroidMediaPlayer::positionChanged(qint64 position)
863{
864 QPlatformMediaPlayer::positionChanged(position);
865}
866
867void QAndroidMediaPlayer::durationChanged(qint64 duration)
868{
869 QPlatformMediaPlayer::durationChanged(duration);
870}
871
872void QAndroidMediaPlayer::onVideoOutputReady(bool ready)
873{
874 if ((mMediaPlayer->display() == 0) && mVideoOutput && ready)
875 mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture());
876
877 flushPendingStates();
878}
879
880void QAndroidMediaPlayer::setMediaStatus(QMediaPlayer::MediaStatus status)
881{
882 mediaStatusChanged(status);
883
884 if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) {
885 Q_EMIT durationChanged(0);
886 Q_EMIT metaDataChanged();
887 setAudioAvailable(false);
888 setVideoAvailable(false);
889 }
890
891 if (status == QMediaPlayer::EndOfMedia)
892 Q_EMIT positionChanged(position());
893
894 updateBufferStatus();
895}
896
897void QAndroidMediaPlayer::setAudioAvailable(bool available)
898{
899 if (mAudioAvailable == available)
900 return;
901
902 mAudioAvailable = available;
903 Q_EMIT audioAvailableChanged(mAudioAvailable);
904}
905
906void QAndroidMediaPlayer::setVideoAvailable(bool available)
907{
908 if (mVideoAvailable == available)
909 return;
910
911 if (!available)
912 mVideoSize = QSize();
913
914 mVideoAvailable = available;
915 Q_EMIT videoAvailableChanged(mVideoAvailable);
916}
917
918void QAndroidMediaPlayer::resetBufferingProgress()
919{
920 mBuffering = false;
921 mBufferPercent = 0;
922 mAvailablePlaybackRange = QMediaTimeRange();
923}
924
925void QAndroidMediaPlayer::flushPendingStates()
926{
927 if (mPendingSetMedia) {
928 setMedia(mMediaContent, 0);
929 mPendingSetMedia = false;
930 return;
931 }
932
933 const int newState = mPendingState;
934 mPendingState = -1;
935
936 if (mPendingPosition != -1)
937 setPosition(mPendingPosition);
938 if (mPendingVolume >= 0)
939 setVolume(mPendingVolume);
940 if (mPendingMute != -1)
941 setMuted((mPendingMute == 1));
942
943 switch (newState) {
944 case QMediaPlayer::PlayingState:
945 play();
946 break;
947 case QMediaPlayer::PausedState:
948 pause();
949 break;
950 case QMediaPlayer::StoppedState:
951 stop();
952 break;
953 default:
954 break;
955 }
956}
957
958void QAndroidMediaPlayer::updateBufferStatus()
959{
960 const auto &status = mediaStatus();
961 bool bufferFilled = (status == QMediaPlayer::BufferedMedia || status == QMediaPlayer::BufferingMedia);
962
963 if (mBufferFilled != bufferFilled)
964 mBufferFilled = bufferFilled;
965
966 emit bufferProgressChanged(bufferProgress());
967}
968
969void QAndroidMediaPlayer::updateTrackInfo()
970{
971 const auto &androidTracksInfo = mMediaPlayer->tracksInfo();
972
973 // prepare mTracksMetadata
974 mTracksMetadata[TrackType::VideoStream] = QList<QAndroidMetaData>();
975 mTracksMetadata[TrackType::AudioStream] = QList<QAndroidMetaData>();
976 mTracksMetadata[TrackType::SubtitleStream] = QList<QAndroidMetaData>();
977 mTracksMetadata[TrackType::NTrackTypes] = QList<QAndroidMetaData>();
978
979 for (const auto &androidTrackInfo : androidTracksInfo) {
980
981 const auto &mediaPlayerType = convertTrackType(androidTrackInfo.trackType);
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.
QPlatformMediaPlayer::TrackType convertTrackType(AndroidMediaPlayer::TrackType type)
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)