10#include "private/qplatformaudiooutput_p.h"
14#include <QtCore/qmath.h>
15#include <QtCore/qmutex.h>
17#import <AVFoundation/AVFoundation.h>
51- (
void)
setURL:(NSURL *)url mimeType:(NSString *)mimeType;
53- (
void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys;
54- (
void) assetFailedToPrepareForPlayback:(NSError *)error;
55- (
void) playerItemDidReachEnd:(NSNotification *)notification;
56- (
void) observeValueForKeyPath:(NSString *)keyPath ofObject:(
id)object
57 change:(NSDictionary *)change context:(
void *)context;
60- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
65static unsigned sessionActivationCount;
88- (
void)setSessionActive:(BOOL)active
96 if (!sessionActivationCount)
97 [AVAudioSession.sharedInstance setActive:YES error:nil];
98 ++sessionActivationCount;
101 if (!sessionActivationCount || !m_activated) {
102 qWarning(
"Unbalanced audio session deactivation, ignoring.");
105 --sessionActivationCount;
107 if (!sessionActivationCount)
108 [AVAudioSession.sharedInstance setActive:NO error:nil];
115 if (!(self = [super
init]))
122 [m_playerLayer retain];
123 m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
128- (
void)
setURL:(NSURL *)url mimeType:(NSString *)mimeType
133 [m_mimeType release];
147 BOOL isAccessing = [m_URL startAccessingSecurityScopedResource];
149 __block AVURLAsset *asset = [[AVURLAsset URLAssetWithURL:m_URL options:nil] retain];
150 [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
152 __block NSArray *requestedKeys = [[NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil] retain];
157 [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
159 dispatch_async( dispatch_get_main_queue(),
163 [m_URL stopAccessingSecurityScopedResource];
165 [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
167 [requestedKeys release];
177 [m_playerItem removeObserver:self forKeyPath:@"presentationSize"];
178 [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY];
179 [m_playerItem removeObserver:self forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY];
180 [m_playerItem removeObserver:self forKeyPath:AVF_TRACKS_KEY];
182 [[NSNotificationCenter defaultCenter] removeObserver:self
183 name:AVPlayerItemDidPlayToEndTimeNotification
184 object:m_playerItem];
188 [m_player setRate:0.0];
189 [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY];
190 [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
191 [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
198 [
self setSessionActive:NO];
202- (
void) prepareToPlayAsset:(AVURLAsset *)asset
203 withKeys:(NSArray *)requestedKeys
209 for (NSString *thisKey
in requestedKeys)
211 NSError *
error = nil;
212 AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
216 if (keyStatus == AVKeyValueStatusFailed)
218 [
self assetFailedToPrepareForPlayback:error];
228 qWarning() <<
"Asset reported to be not playable. Playback of this asset may not be possible.";
239 m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
241 qWarning() <<
"Failed to create player item";
243 NSString *localizedDescription = NSLocalizedString(
@"Item cannot be played",
@"Item cannot be played description");
244 NSString *localizedFailureReason = NSLocalizedString(
@"The assets tracks were loaded, but couldn't create player item.",
@"Item cannot be played failure reason");
245 NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
246 localizedDescription, NSLocalizedDescriptionKey,
247 localizedFailureReason, NSLocalizedFailureReasonErrorKey,
249 NSError *assetCannotBePlayedError = [NSError errorWithDomain:@"StitchedStreamPlayer" code:0 userInfo:errorDict];
251 [
self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
256 [m_playerItem addObserver:self
257 forKeyPath:AVF_STATUS_KEY
258 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
259 context:AVFMediaPlayerObserverStatusObservationContext];
261 [m_playerItem addObserver:self
262 forKeyPath:@"presentationSize"
263 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
264 context:AVFMediaPlayerObserverPresentationSizeContext];
266 [m_playerItem addObserver:self
267 forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
268 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
269 context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext];
271 [m_playerItem addObserver:self
272 forKeyPath:AVF_TRACKS_KEY
273 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
274 context:AVFMediaPlayerObserverTracksContext];
278 [[NSNotificationCenter defaultCenter] addObserver:self
279 selector:@selector(playerItemDidReachEnd:)
280 name:AVPlayerItemDidPlayToEndTimeNotification
281 object:m_playerItem];
284 m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
290 m_player.volume = (audioOutput ? audioOutput->volume : 1.);
291 m_player.muted = (audioOutput ? audioOutput->muted :
true);
300 [m_player addObserver:self
301 forKeyPath:AVF_CURRENT_ITEM_KEY
302 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
303 context:AVFMediaPlayerObserverCurrentItemObservationContext];
306 [m_player addObserver:self
307 forKeyPath:AVF_RATE_KEY
308 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
309 context:AVFMediaPlayerObserverRateObservationContext];
312 [m_player addObserver:self
313 forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
315 context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
317 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
318 [
self setSessionActive:YES];
322-(
void) assetFailedToPrepareForPlayback:(NSError *)error
328 qDebug() << [[error localizedDescription] UTF8String];
329 qDebug() << [[error localizedFailureReason] UTF8String];
330 qDebug() << [[error localizedRecoverySuggestion] UTF8String];
334- (
void) playerItemDidReachEnd:(NSNotification *)notification
341- (
void) observeValueForKeyPath:(NSString*) path
343 change:(NSDictionary*)change
344 context:(
void*)context
349 AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
354 case AVPlayerStatusUnknown:
360 case AVPlayerStatusReadyToPlay:
370 case AVPlayerStatusFailed:
372 AVPlayerItem *playerItem =
static_cast<AVPlayerItem*
>(
object);
373 [
self assetFailedToPrepareForPlayback:playerItem.error];
385 const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
389 Q_ARG(
int, isPlaybackLikelyToKeepUp ? 100 : 0));
406 AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
412 const CMTime
time = [m_playerItem duration];
413 const qint64 duration =
static_cast<qint64>(float(
time.value) / float(
time.timescale) * 1000.0f);
419 [
super observeValueForKeyPath:path ofObject:object change:change context:context];
442 [m_mimeType release];
443 [m_playerLayer release];
447- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
451 if (![loadingRequest.request.URL.scheme isEqualToString:
@"iodevice"])
458 device->seek(loadingRequest.dataRequest.requestedOffset);
459 if (loadingRequest.contentInformationRequest) {
460 loadingRequest.contentInformationRequest.contentType =
m_mimeType;
461 loadingRequest.contentInformationRequest.contentLength =
device->size();
462 loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
465 if (loadingRequest.dataRequest) {
466 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
467 int maxBytes =
qMin(32 * 1064,
int(requestedLength));
470 while (submitted < requestedLength) {
475 [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]];
480 [loadingRequest finishLoading];
494 m_requestedPosition(-1),
497 m_videoAvailable(
false),
498 m_audioAvailable(
false),
513 [static_cast<AVFMediaPlayerObserver*>(m_observer) release];
528 if (m_videoOutput ==
output)
547 AVAsset *currentAsset = [[static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem] asset];
558 return m_mediaStatus;
568 return m_mediaStream;
573 NSString *urlString = [NSString stringWithUTF8String:url.constData()];
574 NSURL *nsurl = [NSURL URLWithString:urlString];
575 [observer
setURL:nsurl
mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
589 [static_cast<AVFMediaPlayerObserver*>(m_observer) unloadMedia];
591 m_resources = content;
594 setAudioAvailable(
false);
595 setVideoAvailable(
false);
597 m_requestedPosition = -1;
600 if (m_duration != 0) {
617 if (!m_mediaStream && content.
isEmpty()) {
619 if (m_mediaStatus != oldMediaStatus)
623 if (m_state != oldState)
630 if (m_mediaStatus != oldMediaStatus)
636 if (m_mediaStream->
size())
645 if (m_state != oldState)
651 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
653 if (m_requestedPosition != -1)
654 return m_requestedPosition;
659 CMTime
time = [playerItem currentTime];
660 return static_cast<quint64>(float(
time.value) / float(
time.timescale) * 1000.0f);
676 return m_bufferProgress/100.;
679void AVFMediaPlayer::setAudioAvailable(
bool available)
681 if (m_audioAvailable == available)
684 m_audioAvailable = available;
690 return m_audioAvailable;
693void AVFMediaPlayer::setVideoAvailable(
bool available)
695 if (m_videoAvailable == available)
698 m_videoAvailable = available;
704 return m_videoAvailable;
712void AVFMediaPlayer::setSeekable(
bool seekable)
714 if (m_seekable == seekable)
717 m_seekable = seekable;
723 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
728 NSArray *ranges = [playerItem loadedTimeRanges];
729 for (NSValue *timeRange
in ranges) {
730 CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
731 qint64 startTime =
qint64(
float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
732 timeRanges.addInterval(
startTime,
startTime +
qint64(
float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
734 if (!timeRanges.isEmpty())
779 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
781 [player setRate:m_rate];
795 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
797 m_requestedPosition =
pos;
803 if (m_requestedPosition != -1) {
804 m_requestedPosition = -1;
813 m_requestedPosition =
pos;
815 CMTime newTime = [playerItem currentTime];
816 newTime.value = (
pos / 1000.0f) * newTime.timescale;
817 [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero
818 completionHandler:^(BOOL finished) {
820 m_requestedPosition = -1;
847 if (m_videoOutput && m_videoSink)
856 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
863 m_playbackTimer.
start(100);
880 if (m_videoOutput && m_videoSink)
883 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
891 m_playbackTimer.
stop();
904 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
914 m_playbackTimer.
stop();
923 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
934 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
942 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
944 player.audioOutputDeviceUniqueID = nil;
958 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
979 AVPlayerStatus currentStatus = [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] status];
988 if (currentStatus == AVPlayerStatusReadyToPlay) {
992 AVPlayerItem *playerItem = [
m_observer playerItem];
1000 setSeekable([[playerItem seekableTimeRanges]
count] > 0);
1003 AVPlayerLayer *playerLayer = [
m_observer playerLayer];
1005 if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
1006 playerLayer.bounds = CGRectMake(0.0f, 0.0f,
1007 m_observer.
videoTrack.assetTrack.naturalSize.width,
1008 m_observer.
videoTrack.assetTrack.naturalSize.height);
1012 if (m_requestedPosition != -1) {
1014 m_requestedPosition = -1;
1021 if (newStatus != m_mediaStatus)
1028 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
1029 m_playbackTimer.
start();
1050 auto status = m_mediaStatus;
1058 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
1059 m_playbackTimer.
start();
1063 if (m_mediaStatus != status)
1089 if (m_requestedPosition != -1) {
1090 m_requestedPosition = -1;
1106 resetStream(
nullptr);
1111 bool firstLoad =
true;
1118 AVPlayerItem *playerItem = [
m_observer playerItem];
1121 NSArray *
tracks = playerItem.tracks;
1122 for (AVPlayerItemTrack *track
in tracks) {
1123 AVAssetTrack *assetTrack = track.assetTrack;
1126 if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
1128 setAudioAvailable(
true);
1129 }
else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
1131 setVideoAvailable(
true);
1134 bool isMirrored =
false;
1137 orientationChanged(orientation, isMirrored);
1140 else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
1143 if (qtTrack != -1) {
1145 this->tracks[qtTrack].append(
metaData);
1165 AVAsset *asset = playerItem.asset;
1168 AVMediaSelectionGroup *
group = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1171 auto *options =
group.options;
1173 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1176 for (
int i = 0;
i <
t.count(); ++
i)
1184 for (
int i = 0;
i <
t.count(); ++
i)
1185 if (
t.at(
i).enabled)
1198 if (trackNumber < 0 || trackNumber >=
t.count())
1200 return t.at(trackNumber);
1205 if (m_mediaStream) {
1212 if (m_mediaStream) {
1225void AVFMediaPlayer::orientationChanged(
QtVideo::Rotation rotation,
bool mirrored)
1240 CGAffineTransform
transform = videoTrack.preferredTransform;
1241 if (CGAffineTransformIsIdentity(
transform))
1253 }
else if (scaleY < 0.0) {
1255 degrees = (180 + (int)degrees) % 360;
1270#include "moc_avfmediaplayer_p.cpp"
IOBluetoothDevice * device
void setVideoMirrored(bool mirrored)
void setVideoRotation(QtVideo::Rotation)
void setLayer(CALayer *layer) override
void setVideoSink(AVFVideoSink *sink)
void setNativeSize(QSize size)
QByteArray id
\qmlproperty string QtMultimedia::audioDevice::id
void mutedChanged(bool muted)
void volumeChanged(float volume)
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
\inmodule QtCore \reentrant
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
virtual qint64 size() const
For open random-access devices, this function returns the size of the device.
qsizetype count() const noexcept
void append(parameter_type t)
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
void stop()
Stops the timer.
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
QString url(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
QByteArray toEncoded(FormattingOptions options=FullyEncoded) const
Returns the encoded representation of the URL if it's valid; otherwise an empty QByteArray is returne...
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
The QVideoSink class represents a generic sink for video data.
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
#define QByteArrayLiteral(str)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError * error
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
qfloat16 qSqrt(qfloat16 f)
constexpr float qRadiansToDegrees(float radians)
constexpr const T & qMin(const T &a, const T &b)
constexpr const T & qMax(const T &a, const T &b)
constexpr T qAbs(const T &t)
#define Q_ARG(Type, data)
n void setPosition(void) \n\
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLenum GLsizei count
GLuint GLenum GLenum transform
GLsizei GLenum GLboolean sink
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
unsigned long long quint64
QT_BEGIN_NAMESPACE typedef uchar * output
QUrl url("example.com")
[constructor-url-reference]
myObject disconnect()
[26]