10#include "private/qplatformaudiooutput_p.h"
14#include <QtCore/qmath.h>
16#import <AVFoundation/AVFoundation.h>
50- (
void)
setURL:(NSURL *)url mimeType:(NSString *)mimeType;
52- (
void) prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys;
53- (
void) assetFailedToPrepareForPlayback:(NSError *)error;
54- (
void) playerItemDidReachEnd:(NSNotification *)notification;
55- (
void) observeValueForKeyPath:(NSString *)keyPath ofObject:(
id)object
56 change:(NSDictionary *)change context:(
void *)context;
59- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
79 if (!(self = [super
init]))
86 [m_playerLayer retain];
87 m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
92- (
void)
setURL:(NSURL *)url mimeType:(NSString *)mimeType
111 BOOL isAccessing = [m_URL startAccessingSecurityScopedResource];
113 __block AVURLAsset *asset = [[AVURLAsset URLAssetWithURL:m_URL options:nil] retain];
114 [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
116 __block NSArray *requestedKeys = [[NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil] retain];
121 [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
123 dispatch_async( dispatch_get_main_queue(),
127 [m_URL stopAccessingSecurityScopedResource];
129 [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
131 [requestedKeys release];
141 [m_playerItem removeObserver:self forKeyPath:@"presentationSize"];
142 [m_playerItem removeObserver:self forKeyPath:AVF_STATUS_KEY];
143 [m_playerItem removeObserver:self forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY];
144 [m_playerItem removeObserver:self forKeyPath:AVF_TRACKS_KEY];
146 [[NSNotificationCenter defaultCenter] removeObserver:self
147 name:AVPlayerItemDidPlayToEndTimeNotification
148 object:m_playerItem];
152 [m_player setRate:0.0];
153 [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY];
154 [m_player removeObserver:self forKeyPath:AVF_CURRENT_ITEM_KEY];
155 [m_player removeObserver:self forKeyPath:AVF_RATE_KEY];
162 [[AVAudioSession sharedInstance] setActive:NO error:nil];
166- (
void) prepareToPlayAsset:(AVURLAsset *)asset
167 withKeys:(NSArray *)requestedKeys
173 for (NSString *thisKey
in requestedKeys)
175 NSError *
error = nil;
176 AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
180 if (keyStatus == AVKeyValueStatusFailed)
182 [
self assetFailedToPrepareForPlayback:error];
192 qWarning() <<
"Asset reported to be not playable. Playback of this asset may not be possible.";
203 m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
205 qWarning() <<
"Failed to create player item";
207 NSString *localizedDescription = NSLocalizedString(
@"Item cannot be played",
@"Item cannot be played description");
208 NSString *localizedFailureReason = NSLocalizedString(
@"The assets tracks were loaded, but couldn't create player item.",
@"Item cannot be played failure reason");
209 NSDictionary *errorDict = [NSDictionary dictionaryWithObjectsAndKeys:
210 localizedDescription, NSLocalizedDescriptionKey,
211 localizedFailureReason, NSLocalizedFailureReasonErrorKey,
213 NSError *assetCannotBePlayedError = [NSError errorWithDomain:@"StitchedStreamPlayer" code:0 userInfo:errorDict];
215 [
self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
220 [m_playerItem addObserver:self
221 forKeyPath:AVF_STATUS_KEY
222 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
223 context:AVFMediaPlayerObserverStatusObservationContext];
225 [m_playerItem addObserver:self
226 forKeyPath:@"presentationSize"
227 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
228 context:AVFMediaPlayerObserverPresentationSizeContext];
230 [m_playerItem addObserver:self
231 forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
232 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
233 context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext];
235 [m_playerItem addObserver:self
236 forKeyPath:AVF_TRACKS_KEY
237 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
238 context:AVFMediaPlayerObserverTracksContext];
242 [[NSNotificationCenter defaultCenter] addObserver:self
243 selector:@selector(playerItemDidReachEnd:)
244 name:AVPlayerItemDidPlayToEndTimeNotification
245 object:m_playerItem];
248 m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
254 m_player.volume = (audioOutput ? audioOutput->volume : 1.);
255 m_player.muted = (audioOutput ? audioOutput->muted :
true);
264 [m_player addObserver:self
265 forKeyPath:AVF_CURRENT_ITEM_KEY
266 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
267 context:AVFMediaPlayerObserverCurrentItemObservationContext];
270 [m_player addObserver:self
271 forKeyPath:AVF_RATE_KEY
272 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
273 context:AVFMediaPlayerObserverRateObservationContext];
276 [m_player addObserver:self
277 forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
279 context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
281 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
282 [[AVAudioSession sharedInstance] setActive:YES error:nil];
286-(
void) assetFailedToPrepareForPlayback:(NSError *)error
292 qDebug() << [[error localizedDescription] UTF8String];
293 qDebug() << [[error localizedFailureReason] UTF8String];
294 qDebug() << [[error localizedRecoverySuggestion] UTF8String];
298- (
void) playerItemDidReachEnd:(NSNotification *)notification
305- (
void) observeValueForKeyPath:(NSString*) path
307 change:(NSDictionary*)change
308 context:(
void*)context
313 AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
318 case AVPlayerStatusUnknown:
324 case AVPlayerStatusReadyToPlay:
334 case AVPlayerStatusFailed:
336 AVPlayerItem *playerItem =
static_cast<AVPlayerItem*
>(
object);
337 [
self assetFailedToPrepareForPlayback:playerItem.error];
349 const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
353 Q_ARG(
int, isPlaybackLikelyToKeepUp ? 100 : 0));
370 AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
376 const CMTime
time = [m_playerItem duration];
377 const qint64 duration =
static_cast<qint64>(float(
time.value) / float(
time.timescale) * 1000.0f);
383 [
super observeValueForKeyPath:path ofObject:object change:change context:context];
406 [m_mimeType release];
407 [m_playerLayer release];
411- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
415 if (![loadingRequest.request.URL.scheme isEqualToString:
@"iodevice"])
422 device->seek(loadingRequest.dataRequest.requestedOffset);
423 if (loadingRequest.contentInformationRequest) {
424 loadingRequest.contentInformationRequest.contentType =
m_mimeType;
425 loadingRequest.contentInformationRequest.contentLength =
device->size();
426 loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
429 if (loadingRequest.dataRequest) {
430 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
431 int maxBytes =
qMin(32 * 1064,
int(requestedLength));
434 while (submitted < requestedLength) {
439 [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]];
444 [loadingRequest finishLoading];
458 m_requestedPosition(-1),
461 m_videoAvailable(
false),
462 m_audioAvailable(
false),
477 [static_cast<AVFMediaPlayerObserver*>(m_observer) release];
492 if (m_videoOutput ==
output)
511 AVAsset *currentAsset = [[static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem] asset];
522 return m_mediaStatus;
532 return m_mediaStream;
537 NSString *urlString = [NSString stringWithUTF8String:url.constData()];
538 NSURL *nsurl = [NSURL URLWithString:urlString];
539 [observer
setURL:nsurl
mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
553 [static_cast<AVFMediaPlayerObserver*>(m_observer) unloadMedia];
555 m_resources = content;
558 setAudioAvailable(
false);
559 setVideoAvailable(
false);
561 m_requestedPosition = -1;
564 if (m_duration != 0) {
581 if (!m_mediaStream && content.
isEmpty()) {
583 if (m_mediaStatus != oldMediaStatus)
587 if (m_state != oldState)
594 if (m_mediaStatus != oldMediaStatus)
600 if (m_mediaStream->
size())
609 if (m_state != oldState)
615 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
617 if (m_requestedPosition != -1)
618 return m_requestedPosition;
623 CMTime
time = [playerItem currentTime];
624 return static_cast<quint64>(float(
time.value) / float(
time.timescale) * 1000.0f);
640 return m_bufferProgress/100.;
643void AVFMediaPlayer::setAudioAvailable(
bool available)
645 if (m_audioAvailable == available)
648 m_audioAvailable = available;
654 return m_audioAvailable;
657void AVFMediaPlayer::setVideoAvailable(
bool available)
659 if (m_videoAvailable == available)
662 m_videoAvailable = available;
668 return m_videoAvailable;
676void AVFMediaPlayer::setSeekable(
bool seekable)
678 if (m_seekable == seekable)
681 m_seekable = seekable;
687 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
692 NSArray *ranges = [playerItem loadedTimeRanges];
693 for (NSValue *timeRange
in ranges) {
694 CMTimeRange currentTimeRange = [timeRange CMTimeRangeValue];
695 qint64 startTime =
qint64(
float(currentTimeRange.start.value) / currentTimeRange.start.timescale * 1000.0);
743 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
745 [player setRate:m_rate];
759 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
761 m_requestedPosition =
pos;
767 if (m_requestedPosition != -1) {
768 m_requestedPosition = -1;
777 m_requestedPosition =
pos;
779 CMTime newTime = [playerItem currentTime];
780 newTime.value = (
pos / 1000.0f) * newTime.timescale;
781 [playerItem seekToTime:newTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero
782 completionHandler:^(BOOL finished) {
784 m_requestedPosition = -1;
811 if (m_videoOutput && m_videoSink)
820 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
827 m_playbackTimer.
start(100);
844 if (m_videoOutput && m_videoSink)
847 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
855 m_playbackTimer.
stop();
868 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
878 m_playbackTimer.
stop();
887 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
898 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
906 AVPlayer *
player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
908 player.audioOutputDeviceUniqueID = nil;
922 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
943 AVPlayerStatus currentStatus = [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] status];
952 if (currentStatus == AVPlayerStatusReadyToPlay) {
956 AVPlayerItem *playerItem = [
m_observer playerItem];
964 setSeekable([[playerItem seekableTimeRanges]
count] > 0);
967 AVPlayerLayer *playerLayer = [
m_observer playerLayer];
969 if (!playerLayer.bounds.size.width || !playerLayer.bounds.size.height) {
970 playerLayer.bounds = CGRectMake(0.0f, 0.0f,
971 m_observer.
videoTrack.assetTrack.naturalSize.width,
972 m_observer.
videoTrack.assetTrack.naturalSize.height);
976 if (m_requestedPosition != -1) {
978 m_requestedPosition = -1;
985 if (newStatus != m_mediaStatus)
992 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
993 m_playbackTimer.
start();
1014 auto status = m_mediaStatus;
1022 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
1023 m_playbackTimer.
start();
1027 if (m_mediaStatus != status)
1053 if (m_requestedPosition != -1) {
1054 m_requestedPosition = -1;
1070 resetStream(
nullptr);
1075 bool firstLoad =
true;
1082 AVPlayerItem *playerItem = [
m_observer playerItem];
1085 NSArray *
tracks = playerItem.tracks;
1086 for (AVPlayerItemTrack *track
in tracks) {
1087 AVAssetTrack *assetTrack = track.assetTrack;
1090 if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
1092 setAudioAvailable(
true);
1093 }
else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
1095 setVideoAvailable(
true);
1098 bool isMirrored =
false;
1101 orientationChanged(orientation, isMirrored);
1104 else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
1107 if (qtTrack != -1) {
1109 this->tracks[qtTrack].append(
metaData);
1129 AVAsset *asset = playerItem.asset;
1132 AVMediaSelectionGroup *
group = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1135 auto *options =
group.options;
1137 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1140 for (
int i = 0;
i <
t.count(); ++
i)
1148 for (
int i = 0;
i <
t.count(); ++
i)
1149 if (
t.at(
i).enabled)
1162 if (trackNumber < 0 || trackNumber >=
t.count())
1164 return t.at(trackNumber);
1169 if (m_mediaStream) {
1176 if (m_mediaStream) {
1204 CGAffineTransform
transform = videoTrack.preferredTransform;
1205 if (CGAffineTransformIsIdentity(
transform))
1217 }
else if (scaleY < 0.0) {
1219 degrees = (180 + (int)degrees) % 360;
1234#include "moc_avfmediaplayer_p.cpp"
IOBluetoothDevice * device
void setVideoMirrored(bool mirrored)
void setLayer(CALayer *layer) override
void setVideoRotation(QVideoFrame::RotationAngle)
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
\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.
RotationAngle
The angle of the clockwise rotation that should be applied to a video frame before displaying.
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)
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]