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];
205 __block BOOL isAccessing = [m_URL startAccessingSecurityScopedResource];
206 __block AVURLAsset *asset = [[AVURLAsset URLAssetWithURL:m_URL options:nil] retain];
207 [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
209 __block NSArray *requestedKeys = [[NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil] retain];
211 __block AVFMediaPlayerObserver *blockSelf = [self retain];
214 [asset loadValuesAsynchronouslyForKeys:requestedKeys
216 dispatch_async(dispatch_get_main_queue(), ^{
218 [m_URL stopAccessingSecurityScopedResource];
219 [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
221 [requestedKeys release];
231 [m_playerItem removeObserver:self forKeyPath:@
"presentationSize"];
232 [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY];
233 [m_playerItem removeObserver:self forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY];
234 [m_playerItem removeObserver:self forKeyPath:AVF_TRACKS_KEY];
236 [[NSNotificationCenter defaultCenter] removeObserver:self
237 name:AVPlayerItemDidPlayToEndTimeNotification
238 object:m_playerItem];
239 m_playerItem =
nullptr;
242 [m_player setRate:0.0];
243 [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY];
244 [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
245 [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
246 [m_player replaceCurrentItemWithPlayerItem:nil];
252 AVPlayer *player = m_player;
254 dispatch_async(dispatch_get_main_queue(), ^{
259 m_playerLayer.player = nil;
261 [self setSessionActive:NO];
265- (
void) prepareToPlayAsset:(AVURLAsset *)asset
266 withKeys:(NSArray *)requestedKeys
268 if (!m_platformPlayer)
272 for (NSString *thisKey in requestedKeys)
274 NSError *error = nil;
275 AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
277 qDebug() << Q_FUNC_INFO << [thisKey UTF8String] <<
" status: " << keyStatus;
279 if (keyStatus == AVKeyValueStatusFailed)
281 [self assetFailedToPrepareForPlayback:error];
288 qDebug() << Q_FUNC_INFO <<
"isPlayable: " << [asset isPlayable];
291 qWarning() <<
"Asset reported to be not playable. Playback of this asset may not be possible.";
302 m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
304 qWarning() <<
"Failed to create player item";
306 NSString *localizedDescription = NSLocalizedString(@
"Item cannot be played", @
"Item cannot be played description");
307 NSString *localizedFailureReason = NSLocalizedString(@
"The assets tracks were loaded, but couldn't create player item.", @
"Item cannot be played failure reason");
308 NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
309 localizedDescription, NSLocalizedDescriptionKey,
310 localizedFailureReason, NSLocalizedFailureReasonErrorKey,
312 NSError *assetCannotBePlayedError = [NSError errorWithDomain:@
"StitchedStreamPlayer" code:0 userInfo:errorDict];
314 [self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
319 [m_playerItem addObserver:self
320 forKeyPath:AVF_STATUS_KEY
321 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
322 context:AVFMediaPlayerObserverStatusObservationContext];
324 [m_playerItem addObserver:self
325 forKeyPath:@
"presentationSize"
326 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
327 context:AVFMediaPlayerObserverPresentationSizeContext];
329 [m_playerItem addObserver:self
330 forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
331 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
332 context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext];
334 [m_playerItem addObserver:self
335 forKeyPath:AVF_TRACKS_KEY
336 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
337 context:AVFMediaPlayerObserverTracksContext];
341 [[NSNotificationCenter defaultCenter] addObserver:self
342 selector:@selector(playerItemDidReachEnd:)
343 name:AVPlayerItemDidPlayToEndTimeNotification
344 object:m_playerItem];
347 m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
352 m_platformPlayer.withPlatformPlayer([&](AVFMediaPlayer *player) {
353 auto *audioOutput = player->m_audioOutput;
354 m_player.volume = (audioOutput ? audioOutput->volume : 1.);
355 m_player.muted = (audioOutput ? audioOutput->muted :
true);
356 player->updateAudioOutputDevice();
361 m_playerLayer.player = m_player;
366 [m_player addObserver:self
367 forKeyPath:AVF_CURRENT_ITEM_KEY
368 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
369 context:AVFMediaPlayerObserverCurrentItemObservationContext];
372 [m_player addObserver:self
373 forKeyPath:AVF_RATE_KEY
374 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
375 context:AVFMediaPlayerObserverRateObservationContext];
378 [m_player addObserver:self
379 forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
381 context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
383 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
384 [self setSessionActive:YES];
388-(
void) assetFailedToPrepareForPlayback:(NSError *)error
390 QMediaPlayer::Error errorCode = QMediaPlayer::FormatError;
392 NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
393 if (underlyingError && ![underlyingError.domain isEqualToString:AVFoundationErrorDomain])
394 errorCode = QMediaPlayer::ResourceError;
396 m_platformPlayer.invokeWithPlatformPlayer([errorCode](AVFMediaPlayer *platformPlayer) {
397 platformPlayer->processMediaLoadError(errorCode);
401 qDebug() << Q_FUNC_INFO;
402 qDebug() << [[error localizedDescription] UTF8String];
403 qDebug() << [[error localizedFailureReason] UTF8String];
404 qDebug() << [[error localizedRecoverySuggestion] UTF8String];
408- (
void) playerItemDidReachEnd:(NSNotification *)notification
410 Q_UNUSED(notification);
412 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
413 platformPlayer->processEOS();
417- (
void) observeValueForKeyPath:(NSString*) path
419 change:(NSDictionary*)change
420 context:(
void*)context
423 if (context == AVFMediaPlayerObserverStatusObservationContext)
425 AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
430 case AVPlayerStatusUnknown:
436 case AVPlayerStatusReadyToPlay:
442 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
443 platformPlayer->processLoadStateChange();
448 case AVPlayerStatusFailed:
450 AVPlayerItem *playerItem =
static_cast<AVPlayerItem*>(object);
451 [self assetFailedToPrepareForPlayback:playerItem.error];
453 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
454 platformPlayer->processLoadStateChange();
459 }
else if (context == AVFMediaPlayerObserverPresentationSizeContext) {
460 QSize size(m_playerItem.presentationSize.width, m_playerItem.presentationSize.height);
461 m_platformPlayer.invokeWithPlatformPlayer([size](AVFMediaPlayer *platformPlayer) {
462 platformPlayer->nativeSizeChanged(size);
464 }
else if (context == AVFMediaPlayerObserverBufferLikelyToKeepUpContext)
466 const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
467 if (isPlaybackLikelyToKeepUp != m_bufferIsLikelyToKeepUp) {
468 m_bufferIsLikelyToKeepUp = isPlaybackLikelyToKeepUp;
469 int bufferProgress = isPlaybackLikelyToKeepUp ? 100 : 0;
471 m_platformPlayer.invokeWithPlatformPlayer(
472 [bufferProgress](AVFMediaPlayer *platformPlayer) {
473 platformPlayer->processBufferStateChange(bufferProgress);
477 else if (context == AVFMediaPlayerObserverTracksContext)
479 m_platformPlayer.invokeWithPlatformPlayer([](AVFMediaPlayer *platformPlayer) {
480 platformPlayer->updateTracks();
484 else if (context == AVFMediaPlayerObserverRateObservationContext) {
490 else if (context == AVFMediaPlayerObserverCurrentItemObservationContext) {
491 AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
492 if (m_playerItem != newPlayerItem)
493 m_playerItem = newPlayerItem;
494 }
else if (context == AVFMediaPlayerObserverCurrentItemDurationObservationContext) {
495 const CMTime time = [m_playerItem duration];
496 const qint64 dur =
static_cast<qint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
498 m_platformPlayer.invokeWithPlatformPlayer([dur](AVFMediaPlayer *platformPlayer) {
499 platformPlayer->processDurationChange(dur);
502 [super observeValueForKeyPath:path ofObject:object change:change context:context];
509 qDebug() << Q_FUNC_INFO;
511 m_platformPlayer.clear();
514- (
void)notifySeekComplete
516 m_platformPlayer.withPlatformPlayer([](AVFMediaPlayer *player) {
517 player->seekCompleted();
524 qDebug() << Q_FUNC_INFO;
528 m_platformPlayer.clear();
534 [m_mimeType release];
535 [m_playerLayer release];
538 self.videoTrack = nil;
542- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
544 Q_UNUSED(resourceLoader);
546 if (![loadingRequest.request.URL.scheme isEqualToString:@
"iodevice"])
549 auto result = m_platformPlayer.withPlatformPlayer([&](AVFMediaPlayer *platformPlayer) {
550 QIODevice *device = platformPlayer->mediaStream();
554 device->seek(loadingRequest.dataRequest.requestedOffset);
555 if (loadingRequest.contentInformationRequest) {
556 loadingRequest.contentInformationRequest.contentType = m_mimeType;
557 loadingRequest.contentInformationRequest.contentLength = device->size();
558 loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
561 if (loadingRequest.dataRequest) {
562 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
563 int maxBytes = qMin(32 * 1024,
int(requestedLength));
565 buffer.resize(maxBytes);
567 NSInteger submitted = 0;
568 while (submitted < requestedLength) {
569 qint64 len = device->read(buffer.data(), maxBytes);
573 [loadingRequest.dataRequest respondWithData:buffer.toNSData()];
578 [loadingRequest finishLoading];
584 return result.value_or(NO);
588AVFMediaPlayer::AVFMediaPlayer(QMediaPlayer *player)
590 QPlatformMediaPlayer(player),
591 m_mediaStream(
nullptr),
593 m_requestedPosition(-1),
597 m_observer = [[AVFMediaPlayerObserver alloc] initWithMediaPlayerSession:
this];
598 connect(&m_playbackTimer, &QTimer::timeout,
this, &AVFMediaPlayer::processPositionChange);
599 setVideoOutput(
new AVFVideoRendererControl(
this));
605 qDebug() << Q_FUNC_INFO;
610 [m_observer unloadMedia];
613 [m_observer clearSession];
614 [m_observer release];
619 m_videoSink = sink ?
static_cast<
AVFVideoSink *>(sink->platformVideoSink()):
nullptr;
626 qDebug() << Q_FUNC_INFO << output;
629 if (m_videoOutput == output)
634 m_videoOutput->setLayer(
nullptr);
637 m_videoOutput = output;
639 if (m_videoOutput && state() != QMediaPlayer::StoppedState)
640 m_videoOutput->setLayer([m_observer playerLayer]);
646 qDebug() << Q_FUNC_INFO;
648 AVAsset *currentAsset = [[m_observer playerItem] asset];
659 return m_mediaStream;
662static void setURL(AVFMediaPlayerObserver *observer,
const QUrl &url,
const QString &mimeType = QString())
664 QUrl resolvedUrl = url;
666 if (url.isLocalFile() && !QDir::isAbsolutePath(url.path()))
667 resolvedUrl = QUrl::fromLocalFile(QFileInfo(url.path()).absoluteFilePath());
668 NSURL *nsurl = resolvedUrl.toNSURL();
669 [observer setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
676 qDebug() << Q_FUNC_INFO << content.request().url();
679 [m_observer unloadMedia];
681 m_resources = content;
684 m_requestedPosition = -1;
685 orientationChanged(QtVideo::Rotation::None,
false);
686 positionChanged(position());
687 if (m_duration != 0) {
691 if (!m_metaData.isEmpty()) {
695 resetBufferProgress();
696 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
698 nativeTracks[i].clear();
702 if (!m_mediaStream && content.isEmpty()) {
703 seekableChanged(
false);
704 audioAvailableChanged(
false);
705 videoAvailableChanged(
false);
707 mediaStatusChanged(QMediaPlayer::NoMedia);
708 stateChanged(QMediaPlayer::StoppedState);
713 mediaStatusChanged(QMediaPlayer::LoadingMedia);
718 if (m_mediaStream->size())
723 setURL(m_observer, m_resources);
726 stateChanged(QMediaPlayer::StoppedState);
731 AVPlayerItem *playerItem = [m_observer playerItem];
733 if (m_requestedPosition != -1)
734 return m_requestedPosition;
739 CMTime time = [playerItem currentTime];
740 return static_cast<quint64>(
float(time.value) /
float(time.timescale) * 1000.0f);
746 qDebug() << Q_FUNC_INFO;
754 qDebug() << Q_FUNC_INFO;
756 return m_bufferProgress/100.;
761 AVPlayerItem *playerItem = [m_observer playerItem];
766 if (state() == QMediaPlayer::StoppedState)
769 QMediaTimeRange timeRanges;
771 NSArray *ranges = [playerItem loadedTimeRanges];
772 for (NSValue *timeRange in ranges) {
773 CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
774 qint64 startTime = qint64(
float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
775 timeRanges.addInterval(startTime, startTime + qint64(
float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
787 if (m_audioOutput == output)
790 m_audioOutput->q->disconnect(
this);
791 m_audioOutput = output;
799 setMuted(m_audioOutput ? m_audioOutput->muted :
true);
800 setVolume(m_audioOutput ? m_audioOutput->volume : 1.);
811 qDebug() << Q_FUNC_INFO << rate;
814 if (QtPrivate::fuzzyCompare(m_rate, rate))
819 AVPlayer *player = [m_observer player];
820 if (player && state() == QMediaPlayer::PlayingState)
821 [player setRate:m_rate];
823 playbackRateChanged(m_rate);
829 qDebug() << Q_FUNC_INFO << pos;
832 if (pos == position())
835 AVPlayerItem *playerItem = [m_observer playerItem];
837 m_requestedPosition = pos;
838 positionChanged(m_requestedPosition);
843 if (m_requestedPosition != -1) {
844 m_requestedPosition = -1;
845 positionChanged(position());
850 pos = qMax(qint64(0), pos);
852 pos = qMin(pos, duration());
853 m_requestedPosition = pos;
855 CMTime newTime = [playerItem currentTime];
856 newTime.value = (pos / 1000.0f) * newTime.timescale;
857 AVFMediaPlayerObserver *observer = m_observer;
858 [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero
859 completionHandler:^(BOOL finished) {
861 [observer notifySeekComplete];
864 positionChanged(pos);
867 if (mediaStatus() == QMediaPlayer::EndOfMedia) {
868 QMediaPlayer::MediaStatus newMediaStatus = (state() == QMediaPlayer::PausedState)
869 ? QMediaPlayer::BufferedMedia
870 : QMediaPlayer::LoadedMedia;
871 mediaStatusChanged(newMediaStatus);
878 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
881 if (mediaStatus() == QMediaPlayer::NoMedia || mediaStatus() == QMediaPlayer::InvalidMedia)
884 if (state() == QMediaPlayer::PlayingState)
887 if (state() != QMediaPlayer::PausedState)
890 if (m_videoOutput && m_videoSink)
891 m_videoOutput->setLayer([m_observer playerLayer]);
894 if (mediaStatus() == QMediaPlayer::EndOfMedia)
897 if (mediaStatus() == QMediaPlayer::LoadedMedia
898 || mediaStatus() == QMediaPlayer::BufferedMedia) {
900 [[m_observer player] setRate:m_rate];
905 stateChanged(QMediaPlayer::PlayingState);
906 m_playbackTimer.start(100);
912 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
915 if (mediaStatus() == QMediaPlayer::NoMedia || mediaStatus() == QMediaPlayer::InvalidMedia)
918 if (state() == QMediaPlayer::PausedState)
921 stateChanged(QMediaPlayer::PausedState);
923 if (m_videoOutput && m_videoSink)
924 m_videoOutput->setLayer([m_observer playerLayer]);
926 [[m_observer player] pause];
929 if (mediaStatus() == QMediaPlayer::EndOfMedia)
932 positionChanged(position());
933 m_playbackTimer.stop();
939 qDebug() << Q_FUNC_INFO <<
"currently: " << state();
942 if (state() == QMediaPlayer::StoppedState && mediaStatus() != QMediaPlayer::EndOfMedia)
946 [[m_observer player] pause];
950 m_videoOutput->setLayer(
nullptr);
952 resetBufferProgress();
954 if (mediaStatus() == QMediaPlayer::BufferedMedia || mediaStatus() == QMediaPlayer::EndOfMedia)
955 mediaStatusChanged(QMediaPlayer::LoadedMedia);
957 stateChanged(QMediaPlayer::StoppedState);
958 m_playbackTimer.stop();
964 qDebug() << Q_FUNC_INFO << volume;
967 AVPlayer *player = [m_observer player];
969 player.volume = volume;
975 qDebug() << Q_FUNC_INFO << muted;
978 AVPlayer *player = [m_observer player];
980 player.muted = muted;
986 AVPlayer *player = [m_observer player];
990 if (!m_audioOutput || m_audioOutput->device.id().isEmpty()) {
993 player.audioOutputDeviceUniqueID = nil;
995 NSString *str = QString::fromUtf8(m_audioOutput->device.id()).toNSString();
996 player.audioOutputDeviceUniqueID = str;
1004 positionChanged(duration());
1006 [[m_observer player] setRate:m_rate];
1012 qDebug() << Q_FUNC_INFO;
1014 positionChanged(position());
1017 m_videoOutput->setLayer(
nullptr);
1019 resetBufferProgress();
1021 stateChanged(QMediaPlayer::StoppedState);
1022 mediaStatusChanged(QMediaPlayer::EndOfMedia);
1027 AVPlayerStatus currentStatus = [[m_observer player] status];
1030 qDebug() << Q_FUNC_INFO << currentStatus <<
", " << mediaStatus() <<
", " << newState;
1033 if (mediaStatus() == QMediaPlayer::NoMedia)
1036 if (currentStatus == AVPlayerStatusReadyToPlay) {
1038 AVPlayerItem *playerItem = [m_observer playerItem];
1040 applyPitchCompensation(m_pitchCompensationEnabled);
1043 m_metaData = AVFMetaData::fromAsset(playerItem.asset);
1048 seekableChanged([[playerItem seekableTimeRanges] count] > 0);
1051 AVPlayerLayer *playerLayer = [m_observer playerLayer];
1052 if (m_observer.videoTrack && playerLayer) {
1053 if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
1054 playerLayer.bounds = CGRectMake(0.0f, 0.0f,
1055 m_observer.videoTrack.assetTrack.naturalSize.width,
1056 m_observer.videoTrack.assetTrack.naturalSize.height);
1060 if (m_requestedPosition != -1)
1061 setPosition(m_requestedPosition);
1064 QMediaPlayer::MediaStatus newStatus = (newState != QMediaPlayer::StoppedState)
1065 ? QMediaPlayer::BufferedMedia
1066 : QMediaPlayer::LoadedMedia;
1068 if (newStatus != mediaStatus()) {
1069 if (newStatus == QMediaPlayer::BufferedMedia
1070 && mediaStatus() == QMediaPlayer::LoadingMedia) {
1072 mediaStatusChanged(QMediaPlayer::LoadedMedia);
1073 mediaStatusChanged(QMediaPlayer::BufferingMedia);
1074 }
else if (newStatus == QMediaPlayer::BufferedMedia
1075 && mediaStatus() == QMediaPlayer::LoadedMedia) {
1076 mediaStatusChanged(QMediaPlayer::BufferingMedia);
1078 mediaStatusChanged(newStatus);
1082 if (newState == QMediaPlayer::PlayingState && [m_observer player]) {
1084 [[m_observer player] setRate:m_rate];
1085 m_playbackTimer.start();
1098 stateChanged(QMediaPlayer::StoppedState);
1103 if (state() == QMediaPlayer::StoppedState)
1106 if (bufferProgress == m_bufferProgress)
1109 auto status = mediaStatus();
1111 if (!bufferProgress) {
1112 status = QMediaPlayer::StalledMedia;
1113 }
else if (status == QMediaPlayer::StalledMedia) {
1114 status = QMediaPlayer::BufferedMedia;
1116 if (state() == QMediaPlayer::PlayingState) {
1117 [[m_observer player] setRate:m_rate];
1118 m_playbackTimer.start();
1122 mediaStatusChanged(status);
1124 m_bufferProgress = bufferProgress;
1125 bufferProgressChanged(bufferProgress / 100.);
1130 if (duration == m_duration)
1133 m_duration = duration;
1134 durationChanged(duration);
1139 if (state() == QMediaPlayer::StoppedState)
1142 positionChanged(position());
1147 if (m_requestedPosition != -1) {
1148 m_requestedPosition = -1;
1149 positionChanged(position());
1152 setInvalidMediaWithError(errorCode, tr(
"Failed to load media"));
1157 m_requestedPosition = -1;
1168 if (!m_resources.isEmpty())
1169 suffix = QFileInfo(m_resources.path()).suffix();
1170 if (suffix.isEmpty() && m_mediaStream)
1171 suffix = QMimeDatabase().mimeTypeForData(m_mediaStream).preferredSuffix();
1172 const QString url = QStringLiteral(
"iodevice:///iodevice.") + suffix;
1173 setURL(m_observer, QUrl(url), suffix);
1178 resetStream(
nullptr);
1183 bool firstLoad =
true;
1184 for (
int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
1185 if (tracks[i].count())
1188 nativeTracks[i].clear();
1190 bool hasAudio =
false;
1191 bool hasVideo =
false;
1192 AVPlayerItem *playerItem = [m_observer playerItem];
1195 NSArray *tracks = playerItem.tracks;
1196 for (AVPlayerItemTrack *track in tracks) {
1197 AVAssetTrack *assetTrack = track.assetTrack;
1200 if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
1201 qtTrack = QPlatformMediaPlayer::AudioStream;
1203 }
else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
1204 qtTrack = QPlatformMediaPlayer::VideoStream;
1206 if (m_observer.videoTrack != track) {
1207 m_observer.videoTrack = track;
1208 bool isMirrored =
false;
1209 QtVideo::Rotation orientation = QtVideo::Rotation::None;
1210 videoOrientationForAssetTrack(assetTrack, orientation, isMirrored);
1211 orientationChanged(orientation, isMirrored);
1214 else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
1215 qtTrack = QPlatformMediaPlayer::SubtitleStream;
1217 if (qtTrack != -1) {
1218 QMediaMetaData metaData = AVFMetaData::fromAssetTrack(assetTrack);
1219 this->tracks[qtTrack].append(metaData);
1220 nativeTracks[qtTrack].append(track);
1226 setActiveTrack(SubtitleStream, -1);
1228 audioAvailableChanged(hasAudio);
1229 videoAvailableChanged(hasVideo);
1235 const auto &t = nativeTracks[type];
1236 if (type == QPlatformMediaPlayer::SubtitleStream) {
1239 AVPlayerItem *playerItem = m_observer.m_playerItem;
1241 AVAsset *asset = playerItem.asset;
1244 [asset loadMediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible
1245 completionHandler:[=](AVMediaSelectionGroup *group,
1249 auto *options = group.options;
1251 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1255 for (
int i = 0; i < t.count(); ++i)
1256 t.at(i).enabled = (i == index);
1257 activeTracksChanged();
1262 const auto &t = nativeTracks[type];
1263 for (
int i = 0; i < t.count(); ++i)
1264 if (t.at(i).enabled)
1271 return nativeTracks[type].count();
1276 const auto &t = tracks[type];
1277 if (trackNumber < 0 || trackNumber >= t.count())
1278 return QMediaMetaData();
1279 return t.at(trackNumber);
1284 if (m_mediaStream) {
1289 m_mediaStream = stream;
1291 if (m_mediaStream) {
1299 AVPlayerItem *playerItem = [m_observer playerItem];
1302 playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmSpectral;
1304 playerItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed;
1310 if (m_bufferProgress != 0) {
1311 m_bufferProgress = 0;
1312 bufferProgressChanged(0);
1320 m_videoSink->setNativeSize(size);
1323void AVFMediaPlayer::orientationChanged(QtVideo::Rotation rotation,
bool mirrored)
1328 m_videoOutput->setVideoRotation(rotation);
1333 QtVideo::Rotation &angle,
1336 angle = QtVideo::Rotation::None;
1339 CGAffineTransform transform = videoTrack.preferredTransform;
1340 if (CGAffineTransformIsIdentity(transform))
1344 qreal det = transform.a * transform.d - transform.b * transform.c;
1345 mirrored = (det < 0.0);
1349 qreal ra = mirrored ? -transform.a : transform.a;
1350 qreal rb = mirrored ? -transform.b : transform.b;
1352 qreal degrees = qRadiansToDegrees(qAtan2(rb, ra));
1356 if (QtPrivate::fuzzyCompare(degrees, qreal(90))
1357 || QtPrivate::fuzzyCompare(degrees, qreal(-270))) {
1358 angle = QtVideo::Rotation::Clockwise90;
1359 }
else if (QtPrivate::fuzzyCompare(degrees, qreal(-90))
1360 || QtPrivate::fuzzyCompare(degrees, qreal(270))) {
1361 angle = QtVideo::Rotation::Clockwise270;
1362 }
else if (QtPrivate::fuzzyCompare(degrees, qreal(180))
1363 || QtPrivate::fuzzyCompare(degrees, qreal(-180))) {
1364 angle = QtVideo::Rotation::Clockwise180;
1371 if (m_pitchCompensationEnabled == enabled)
1374 applyPitchCompensation(enabled);
1376 m_pitchCompensationEnabled = enabled;
1377 pitchCompensationChanged(enabled);
1382 return m_pitchCompensationEnabled;
1388 return QPlatformMediaPlayer::PitchCompensationAvailability::Available;
1391#include "moc_avfmediaplayer_p.cpp"
void setVideoMirrored(bool mirrored)
void setVideoSink(AVFVideoSink *sink)