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,
317 connect(mMediaPlayer, &AndroidMediaPlayer::timedTextChanged, mVideoOutput,
318 &QAndroidTextureVideoOutput::setSubtitle);
320 if (mVideoOutput->isReady())
323 connect(m_videoSink->platformVideoSink(), &QPlatformVideoSink::rhiChanged,
this, [&]()
329 if (m_audioOutput == output)
332 m_audioOutput->q->disconnect(
this);
333 m_audioOutput =
static_cast<QAndroidAudioOutput *>(output);
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 setInvalidMediaWithError(QMediaPlayer::NetworkError, errorString);
497 errorString += QLatin1String(
" (Malformed bitstream)");
498 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
501 errorString += QLatin1String(
" (Unsupported media)");
502 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
505 errorString += QLatin1String(
" (Timed out)");
508 errorString += QLatin1String(
" (Unable to start progressive playback')");
509 setInvalidMediaWithError(QMediaPlayer::FormatError, errorString);
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;
519 Q_EMIT QPlatformMediaPlayer::error(error, errorString);
526 mBuffering = percent != 100;
527 mBufferPercent = percent;
529 updateAvailablePlaybackRanges();
531 if (state() != QMediaPlayer::StoppedState)
532 setMediaStatus(mBuffering ? QMediaPlayer::BufferingMedia : QMediaPlayer::BufferedMedia);
534 updateBufferStatus();
539 QSize newSize(width, height);
541 if (width == 0 || height == 0 || newSize == mVideoSize)
544 setVideoAvailable(
true);
545 mVideoSize = newSize;
548 mVideoOutput->setVideoSize(mVideoSize);
567 case AndroidMediaPlayer::Preparing:
568 if (!mReloadingMedia)
569 setMediaStatus(QMediaPlayer::LoadingMedia);
571 case AndroidMediaPlayer::Prepared:
572 setMediaStatus(QMediaPlayer::LoadedMedia);
574 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
575 : QMediaPlayer::BufferingMedia);
577 onBufferingChanged(100);
580 Q_EMIT metaDataChanged();
581 setAudioAvailable(
true);
582 flushPendingStates();
585 stateChanged(QMediaPlayer::PlayingState);
587 setMediaStatus(mBufferPercent == 100 ? QMediaPlayer::BufferedMedia
588 : QMediaPlayer::BufferingMedia);
590 setMediaStatus(QMediaPlayer::BufferedMedia);
592 Q_EMIT positionChanged(position());
595 stateChanged(QMediaPlayer::PausedState);
596 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
598 setMediaStatus(QMediaPlayer::BufferedMedia);
600 Q_EMIT positionChanged(position());
604 stateChanged(QMediaPlayer::StoppedState);
605 setMediaStatus(QMediaPlayer::InvalidMedia);
607 Q_EMIT positionChanged(0);
610 stateChanged(QMediaPlayer::StoppedState);
611 setMediaStatus(QMediaPlayer::LoadedMedia);
612 Q_EMIT positionChanged(0);
620 stateChanged(QMediaPlayer::StoppedState);
621 setMediaStatus(QMediaPlayer::EndOfMedia);
625 if (!mReloadingMedia) {
626 resetBufferingProgress();
627 mPendingPosition = -1;
628 mPendingSetMedia =
false;
631 Q_EMIT durationChanged(0);
632 Q_EMIT positionChanged(0);
634 setAudioAvailable(
false);
635 setVideoAvailable(
false);
636 seekableChanged(
true);
653 if (!mTracksMetadata.contains(trackType))
656 auto tracks = mTracksMetadata.value(trackType);
657 return tracks.count();
662 if (!mTracksMetadata.contains(trackType))
663 return QMediaMetaData();
665 auto tracks = mTracksMetadata.value(trackType);
666 if (tracks.count() < streamNumber)
667 return QMediaMetaData();
670 return static_cast<QMediaMetaData>(trackInfo);
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;
689 return QPlatformMediaPlayer::TrackType::NTrackTypes;
694 int trackNumber = androidTrackNumber;
696 int videoTrackCount = trackCount(QPlatformMediaPlayer::TrackType::VideoStream);
697 if (trackNumber <= videoTrackCount)
700 trackNumber = trackNumber - videoTrackCount;
702 int audioTrackCount = trackCount(QPlatformMediaPlayer::TrackType::AudioStream);
703 if (trackNumber <= audioTrackCount)
706 trackNumber = trackNumber - audioTrackCount;
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)
715 if (track.androidTrackType() == 4)
719 if (trackNumber <= timedTextCount)
722 trackNumber = trackNumber - timedTextCount;
724 if (trackNumber <= subtitleTextCount)
732 int androidTrackNumber = -1;
735 case QPlatformMediaPlayer::TrackType::VideoStream: {
736 if (!mIsVideoTrackEnabled)
740 case QPlatformMediaPlayer::TrackType::AudioStream: {
741 if (!mIsAudioTrackEnabled)
746 case QPlatformMediaPlayer::TrackType::SubtitleStream: {
747 int timedTextSelectedTrack =
750 if (timedTextSelectedTrack > -1) {
751 androidTrackNumber = timedTextSelectedTrack;
755 int subtitleSelectedTrack =
757 if (subtitleSelectedTrack > -1) {
758 androidTrackNumber = subtitleSelectedTrack;
764 case QPlatformMediaPlayer::TrackType::NTrackTypes:
768 return convertTrackNumber(androidTrackNumber);
773 const auto track = activeTrack(trackType);
779 mIsVideoTrackEnabled =
false;
787 mIsAudioTrackEnabled =
false;
791 case SubtitleStream: {
793 int subtitleSelectedTrack =
795 if (subtitleSelectedTrack > -1)
798 int timedTextSelectedTrack =
800 if (timedTextSelectedTrack > -1)
813 if (!mTracksMetadata.contains(trackType)) {
815 <<
"Trying to set a active track which type has no available tracks.";
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.";
826 if (streamNumber < 0) {
827 disableTrack(trackType);
831 const auto currentTrack = activeTrack(trackType);
832 if (streamNumber == currentTrack) {
836 if (trackType == TrackType::VideoStream && !mIsVideoTrackEnabled) {
839 mIsVideoTrackEnabled =
true;
842 if (trackType == TrackType::AudioStream && !mIsAudioTrackEnabled) {
846 mIsAudioTrackEnabled =
true;
849 if (trackType == TrackType::SubtitleStream) {
852 disableTrack(TrackType::SubtitleStream);
855 const auto &trackInfo = tracks.at(streamNumber);
856 const auto &trackNumber = trackInfo.androidTrackNumber();
859 emit activeTracksChanged();
864 QPlatformMediaPlayer::positionChanged(position);
869 QPlatformMediaPlayer::durationChanged(duration);
874 if ((mMediaPlayer->display() == 0) && mVideoOutput && ready)
877 flushPendingStates();
882 mediaStatusChanged(status);
884 if (status == QMediaPlayer::NoMedia || status == QMediaPlayer::InvalidMedia) {
885 Q_EMIT durationChanged(0);
886 Q_EMIT metaDataChanged();
887 setAudioAvailable(
false);
888 setVideoAvailable(
false);
891 if (status == QMediaPlayer::EndOfMedia)
892 Q_EMIT positionChanged(position());
894 updateBufferStatus();
899 if (mAudioAvailable == available)
902 mAudioAvailable = available;
903 Q_EMIT audioAvailableChanged(mAudioAvailable);
908 if (mVideoAvailable == available)
912 mVideoSize = QSize();
914 mVideoAvailable = available;
915 Q_EMIT videoAvailableChanged(mVideoAvailable);
922 mAvailablePlaybackRange = QMediaTimeRange();
927 if (mPendingSetMedia) {
928 setMedia(mMediaContent, 0);
929 mPendingSetMedia =
false;
933 const int newState = mPendingState;
936 if (mPendingPosition != -1)
937 setPosition(mPendingPosition);
938 if (mPendingVolume >= 0)
939 setVolume(mPendingVolume);
940 if (mPendingMute != -1)
941 setMuted((mPendingMute == 1));
944 case QMediaPlayer::PlayingState:
947 case QMediaPlayer::PausedState:
950 case QMediaPlayer::StoppedState:
960 const auto &status = mediaStatus();
961 bool bufferFilled = (status == QMediaPlayer::BufferedMedia || status == QMediaPlayer::BufferingMedia);
963 if (mBufferFilled != bufferFilled)
964 mBufferFilled = bufferFilled;
971 const auto &androidTracksInfo = mMediaPlayer->tracksInfo();
974 mTracksMetadata[TrackType::VideoStream] = QList<QAndroidMetaData>();
975 mTracksMetadata[TrackType::AudioStream] = QList<QAndroidMetaData>();
976 mTracksMetadata[TrackType::SubtitleStream] = QList<QAndroidMetaData>();
977 mTracksMetadata[TrackType::NTrackTypes] = QList<QAndroidMetaData>();
979 for (
const auto &androidTrackInfo : androidTracksInfo) {
981 const auto &mediaPlayerType = convertTrackType(androidTrackInfo.trackType);
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,...)