6#include <avfvideosink_p.h>
7#include <avfmetadata_p.h>
10#include "private/qplatformaudiooutput_p.h"
12#include <QtCore/qdir.h>
13#include <QtCore/qfileinfo.h>
14#include <QtCore/qmimedatabase.h>
15#include <QtCore/qpointer.h>
16#include <QtCore/qmath.h>
17#include <QtCore/qmutex.h>
18#include <QtCore/qthread.h>
19#include <QtCore/private/qexpected_p.h>
23#import <AVFoundation/AVFoundation.h>
28static NSString*
const AVF_TRACKS_KEY = @
"tracks";
49@interface AVFMediaPlayerObserver : NSObject<AVAssetResourceLoaderDelegate>
51@property (readonly, getter=player) AVPlayer* m_player;
52@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem;
53@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer;
54@property (retain) AVPlayerItemTrack *videoTrack;
56- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session;
57- (
void) setURL:(NSURL *)url mimeType:(NSString *)mimeType;
59- (
void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys;
60- (
void) assetFailedToPrepareForPlayback:(NSError *)error;
61- (
void) playerItemDidReachEnd:(NSNotification *)notification;
62- (
void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
63 change:(NSDictionary *)change context:(
void *)context;
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];
255 m_playerLayer.player = nil;
257 [self setSessionActive:NO];
261- (
void) prepareToPlayAsset:(AVURLAsset *)asset
262 withKeys:(NSArray *)requestedKeys
264 if (!m_platformPlayer)
268 for (NSString *thisKey in requestedKeys)
270 NSError *error = nil;
271 AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
273 qDebug() << Q_FUNC_INFO << [thisKey UTF8String] <<
" status: " << keyStatus;
275 if (keyStatus == AVKeyValueStatusFailed)
277 [self assetFailedToPrepareForPlayback:error];
284 qDebug() << Q_FUNC_INFO <<
"isPlayable: " << [asset isPlayable];
287 qWarning() <<
"Asset reported to be not playable. Playback of this asset may not be possible.";
298 m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
300 qWarning() <<
"Failed to create player item";
302 NSString *localizedDescription = NSLocalizedString(@
"Item cannot be played", @
"Item cannot be played description");
303 NSString *localizedFailureReason = NSLocalizedString(@
"The assets tracks were loaded, but couldn't create player item.", @
"Item cannot be played failure reason");
304 NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
305 localizedDescription, NSLocalizedDescriptionKey,
306 localizedFailureReason, NSLocalizedFailureReasonErrorKey,
308 NSError *assetCannotBePlayedError = [NSError errorWithDomain:@
"StitchedStreamPlayer" code:0 userInfo:errorDict];
310 [self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
315 [m_playerItem addObserver:self
316 forKeyPath:AVF_STATUS_KEY
317 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
318 context:AVFMediaPlayerObserverStatusObservationContext];
320 [m_playerItem addObserver:self
321 forKeyPath:@
"presentationSize"
322 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
323 context:AVFMediaPlayerObserverPresentationSizeContext];
325 [m_playerItem addObserver:self
326 forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
327 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
328 context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext];
330 [m_playerItem addObserver:self
331 forKeyPath:AVF_TRACKS_KEY
332 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
333 context:AVFMediaPlayerObserverTracksContext];
337 [[NSNotificationCenter defaultCenter] addObserver:self
338 selector:@selector(playerItemDidReachEnd:)
339 name:AVPlayerItemDidPlayToEndTimeNotification
340 object:m_playerItem];
343 m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
348 m_platformPlayer.withPlatformPlayer([&](AVFMediaPlayer *player) {
349 auto *audioOutput = player->m_audioOutput;
350 m_player.volume = (audioOutput ? audioOutput->volume : 1.);
351 m_player.muted = (audioOutput ? audioOutput->muted :
true);
352 player->updateAudioOutputDevice();
357 m_playerLayer.player = m_player;
362 [m_player addObserver:self
363 forKeyPath:AVF_CURRENT_ITEM_KEY
364 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
365 context:AVFMediaPlayerObserverCurrentItemObservationContext];
368 [m_player addObserver:self
369 forKeyPath:AVF_RATE_KEY
370 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
371 context:AVFMediaPlayerObserverRateObservationContext];
374 [m_player addObserver:self
375 forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
377 context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
379 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
380 [self setSessionActive:YES];
384-(
void) assetFailedToPrepareForPlayback:(NSError *)error
386 QMediaPlayer::Error errorCode = QMediaPlayer::FormatError;
388 NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
389 if (underlyingError && ![underlyingError.domain isEqualToString:AVFoundationErrorDomain])
390 errorCode = QMediaPlayer::ResourceError;
392 m_platformPlayer.invokeWithPlatformPlayer([errorCode](AVFMediaPlayer *platformPlayer) {
393 platformPlayer->processMediaLoadError(errorCode);
397 qDebug() << Q_FUNC_INFO;
398 qDebug() << [[error localizedDescription] UTF8String];
399 qDebug() << [[error localizedFailureReason] UTF8String];
400 qDebug() << [[error localizedRecoverySuggestion] UTF8String];
404- (
void) playerItemDidReachEnd:(NSNotification *)notification
406 Q_UNUSED(notification);
408 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
409 platformPlayer->processEOS();
413- (
void) observeValueForKeyPath:(NSString*) path
415 change:(NSDictionary*)change
416 context:(
void*)context
419 if (context == AVFMediaPlayerObserverStatusObservationContext)
421 AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
426 case AVPlayerStatusUnknown:
432 case AVPlayerStatusReadyToPlay:
438 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
439 platformPlayer->processLoadStateChange();
444 case AVPlayerStatusFailed:
446 AVPlayerItem *playerItem =
static_cast<AVPlayerItem*>(object);
447 [self assetFailedToPrepareForPlayback:playerItem.error];
449 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
450 platformPlayer->processLoadStateChange();
455 }
else if (context == AVFMediaPlayerObserverPresentationSizeContext) {
456 QSize size(m_playerItem.presentationSize.width, m_playerItem.presentationSize.height);
457 m_platformPlayer.invokeWithPlatformPlayer([size](AVFMediaPlayer *platformPlayer) {
458 platformPlayer->nativeSizeChanged(size);
460 }
else if (context == AVFMediaPlayerObserverBufferLikelyToKeepUpContext)
462 const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
463 if (isPlaybackLikelyToKeepUp != m_bufferIsLikelyToKeepUp) {
464 m_bufferIsLikelyToKeepUp = isPlaybackLikelyToKeepUp;
465 int bufferProgress = isPlaybackLikelyToKeepUp ? 100 : 0;
467 m_platformPlayer.invokeWithPlatformPlayer(
468 [bufferProgress](AVFMediaPlayer *platformPlayer) {
469 platformPlayer->processBufferStateChange(bufferProgress);
473 else if (context == AVFMediaPlayerObserverTracksContext)
475 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
476 platformPlayer->updateTracks();
480 else if (context == AVFMediaPlayerObserverRateObservationContext) {
486 else if (context == AVFMediaPlayerObserverCurrentItemObservationContext) {
487 AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
488 if (m_playerItem != newPlayerItem)
489 m_playerItem = newPlayerItem;
490 }
else if (context == AVFMediaPlayerObserverCurrentItemDurationObservationContext) {
491 const CMTime time = [m_playerItem duration];
492 const qint64 dur =
static_cast<qint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
494 m_platformPlayer.invokeWithPlatformPlayer([dur](AVFMediaPlayer *platformPlayer) {
495 platformPlayer->processDurationChange(dur);
498 [super observeValueForKeyPath:path ofObject:object change:change context:context];
505 qDebug() << Q_FUNC_INFO;
507 m_platformPlayer.clear();
513 qDebug() << Q_FUNC_INFO;
517 m_platformPlayer.clear();
523 [m_mimeType release];
524 [m_playerLayer release];
527 self.videoTrack = nil;
531- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
533 Q_UNUSED(resourceLoader);
535 if (![loadingRequest.request.URL.scheme isEqualToString:@
"iodevice"])
538 auto result = m_platformPlayer.withPlatformPlayer([&](AVFMediaPlayer *platformPlayer) {
539 QIODevice *device = platformPlayer->mediaStream();
543 device->seek(loadingRequest.dataRequest.requestedOffset);
544 if (loadingRequest.contentInformationRequest) {
545 loadingRequest.contentInformationRequest.contentType = m_mimeType;
546 loadingRequest.contentInformationRequest.contentLength = device->size();
547 loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
550 if (loadingRequest.dataRequest) {
551 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
552 int maxBytes = qMin(32 * 1064,
int(requestedLength));
554 buffer.resize(maxBytes);
556 NSInteger submitted = 0;
557 while (submitted < requestedLength) {
558 qint64 len = device->read(buffer.data(), maxBytes);
562 [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer
568 [loadingRequest finishLoading];
574 return result.value_or(NO);
578AVFMediaPlayer::AVFMediaPlayer(QMediaPlayer *player)
580 QPlatformMediaPlayer(player),
581 m_mediaStream(
nullptr),
583 m_requestedPosition(-1),
587 m_observer = [[AVFMediaPlayerObserver alloc] initWithMediaPlayerSession:
this];
588 connect(&m_playbackTimer, &QTimer::timeout,
this, &AVFMediaPlayer::processPositionChange);
589 setVideoOutput(
new AVFVideoRendererControl(
this));
595 qDebug() << Q_FUNC_INFO;
598 [m_observer clearSession];
599 [m_observer release];
604 m_videoSink = sink ?
static_cast<
AVFVideoSink *>(sink->platformVideoSink()):
nullptr;
611 qDebug() << Q_FUNC_INFO << output;
614 if (m_videoOutput == output)
619 m_videoOutput->setLayer(
nullptr);
622 m_videoOutput = output;
624 if (m_videoOutput && state() != QMediaPlayer::StoppedState)
625 m_videoOutput->setLayer([m_observer playerLayer]);
631 qDebug() << Q_FUNC_INFO;
633 AVAsset *currentAsset = [[m_observer playerItem] asset];
644 return m_mediaStream;
647static void setURL(AVFMediaPlayerObserver *observer,
const QUrl &url,
const QString &mimeType = QString())
649 QUrl resolvedUrl = url;
651 if (url.isLocalFile() && !QDir::isAbsolutePath(url.path()))
652 resolvedUrl = QUrl::fromLocalFile(QFileInfo(url.path()).absoluteFilePath());
653 NSURL *nsurl = resolvedUrl.toNSURL();
654 [observer setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
661 qDebug() << Q_FUNC_INFO << content.request().url();
664 [m_observer unloadMedia];
666 m_resources = content;
669 m_requestedPosition = -1;
670 orientationChanged(QtVideo::Rotation::None,
false);
671 positionChanged(position());
672 if (m_duration != 0) {
676 if (!m_metaData.isEmpty()) {
680 resetBufferProgress();
681 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
683 nativeTracks[i].clear();
687 if (!m_mediaStream && content.isEmpty()) {
688 seekableChanged(
false);
689 audioAvailableChanged(
false);
690 videoAvailableChanged(
false);
692 mediaStatusChanged(QMediaPlayer::NoMedia);
693 stateChanged(QMediaPlayer::StoppedState);
698 mediaStatusChanged(QMediaPlayer::LoadingMedia);
703 if (m_mediaStream->size())
708 setURL(m_observer, m_resources);
711 stateChanged(QMediaPlayer::StoppedState);
716 AVPlayerItem *playerItem = [m_observer playerItem];
718 if (m_requestedPosition != -1)
719 return m_requestedPosition;
724 CMTime time = [playerItem currentTime];
725 return static_cast<quint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
731 qDebug() << Q_FUNC_INFO;
739 qDebug() << Q_FUNC_INFO;
741 return m_bufferProgress/100.;
746 AVPlayerItem *playerItem = [m_observer playerItem];
751 if (state() == QMediaPlayer::StoppedState)
754 QMediaTimeRange timeRanges;
756 NSArray *ranges = [playerItem loadedTimeRanges];
757 for (NSValue *timeRange in ranges) {
758 CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
759 qint64 startTime = qint64(
float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
760 timeRanges.addInterval(startTime, startTime + qint64(
float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
772 if (m_audioOutput == output)
775 m_audioOutput->q->disconnect(
this);
776 m_audioOutput = output;
784 setMuted(m_audioOutput ? m_audioOutput->muted :
true);
785 setVolume(m_audioOutput ? m_audioOutput->volume : 1.);
796 qDebug() << Q_FUNC_INFO << rate;
799 if (QtPrivate::fuzzyCompare(m_rate, rate))
804 AVPlayer *player = [m_observer player];
805 if (player && state() == QMediaPlayer::PlayingState)
806 [player setRate:m_rate];
808 playbackRateChanged(m_rate);
814 qDebug() << Q_FUNC_INFO << pos;
817 if (pos == position())
820 AVPlayerItem *playerItem = [m_observer playerItem];
822 m_requestedPosition = pos;
823 positionChanged(m_requestedPosition);
828 if (m_requestedPosition != -1) {
829 m_requestedPosition = -1;
830 positionChanged(position());
835 pos = qMax(qint64(0), pos);
837 pos = qMin(pos, duration());
838 m_requestedPosition = pos;
840 CMTime newTime = [playerItem currentTime];
841 newTime.value = (pos / 1000.0f) * newTime.timescale;
842 [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero
843 completionHandler:^(BOOL finished) {
845 m_requestedPosition = -1;
848 positionChanged(pos);
851 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
852 QMediaPlayer::MediaStatus newMediaStatus = (state() == QMediaPlayer::PausedState)
853 ? QMediaPlayer::BufferedMedia
854 : QMediaPlayer::LoadedMedia;
855 mediaStatusChanged(newMediaStatus);
862 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
865 if (mediaStatus() == QMediaPlayer::NoMedia || mediaStatus() == QMediaPlayer::InvalidMedia)
868 if (state() == QMediaPlayer::PlayingState)
871 if (state() != QMediaPlayer::PausedState)
874 if (m_videoOutput && m_videoSink)
875 m_videoOutput->setLayer([m_observer playerLayer]);
878 if (mediaStatus() == QMediaPlayer::EndOfMedia)
881 if (mediaStatus() == QMediaPlayer::LoadedMedia
882 || mediaStatus() == QMediaPlayer::BufferedMedia) {
884 [[m_observer player] setRate:m_rate];
889 stateChanged(QMediaPlayer::PlayingState);
890 m_playbackTimer.start(100);
896 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
899 if (mediaStatus() == QMediaPlayer::NoMedia || mediaStatus() == QMediaPlayer::InvalidMedia)
902 if (state() == QMediaPlayer::PausedState)
905 stateChanged(QMediaPlayer::PausedState);
907 if (m_videoOutput && m_videoSink)
908 m_videoOutput->setLayer([m_observer playerLayer]);
910 [[m_observer player] pause];
913 if (mediaStatus() == QMediaPlayer::EndOfMedia)
916 positionChanged(position());
917 m_playbackTimer.stop();
923 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
926 if (state() == QMediaPlayer::StoppedState && mediaStatus() != QMediaPlayer::EndOfMedia)
930 [[m_observer player] pause];
934 m_videoOutput->setLayer(
nullptr);
936 resetBufferProgress();
938 if (mediaStatus() == QMediaPlayer::BufferedMedia || mediaStatus() == QMediaPlayer::EndOfMedia)
939 mediaStatusChanged(QMediaPlayer::LoadedMedia);
941 stateChanged(QMediaPlayer::StoppedState);
942 m_playbackTimer.stop();
948 qDebug() << Q_FUNC_INFO << volume;
951 AVPlayer *player = [m_observer player];
953 player.volume = volume;
959 qDebug() << Q_FUNC_INFO << muted;
962 AVPlayer *player = [m_observer player];
964 player.muted = muted;
970 AVPlayer *player = [m_observer player];
974 if (!m_audioOutput || m_audioOutput->device.id().isEmpty()) {
977 player.audioOutputDeviceUniqueID = nil;
979 NSString *str = QString::fromUtf8(m_audioOutput->device.id()).toNSString();
980 player.audioOutputDeviceUniqueID = str;
988 positionChanged(duration());
990 [[m_observer player] setRate:m_rate];
996 qDebug() << Q_FUNC_INFO;
998 positionChanged(position());
1001 m_videoOutput->setLayer(
nullptr);
1003 resetBufferProgress();
1005 stateChanged(QMediaPlayer::StoppedState);
1006 mediaStatusChanged(QMediaPlayer::EndOfMedia);
1011 AVPlayerStatus currentStatus = [[m_observer player] status];
1014 qDebug() << Q_FUNC_INFO << currentStatus <<
", " << mediaStatus() <<
", " << newState;
1017 if (mediaStatus() == QMediaPlayer::NoMedia)
1020 if (currentStatus == AVPlayerStatusReadyToPlay) {
1022 AVPlayerItem *playerItem = [m_observer playerItem];
1024 applyPitchCompensation(m_pitchCompensationEnabled);
1027 m_metaData = AVFMetaData::fromAsset(playerItem.asset);
1032 seekableChanged([[playerItem seekableTimeRanges] count] > 0);
1035 AVPlayerLayer *playerLayer = [m_observer playerLayer];
1036 if (m_observer.videoTrack && playerLayer) {
1037 if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
1038 playerLayer.bounds = CGRectMake(0.0f, 0.0f,
1039 m_observer.videoTrack.assetTrack.naturalSize.width,
1040 m_observer.videoTrack.assetTrack.naturalSize.height);
1044 if (m_requestedPosition != -1)
1045 setPosition(m_requestedPosition);
1048 QMediaPlayer::MediaStatus newStatus = (newState != QMediaPlayer::StoppedState)
1049 ? QMediaPlayer::BufferedMedia
1050 : QMediaPlayer::LoadedMedia;
1052 if (newStatus != mediaStatus()) {
1053 if (newStatus == QMediaPlayer::BufferedMedia
1054 && mediaStatus() == QMediaPlayer::LoadingMedia) {
1056 mediaStatusChanged(QMediaPlayer::LoadedMedia);
1057 mediaStatusChanged(QMediaPlayer::BufferingMedia);
1058 }
else if (newStatus == QMediaPlayer::BufferedMedia
1059 && mediaStatus() == QMediaPlayer::LoadedMedia) {
1060 mediaStatusChanged(QMediaPlayer::BufferingMedia);
1062 mediaStatusChanged(newStatus);
1066 if (newState == QMediaPlayer::PlayingState && [m_observer player]) {
1068 [[m_observer player] setRate:m_rate];
1069 m_playbackTimer.start();
1082 stateChanged(QMediaPlayer::StoppedState);
1087 if (state() == QMediaPlayer::StoppedState)
1090 if (bufferProgress == m_bufferProgress)
1093 auto status = mediaStatus();
1095 if (!bufferProgress) {
1096 status = QMediaPlayer::StalledMedia;
1097 }
else if (status == QMediaPlayer::StalledMedia) {
1098 status = QMediaPlayer::BufferedMedia;
1100 if (state() == QMediaPlayer::PlayingState) {
1101 [[m_observer player] setRate:m_rate];
1102 m_playbackTimer.start();
1106 mediaStatusChanged(status);
1108 m_bufferProgress = bufferProgress;
1109 bufferProgressChanged(bufferProgress / 100.);
1114 if (duration == m_duration)
1117 m_duration = duration;
1118 durationChanged(duration);
1123 if (state() == QMediaPlayer::StoppedState)
1126 positionChanged(position());
1131 if (m_requestedPosition != -1) {
1132 m_requestedPosition = -1;
1133 positionChanged(position());
1136 mediaStatusChanged(QMediaPlayer::InvalidMedia);
1138 error(errorCode, tr(
"Failed to load media"));
1149 if (!m_resources.isEmpty())
1150 suffix = QFileInfo(m_resources.path()).suffix();
1151 if (suffix.isEmpty() && m_mediaStream)
1152 suffix = QMimeDatabase().mimeTypeForData(m_mediaStream).preferredSuffix();
1153 const QString url = QStringLiteral(
"iodevice:///iodevice.") + suffix;
1154 setURL(m_observer, QUrl(url), suffix);
1159 resetStream(
nullptr);
1164 bool firstLoad =
true;
1165 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
1166 if (tracks[i].count())
1169 nativeTracks[i].clear();
1171 bool hasAudio =
false;
1172 bool hasVideo =
false;
1173 AVPlayerItem *playerItem = [m_observer playerItem];
1176 NSArray *tracks = playerItem.tracks;
1177 for (AVPlayerItemTrack *track in tracks) {
1178 AVAssetTrack *assetTrack = track.assetTrack;
1181 if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
1182 qtTrack = QPlatformMediaPlayer::AudioStream;
1184 }
else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
1185 qtTrack = QPlatformMediaPlayer::VideoStream;
1187 if (m_observer.videoTrack != track) {
1188 m_observer.videoTrack = track;
1189 bool isMirrored =
false;
1190 QtVideo::Rotation orientation = QtVideo::Rotation::None;
1191 videoOrientationForAssetTrack(assetTrack, orientation, isMirrored);
1192 orientationChanged(orientation, isMirrored);
1195 else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
1196 qtTrack = QPlatformMediaPlayer::SubtitleStream;
1198 if (qtTrack != -1) {
1199 QMediaMetaData metaData = AVFMetaData::fromAssetTrack(assetTrack);
1200 this->tracks[qtTrack].append(metaData);
1201 nativeTracks[qtTrack].append(track);
1207 setActiveTrack(SubtitleStream, -1);
1209 audioAvailableChanged(hasAudio);
1210 videoAvailableChanged(hasVideo);
1216 const auto &t = nativeTracks[type];
1217 if (type == QPlatformMediaPlayer::SubtitleStream) {
1220 AVPlayerItem *playerItem = m_observer.m_playerItem;
1222 AVAsset *asset = playerItem.asset;
1225#if defined(Q_OS_VISIONOS)
1226 [asset loadMediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible
1227 completionHandler:[=](AVMediaSelectionGroup *group, NSError *error) {
1231 auto *options = group.options;
1233 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1236 AVMediaSelectionGroup *group = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1239 auto *options = group.options;
1241 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1245 for (
int i = 0; i < t.count(); ++i)
1246 t.at(i).enabled = (i == index);
1247 activeTracksChanged();
1252 const auto &t = nativeTracks[type];
1253 for (
int i = 0; i < t.count(); ++i)
1254 if (t.at(i).enabled)
1261 return nativeTracks[type].count();
1264QMediaMetaData
AVFMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackType type,
int trackNumber)
1266 const auto &t = tracks[type];
1267 if (trackNumber < 0 || trackNumber >= t.count())
1268 return QMediaMetaData();
1269 return t.at(trackNumber);
1274 if (m_mediaStream) {
1279 m_mediaStream = stream;
1281 if (m_mediaStream) {
1289 AVPlayerItem *playerItem = [m_observer playerItem];
1292 playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmSpectral;
1294 playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed;
1300 if (m_bufferProgress != 0) {
1301 m_bufferProgress = 0;
1302 bufferProgressChanged(0);
1310 m_videoSink->setNativeSize(size);
1313void AVFMediaPlayer::orientationChanged(QtVideo::Rotation rotation,
bool mirrored)
1318 m_videoOutput->setVideoRotation(rotation);
1323 QtVideo::Rotation &angle,
1326 angle = QtVideo::Rotation::None;
1329 CGAffineTransform transform = videoTrack.preferredTransform;
1330 if (CGAffineTransformIsIdentity(transform))
1334 qreal det = transform.a * transform.d - transform.b * transform.c;
1335 mirrored = (det < 0.0);
1339 qreal ra = mirrored ? -transform.a : transform.a;
1340 qreal rb = mirrored ? -transform.b : transform.b;
1342 qreal degrees = qRadiansToDegrees(qAtan2(rb, ra));
1346 if (QtPrivate::fuzzyCompare(degrees, qreal(90))
1347 || QtPrivate::fuzzyCompare(degrees, qreal(-270))) {
1348 angle = QtVideo::Rotation::Clockwise90;
1349 }
else if (QtPrivate::fuzzyCompare(degrees, qreal(-90))
1350 || QtPrivate::fuzzyCompare(degrees, qreal(270))) {
1351 angle = QtVideo::Rotation::Clockwise270;
1352 }
else if (QtPrivate::fuzzyCompare(degrees, qreal(180))
1353 || QtPrivate::fuzzyCompare(degrees, qreal(-180))) {
1354 angle = QtVideo::Rotation::Clockwise180;
1361 if (m_pitchCompensationEnabled == enabled)
1364 applyPitchCompensation(enabled);
1366 m_pitchCompensationEnabled = enabled;
1367 pitchCompensationChanged(enabled);
1372 return m_pitchCompensationEnabled;
1378 return QPlatformMediaPlayer::PitchCompensationAvailability::Available;
1381#include "moc_avfmediaplayer_p.cpp"
void setVideoMirrored(bool mirrored)
void setVideoSink(AVFVideoSink *sink)