6#include <avfvideosink_p.h>
7#include <common/avfmetadata_p.h>
8#include <mediaplayer/avfvideorenderercontrol_p.h>
10#include <QtMultimedia/qaudiooutput.h>
11#include <QtMultimedia/private/qplatformaudiooutput_p.h>
13#include <QtCore/qdir.h>
14#include <QtCore/qfileinfo.h>
15#include <QtCore/qmath.h>
16#include <QtCore/qmimedatabase.h>
17#include <QtCore/qmutex.h>
18#include <QtCore/qpointer.h>
19#include <QtCore/qthread.h>
20#include <QtCore/private/qexpected_p.h>
22#import <AVFoundation/AVFoundation.h>
48@interface AVFMediaPlayerObserver : NSObject<AVAssetResourceLoaderDelegate>
50@property (readonly, getter=player) AVPlayer* m_player;
51@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem;
52@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer;
53@property (retain) AVPlayerItemTrack *videoTrack;
55- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session;
56- (
void) setURL:(NSURL *)url mimeType:(NSString *)mimeType;
58- (
void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys;
59- (
void) assetFailedToPrepareForPlayback:(NSError *)error;
60- (
void) playerItemDidReachEnd:(NSNotification *)notification;
61- (
void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
62 change:(NSDictionary *)change context:(
void *)context;
64- (
void) notifySeekComplete;
66- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
71static unsigned sessionActivationCount;
72static QMutex sessionMutex;
77struct GuardedPlatformPlayer
82 explicit operator
bool()
const
84 std::lock_guard guard(mutex);
88 struct not_a_platform_player_t
92 template <
typename Functor>
93 auto withPlatformPlayer(Functor &&f)
94 -> q23::expected<std::invoke_result_t<Functor, AVFMediaPlayer *>,
95 not_a_platform_player_t>
97 std::unique_lock guard(mutex);
99 return q23::unexpected{ not_a_platform_player_t{} };
100 if constexpr (std::is_void_v<std::invoke_result_t<Functor, AVFMediaPlayer *>>) {
108 template <
typename Functor>
109 void invokeWithPlatformPlayer(Functor f)
111 std::unique_lock guard(mutex);
115 if (player->thread()->isCurrentThread()) {
119 QMetaObject::invokeMethod(player, [f = std::move(f), player = player]() {
127 std::lock_guard<QMutex> guard(mutex);
133@implementation AVFMediaPlayerObserver {
135 GuardedPlatformPlayer m_platformPlayer;
137 AVPlayerItem *m_playerItem;
138 AVPlayerLayer *m_playerLayer;
140 BOOL m_bufferIsLikelyToKeepUp;
142 NSString *m_mimeType;
148@synthesize m_player, m_playerItem, m_playerLayer;
151- (
void)setSessionActive:(BOOL)active
153 const QMutexLocker lock(&sessionMutex);
159 if (!sessionActivationCount)
160 [AVAudioSession.sharedInstance setActive:YES error:nil];
161 ++sessionActivationCount;
164 if (!sessionActivationCount || !m_activated) {
165 qWarning(
"Unbalanced audio session deactivation, ignoring.");
168 --sessionActivationCount;
170 if (!sessionActivationCount)
171 [AVAudioSession.sharedInstance setActive:NO error:nil];
176- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session
178 if (!(self = [super init]))
180 m_platformPlayer.player = session;
181 m_bufferIsLikelyToKeepUp = FALSE;
183 m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:nil];
184 [m_playerLayer retain];
185 m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
186 m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
190- (
void) setURL:(NSURL *)url mimeType:(NSString *)mimeType
192 [m_mimeType release];
193 m_mimeType = [mimeType retain];
206 BOOL isAccessing = [m_URL startAccessingSecurityScopedResource];
208 __block AVURLAsset *asset = [[AVURLAsset URLAssetWithURL:m_URL options:nil] retain];
209 [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
211 __block NSArray *requestedKeys = [[NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil] retain];
213 __block AVFMediaPlayerObserver *blockSelf = [self retain];
216 [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
218 dispatch_async( dispatch_get_main_queue(),
222 [m_URL stopAccessingSecurityScopedResource];
224 [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
226 [requestedKeys release];
236 [m_playerItem removeObserver:self forKeyPath:@
"presentationSize"];
237 [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY];
238 [m_playerItem removeObserver:self forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY];
239 [m_playerItem removeObserver:self forKeyPath:AVF_TRACKS_KEY];
241 [[NSNotificationCenter defaultCenter] removeObserver:self
242 name:AVPlayerItemDidPlayToEndTimeNotification
243 object:m_playerItem];
244 m_playerItem =
nullptr;
247 [m_player setRate:0.0];
248 [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY];
249 [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
250 [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
251 [m_player replaceCurrentItemWithPlayerItem:nil];
257 AVPlayer *player = m_player;
259 dispatch_async(dispatch_get_main_queue(), ^{
264 m_playerLayer.player = nil;
266 [self setSessionActive:NO];
270- (
void) prepareToPlayAsset:(AVURLAsset *)asset
271 withKeys:(NSArray *)requestedKeys
273 if (!m_platformPlayer)
277 for (NSString *thisKey in requestedKeys)
279 NSError *error = nil;
280 AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
282 qDebug() << Q_FUNC_INFO << [thisKey UTF8String] <<
" status: " << keyStatus;
284 if (keyStatus == AVKeyValueStatusFailed)
286 [self assetFailedToPrepareForPlayback:error];
293 qDebug() << Q_FUNC_INFO <<
"isPlayable: " << [asset isPlayable];
296 qWarning() <<
"Asset reported to be not playable. Playback of this asset may not be possible.";
307 m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
309 qWarning() <<
"Failed to create player item";
311 NSString *localizedDescription = NSLocalizedString(@
"Item cannot be played", @
"Item cannot be played description");
312 NSString *localizedFailureReason = NSLocalizedString(@
"The assets tracks were loaded, but couldn't create player item.", @
"Item cannot be played failure reason");
313 NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
314 localizedDescription, NSLocalizedDescriptionKey,
315 localizedFailureReason, NSLocalizedFailureReasonErrorKey,
317 NSError *assetCannotBePlayedError = [NSError errorWithDomain:@
"StitchedStreamPlayer" code:0 userInfo:errorDict];
319 [self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
324 [m_playerItem addObserver:self
325 forKeyPath:AVF_STATUS_KEY
326 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
327 context:AVFMediaPlayerObserverStatusObservationContext];
329 [m_playerItem addObserver:self
330 forKeyPath:@
"presentationSize"
331 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
332 context:AVFMediaPlayerObserverPresentationSizeContext];
334 [m_playerItem addObserver:self
335 forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
336 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
337 context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext];
339 [m_playerItem addObserver:self
340 forKeyPath:AVF_TRACKS_KEY
341 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
342 context:AVFMediaPlayerObserverTracksContext];
346 [[NSNotificationCenter defaultCenter] addObserver:self
347 selector:@selector(playerItemDidReachEnd:)
348 name:AVPlayerItemDidPlayToEndTimeNotification
349 object:m_playerItem];
352 m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
357 m_platformPlayer.withPlatformPlayer([&](AVFMediaPlayer *player) {
358 auto *audioOutput = player->m_audioOutput;
359 m_player.volume = (audioOutput ? audioOutput->volume : 1.);
360 m_player.muted = (audioOutput ? audioOutput->muted :
true);
361 player->updateAudioOutputDevice();
366 m_playerLayer.player = m_player;
371 [m_player addObserver:self
372 forKeyPath:AVF_CURRENT_ITEM_KEY
373 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
374 context:AVFMediaPlayerObserverCurrentItemObservationContext];
377 [m_player addObserver:self
378 forKeyPath:AVF_RATE_KEY
379 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
380 context:AVFMediaPlayerObserverRateObservationContext];
383 [m_player addObserver:self
384 forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
386 context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
388 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
389 [self setSessionActive:YES];
393-(
void) assetFailedToPrepareForPlayback:(NSError *)error
395 QMediaPlayer::Error errorCode = QMediaPlayer::FormatError;
397 NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
398 if (underlyingError && ![underlyingError.domain isEqualToString:AVFoundationErrorDomain])
399 errorCode = QMediaPlayer::ResourceError;
401 m_platformPlayer.invokeWithPlatformPlayer([errorCode](AVFMediaPlayer *platformPlayer) {
402 platformPlayer->processMediaLoadError(errorCode);
406 qDebug() << Q_FUNC_INFO;
407 qDebug() << [[error localizedDescription] UTF8String];
408 qDebug() << [[error localizedFailureReason] UTF8String];
409 qDebug() << [[error localizedRecoverySuggestion] UTF8String];
413- (
void) playerItemDidReachEnd:(NSNotification *)notification
415 Q_UNUSED(notification);
417 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
418 platformPlayer->processEOS();
422- (
void) observeValueForKeyPath:(NSString*) path
424 change:(NSDictionary*)change
425 context:(
void*)context
428 if (context == AVFMediaPlayerObserverStatusObservationContext)
430 AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
435 case AVPlayerStatusUnknown:
441 case AVPlayerStatusReadyToPlay:
447 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
448 platformPlayer->processLoadStateChange();
453 case AVPlayerStatusFailed:
455 AVPlayerItem *playerItem =
static_cast<AVPlayerItem*>(object);
456 [self assetFailedToPrepareForPlayback:playerItem.error];
458 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
459 platformPlayer->processLoadStateChange();
464 }
else if (context == AVFMediaPlayerObserverPresentationSizeContext) {
465 QSize size(m_playerItem.presentationSize.width, m_playerItem.presentationSize.height);
466 m_platformPlayer.invokeWithPlatformPlayer([size](AVFMediaPlayer *platformPlayer) {
467 platformPlayer->nativeSizeChanged(size);
469 }
else if (context == AVFMediaPlayerObserverBufferLikelyToKeepUpContext)
471 const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
472 if (isPlaybackLikelyToKeepUp != m_bufferIsLikelyToKeepUp) {
473 m_bufferIsLikelyToKeepUp = isPlaybackLikelyToKeepUp;
474 int bufferProgress = isPlaybackLikelyToKeepUp ? 100 : 0;
476 m_platformPlayer.invokeWithPlatformPlayer(
477 [bufferProgress](AVFMediaPlayer *platformPlayer) {
478 platformPlayer->processBufferStateChange(bufferProgress);
482 else if (context == AVFMediaPlayerObserverTracksContext)
484 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
485 platformPlayer->updateTracks();
489 else if (context == AVFMediaPlayerObserverRateObservationContext) {
495 else if (context == AVFMediaPlayerObserverCurrentItemObservationContext) {
496 AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
497 if (m_playerItem != newPlayerItem)
498 m_playerItem = newPlayerItem;
499 }
else if (context == AVFMediaPlayerObserverCurrentItemDurationObservationContext) {
500 const CMTime time = [m_playerItem duration];
501 const qint64 dur =
static_cast<qint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
503 m_platformPlayer.invokeWithPlatformPlayer([dur](AVFMediaPlayer *platformPlayer) {
504 platformPlayer->processDurationChange(dur);
507 [super observeValueForKeyPath:path ofObject:object change:change context:context];
514 qDebug() << Q_FUNC_INFO;
516 m_platformPlayer.clear();
519- (
void)notifySeekComplete
521 m_platformPlayer.withPlatformPlayer([](AVFMediaPlayer *player) {
522 player->seekCompleted();
529 qDebug() << Q_FUNC_INFO;
533 m_platformPlayer.clear();
539 [m_mimeType release];
540 [m_playerLayer release];
543 self.videoTrack = nil;
547- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
549 Q_UNUSED(resourceLoader);
551 if (![loadingRequest.request.URL.scheme isEqualToString:@
"iodevice"])
554 auto result = m_platformPlayer.withPlatformPlayer([&](AVFMediaPlayer *platformPlayer) {
555 QIODevice *device = platformPlayer->mediaStream();
559 device->seek(loadingRequest.dataRequest.requestedOffset);
560 if (loadingRequest.contentInformationRequest) {
561 loadingRequest.contentInformationRequest.contentType = m_mimeType;
562 loadingRequest.contentInformationRequest.contentLength = device->size();
563 loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
566 if (loadingRequest.dataRequest) {
567 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
568 int maxBytes = qMin(32 * 1064,
int(requestedLength));
570 buffer.resize(maxBytes);
572 NSInteger submitted = 0;
573 while (submitted < requestedLength) {
574 qint64 len = device->read(buffer.data(), maxBytes);
578 [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer.constData()
584 [loadingRequest finishLoading];
590 return result.value_or(NO);
594AVFMediaPlayer::AVFMediaPlayer(QMediaPlayer *player)
596 QPlatformMediaPlayer(player),
597 m_mediaStream(
nullptr),
599 m_requestedPosition(-1),
603 m_observer = [[AVFMediaPlayerObserver alloc] initWithMediaPlayerSession:
this];
604 connect(&m_playbackTimer, &QTimer::timeout,
this, &AVFMediaPlayer::processPositionChange);
605 setVideoOutput(
new AVFVideoRendererControl(
this));
611 qDebug() << Q_FUNC_INFO;
616 [m_observer unloadMedia];
619 [m_observer clearSession];
620 [m_observer release];
625 m_videoSink = sink ?
static_cast<
AVFVideoSink *>(sink->platformVideoSink()):
nullptr;
632 qDebug() << Q_FUNC_INFO << output;
635 if (m_videoOutput == output)
640 m_videoOutput->setLayer(
nullptr);
643 m_videoOutput = output;
645 if (m_videoOutput && state() != QMediaPlayer::StoppedState)
646 m_videoOutput->setLayer([m_observer playerLayer]);
652 qDebug() << Q_FUNC_INFO;
654 AVAsset *currentAsset = [[m_observer playerItem] asset];
665 return m_mediaStream;
668static void setURL(AVFMediaPlayerObserver *observer,
const QUrl &url,
const QString &mimeType = QString())
670 QUrl resolvedUrl = url;
672 if (url.isLocalFile() && !QDir::isAbsolutePath(url.path()))
673 resolvedUrl = QUrl::fromLocalFile(QFileInfo(url.path()).absoluteFilePath());
674 NSURL *nsurl = resolvedUrl.toNSURL();
675 [observer setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
682 qDebug() << Q_FUNC_INFO << content.request().url();
685 [m_observer unloadMedia];
687 m_resources = content;
690 m_requestedPosition = -1;
691 orientationChanged(QtVideo::Rotation::None,
false);
692 positionChanged(position());
693 if (m_duration != 0) {
697 if (!m_metaData.isEmpty()) {
701 resetBufferProgress();
702 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
704 nativeTracks[i].clear();
708 if (!m_mediaStream && content.isEmpty()) {
709 seekableChanged(
false);
710 audioAvailableChanged(
false);
711 videoAvailableChanged(
false);
713 mediaStatusChanged(QMediaPlayer::NoMedia);
714 stateChanged(QMediaPlayer::StoppedState);
719 mediaStatusChanged(QMediaPlayer::LoadingMedia);
724 if (m_mediaStream->size())
729 setURL(m_observer, m_resources);
732 stateChanged(QMediaPlayer::StoppedState);
737 AVPlayerItem *playerItem = [m_observer playerItem];
739 if (m_requestedPosition != -1)
740 return m_requestedPosition;
745 CMTime time = [playerItem currentTime];
746 return static_cast<quint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
752 qDebug() << Q_FUNC_INFO;
760 qDebug() << Q_FUNC_INFO;
762 return m_bufferProgress/100.;
767 AVPlayerItem *playerItem = [m_observer playerItem];
772 if (state() == QMediaPlayer::StoppedState)
775 QMediaTimeRange timeRanges;
777 NSArray *ranges = [playerItem loadedTimeRanges];
778 for (NSValue *timeRange in ranges) {
779 CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
780 qint64 startTime = qint64(
float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
781 timeRanges.addInterval(startTime, startTime + qint64(
float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
793 if (m_audioOutput == output)
796 m_audioOutput->q->disconnect(
this);
797 m_audioOutput = output;
805 setMuted(m_audioOutput ? m_audioOutput->muted :
true);
806 setVolume(m_audioOutput ? m_audioOutput->volume : 1.);
817 qDebug() << Q_FUNC_INFO << rate;
820 if (QtPrivate::fuzzyCompare(m_rate, rate))
825 AVPlayer *player = [m_observer player];
826 if (player && state() == QMediaPlayer::PlayingState)
827 [player setRate:m_rate];
829 playbackRateChanged(m_rate);
835 qDebug() << Q_FUNC_INFO << pos;
838 if (pos == position())
841 AVPlayerItem *playerItem = [m_observer playerItem];
843 m_requestedPosition = pos;
844 positionChanged(m_requestedPosition);
849 if (m_requestedPosition != -1) {
850 m_requestedPosition = -1;
851 positionChanged(position());
856 pos = qMax(qint64(0), pos);
858 pos = qMin(pos, duration());
859 m_requestedPosition = pos;
861 CMTime newTime = [playerItem currentTime];
862 newTime.value = (pos / 1000.0f) * newTime.timescale;
863 AVFMediaPlayerObserver *observer = m_observer;
864 [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero
865 completionHandler:^(BOOL finished) {
867 [observer notifySeekComplete];
870 positionChanged(pos);
873 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
874 QMediaPlayer::MediaStatus newMediaStatus = (state() == QMediaPlayer::PausedState)
875 ? QMediaPlayer::BufferedMedia
876 : QMediaPlayer::LoadedMedia;
877 mediaStatusChanged(newMediaStatus);
884 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
887 if (mediaStatus() == QMediaPlayer::NoMedia || mediaStatus() == QMediaPlayer::InvalidMedia)
890 if (state() == QMediaPlayer::PlayingState)
893 if (state() != QMediaPlayer::PausedState)
896 if (m_videoOutput && m_videoSink)
897 m_videoOutput->setLayer([m_observer playerLayer]);
900 if (mediaStatus() == QMediaPlayer::EndOfMedia)
903 if (mediaStatus() == QMediaPlayer::LoadedMedia
904 || mediaStatus() == QMediaPlayer::BufferedMedia) {
906 [[m_observer player] setRate:m_rate];
911 stateChanged(QMediaPlayer::PlayingState);
912 m_playbackTimer.start(100);
918 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
921 if (mediaStatus() == QMediaPlayer::NoMedia || mediaStatus() == QMediaPlayer::InvalidMedia)
924 if (state() == QMediaPlayer::PausedState)
927 stateChanged(QMediaPlayer::PausedState);
929 if (m_videoOutput && m_videoSink)
930 m_videoOutput->setLayer([m_observer playerLayer]);
932 [[m_observer player] pause];
935 if (mediaStatus() == QMediaPlayer::EndOfMedia)
938 positionChanged(position());
939 m_playbackTimer.stop();
945 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
948 if (state() == QMediaPlayer::StoppedState && mediaStatus() != QMediaPlayer::EndOfMedia)
952 [[m_observer player] pause];
956 m_videoOutput->setLayer(
nullptr);
958 resetBufferProgress();
960 if (mediaStatus() == QMediaPlayer::BufferedMedia || mediaStatus() == QMediaPlayer::EndOfMedia)
961 mediaStatusChanged(QMediaPlayer::LoadedMedia);
963 stateChanged(QMediaPlayer::StoppedState);
964 m_playbackTimer.stop();
970 qDebug() << Q_FUNC_INFO << volume;
973 AVPlayer *player = [m_observer player];
975 player.volume = volume;
981 qDebug() << Q_FUNC_INFO << muted;
984 AVPlayer *player = [m_observer player];
986 player.muted = muted;
992 AVPlayer *player = [m_observer player];
996 if (!m_audioOutput || m_audioOutput->device.id().isEmpty()) {
999 player.audioOutputDeviceUniqueID = nil;
1001 NSString *str = QString::fromUtf8(m_audioOutput->device.id()).toNSString();
1002 player.audioOutputDeviceUniqueID = str;
1010 positionChanged(duration());
1012 [[m_observer player] setRate:m_rate];
1018 qDebug() << Q_FUNC_INFO;
1020 positionChanged(position());
1023 m_videoOutput->setLayer(
nullptr);
1025 resetBufferProgress();
1027 stateChanged(QMediaPlayer::StoppedState);
1028 mediaStatusChanged(QMediaPlayer::EndOfMedia);
1033 AVPlayerStatus currentStatus = [[m_observer player] status];
1036 qDebug() << Q_FUNC_INFO << currentStatus <<
", " << mediaStatus() <<
", " << newState;
1039 if (mediaStatus() == QMediaPlayer::NoMedia)
1042 if (currentStatus == AVPlayerStatusReadyToPlay) {
1044 AVPlayerItem *playerItem = [m_observer playerItem];
1046 applyPitchCompensation(m_pitchCompensationEnabled);
1049 m_metaData = AVFMetaData::fromAsset(playerItem.asset);
1054 seekableChanged([[playerItem seekableTimeRanges] count] > 0);
1057 AVPlayerLayer *playerLayer = [m_observer playerLayer];
1058 if (m_observer.videoTrack && playerLayer) {
1059 if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
1060 playerLayer.bounds = CGRectMake(0.0f, 0.0f,
1061 m_observer.videoTrack.assetTrack.naturalSize.width,
1062 m_observer.videoTrack.assetTrack.naturalSize.height);
1066 if (m_requestedPosition != -1)
1067 setPosition(m_requestedPosition);
1070 QMediaPlayer::MediaStatus newStatus = (newState != QMediaPlayer::StoppedState)
1071 ? QMediaPlayer::BufferedMedia
1072 : QMediaPlayer::LoadedMedia;
1074 if (newStatus != mediaStatus()) {
1075 if (newStatus == QMediaPlayer::BufferedMedia
1076 && mediaStatus() == QMediaPlayer::LoadingMedia) {
1078 mediaStatusChanged(QMediaPlayer::LoadedMedia);
1079 mediaStatusChanged(QMediaPlayer::BufferingMedia);
1080 }
else if (newStatus == QMediaPlayer::BufferedMedia
1081 && mediaStatus() == QMediaPlayer::LoadedMedia) {
1082 mediaStatusChanged(QMediaPlayer::BufferingMedia);
1084 mediaStatusChanged(newStatus);
1088 if (newState == QMediaPlayer::PlayingState && [m_observer player]) {
1090 [[m_observer player] setRate:m_rate];
1091 m_playbackTimer.start();
1104 stateChanged(QMediaPlayer::StoppedState);
1109 if (state() == QMediaPlayer::StoppedState)
1112 if (bufferProgress == m_bufferProgress)
1115 auto status = mediaStatus();
1117 if (!bufferProgress) {
1118 status = QMediaPlayer::StalledMedia;
1119 }
else if (status == QMediaPlayer::StalledMedia) {
1120 status = QMediaPlayer::BufferedMedia;
1122 if (state() == QMediaPlayer::PlayingState) {
1123 [[m_observer player] setRate:m_rate];
1124 m_playbackTimer.start();
1128 mediaStatusChanged(status);
1130 m_bufferProgress = bufferProgress;
1131 bufferProgressChanged(bufferProgress / 100.);
1136 if (duration == m_duration)
1139 m_duration = duration;
1140 durationChanged(duration);
1145 if (state() == QMediaPlayer::StoppedState)
1148 positionChanged(position());
1153 if (m_requestedPosition != -1) {
1154 m_requestedPosition = -1;
1155 positionChanged(position());
1158 setInvalidMediaWithError(errorCode, tr(
"Failed to load media"));
1163 m_requestedPosition = -1;
1174 if (!m_resources.isEmpty())
1175 suffix = QFileInfo(m_resources.path()).suffix();
1176 if (suffix.isEmpty() && m_mediaStream)
1177 suffix = QMimeDatabase().mimeTypeForData(m_mediaStream).preferredSuffix();
1178 const QString url = QStringLiteral(
"iodevice:///iodevice.") + suffix;
1179 setURL(m_observer, QUrl(url), suffix);
1184 resetStream(
nullptr);
1189 bool firstLoad =
true;
1190 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
1191 if (tracks[i].count())
1194 nativeTracks[i].clear();
1196 bool hasAudio =
false;
1197 bool hasVideo =
false;
1198 AVPlayerItem *playerItem = [m_observer playerItem];
1201 NSArray *tracks = playerItem.tracks;
1202 for (AVPlayerItemTrack *track in tracks) {
1203 AVAssetTrack *assetTrack = track.assetTrack;
1206 if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
1207 qtTrack = QPlatformMediaPlayer::AudioStream;
1209 }
else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
1210 qtTrack = QPlatformMediaPlayer::VideoStream;
1212 if (m_observer.videoTrack != track) {
1213 m_observer.videoTrack = track;
1214 bool isMirrored =
false;
1215 QtVideo::Rotation orientation = QtVideo::Rotation::None;
1216 videoOrientationForAssetTrack(assetTrack, orientation, isMirrored);
1217 orientationChanged(orientation, isMirrored);
1220 else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
1221 qtTrack = QPlatformMediaPlayer::SubtitleStream;
1223 if (qtTrack != -1) {
1224 QMediaMetaData metaData = AVFMetaData::fromAssetTrack(assetTrack);
1225 this->tracks[qtTrack].append(metaData);
1226 nativeTracks[qtTrack].append(track);
1232 setActiveTrack(SubtitleStream, -1);
1234 audioAvailableChanged(hasAudio);
1235 videoAvailableChanged(hasVideo);
1241 const auto &t = nativeTracks[type];
1242 if (type == QPlatformMediaPlayer::SubtitleStream) {
1245 AVPlayerItem *playerItem = m_observer.m_playerItem;
1247 AVAsset *asset = playerItem.asset;
1250#if defined(Q_OS_VISIONOS)
1251 [asset loadMediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible
1252 completionHandler:[=](AVMediaSelectionGroup *group, NSError *error) {
1256 auto *options = group.options;
1258 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1261 AVMediaSelectionGroup *group = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1264 auto *options = group.options;
1266 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1270 for (
int i = 0; i < t.count(); ++i)
1271 t.at(i).enabled = (i == index);
1272 activeTracksChanged();
1277 const auto &t = nativeTracks[type];
1278 for (
int i = 0; i < t.count(); ++i)
1279 if (t.at(i).enabled)
1286 return nativeTracks[type].count();
1291 const auto &t = tracks[type];
1292 if (trackNumber < 0 || trackNumber >= t.count())
1293 return QMediaMetaData();
1294 return t.at(trackNumber);
1299 if (m_mediaStream) {
1304 m_mediaStream = stream;
1306 if (m_mediaStream) {
1314 AVPlayerItem *playerItem = [m_observer playerItem];
1317 playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmSpectral;
1319 playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed;
1325 if (m_bufferProgress != 0) {
1326 m_bufferProgress = 0;
1327 bufferProgressChanged(0);
1335 m_videoSink->setNativeSize(size);
1338void AVFMediaPlayer::orientationChanged(QtVideo::Rotation rotation,
bool mirrored)
1343 m_videoOutput->setVideoRotation(rotation);
1348 QtVideo::Rotation &angle,
1351 angle = QtVideo::Rotation::None;
1354 CGAffineTransform transform = videoTrack.preferredTransform;
1355 if (CGAffineTransformIsIdentity(transform))
1359 qreal det = transform.a * transform.d - transform.b * transform.c;
1360 mirrored = (det < 0.0);
1364 qreal ra = mirrored ? -transform.a : transform.a;
1365 qreal rb = mirrored ? -transform.b : transform.b;
1367 qreal degrees = qRadiansToDegrees(qAtan2(rb, ra));
1371 if (QtPrivate::fuzzyCompare(degrees, qreal(90))
1372 || QtPrivate::fuzzyCompare(degrees, qreal(-270))) {
1373 angle = QtVideo::Rotation::Clockwise90;
1374 }
else if (QtPrivate::fuzzyCompare(degrees, qreal(-90))
1375 || QtPrivate::fuzzyCompare(degrees, qreal(270))) {
1376 angle = QtVideo::Rotation::Clockwise270;
1377 }
else if (QtPrivate::fuzzyCompare(degrees, qreal(180))
1378 || QtPrivate::fuzzyCompare(degrees, qreal(-180))) {
1379 angle = QtVideo::Rotation::Clockwise180;
1386 if (m_pitchCompensationEnabled == enabled)
1389 applyPitchCompensation(enabled);
1391 m_pitchCompensationEnabled = enabled;
1392 pitchCompensationChanged(enabled);
1397 return m_pitchCompensationEnabled;
1403 return QPlatformMediaPlayer::PitchCompensationAvailability::Available;
1406#include "moc_avfmediaplayer_p.cpp"
void setVideoMirrored(bool mirrored)
void setVideoSink(AVFVideoSink *sink)