11#include <private/qplatformvideosink_p.h>
12#include <qloggingcategory.h>
26 ++mControl->mActiveStateChangeNotifiers;
31 if (--mControl->mActiveStateChangeNotifiers)
34 if (mPreviousMediaStatus != mControl->mediaStatus())
35 Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus());
37 if (mPreviousState != mControl->state())
38 Q_EMIT mControl->stateChanged(mControl->state());
43 QMediaPlayer::PlaybackState mPreviousState;
44 QMediaPlayer::MediaStatus mPreviousMediaStatus;
48 : QPlatformMediaPlayer(parent),
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);
73 disconnect(m_videoSink->platformVideoSink(),
nullptr,
this,
nullptr);
75 mMediaPlayer->disconnect();
82 if (mediaStatus() == QMediaPlayer::NoMedia)
98 if (mediaStatus() == QMediaPlayer::EndOfMedia)
108 return (mPendingPosition == -1) ? 0 : mPendingPosition;
116 const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
119 if (seekPosition == currentPosition) {
122 mMediaPlayer->seekTo(seekPosition);
127 if (mediaStatus() == QMediaPlayer::EndOfMedia)
128 setMediaStatus(QMediaPlayer::LoadedMedia);
134 mPendingPosition = seekPosition;
136 mMediaPlayer->seekTo(seekPosition);
138 if (mPendingPosition != -1) {
139 mPendingPosition = -1;
143 Q_EMIT positionChanged(seekPosition);
155 mPendingVolume = volume;
172 mPendingMute = muted;
182 return QAndroidMetaData::extractMetadata(mMediaContent);
187 return mBufferFilled ? 1. : mBufferPercent;
192 return mAudioAvailable;
197 return mVideoAvailable;
202 return mAvailablePlaybackRange;
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());
214 mAvailablePlaybackRange = QMediaTimeRange();
222 return mCurrentPlaybackRate;
230 if (mCurrentPlaybackRate != rate) {
231 mCurrentPlaybackRate = rate;
232 mHasPendingPlaybackRate =
true;
233 Q_EMIT playbackRateChanged(rate);
238 if (mMediaPlayer->setPlaybackRate(rate)) {
239 mCurrentPlaybackRate = rate;
240 Q_EMIT playbackRateChanged(rate);
246 return mMediaContent;
259 mReloadingMedia = (mMediaContent == mediaContent) && !mPendingSetMedia;
261 if (!mReloadingMedia) {
262 mMediaContent = mediaContent;
263 mMediaStream = stream;
266 if (mediaContent.isEmpty()) {
267 setMediaStatus(QMediaPlayer::NoMedia);
269 if (mVideoOutput && !mVideoOutput->isReady()) {
272 mPendingSetMedia =
true;
276 if (mVideoSize.isValid() && mVideoOutput)
277 mVideoOutput->setVideoSize(mVideoSize);
282 mMediaPlayer->setDataSource(QNetworkRequest(mediaContent));
285 if (!mReloadingMedia)
286 setMediaStatus(QMediaPlayer::LoadingMedia);
289 resetBufferingProgress();
291 mReloadingMedia =
false;
296 if (m_videoSink == sink)
300 disconnect(m_videoSink->platformVideoSink(),
nullptr,
this,
nullptr);
310 mVideoOutput =
nullptr;
314 mVideoOutput =
new QAndroidTextureVideoOutput(sink,
this);
315 connect(mVideoOutput, &QAndroidTextureVideoOutput::readyChanged,
this,
316 &QAndroidMediaPlayer::onVideoOutputReady);
317 connect(mMediaPlayer, &AndroidMediaPlayer::timedTextChanged, mVideoOutput,
318 &QAndroidTextureVideoOutput::setSubtitle);
320 if (mVideoOutput->isReady())
323 connect(m_videoSink->platformVideoSink(), &QPlatformVideoSink::rhiChanged,
this, [&]()
324 { mMediaPlayer->setDisplay(mVideoOutput->surfaceTexture()); });
329 if (m_audioOutput == output)
332 m_audioOutput->q->disconnect(
this);
333 m_audioOutput =
static_cast<QAndroidAudioOutput *>(output);
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);
345 mMediaPlayer->setAudioOutput(m_audioOutput->device.id());
355 if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isEmpty()) {
356 setMedia(mMediaContent, mMediaStream);
359 if (!mMediaContent.isEmpty())
360 stateChanged(QMediaPlayer::PlayingState);
366 mPendingState = QMediaPlayer::PlayingState;
371 mVideoOutput->start();
375 if (mHasPendingPlaybackRate) {
376 mHasPendingPlaybackRate =
false;
377 if (mMediaPlayer->setPlaybackRate(mCurrentPlaybackRate))
379 mCurrentPlaybackRate = mMediaPlayer->playbackRate();
380 Q_EMIT playbackRateChanged(mCurrentPlaybackRate);
389 if (mediaStatus() == QMediaPlayer::NoMedia)
394 stateChanged(QMediaPlayer::PausedState);
401 mPendingState = QMediaPlayer::PausedState;
406 setPosition(currentPosition);
415 stateChanged(QMediaPlayer::StoppedState);
422 if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized | AndroidMediaPlayer::Error)) == 0)
423 mPendingState = QMediaPlayer::StoppedState;
427 if (mCurrentPlaybackRate != 1.)
429 mHasPendingPlaybackRate =
true;
451 mPendingState = state();
452 stateChanged(QMediaPlayer::PausedState);
453 setMediaStatus(QMediaPlayer::StalledMedia);
456 if (state() != QMediaPlayer::StoppedState)
457 flushPendingStates();
462 seekableChanged(
false);
465 Q_EMIT metaDataChanged();
475 QMediaPlayer::Error error = QMediaPlayer::ResourceError;
479 errorString = QLatin1String(
"Error:");
482 errorString = QLatin1String(
"Error: Server died");
483 error = QMediaPlayer::ResourceError;
486 errorString = QLatin1String(
"Error: Invalid state");
487 error = QMediaPlayer::ResourceError;
493 errorString += QLatin1String(
" (I/O operation failed)");
494 error = QMediaPlayer::NetworkError;
495 setMediaStatus(QMediaPlayer::InvalidMedia);
498 errorString += QLatin1String(
" (Malformed bitstream)");
499 error = QMediaPlayer::FormatError;
500 setMediaStatus(QMediaPlayer::InvalidMedia);
503 errorString += QLatin1String(
" (Unsupported media)");
504 error = QMediaPlayer::FormatError;
505 setMediaStatus(QMediaPlayer::InvalidMedia);
508 errorString += QLatin1String(
" (Timed out)");
511 errorString += QLatin1String(
" (Unable to start progressive playback')");
512 error = QMediaPlayer::FormatError;
513 setMediaStatus(QMediaPlayer::InvalidMedia);
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;
523 Q_EMIT QPlatformMediaPlayer::error(error, errorString);
530 mBuffering = percent != 100;
531 mBufferPercent = percent;
533 updateAvailablePlaybackRanges();
535 if (state() != QMediaPlayer::StoppedState)
536 setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
538 updateBufferStatus();
543 QSize newSize(width, height);
545 if (width == 0 || height == 0 || newSize == mVideoSize)
548 setVideoAvailable(
true);
549 mVideoSize = newSize;
552 mVideoOutput->setVideoSize(mVideoSize);
571 case AndroidMediaPlayer::Preparing:
572 if (!mReloadingMedia)
573 setMediaStatus(QMediaPlayer::LoadingMedia);
575 case AndroidMediaPlayer::Prepared:
576 setMediaStatus(QMediaPlayer::LoadedMedia);
578 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
579 : QMediaPlayer::BufferingMedia);
581 onBufferingChanged(100);
584 Q_EMIT metaDataChanged();
585 setAudioAvailable(
true);
586 flushPendingStates();
588 case AndroidMediaPlayer::Started:
589 stateChanged(QMediaPlayer::PlayingState);
591 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
592 : QMediaPlayer::BufferingMedia);
594 setMediaStatus(QMediaPlayer::BufferedMedia);
596 Q_EMIT positionChanged(position());
598 case AndroidMediaPlayer::Paused:
599 stateChanged(QMediaPlayer::PausedState);
600 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
602 setMediaStatus(QMediaPlayer::BufferedMedia);
604 Q_EMIT positionChanged(position());
607 case AndroidMediaPlayer::Error:
608 stateChanged(QMediaPlayer::StoppedState);
609 setMediaStatus(QMediaPlayer::InvalidMedia);
611 Q_EMIT positionChanged(0);
613 case AndroidMediaPlayer::Stopped:
614 stateChanged(QMediaPlayer::StoppedState);
615 setMediaStatus(QMediaPlayer::LoadedMedia);
616 Q_EMIT positionChanged(0);
624 stateChanged(QMediaPlayer::StoppedState);
625 setMediaStatus(QMediaPlayer::EndOfMedia);
629 if (!mReloadingMedia) {
630 resetBufferingProgress();
631 mPendingPosition = -1;
632 mPendingSetMedia =
false;
635 Q_EMIT durationChanged(0);
636 Q_EMIT positionChanged(0);
638 setAudioAvailable(
false);
639 setVideoAvailable(
false);
640 seekableChanged(
true);
657 if (!mTracksMetadata.contains(trackType))
660 auto tracks = mTracksMetadata.value(trackType);
661 return tracks.count();
666 if (!mTracksMetadata.contains(trackType))
667 return QMediaMetaData();
669 auto tracks = mTracksMetadata.value(trackType);
670 if (tracks.count() < streamNumber)
671 return QMediaMetaData();
674 return static_cast<QMediaMetaData>(trackInfo);
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;
693 return QPlatformMediaPlayer::TrackType::NTrackTypes;
698 int trackNumber = androidTrackNumber;
700 int videoTrackCount = trackCount(QPlatformMediaPlayer::TrackType::VideoStream);
701 if (trackNumber <= videoTrackCount)
704 trackNumber = trackNumber - videoTrackCount;
706 int audioTrackCount = trackCount(QPlatformMediaPlayer::TrackType::AudioStream);
707 if (trackNumber <= audioTrackCount)
710 trackNumber = trackNumber - audioTrackCount;
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)
719 if (track.androidTrackType() == 4)
723 if (trackNumber <= timedTextCount)
726 trackNumber = trackNumber - timedTextCount;
728 if (trackNumber <= subtitleTextCount)
736 int androidTrackNumber = -1;
739 case QPlatformMediaPlayer::TrackType::VideoStream: {
740 if (!mIsVideoTrackEnabled)
744 case QPlatformMediaPlayer::TrackType::AudioStream: {
745 if (!mIsAudioTrackEnabled)
750 case QPlatformMediaPlayer::TrackType::SubtitleStream: {
751 int timedTextSelectedTrack =
754 if (timedTextSelectedTrack > -1) {
755 androidTrackNumber = timedTextSelectedTrack;
759 int subtitleSelectedTrack =
761 if (subtitleSelectedTrack > -1) {
762 androidTrackNumber = subtitleSelectedTrack;
768 case QPlatformMediaPlayer::TrackType::NTrackTypes:
772 return convertTrackNumber(androidTrackNumber);
777 const auto track = activeTrack(trackType);
783 mIsVideoTrackEnabled =
false;
791 mIsAudioTrackEnabled =
false;
795 case SubtitleStream: {
797 int subtitleSelectedTrack =
799 if (subtitleSelectedTrack > -1)
802 int timedTextSelectedTrack =
804 if (timedTextSelectedTrack > -1)
817 if (!mTracksMetadata.contains(trackType)) {
819 <<
"Trying to set a active track which type has no available tracks.";
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.";
830 if (streamNumber < 0) {
831 disableTrack(trackType);
835 const auto currentTrack = activeTrack(trackType);
836 if (streamNumber == currentTrack) {
840 if (trackType == TrackType::VideoStream && !mIsVideoTrackEnabled) {
843 mIsVideoTrackEnabled =
true;
846 if (trackType == TrackType::AudioStream && !mIsAudioTrackEnabled) {
850 mIsAudioTrackEnabled =
true;
853 if (trackType == TrackType::SubtitleStream) {
856 disableTrack(TrackType::SubtitleStream);
859 const auto &trackInfo = tracks.at(streamNumber);
860 const auto &trackNumber = trackInfo.androidTrackNumber();
863 emit activeTracksChanged();
868 QPlatformMediaPlayer::positionChanged(position);
873 QPlatformMediaPlayer::durationChanged(duration);
878 if ((mMediaPlayer->display() == 0) && mVideoOutput && ready)
881 flushPendingStates();
886 mediaStatusChanged(status);
888 if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) {
889 Q_EMIT durationChanged(0);
890 Q_EMIT metaDataChanged();
891 setAudioAvailable(
false);
892 setVideoAvailable(
false);
895 if (status == QMediaPlayer::EndOfMedia)
896 Q_EMIT positionChanged(position());
898 updateBufferStatus();
903 if (mAudioAvailable == available)
906 mAudioAvailable = available;
907 Q_EMIT audioAvailableChanged(mAudioAvailable);
912 if (mVideoAvailable == available)
916 mVideoSize = QSize();
918 mVideoAvailable = available;
919 Q_EMIT videoAvailableChanged(mVideoAvailable);
926 mAvailablePlaybackRange = QMediaTimeRange();
931 if (mPendingSetMedia) {
932 setMedia(mMediaContent, 0);
933 mPendingSetMedia =
false;
937 const int newState = mPendingState;
940 if (mPendingPosition != -1)
941 setPosition(mPendingPosition);
942 if (mPendingVolume >= 0)
943 setVolume(mPendingVolume);
944 if (mPendingMute != -1)
945 setMuted((mPendingMute == 1));
948 case QMediaPlayer::PlayingState:
951 case QMediaPlayer::PausedState:
954 case QMediaPlayer::StoppedState:
964 const auto &status = mediaStatus();
965 bool bufferFilled = (status == QMediaPlayer::BufferedMedia || status == QMediaPlayer::BufferingMedia);
967 if (mBufferFilled != bufferFilled)
968 mBufferFilled = bufferFilled;
975 const auto &androidTracksInfo = mMediaPlayer->tracksInfo();
978 mTracksMetadata[TrackType::VideoStream] = QList<QAndroidMetaData>();
979 mTracksMetadata[TrackType::AudioStream] = QList<QAndroidMetaData>();
980 mTracksMetadata[TrackType::SubtitleStream] = QList<QAndroidMetaData>();
981 mTracksMetadata[TrackType::NTrackTypes] = QList<QAndroidMetaData>();
983 for (
const auto &androidTrackInfo : androidTracksInfo) {
985 const auto &mediaPlayerType = convertTrackType(androidTrackInfo.trackType);
986 auto &tracks = mTracksMetadata[mediaPlayerType];
988 const QAndroidMetaData metadata(mediaPlayerType, androidTrackInfo.trackType,
989 androidTrackInfo.trackNumber, androidTrackInfo.mimeType,
990 androidTrackInfo.language);
991 tracks.append(metadata);
994 emit tracksChanged();
999#include "moc_qandroidmediaplayer_p.cpp"
AndroidSurfaceTexture * surfaceTexture() override
bool shouldTextureBeUpdated() const
StateChangeNotifier(QAndroidMediaPlayer *mp)
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)