11#include <private/qplatformvideosink_p.h>
12#include <qloggingcategory.h>
16using TrackType = QPlatformMediaPlayer::TrackType;
28 ++mControl->mActiveStateChangeNotifiers;
33 if (--mControl->mActiveStateChangeNotifiers)
36 if (mPreviousMediaStatus != mControl->mediaStatus())
37 Q_EMIT mControl->mediaStatusChanged(mControl->mediaStatus());
39 if (mPreviousState != mControl->state())
40 Q_EMIT mControl->stateChanged(mControl->state());
45 QMediaPlayer::PlaybackState mPreviousState;
46 QMediaPlayer::MediaStatus mPreviousMediaStatus;
50 : QPlatformMediaPlayer(parent),
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);
75 disconnect(m_videoSink->platformVideoSink(),
nullptr,
this,
nullptr);
77 mMediaPlayer->disconnect();
84 if (mediaStatus() == QMediaPlayer::NoMedia)
100 if (mediaStatus() == QMediaPlayer::EndOfMedia)
110 return (mPendingPosition == -1) ? 0 : mPendingPosition;
118 const int seekPosition = (position > INT_MAX) ? INT_MAX : position;
121 if (seekPosition == currentPosition) {
124 mMediaPlayer->seekTo(seekPosition);
129 if (mediaStatus() == QMediaPlayer::EndOfMedia)
130 setMediaStatus(QMediaPlayer::LoadedMedia);
136 mPendingPosition = seekPosition;
138 mMediaPlayer->seekTo(seekPosition);
140 if (mPendingPosition != -1) {
141 mPendingPosition = -1;
145 Q_EMIT positionChanged(seekPosition);
157 mPendingVolume = volume;
174 mPendingMute = muted;
184 return QAndroidMetaData::extractMetadata(mMediaContent);
189 return mBufferFilled ? 1. : mBufferPercent;
194 return mAudioAvailable;
199 return mVideoAvailable;
204 return mAvailablePlaybackRange;
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());
216 mAvailablePlaybackRange = QMediaTimeRange();
224 return mCurrentPlaybackRate;
232 if (mCurrentPlaybackRate != rate) {
233 mCurrentPlaybackRate = rate;
234 mHasPendingPlaybackRate =
true;
235 Q_EMIT playbackRateChanged(rate);
240 if (mMediaPlayer->setPlaybackRate(rate)) {
241 mCurrentPlaybackRate = rate;
242 Q_EMIT playbackRateChanged(rate);
248 return mMediaContent;
261 mReloadingMedia = (mMediaContent == mediaContent) && !mPendingSetMedia;
263 if (!mReloadingMedia) {
264 mMediaContent = mediaContent;
265 mMediaStream = stream;
268 if (mediaContent.isEmpty()) {
269 setMediaStatus(QMediaPlayer::NoMedia);
271 if (mVideoOutput && !mVideoOutput->isReady()) {
274 mPendingSetMedia =
true;
278 if (mVideoSize.isValid() && mVideoOutput)
279 mVideoOutput->setVideoSize(mVideoSize);
284 mMediaPlayer->setDataSource(QNetworkRequest(mediaContent));
287 if (!mReloadingMedia)
288 setMediaStatus(QMediaPlayer::LoadingMedia);
291 resetBufferingProgress();
293 mReloadingMedia =
false;
298 if (m_videoSink == sink)
302 disconnect(m_videoSink->platformVideoSink(),
nullptr,
this,
nullptr);
312 mVideoOutput =
nullptr;
316 mVideoOutput =
new QAndroidTextureVideoOutput(sink,
this);
317 connect(mVideoOutput, &QAndroidTextureVideoOutput::readyChanged,
this,
319 connect(mMediaPlayer, &AndroidMediaPlayer::timedTextChanged, mVideoOutput,
320 &QAndroidTextureVideoOutput::setSubtitle);
322 if (mVideoOutput->isReady())
325 connect(m_videoSink->platformVideoSink(), &QPlatformVideoSink::rhiChanged,
this, [&]()
331 if (m_audioOutput == output)
334 m_audioOutput->q->disconnect(
this);
335 m_audioOutput =
static_cast<QAndroidAudioOutput *>(output);
338 connect(m_audioOutput->q, &QAudioOutput::volumeChanged,
this, &QAndroidMediaPlayer::setVolume);
339 connect(m_audioOutput->q, &QAudioOutput::mutedChanged,
this, &
QAndroidMediaPlayer::setMuted);
347 mMediaPlayer->setAudioOutput(m_audioOutput->device.id());
357 if ((mState & AndroidMediaPlayer::Stopped) && !mMediaContent.isEmpty()) {
358 setMedia(mMediaContent, mMediaStream);
361 if (!mMediaContent.isEmpty())
362 stateChanged(QMediaPlayer::PlayingState);
368 mPendingState = QMediaPlayer::PlayingState;
373 mVideoOutput->start();
377 if (mHasPendingPlaybackRate) {
378 mHasPendingPlaybackRate =
false;
379 if (mMediaPlayer->setPlaybackRate(mCurrentPlaybackRate))
381 mCurrentPlaybackRate = mMediaPlayer->playbackRate();
382 Q_EMIT playbackRateChanged(mCurrentPlaybackRate);
391 if (mediaStatus() == QMediaPlayer::NoMedia)
396 stateChanged(QMediaPlayer::PausedState);
403 mPendingState = QMediaPlayer::PausedState;
408 setPosition(currentPosition);
417 stateChanged(QMediaPlayer::StoppedState);
424 if ((mState & (AndroidMediaPlayer::Idle | AndroidMediaPlayer::Uninitialized | AndroidMediaPlayer::Error)) == 0)
425 mPendingState = QMediaPlayer::StoppedState;
429 if (mCurrentPlaybackRate != 1.)
431 mHasPendingPlaybackRate =
true;
453 mPendingState = state();
454 stateChanged(QMediaPlayer::PausedState);
455 setMediaStatus(QMediaPlayer::StalledMedia);
458 if (state() != QMediaPlayer::StoppedState)
459 flushPendingStates();
464 seekableChanged(
false);
467 Q_EMIT metaDataChanged();
477 QMediaPlayer::Error error = QMediaPlayer::ResourceError;
481 errorString = QLatin1String(
"Error:");
484 errorString = QLatin1String(
"Error: Server died");
485 error = QMediaPlayer::ResourceError;
488 errorString = QLatin1String(
"Error: Invalid state");
489 error = QMediaPlayer::ResourceError;
495 errorString += QLatin1String(
" (I/O operation failed)");
496 setInvalidMediaWithError(QMediaPlayer::NetworkError, errorString);
499 errorString += QLatin1String(
" (Malformed bitstream)");
500 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
503 errorString += QLatin1String(
" (Unsupported media)");
504 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
507 errorString += QLatin1String(
" (Timed out)");
510 errorString += QLatin1String(
" (Unable to start progressive playback')");
511 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
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;
521 Q_EMIT QPlatformMediaPlayer::error(error, errorString);
528 mBuffering = percent != 100;
529 mBufferPercent = percent;
531 updateAvailablePlaybackRanges();
533 if (state() != QMediaPlayer::StoppedState)
534 setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
536 updateBufferStatus();
541 QSize newSize(width, height);
543 if (width == 0 || height == 0 || newSize == mVideoSize)
546 setVideoAvailable(
true);
547 mVideoSize = newSize;
550 mVideoOutput->setVideoSize(mVideoSize);
569 case AndroidMediaPlayer::Preparing:
570 if (!mReloadingMedia)
571 setMediaStatus(QMediaPlayer::LoadingMedia);
573 case AndroidMediaPlayer::Prepared:
574 setMediaStatus(QMediaPlayer::LoadedMedia);
576 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
577 : QMediaPlayer::BufferingMedia);
579 onBufferingChanged(100);
582 Q_EMIT metaDataChanged();
583 setAudioAvailable(
true);
584 flushPendingStates();
587 stateChanged(QMediaPlayer::PlayingState);
589 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
590 : QMediaPlayer::BufferingMedia);
592 setMediaStatus(QMediaPlayer::BufferedMedia);
594 Q_EMIT positionChanged(position());
597 stateChanged(QMediaPlayer::PausedState);
598 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
600 setMediaStatus(QMediaPlayer::BufferedMedia);
602 Q_EMIT positionChanged(position());
606 stateChanged(QMediaPlayer::StoppedState);
607 setMediaStatus(QMediaPlayer::InvalidMedia);
609 Q_EMIT positionChanged(0);
612 stateChanged(QMediaPlayer::StoppedState);
613 setMediaStatus(QMediaPlayer::LoadedMedia);
614 Q_EMIT positionChanged(0);
622 stateChanged(QMediaPlayer::StoppedState);
623 setMediaStatus(QMediaPlayer::EndOfMedia);
627 if (!mReloadingMedia) {
628 resetBufferingProgress();
629 mPendingPosition = -1;
630 mPendingSetMedia =
false;
633 Q_EMIT durationChanged(0);
634 Q_EMIT positionChanged(0);
636 setAudioAvailable(
false);
637 setVideoAvailable(
false);
638 seekableChanged(
true);
655 if (!mTracksMetadata.contains(trackType))
658 auto tracks = mTracksMetadata.value(trackType);
659 return tracks.count();
664 if (!mTracksMetadata.contains(trackType))
665 return QMediaMetaData();
667 auto tracks = mTracksMetadata.value(trackType);
668 if (tracks.count() < streamNumber)
669 return QMediaMetaData();
672 return static_cast<QMediaMetaData>(trackInfo);
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:
696 int trackNumber = androidTrackNumber;
698 int videoTrackCount = trackCount(TrackType::VideoStream);
699 if (trackNumber <= videoTrackCount)
702 trackNumber = trackNumber - videoTrackCount;
704 int audioTrackCount = trackCount(TrackType::AudioStream);
705 if (trackNumber <= audioTrackCount)
708 trackNumber = trackNumber - audioTrackCount;
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)
717 if (track.androidTrackType() == 4)
721 if (trackNumber <= timedTextCount)
724 trackNumber = trackNumber - timedTextCount;
726 if (trackNumber <= subtitleTextCount)
734 int androidTrackNumber = -1;
737 case TrackType::VideoStream: {
738 if (!mIsVideoTrackEnabled)
742 case TrackType::AudioStream: {
743 if (!mIsAudioTrackEnabled)
748 case TrackType::SubtitleStream: {
749 int timedTextSelectedTrack =
752 if (timedTextSelectedTrack > -1) {
753 androidTrackNumber = timedTextSelectedTrack;
757 int subtitleSelectedTrack =
759 if (subtitleSelectedTrack > -1) {
760 androidTrackNumber = subtitleSelectedTrack;
768 return convertTrackNumber(androidTrackNumber);
773 const auto track = activeTrack(trackType);
776 case TrackType::VideoStream: {
779 mIsVideoTrackEnabled =
false;
783 case TrackType::AudioStream: {
787 mIsAudioTrackEnabled =
false;
791 case TrackType::SubtitleStream: {
793 int subtitleSelectedTrack =
795 if (subtitleSelectedTrack > -1)
798 int timedTextSelectedTrack =
800 if (timedTextSelectedTrack > -1)
811 if (!mTracksMetadata.contains(trackType)) {
813 <<
"Trying to set a active track which type has no available tracks.";
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.";
824 if (streamNumber < 0) {
825 disableTrack(trackType);
829 const auto currentTrack = activeTrack(trackType);
830 if (streamNumber == currentTrack) {
834 if (trackType == TrackType::VideoStream && !mIsVideoTrackEnabled) {
837 mIsVideoTrackEnabled =
true;
840 if (trackType == TrackType::AudioStream && !mIsAudioTrackEnabled) {
844 mIsAudioTrackEnabled =
true;
847 if (trackType == TrackType::SubtitleStream) {
850 disableTrack(TrackType::SubtitleStream);
853 const auto &trackInfo = tracks.at(streamNumber);
854 const auto &trackNumber = trackInfo.androidTrackNumber();
857 emit activeTracksChanged();
862 QPlatformMediaPlayer::positionChanged(position);
867 QPlatformMediaPlayer::durationChanged(duration);
872 if ((mMediaPlayer->display() == 0) && mVideoOutput && ready)
875 flushPendingStates();
880 mediaStatusChanged(status);
882 if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) {
883 Q_EMIT durationChanged(0);
884 Q_EMIT metaDataChanged();
885 setAudioAvailable(
false);
886 setVideoAvailable(
false);
889 if (status == QMediaPlayer::EndOfMedia)
890 Q_EMIT positionChanged(position());
892 updateBufferStatus();
897 if (mAudioAvailable == available)
900 mAudioAvailable = available;
901 Q_EMIT audioAvailableChanged(mAudioAvailable);
906 if (mVideoAvailable == available)
910 mVideoSize = QSize();
912 mVideoAvailable = available;
913 Q_EMIT videoAvailableChanged(mVideoAvailable);
920 mAvailablePlaybackRange = QMediaTimeRange();
925 if (mPendingSetMedia) {
926 setMedia(mMediaContent, 0);
927 mPendingSetMedia =
false;
931 const int newState = mPendingState;
934 if (mPendingPosition != -1)
935 setPosition(mPendingPosition);
936 if (mPendingVolume >= 0)
937 setVolume(mPendingVolume);
938 if (mPendingMute != -1)
939 setMuted((mPendingMute == 1));
942 case QMediaPlayer::PlayingState:
945 case QMediaPlayer::PausedState:
948 case QMediaPlayer::StoppedState:
958 const auto &status = mediaStatus();
959 bool bufferFilled = (status == QMediaPlayer::BufferedMedia || status == QMediaPlayer::BufferingMedia);
961 if (mBufferFilled != bufferFilled)
962 mBufferFilled = bufferFilled;
969 const auto &androidTracksInfo = mMediaPlayer->tracksInfo();
972 mTracksMetadata[TrackType::VideoStream] = QList<QAndroidMetaData>();
973 mTracksMetadata[TrackType::AudioStream] = QList<QAndroidMetaData>();
974 mTracksMetadata[TrackType::SubtitleStream] = QList<QAndroidMetaData>();
976 for (
const auto &androidTrackInfo : androidTracksInfo) {
978 const auto mediaPlayerType = convertTrackType(androidTrackInfo.trackType);
979 if (!mediaPlayerType)
982 auto &tracks = mTracksMetadata[*mediaPlayerType];
984 const QAndroidMetaData metadata(*mediaPlayerType, androidTrackInfo.trackType,
985 androidTrackInfo.trackNumber, androidTrackInfo.mimeType,
986 androidTrackInfo.language);
987 tracks.append(metadata);
990 emit tracksChanged();
995#include "moc_qandroidmediaplayer_p.cpp"
AndroidSurfaceTexture * surfaceTexture() override
bool shouldTextureBeUpdated() const
StateChangeNotifier(QAndroidMediaPlayer *mp)
Combined button and popup list for selecting options.
#define qCWarning(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)