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];
251 [m_player replaceCurrentItemWithPlayerItem:nil];
256 m_playerLayer.player = nil;
258 [self setSessionActive:NO];
262- (
void) prepareToPlayAsset:(AVURLAsset *)asset
263 withKeys:(NSArray *)requestedKeys
265 if (!m_platformPlayer)
269 for (NSString *thisKey in requestedKeys)
271 NSError *error = nil;
272 AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
274 qDebug() << Q_FUNC_INFO << [thisKey UTF8String] <<
" status: " << keyStatus;
276 if (keyStatus == AVKeyValueStatusFailed)
278 [self assetFailedToPrepareForPlayback:error];
285 qDebug() << Q_FUNC_INFO <<
"isPlayable: " << [asset isPlayable];
288 qWarning() <<
"Asset reported to be not playable. Playback of this asset may not be possible.";
299 m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
301 qWarning() <<
"Failed to create player item";
303 NSString *localizedDescription = NSLocalizedString(@
"Item cannot be played", @
"Item cannot be played description");
304 NSString *localizedFailureReason = NSLocalizedString(@
"The assets tracks were loaded, but couldn't create player item.", @
"Item cannot be played failure reason");
305 NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
306 localizedDescription, NSLocalizedDescriptionKey,
307 localizedFailureReason, NSLocalizedFailureReasonErrorKey,
309 NSError *assetCannotBePlayedError = [NSError errorWithDomain:@
"StitchedStreamPlayer" code:0 userInfo:errorDict];
311 [self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
316 [m_playerItem addObserver:self
317 forKeyPath:AVF_STATUS_KEY
318 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
319 context:AVFMediaPlayerObserverStatusObservationContext];
321 [m_playerItem addObserver:self
322 forKeyPath:@
"presentationSize"
323 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
324 context:AVFMediaPlayerObserverPresentationSizeContext];
326 [m_playerItem addObserver:self
327 forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
328 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
329 context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext];
331 [m_playerItem addObserver:self
332 forKeyPath:AVF_TRACKS_KEY
333 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
334 context:AVFMediaPlayerObserverTracksContext];
338 [[NSNotificationCenter defaultCenter] addObserver:self
339 selector:@selector(playerItemDidReachEnd:)
340 name:AVPlayerItemDidPlayToEndTimeNotification
341 object:m_playerItem];
344 m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
349 m_platformPlayer.withPlatformPlayer([&](AVFMediaPlayer *player) {
350 auto *audioOutput = player->m_audioOutput;
351 m_player.volume = (audioOutput ? audioOutput->volume : 1.);
352 m_player.muted = (audioOutput ? audioOutput->muted :
true);
353 player->updateAudioOutputDevice();
358 m_playerLayer.player = m_player;
363 [m_player addObserver:self
364 forKeyPath:AVF_CURRENT_ITEM_KEY
365 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
366 context:AVFMediaPlayerObserverCurrentItemObservationContext];
369 [m_player addObserver:self
370 forKeyPath:AVF_RATE_KEY
371 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
372 context:AVFMediaPlayerObserverRateObservationContext];
375 [m_player addObserver:self
376 forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
378 context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
380 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
381 [self setSessionActive:YES];
385-(
void) assetFailedToPrepareForPlayback:(NSError *)error
387 QMediaPlayer::Error errorCode = QMediaPlayer::FormatError;
389 NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
390 if (underlyingError && ![underlyingError.domain isEqualToString:AVFoundationErrorDomain])
391 errorCode = QMediaPlayer::ResourceError;
393 m_platformPlayer.invokeWithPlatformPlayer([errorCode](AVFMediaPlayer *platformPlayer) {
394 platformPlayer->processMediaLoadError(errorCode);
398 qDebug() << Q_FUNC_INFO;
399 qDebug() << [[error localizedDescription] UTF8String];
400 qDebug() << [[error localizedFailureReason] UTF8String];
401 qDebug() << [[error localizedRecoverySuggestion] UTF8String];
405- (
void) playerItemDidReachEnd:(NSNotification *)notification
407 Q_UNUSED(notification);
409 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
410 platformPlayer->processEOS();
414- (
void) observeValueForKeyPath:(NSString*) path
416 change:(NSDictionary*)change
417 context:(
void*)context
420 if (context == AVFMediaPlayerObserverStatusObservationContext)
422 AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
427 case AVPlayerStatusUnknown:
433 case AVPlayerStatusReadyToPlay:
439 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
440 platformPlayer->processLoadStateChange();
445 case AVPlayerStatusFailed:
447 AVPlayerItem *playerItem =
static_cast<AVPlayerItem*>(object);
448 [self assetFailedToPrepareForPlayback:playerItem.error];
450 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
451 platformPlayer->processLoadStateChange();
456 }
else if (context == AVFMediaPlayerObserverPresentationSizeContext) {
457 QSize size(m_playerItem.presentationSize.width, m_playerItem.presentationSize.height);
458 m_platformPlayer.invokeWithPlatformPlayer([size](AVFMediaPlayer *platformPlayer) {
459 platformPlayer->nativeSizeChanged(size);
461 }
else if (context == AVFMediaPlayerObserverBufferLikelyToKeepUpContext)
463 const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
464 if (isPlaybackLikelyToKeepUp != m_bufferIsLikelyToKeepUp) {
465 m_bufferIsLikelyToKeepUp = isPlaybackLikelyToKeepUp;
466 int bufferProgress = isPlaybackLikelyToKeepUp ? 100 : 0;
468 m_platformPlayer.invokeWithPlatformPlayer(
469 [bufferProgress](AVFMediaPlayer *platformPlayer) {
470 platformPlayer->processBufferStateChange(bufferProgress);
474 else if (context == AVFMediaPlayerObserverTracksContext)
476 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
477 platformPlayer->updateTracks();
481 else if (context == AVFMediaPlayerObserverRateObservationContext) {
487 else if (context == AVFMediaPlayerObserverCurrentItemObservationContext) {
488 AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
489 if (m_playerItem != newPlayerItem)
490 m_playerItem = newPlayerItem;
491 }
else if (context == AVFMediaPlayerObserverCurrentItemDurationObservationContext) {
492 const CMTime time = [m_playerItem duration];
493 const qint64 dur =
static_cast<qint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
495 m_platformPlayer.invokeWithPlatformPlayer([dur](AVFMediaPlayer *platformPlayer) {
496 platformPlayer->processDurationChange(dur);
499 [super observeValueForKeyPath:path ofObject:object change:change context:context];
506 qDebug() << Q_FUNC_INFO;
508 m_platformPlayer.clear();
514 qDebug() << Q_FUNC_INFO;
518 m_platformPlayer.clear();
524 [m_mimeType release];
525 [m_playerLayer release];
528 self.videoTrack = nil;
532- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
534 Q_UNUSED(resourceLoader);
536 if (![loadingRequest.request.URL.scheme isEqualToString:@
"iodevice"])
539 auto result = m_platformPlayer.withPlatformPlayer([&](AVFMediaPlayer *platformPlayer) {
540 QIODevice *device = platformPlayer->mediaStream();
544 device->seek(loadingRequest.dataRequest.requestedOffset);
545 if (loadingRequest.contentInformationRequest) {
546 loadingRequest.contentInformationRequest.contentType = m_mimeType;
547 loadingRequest.contentInformationRequest.contentLength = device->size();
548 loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
551 if (loadingRequest.dataRequest) {
552 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
553 int maxBytes = qMin(32 * 1064,
int(requestedLength));
555 buffer.resize(maxBytes);
557 NSInteger submitted = 0;
558 while (submitted < requestedLength) {
559 qint64 len = device->read(buffer.data(), maxBytes);
563 [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer.constData()
569 [loadingRequest finishLoading];
575 return result.value_or(NO);
579AVFMediaPlayer::AVFMediaPlayer(QMediaPlayer *player)
581 QPlatformMediaPlayer(player),
582 m_mediaStream(
nullptr),
584 m_requestedPosition(-1),
588 m_observer = [[AVFMediaPlayerObserver alloc] initWithMediaPlayerSession:
this];
589 connect(&m_playbackTimer, &QTimer::timeout,
this, &AVFMediaPlayer::processPositionChange);
590 setVideoOutput(
new AVFVideoRendererControl(
this));
596 qDebug() << Q_FUNC_INFO;
599 [m_observer clearSession];
600 [m_observer release];
605 m_videoSink = sink ?
static_cast<
AVFVideoSink *>(sink->platformVideoSink()):
nullptr;
612 qDebug() << Q_FUNC_INFO << output;
615 if (m_videoOutput == output)
620 m_videoOutput->setLayer(
nullptr);
623 m_videoOutput = output;
625 if (m_videoOutput && state() != QMediaPlayer::StoppedState)
626 m_videoOutput->setLayer([m_observer playerLayer]);
632 qDebug() << Q_FUNC_INFO;
634 AVAsset *currentAsset = [[m_observer playerItem] asset];
645 return m_mediaStream;
648static void setURL(AVFMediaPlayerObserver *observer,
const QUrl &url,
const QString &mimeType = QString())
650 QUrl resolvedUrl = url;
652 if (url.isLocalFile() && !QDir::isAbsolutePath(url.path()))
653 resolvedUrl = QUrl::fromLocalFile(QFileInfo(url.path()).absoluteFilePath());
654 NSURL *nsurl = resolvedUrl.toNSURL();
655 [observer setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
662 qDebug() << Q_FUNC_INFO << content.request().url();
665 [m_observer unloadMedia];
667 m_resources = content;
670 m_requestedPosition = -1;
671 orientationChanged(QtVideo::Rotation::None,
false);
672 positionChanged(position());
673 if (m_duration != 0) {
677 if (!m_metaData.isEmpty()) {
681 resetBufferProgress();
682 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
684 nativeTracks[i].clear();
688 if (!m_mediaStream && content.isEmpty()) {
689 seekableChanged(
false);
690 audioAvailableChanged(
false);
691 videoAvailableChanged(
false);
693 mediaStatusChanged(QMediaPlayer::NoMedia);
694 stateChanged(QMediaPlayer::StoppedState);
699 mediaStatusChanged(QMediaPlayer::LoadingMedia);
704 if (m_mediaStream->size())
709 setURL(m_observer, m_resources);
712 stateChanged(QMediaPlayer::StoppedState);
717 AVPlayerItem *playerItem = [m_observer playerItem];
719 if (m_requestedPosition != -1)
720 return m_requestedPosition;
725 CMTime time = [playerItem currentTime];
726 return static_cast<quint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
732 qDebug() << Q_FUNC_INFO;
740 qDebug() << Q_FUNC_INFO;
742 return m_bufferProgress/100.;
747 AVPlayerItem *playerItem = [m_observer playerItem];
752 if (state() == QMediaPlayer::StoppedState)
755 QMediaTimeRange timeRanges;
757 NSArray *ranges = [playerItem loadedTimeRanges];
758 for (NSValue *timeRange in ranges) {
759 CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
760 qint64 startTime = qint64(
float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
761 timeRanges.addInterval(startTime, startTime + qint64(
float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
773 if (m_audioOutput == output)
776 m_audioOutput->q->disconnect(
this);
777 m_audioOutput = output;
785 setMuted(m_audioOutput ? m_audioOutput->muted :
true);
786 setVolume(m_audioOutput ? m_audioOutput->volume : 1.);
797 qDebug() << Q_FUNC_INFO << rate;
800 if (QtPrivate::fuzzyCompare(m_rate, rate))
805 AVPlayer *player = [m_observer player];
806 if (player && state() == QMediaPlayer::PlayingState)
807 [player setRate:m_rate];
809 playbackRateChanged(m_rate);
815 qDebug() << Q_FUNC_INFO << pos;
818 if (pos == position())
821 AVPlayerItem *playerItem = [m_observer playerItem];
823 m_requestedPosition = pos;
824 positionChanged(m_requestedPosition);
829 if (m_requestedPosition != -1) {
830 m_requestedPosition = -1;
831 positionChanged(position());
836 pos = qMax(qint64(0), pos);
838 pos = qMin(pos, duration());
839 m_requestedPosition = pos;
841 CMTime newTime = [playerItem currentTime];
842 newTime.value = (pos / 1000.0f) * newTime.timescale;
843 [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero
844 completionHandler:^(BOOL finished) {
846 m_requestedPosition = -1;
849 positionChanged(pos);
852 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
853 QMediaPlayer::MediaStatus newMediaStatus = (state() == QMediaPlayer::PausedState)
854 ? QMediaPlayer::BufferedMedia
855 : QMediaPlayer::LoadedMedia;
856 mediaStatusChanged(newMediaStatus);
863 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
866 if (mediaStatus() == QMediaPlayer::NoMedia || mediaStatus() == QMediaPlayer::InvalidMedia)
869 if (state() == QMediaPlayer::PlayingState)
872 if (state() != QMediaPlayer::PausedState)
875 if (m_videoOutput && m_videoSink)
876 m_videoOutput->setLayer([m_observer playerLayer]);
879 if (mediaStatus() == QMediaPlayer::EndOfMedia)
882 if (mediaStatus() == QMediaPlayer::LoadedMedia
883 || mediaStatus() == QMediaPlayer::BufferedMedia) {
885 [[m_observer player] setRate:m_rate];
890 stateChanged(QMediaPlayer::PlayingState);
891 m_playbackTimer.start(100);
897 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
900 if (mediaStatus() == QMediaPlayer::NoMedia || mediaStatus() == QMediaPlayer::InvalidMedia)
903 if (state() == QMediaPlayer::PausedState)
906 stateChanged(QMediaPlayer::PausedState);
908 if (m_videoOutput && m_videoSink)
909 m_videoOutput->setLayer([m_observer playerLayer]);
911 [[m_observer player] pause];
914 if (mediaStatus() == QMediaPlayer::EndOfMedia)
917 positionChanged(position());
918 m_playbackTimer.stop();
924 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
927 if (state() == QMediaPlayer::StoppedState && mediaStatus() != QMediaPlayer::EndOfMedia)
931 [[m_observer player] pause];
935 m_videoOutput->setLayer(
nullptr);
937 resetBufferProgress();
939 if (mediaStatus() == QMediaPlayer::BufferedMedia || mediaStatus() == QMediaPlayer::EndOfMedia)
940 mediaStatusChanged(QMediaPlayer::LoadedMedia);
942 stateChanged(QMediaPlayer::StoppedState);
943 m_playbackTimer.stop();
949 qDebug() << Q_FUNC_INFO << volume;
952 AVPlayer *player = [m_observer player];
954 player.volume = volume;
960 qDebug() << Q_FUNC_INFO << muted;
963 AVPlayer *player = [m_observer player];
965 player.muted = muted;
971 AVPlayer *player = [m_observer player];
975 if (!m_audioOutput || m_audioOutput->device.id().isEmpty()) {
978 player.audioOutputDeviceUniqueID = nil;
980 NSString *str = QString::fromUtf8(m_audioOutput->device.id()).toNSString();
981 player.audioOutputDeviceUniqueID = str;
989 positionChanged(duration());
991 [[m_observer player] setRate:m_rate];
997 qDebug() << Q_FUNC_INFO;
999 positionChanged(position());
1002 m_videoOutput->setLayer(
nullptr);
1004 resetBufferProgress();
1006 stateChanged(QMediaPlayer::StoppedState);
1007 mediaStatusChanged(QMediaPlayer::EndOfMedia);
1012 AVPlayerStatus currentStatus = [[m_observer player] status];
1015 qDebug() << Q_FUNC_INFO << currentStatus <<
", " << mediaStatus() <<
", " << newState;
1018 if (mediaStatus() == QMediaPlayer::NoMedia)
1021 if (currentStatus == AVPlayerStatusReadyToPlay) {
1023 AVPlayerItem *playerItem = [m_observer playerItem];
1025 applyPitchCompensation(m_pitchCompensationEnabled);
1028 m_metaData = AVFMetaData::fromAsset(playerItem.asset);
1033 seekableChanged([[playerItem seekableTimeRanges] count] > 0);
1036 AVPlayerLayer *playerLayer = [m_observer playerLayer];
1037 if (m_observer.videoTrack && playerLayer) {
1038 if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
1039 playerLayer.bounds = CGRectMake(0.0f, 0.0f,
1040 m_observer.videoTrack.assetTrack.naturalSize.width,
1041 m_observer.videoTrack.assetTrack.naturalSize.height);
1045 if (m_requestedPosition != -1)
1046 setPosition(m_requestedPosition);
1049 QMediaPlayer::MediaStatus newStatus = (newState != QMediaPlayer::StoppedState)
1050 ? QMediaPlayer::BufferedMedia
1051 : QMediaPlayer::LoadedMedia;
1053 if (newStatus != mediaStatus()) {
1054 if (newStatus == QMediaPlayer::BufferedMedia
1055 && mediaStatus() == QMediaPlayer::LoadingMedia) {
1057 mediaStatusChanged(QMediaPlayer::LoadedMedia);
1058 mediaStatusChanged(QMediaPlayer::BufferingMedia);
1059 }
else if (newStatus == QMediaPlayer::BufferedMedia
1060 && mediaStatus() == QMediaPlayer::LoadedMedia) {
1061 mediaStatusChanged(QMediaPlayer::BufferingMedia);
1063 mediaStatusChanged(newStatus);
1067 if (newState == QMediaPlayer::PlayingState && [m_observer player]) {
1069 [[m_observer player] setRate:m_rate];
1070 m_playbackTimer.start();
1083 stateChanged(QMediaPlayer::StoppedState);
1088 if (state() == QMediaPlayer::StoppedState)
1091 if (bufferProgress == m_bufferProgress)
1094 auto status = mediaStatus();
1096 if (!bufferProgress) {
1097 status = QMediaPlayer::StalledMedia;
1098 }
else if (status == QMediaPlayer::StalledMedia) {
1099 status = QMediaPlayer::BufferedMedia;
1101 if (state() == QMediaPlayer::PlayingState) {
1102 [[m_observer player] setRate:m_rate];
1103 m_playbackTimer.start();
1107 mediaStatusChanged(status);
1109 m_bufferProgress = bufferProgress;
1110 bufferProgressChanged(bufferProgress / 100.);
1115 if (duration == m_duration)
1118 m_duration = duration;
1119 durationChanged(duration);
1124 if (state() == QMediaPlayer::StoppedState)
1127 positionChanged(position());
1132 if (m_requestedPosition != -1) {
1133 m_requestedPosition = -1;
1134 positionChanged(position());
1137 setInvalidMediaWithError(errorCode, tr(
"Failed to load media"));
1148 if (!m_resources.isEmpty())
1149 suffix = QFileInfo(m_resources.path()).suffix();
1150 if (suffix.isEmpty() && m_mediaStream)
1151 suffix = QMimeDatabase().mimeTypeForData(m_mediaStream).preferredSuffix();
1152 const QString url = QStringLiteral(
"iodevice:///iodevice.") + suffix;
1153 setURL(m_observer, QUrl(url), suffix);
1158 resetStream(
nullptr);
1163 bool firstLoad =
true;
1164 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
1165 if (tracks[i].count())
1168 nativeTracks[i].clear();
1170 bool hasAudio =
false;
1171 bool hasVideo =
false;
1172 AVPlayerItem *playerItem = [m_observer playerItem];
1175 NSArray *tracks = playerItem.tracks;
1176 for (AVPlayerItemTrack *track in tracks) {
1177 AVAssetTrack *assetTrack = track.assetTrack;
1180 if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
1181 qtTrack = QPlatformMediaPlayer::AudioStream;
1183 }
else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
1184 qtTrack = QPlatformMediaPlayer::VideoStream;
1186 if (m_observer.videoTrack != track) {
1187 m_observer.videoTrack = track;
1188 bool isMirrored =
false;
1189 QtVideo::Rotation orientation = QtVideo::Rotation::None;
1190 videoOrientationForAssetTrack(assetTrack, orientation, isMirrored);
1191 orientationChanged(orientation, isMirrored);
1194 else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
1195 qtTrack = QPlatformMediaPlayer::SubtitleStream;
1197 if (qtTrack != -1) {
1198 QMediaMetaData metaData = AVFMetaData::fromAssetTrack(assetTrack);
1199 this->tracks[qtTrack].append(metaData);
1200 nativeTracks[qtTrack].append(track);
1206 setActiveTrack(SubtitleStream, -1);
1208 audioAvailableChanged(hasAudio);
1209 videoAvailableChanged(hasVideo);
1215 const auto &t = nativeTracks[type];
1216 if (type == QPlatformMediaPlayer::SubtitleStream) {
1219 AVPlayerItem *playerItem = m_observer.m_playerItem;
1221 AVAsset *asset = playerItem.asset;
1224#if defined(Q_OS_VISIONOS)
1225 [asset loadMediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible
1226 completionHandler:[=](AVMediaSelectionGroup *group, NSError *error) {
1230 auto *options = group.options;
1232 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1235 AVMediaSelectionGroup *group = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1238 auto *options = group.options;
1240 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1244 for (
int i = 0; i < t.count(); ++i)
1245 t.at(i).enabled = (i == index);
1246 activeTracksChanged();
1251 const auto &t = nativeTracks[type];
1252 for (
int i = 0; i < t.count(); ++i)
1253 if (t.at(i).enabled)
1260 return nativeTracks[type].count();
1263QMediaMetaData
AVFMediaPlayer::trackMetaData(QPlatformMediaPlayer::TrackType type,
int trackNumber)
1265 const auto &t = tracks[type];
1266 if (trackNumber < 0 || trackNumber >= t.count())
1267 return QMediaMetaData();
1268 return t.at(trackNumber);
1273 if (m_mediaStream) {
1278 m_mediaStream = stream;
1280 if (m_mediaStream) {
1288 AVPlayerItem *playerItem = [m_observer playerItem];
1291 playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmSpectral;
1293 playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed;
1299 if (m_bufferProgress != 0) {
1300 m_bufferProgress = 0;
1301 bufferProgressChanged(0);
1309 m_videoSink->setNativeSize(size);
1312void AVFMediaPlayer::orientationChanged(QtVideo::Rotation rotation,
bool mirrored)
1317 m_videoOutput->setVideoRotation(rotation);
1322 QtVideo::Rotation &angle,
1325 angle = QtVideo::Rotation::None;
1328 CGAffineTransform transform = videoTrack.preferredTransform;
1329 if (CGAffineTransformIsIdentity(transform))
1333 qreal det = transform.a * transform.d - transform.b * transform.c;
1334 mirrored = (det < 0.0);
1338 qreal ra = mirrored ? -transform.a : transform.a;
1339 qreal rb = mirrored ? -transform.b : transform.b;
1341 qreal degrees = qRadiansToDegrees(qAtan2(rb, ra));
1345 if (QtPrivate::fuzzyCompare(degrees, qreal(90))
1346 || QtPrivate::fuzzyCompare(degrees, qreal(-270))) {
1347 angle = QtVideo::Rotation::Clockwise90;
1348 }
else if (QtPrivate::fuzzyCompare(degrees, qreal(-90))
1349 || QtPrivate::fuzzyCompare(degrees, qreal(270))) {
1350 angle = QtVideo::Rotation::Clockwise270;
1351 }
else if (QtPrivate::fuzzyCompare(degrees, qreal(180))
1352 || QtPrivate::fuzzyCompare(degrees, qreal(-180))) {
1353 angle = QtVideo::Rotation::Clockwise180;
1360 if (m_pitchCompensationEnabled == enabled)
1363 applyPitchCompensation(enabled);
1365 m_pitchCompensationEnabled = enabled;
1366 pitchCompensationChanged(enabled);
1371 return m_pitchCompensationEnabled;
1377 return QPlatformMediaPlayer::PitchCompensationAvailability::Available;
1380#include "moc_avfmediaplayer_p.cpp"
void setVideoMirrored(bool mirrored)
void setVideoSink(AVFVideoSink *sink)