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);
301 [m_player addObserver:self
302 forKeyPath:AVF_CURRENT_ITEM_KEY
303 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
304 context:AVFMediaPlayerObserverCurrentItemObservationContext];
307 [m_player addObserver:self
308 forKeyPath:AVF_RATE_KEY
309 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
310 context:AVFMediaPlayerObserverRateObservationContext];
313 [m_player addObserver:self
314 forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
316 context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
318 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
319 [
self setSessionActive:YES];
323-(
void) assetFailedToPrepareForPlayback:(NSError *)error
329 qDebug() << [[error localizedDescription] UTF8String];
330 qDebug() << [[error localizedFailureReason] UTF8String];
331 qDebug() << [[error localizedRecoverySuggestion] UTF8String];
335- (
void) playerItemDidReachEnd:(NSNotification *)notification
342- (
void) observeValueForKeyPath:(NSString*) path
344 change:(NSDictionary*)change
345 context:(
void*)context
350 AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
355 case AVPlayerStatusUnknown:
361 case AVPlayerStatusReadyToPlay:
371 case AVPlayerStatusFailed:
373 AVPlayerItem *playerItem =
static_cast<AVPlayerItem*
>(
object);
374 [
self assetFailedToPrepareForPlayback:playerItem.error];
386 const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
390 Q_ARG(
int, isPlaybackLikelyToKeepUp ? 100 : 0));
407 AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
413 const CMTime
time = [m_playerItem duration];
414 const qint64 duration =
static_cast<qint64>(float(
time.value) / float(
time.timescale) * 1000.0f);
420 [
super observeValueForKeyPath:path ofObject:object change:change context:context];
443 [m_mimeType release];
444 [m_playerLayer release];
448- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
452 if (![loadingRequest.request.URL.scheme isEqualToString:
@"iodevice"])
459 device->seek(loadingRequest.dataRequest.requestedOffset);
460 if (loadingRequest.contentInformationRequest) {
461 loadingRequest.contentInformationRequest.contentType =
m_mimeType;
462 loadingRequest.contentInformationRequest.contentLength =
device->size();
463 loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
466 if (loadingRequest.dataRequest) {
467 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
468 int maxBytes =
qMin(32 * 1064,
int(requestedLength));
471 while (submitted < requestedLength) {
476 [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]];
481 [loadingRequest finishLoading];
495 m_requestedPosition(-1),
498 m_videoAvailable(
false),
499 m_audioAvailable(
false),
514 [static_cast<AVFMediaPlayerObserver*>(m_observer) release];
529 if (m_videoOutput ==
output)
548 AVAsset *currentAsset = [[static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem] asset];
559 return m_mediaStatus;
569 return m_mediaStream;
574 NSString *urlString = [NSString stringWithUTF8String:url.constData()];
575 NSURL *nsurl = [NSURL URLWithString:urlString];
576 [observer
setURL:nsurl
mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
590 [static_cast<AVFMediaPlayerObserver*>(m_observer) unloadMedia];
592 m_resources = content;
595 setAudioAvailable(
false);
596 setVideoAvailable(
false);
598 m_requestedPosition = -1;
601 if (m_duration != 0) {
618 if (!m_mediaStream && content.
isEmpty()) {
620 if (m_mediaStatus != oldMediaStatus)
624 if (m_state != oldState)
631 if (m_mediaStatus != oldMediaStatus)
637 if (m_mediaStream->
size())
646 if (m_state != oldState)
652 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
654 if (m_requestedPosition != -1)
655 return m_requestedPosition;
660 CMTime
time = [playerItem currentTime];
661 return static_cast<quint64>(float(
time.value) / float(
time.timescale) * 1000.0f);
677 return m_bufferProgress/100.;
680void AVFMediaPlayer::setAudioAvailable(
bool available)
682 if (m_audioAvailable == available)
685 m_audioAvailable = available;
691 return m_audioAvailable;
694void AVFMediaPlayer::setVideoAvailable(
bool available)
696 if (m_videoAvailable == available)
699 m_videoAvailable = available;
705 return m_videoAvailable;
713void AVFMediaPlayer::setSeekable(
bool seekable)
715 if (m_seekable == seekable)
718 m_seekable = seekable;
724 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
729 NSArray *ranges = [playerItem loadedTimeRanges];
730 for (NSValue *timeRange
in ranges) {
731 CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
732 qint64 startTime =
qint64(
float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
733 timeRanges.addInterval(
startTime,
startTime +
qint64(
float(currentTimeRange.duration.value) / currentTimeRange.duration.timescale * 1000.0));
735 if (!timeRanges.isEmpty())
780 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
782 [player setRate:m_rate];
796 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
798 m_requestedPosition =
pos;
804 if (m_requestedPosition != -1) {
805 m_requestedPosition = -1;
814 m_requestedPosition =
pos;
816 CMTime newTime = [playerItem currentTime];
817 newTime.value = (
pos / 1000.0f) * newTime.timescale;
818 [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero
819 completionHandler:^(BOOL finished) {
821 m_requestedPosition = -1;
848 if (m_videoOutput && m_videoSink)
857 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
864 m_playbackTimer.
start(100);
881 if (m_videoOutput && m_videoSink)
884 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
892 m_playbackTimer.
stop();
905 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
915 m_playbackTimer.
stop();
924 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
935 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
943 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
945 player.audioOutputDeviceUniqueID = nil;
959 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
980 AVPlayerStatus currentStatus = [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] status];
989 if (currentStatus == AVPlayerStatusReadyToPlay) {
993 AVPlayerItem *playerItem = [
m_observer playerItem];
1001 setSeekable([[playerItem seekableTimeRanges]
count] > 0);
1004 AVPlayerLayer *playerLayer = [
m_observer playerLayer];
1006 if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
1007 playerLayer.bounds = CGRectMake(0.0f, 0.0f,
1008 m_observer.
videoTrack.assetTrack.naturalSize.width,
1009 m_observer.
videoTrack.assetTrack.naturalSize.height);
1013 if (m_requestedPosition != -1) {
1015 m_requestedPosition = -1;
1022 if (newStatus != m_mediaStatus)
1029 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
1030 m_playbackTimer.
start();
1051 auto status = m_mediaStatus;
1059 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
1060 m_playbackTimer.
start();
1064 if (m_mediaStatus != status)
1090 if (m_requestedPosition != -1) {
1091 m_requestedPosition = -1;
1107 resetStream(
nullptr);
1112 bool firstLoad =
true;
1119 AVPlayerItem *playerItem = [
m_observer playerItem];
1122 NSArray *
tracks = playerItem.tracks;
1123 for (AVPlayerItemTrack *track
in tracks) {
1124 AVAssetTrack *assetTrack = track.assetTrack;
1127 if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
1129 setAudioAvailable(
true);
1130 }
else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
1132 setVideoAvailable(
true);
1135 bool isMirrored =
false;
1138 orientationChanged(orientation, isMirrored);
1141 else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
1144 if (qtTrack != -1) {
1146 this->tracks[qtTrack].append(
metaData);
1166 AVAsset *asset = playerItem.asset;
1169 AVMediaSelectionGroup *
group = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1172 auto *options =
group.options;
1174 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1177 for (
int i = 0;
i <
t.count(); ++
i)
1185 for (
int i = 0;
i <
t.count(); ++
i)
1186 if (
t.at(
i).enabled)
1199 if (trackNumber < 0 || trackNumber >=
t.count())
1201 return t.at(trackNumber);
1206 if (m_mediaStream) {
1213 if (m_mediaStream) {
1226void AVFMediaPlayer::orientationChanged(
QtVideo::Rotation rotation,
bool mirrored)
1241 CGAffineTransform
transform = videoTrack.preferredTransform;
1242 if (CGAffineTransformIsIdentity(
transform))
1254 }
else if (scaleY < 0.0) {
1256 degrees = (180 + (int)degrees) % 360;
1271#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)
QT_BEGIN_NAMESPACE 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]