Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
avfmediaplayer.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "avfmediaplayer_p.h"
6#include <avfvideosink_p.h>
7#include <avfmetadata_p.h>
8
9#include "qaudiooutput.h"
10#include "private/qplatformaudiooutput_p.h"
11
12#include <qpointer.h>
13#include <QFileInfo>
14#include <QtCore/qmath.h>
15#include <QtCore/qmutex.h>
16
17#import <AVFoundation/AVFoundation.h>
18
20
21//AVAsset Keys
22static NSString* const AVF_TRACKS_KEY = @"tracks";
23static NSString* const AVF_PLAYABLE_KEY = @"playable";
24
25//AVPlayerItem keys
26static NSString* const AVF_STATUS_KEY = @"status";
27static NSString* const AVF_BUFFER_LIKELY_KEEP_UP_KEY = @"playbackLikelyToKeepUp";
28
29//AVPlayer keys
30static NSString* const AVF_RATE_KEY = @"rate";
31static NSString* const AVF_CURRENT_ITEM_KEY = @"currentItem";
32static NSString* const AVF_CURRENT_ITEM_DURATION_KEY = @"currentItem.duration";
33
41
42@interface AVFMediaPlayerObserver : NSObject<AVAssetResourceLoaderDelegate>
43
44@property (readonly, getter=player) AVPlayer* m_player;
45@property (readonly, getter=playerItem) AVPlayerItem* m_playerItem;
46@property (readonly, getter=playerLayer) AVPlayerLayer* m_playerLayer;
47@property (readonly, getter=session) AVFMediaPlayer* m_session;
48@property (retain) AVPlayerItemTrack *videoTrack;
49
50- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session;
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;
59- (void) dealloc;
60- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
61@end
62
63#ifdef Q_OS_IOS
64// Alas, no such thing as 'class variable', hence globals:
65static unsigned sessionActivationCount;
66static QMutex sessionMutex;
67#endif // Q_OS_IOS
68
69@implementation AVFMediaPlayerObserver
70{
71@private
73 AVPlayer *m_player;
74 AVPlayerItem *m_playerItem;
75 AVPlayerLayer *m_playerLayer;
76 NSURL *m_URL;
78 NSData *m_data;
79 NSString *m_mimeType;
80#ifdef Q_OS_IOS
81 BOOL m_activated;
82#endif
83}
84
86
87#ifdef Q_OS_IOS
88- (void)setSessionActive:(BOOL)active
89{
90 const QMutexLocker lock(&sessionMutex);
91 if (active) {
92 // Don't count the same player twice if already activated,
93 // unless it tried to deactivate first:
94 if (m_activated)
95 return;
96 if (!sessionActivationCount)
97 [AVAudioSession.sharedInstance setActive:YES error:nil];
98 ++sessionActivationCount;
99 m_activated = YES;
100 } else {
101 if (!sessionActivationCount || !m_activated) {
102 qWarning("Unbalanced audio session deactivation, ignoring.");
103 return;
104 }
105 --sessionActivationCount;
106 m_activated = NO;
107 if (!sessionActivationCount)
108 [AVAudioSession.sharedInstance setActive:NO error:nil];
109 }
110}
111#endif // Q_OS_IOS
112
113- (AVFMediaPlayerObserver *) initWithMediaPlayerSession:(AVFMediaPlayer *)session
114{
115 if (!(self = [super init]))
116 return nil;
117
118 m_session = session;
120
121 m_playerLayer = [AVPlayerLayer playerLayerWithPlayer:nil];
122 [m_playerLayer retain];
123 m_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
124 m_playerLayer.anchorPoint = CGPointMake(0.0f, 0.0f);
125 return self;
126}
127
128- (void) setURL:(NSURL *)url mimeType:(NSString *)mimeType
129{
130 if (!m_session)
131 return;
132
133 [m_mimeType release];
134 m_mimeType = [mimeType retain];
135
136 if (m_URL != url)
137 {
138 [m_URL release];
139 m_URL = [url copy];
140
141 //Create an asset for inspection of a resource referenced by a given URL.
142 //Load the values for the asset keys "tracks", "playable".
143
144 // use __block to avoid maintaining strong references on variables captured by the
145 // following block callback
146#if defined(Q_OS_IOS)
147 BOOL isAccessing = [m_URL startAccessingSecurityScopedResource];
148#endif
149 __block AVURLAsset *asset = [[AVURLAsset URLAssetWithURL:m_URL options:nil] retain];
150 [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
151
152 __block NSArray *requestedKeys = [[NSArray arrayWithObjects:AVF_TRACKS_KEY, AVF_PLAYABLE_KEY, nil] retain];
153
154 __block AVFMediaPlayerObserver *blockSelf = [self retain];
155
156 // Tells the asset to load the values of any of the specified keys that are not already loaded.
157 [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
158 ^{
159 dispatch_async( dispatch_get_main_queue(),
160 ^{
161#if defined(Q_OS_IOS)
162 if (isAccessing)
163 [m_URL stopAccessingSecurityScopedResource];
164#endif
165 [blockSelf prepareToPlayAsset:asset withKeys:requestedKeys];
166 [asset release];
167 [requestedKeys release];
168 [blockSelf release];
169 });
170 }];
171 }
172}
173
175{
176 if (m_playerItem) {
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];
181
182 [[NSNotificationCenter defaultCenter] removeObserver:self
183 name:AVPlayerItemDidPlayToEndTimeNotification
184 object:m_playerItem];
185 m_playerItem = 0;
186 }
187 if (m_player) {
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];
192 [m_player release];
193 m_player = 0;
194 }
195 if (m_playerLayer)
196 m_playerLayer.player = nil;
197#if defined(Q_OS_IOS)
198 [self setSessionActive:NO];
199#endif
200}
201
202- (void) prepareToPlayAsset:(AVURLAsset *)asset
203 withKeys:(NSArray *)requestedKeys
204{
205 if (!m_session)
206 return;
207
208 //Make sure that the value of each key has loaded successfully.
209 for (NSString *thisKey in requestedKeys)
210 {
211 NSError *error = nil;
212 AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
213#ifdef QT_DEBUG_AVF
214 qDebug() << Q_FUNC_INFO << [thisKey UTF8String] << " status: " << keyStatus;
215#endif
216 if (keyStatus == AVKeyValueStatusFailed)
217 {
218 [self assetFailedToPrepareForPlayback:error];
219 return;
220 }
221 }
222
223 //Use the AVAsset playable property to detect whether the asset can be played.
224#ifdef QT_DEBUG_AVF
225 qDebug() << Q_FUNC_INFO << "isPlayable: " << [asset isPlayable];
226#endif
227 if (!asset.playable)
228 qWarning() << "Asset reported to be not playable. Playback of this asset may not be possible.";
229
230 //At this point we're ready to set up for playback of the asset.
231 //Stop observing our prior AVPlayerItem, if we have one.
232 if (m_playerItem)
233 {
234 //Remove existing player item key value observers and notifications.
235 [self unloadMedia];
236 }
237
238 //Create a new instance of AVPlayerItem from the now successfully loaded AVAsset.
239 m_playerItem = [AVPlayerItem playerItemWithAsset:asset];
240 if (!m_playerItem) {
241 qWarning() << "Failed to create player item";
242 //Generate an error describing the failure.
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,
248 nil];
249 NSError *assetCannotBePlayedError = [NSError errorWithDomain:@"StitchedStreamPlayer" code:0 userInfo:errorDict];
250
251 [self assetFailedToPrepareForPlayback:assetCannotBePlayedError];
252 return;
253 }
254
255 //Observe the player item "status" key to determine when it is ready to play.
256 [m_playerItem addObserver:self
257 forKeyPath:AVF_STATUS_KEY
258 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
259 context:AVFMediaPlayerObserverStatusObservationContext];
260
261 [m_playerItem addObserver:self
262 forKeyPath:@"presentationSize"
263 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
264 context:AVFMediaPlayerObserverPresentationSizeContext];
265
266 [m_playerItem addObserver:self
267 forKeyPath:AVF_BUFFER_LIKELY_KEEP_UP_KEY
268 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
269 context:AVFMediaPlayerObserverBufferLikelyToKeepUpContext];
270
271 [m_playerItem addObserver:self
272 forKeyPath:AVF_TRACKS_KEY
273 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
274 context:AVFMediaPlayerObserverTracksContext];
275
276 //When the player item has played to its end time we'll toggle
277 //the movie controller Pause button to be the Play button
278 [[NSNotificationCenter defaultCenter] addObserver:self
279 selector:@selector(playerItemDidReachEnd:)
280 name:AVPlayerItemDidPlayToEndTimeNotification
281 object:m_playerItem];
282
283 //Get a new AVPlayer initialized to play the specified player item.
284 m_player = [AVPlayer playerWithPlayerItem:m_playerItem];
285 [m_player retain];
286
287 //Set the initial audio ouptut settings on new player object
288 if (self.session) {
289 auto *audioOutput = m_session->m_audioOutput;
290 m_player.volume = (audioOutput ? audioOutput->volume : 1.);
291 m_player.muted = (audioOutput ? audioOutput->muted : true);
293 }
294
295 //Assign the output layer to the new player
296 m_playerLayer.player = m_player;
297
298 //Observe the AVPlayer "currentItem" property to find out when any
299 //AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did
300 //occur.
301 [m_player addObserver:self
302 forKeyPath:AVF_CURRENT_ITEM_KEY
303 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
304 context:AVFMediaPlayerObserverCurrentItemObservationContext];
305
306 //Observe the AVPlayer "rate" property to update the scrubber control.
307 [m_player addObserver:self
308 forKeyPath:AVF_RATE_KEY
309 options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
310 context:AVFMediaPlayerObserverRateObservationContext];
311
312 //Observe the duration for getting the buffer state
313 [m_player addObserver:self
314 forKeyPath:AVF_CURRENT_ITEM_DURATION_KEY
315 options:0
316 context:AVFMediaPlayerObserverCurrentItemDurationObservationContext];
317#if defined(Q_OS_IOS)
318 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
319 [self setSessionActive:YES];
320#endif
321}
322
323-(void) assetFailedToPrepareForPlayback:(NSError *)error
324{
326 QMetaObject::invokeMethod(m_session, "processMediaLoadError", Qt::AutoConnection);
327#ifdef QT_DEBUG_AVF
328 qDebug() << Q_FUNC_INFO;
329 qDebug() << [[error localizedDescription] UTF8String];
330 qDebug() << [[error localizedFailureReason] UTF8String];
331 qDebug() << [[error localizedRecoverySuggestion] UTF8String];
332#endif
333}
334
335- (void) playerItemDidReachEnd:(NSNotification *)notification
336{
337 Q_UNUSED(notification);
338 if (self.session)
340}
341
342- (void) observeValueForKeyPath:(NSString*) path
343 ofObject:(id)object
344 change:(NSDictionary*)change
345 context:(void*)context
346{
347 //AVPlayerItem "status" property value observer.
349 {
350 AVPlayerStatus status = (AVPlayerStatus)[[change objectForKey:NSKeyValueChangeNewKey] integerValue];
351 switch (status)
352 {
353 //Indicates that the status of the player is not yet known because
354 //it has not tried to load new media resources for playback
355 case AVPlayerStatusUnknown:
356 {
357 //QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
358 }
359 break;
360
361 case AVPlayerStatusReadyToPlay:
362 {
363 //Once the AVPlayerItem becomes ready to play, i.e.
364 //[playerItem status] == AVPlayerItemStatusReadyToPlay,
365 //its duration can be fetched from the item.
366 if (self.session)
367 QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection);
368 }
369 break;
370
371 case AVPlayerStatusFailed:
372 {
373 AVPlayerItem *playerItem = static_cast<AVPlayerItem*>(object);
374 [self assetFailedToPrepareForPlayback:playerItem.error];
375
376 if (self.session)
377 QMetaObject::invokeMethod(m_session, "processLoadStateFailure", Qt::AutoConnection);
378 }
379 break;
380 }
381 } else if (context == AVFMediaPlayerObserverPresentationSizeContext) {
382 QSize size(m_playerItem.presentationSize.width, m_playerItem.presentationSize.height);
385 {
386 const bool isPlaybackLikelyToKeepUp = [m_playerItem isPlaybackLikelyToKeepUp];
387 if (isPlaybackLikelyToKeepUp != m_bufferIsLikelyToKeepUp) {
388 m_bufferIsLikelyToKeepUp = isPlaybackLikelyToKeepUp;
389 QMetaObject::invokeMethod(m_session, "processBufferStateChange", Qt::AutoConnection,
390 Q_ARG(int, isPlaybackLikelyToKeepUp ? 100 : 0));
391 }
392 }
393 else if (context == AVFMediaPlayerObserverTracksContext)
394 {
396 }
397 //AVPlayer "rate" property value observer.
399 {
400 //QMetaObject::invokeMethod(m_session, "setPlaybackRate", Qt::AutoConnection, Q_ARG(qreal, [m_player rate]));
401 }
402 //AVPlayer "currentItem" property observer.
403 //Called when the AVPlayer replaceCurrentItemWithPlayerItem:
404 //replacement will/did occur.
406 {
407 AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
408 if (m_playerItem != newPlayerItem)
409 m_playerItem = newPlayerItem;
410 }
412 {
413 const CMTime time = [m_playerItem duration];
414 const qint64 duration = static_cast<qint64>(float(time.value) / float(time.timescale) * 1000.0f);
415 if (self.session)
416 QMetaObject::invokeMethod(m_session, "processDurationChange", Qt::AutoConnection, Q_ARG(qint64, duration));
417 }
418 else
419 {
420 [super observeValueForKeyPath:path ofObject:object change:change context:context];
421 }
422}
423
425{
426#ifdef QT_DEBUG_AVF
427 qDebug() << Q_FUNC_INFO;
428#endif
429 m_session = 0;
430}
431
432- (void) dealloc
433{
434#ifdef QT_DEBUG_AVF
435 qDebug() << Q_FUNC_INFO;
436#endif
437 [self unloadMedia];
438
439 if (m_URL) {
440 [m_URL release];
441 }
442
443 [m_mimeType release];
444 [m_playerLayer release];
445 [super dealloc];
446}
447
448- (BOOL) resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
449{
450 Q_UNUSED(resourceLoader);
451
452 if (![loadingRequest.request.URL.scheme isEqualToString:@"iodevice"])
453 return NO;
454
456 if (!device)
457 return NO;
458
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;
464 }
465
466 if (loadingRequest.dataRequest) {
467 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
468 int maxBytes = qMin(32 * 1064, int(requestedLength));
469 char buffer[maxBytes];
470 NSInteger submitted = 0;
471 while (submitted < requestedLength) {
472 qint64 len = device->read(buffer, maxBytes);
473 if (len < 1)
474 break;
475
476 [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]];
477 submitted += len;
478 }
479
480 // Finish loading even if not all bytes submitted.
481 [loadingRequest finishLoading];
482 }
483
484 return YES;
485}
486@end
487
489 : QObject(player),
491 m_state(QMediaPlayer::StoppedState),
492 m_mediaStatus(QMediaPlayer::NoMedia),
493 m_mediaStream(nullptr),
494 m_rate(1.0),
495 m_requestedPosition(-1),
496 m_duration(0),
497 m_bufferProgress(0),
498 m_videoAvailable(false),
499 m_audioAvailable(false),
500 m_seekable(false)
501{
502 m_observer = [[AVFMediaPlayerObserver alloc] initWithMediaPlayerSession:this];
505}
506
508{
509#ifdef QT_DEBUG_AVF
510 qDebug() << Q_FUNC_INFO;
511#endif
512 //Detatch the session from the sessionObserver (which could still be alive trying to communicate with this session).
514 [static_cast<AVFMediaPlayerObserver*>(m_observer) release];
515}
516
518{
519 m_videoSink = sink ? static_cast<AVFVideoSink *>(sink->platformVideoSink()): nullptr;
520 m_videoOutput->setVideoSink(m_videoSink);
521}
522
524{
525#ifdef QT_DEBUG_AVF
526 qDebug() << Q_FUNC_INFO << output;
527#endif
528
529 if (m_videoOutput == output)
530 return;
531
532 //Set the current output layer to null to stop rendering
533 if (m_videoOutput) {
534 m_videoOutput->setLayer(nullptr);
535 }
536
537 m_videoOutput = output;
538
539 if (m_videoOutput && m_state != QMediaPlayer::StoppedState)
540 m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]);
541}
542
544{
545#ifdef QT_DEBUG_AVF
546 qDebug() << Q_FUNC_INFO;
547#endif
548 AVAsset *currentAsset = [[static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem] asset];
549 return currentAsset;
550}
551
553{
554 return m_state;
555}
556
558{
559 return m_mediaStatus;
560}
561
563{
564 return m_resources;
565}
566
568{
569 return m_mediaStream;
570}
571
572static void setURL(AVFMediaPlayerObserver *observer, const QByteArray &url, const QString &mimeType = QString())
573{
574 NSString *urlString = [NSString stringWithUTF8String:url.constData()];
575 NSURL *nsurl = [NSURL URLWithString:urlString];
576 [observer setURL:nsurl mimeType:[NSString stringWithUTF8String:mimeType.toLatin1().constData()]];
577}
578
579static void setStreamURL(AVFMediaPlayerObserver *observer, const QByteArray &url)
580{
581 setURL(observer, QByteArrayLiteral("iodevice://") + url, QFileInfo(QString::fromUtf8(url)).suffix());
582}
583
585{
586#ifdef QT_DEBUG_AVF
587 qDebug() << Q_FUNC_INFO << content.request().url();
588#endif
589
590 [static_cast<AVFMediaPlayerObserver*>(m_observer) unloadMedia];
591
592 m_resources = content;
593 resetStream(stream);
594
595 setAudioAvailable(false);
596 setVideoAvailable(false);
597 setSeekable(false);
598 m_requestedPosition = -1;
599 orientationChanged(QtVideo::Rotation::None, false);
601 if (m_duration != 0) {
602 m_duration = 0;
604 }
605 if (!m_metaData.isEmpty()) {
606 m_metaData.clear();
608 }
609 for (int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
610 tracks[i].clear();
611 nativeTracks[i].clear();
612 }
614
615 const QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus;
616 const QMediaPlayer::PlaybackState oldState = m_state;
617
618 if (!m_mediaStream && content.isEmpty()) {
619 m_mediaStatus = QMediaPlayer::NoMedia;
620 if (m_mediaStatus != oldMediaStatus)
621 Q_EMIT mediaStatusChanged(m_mediaStatus);
622
624 if (m_state != oldState)
625 Q_EMIT stateChanged(m_state);
626
627 return;
628 }
629
630 m_mediaStatus = QMediaPlayer::LoadingMedia;
631 if (m_mediaStatus != oldMediaStatus)
632 Q_EMIT mediaStatusChanged(m_mediaStatus);
633
634 if (m_mediaStream) {
635 // If there is a data, try to load it,
636 // otherwise wait for readyRead.
637 if (m_mediaStream->size())
638 setStreamURL(m_observer, m_resources.toEncoded());
639 } else {
640 //Load AVURLAsset
641 //initialize asset using content's URL
642 setURL(m_observer, m_resources.toEncoded());
643 }
644
646 if (m_state != oldState)
647 Q_EMIT stateChanged(m_state);
648}
649
651{
652 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
653
654 if (m_requestedPosition != -1)
655 return m_requestedPosition;
656
657 if (!playerItem)
658 return 0;
659
660 CMTime time = [playerItem currentTime];
661 return static_cast<quint64>(float(time.value) / float(time.timescale) * 1000.0f);
662}
663
665{
666#ifdef QT_DEBUG_AVF
667 qDebug() << Q_FUNC_INFO;
668#endif
669 return m_duration;
670}
671
673{
674#ifdef QT_DEBUG_AVF
675 qDebug() << Q_FUNC_INFO;
676#endif
677 return m_bufferProgress/100.;
678}
679
680void AVFMediaPlayer::setAudioAvailable(bool available)
681{
682 if (m_audioAvailable == available)
683 return;
684
685 m_audioAvailable = available;
686 Q_EMIT audioAvailableChanged(available);
687}
688
690{
691 return m_audioAvailable;
692}
693
694void AVFMediaPlayer::setVideoAvailable(bool available)
695{
696 if (m_videoAvailable == available)
697 return;
698
699 m_videoAvailable = available;
700 Q_EMIT videoAvailableChanged(available);
701}
702
704{
705 return m_videoAvailable;
706}
707
709{
710 return m_seekable;
711}
712
713void AVFMediaPlayer::setSeekable(bool seekable)
714{
715 if (m_seekable == seekable)
716 return;
717
718 m_seekable = seekable;
719 Q_EMIT seekableChanged(seekable);
720}
721
723{
724 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
725
726 if (playerItem) {
727 QMediaTimeRange timeRanges;
728
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));
734 }
735 if (!timeRanges.isEmpty())
736 return timeRanges;
737 }
738 return QMediaTimeRange(0, duration());
739}
740
742{
743 return m_rate;
744}
745
763
765{
766 return m_metaData;
767}
768
770{
771#ifdef QT_DEBUG_AVF
772 qDebug() << Q_FUNC_INFO << rate;
773#endif
774
775 if (qFuzzyCompare(m_rate, rate))
776 return;
777
778 m_rate = rate;
779
780 AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
781 if (player && m_state == QMediaPlayer::PlayingState)
782 [player setRate:m_rate];
783
785}
786
788{
789#ifdef QT_DEBUG_AVF
790 qDebug() << Q_FUNC_INFO << pos;
791#endif
792
793 if (pos == position())
794 return;
795
796 AVPlayerItem *playerItem = [static_cast<AVFMediaPlayerObserver*>(m_observer) playerItem];
797 if (!playerItem) {
798 m_requestedPosition = pos;
799 Q_EMIT positionChanged(m_requestedPosition);
800 return;
801 }
802
803 if (!isSeekable()) {
804 if (m_requestedPosition != -1) {
805 m_requestedPosition = -1;
807 }
808 return;
809 }
810
811 pos = qMax(qint64(0), pos);
812 if (duration() > 0)
813 pos = qMin(pos, duration());
814 m_requestedPosition = pos;
815
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) {
820 if (finished)
821 m_requestedPosition = -1;
822 }];
823
825
826 // Reset media status if the current status is EndOfMedia
827 if (m_mediaStatus == QMediaPlayer::EndOfMedia) {
830 Q_EMIT mediaStatusChanged((m_mediaStatus = newMediaStatus));
831 }
832}
833
835{
836#ifdef QT_DEBUG_AVF
837 qDebug() << Q_FUNC_INFO << "currently: " << m_state;
838#endif
839
840 if (m_mediaStatus == QMediaPlayer::NoMedia || m_mediaStatus == QMediaPlayer::InvalidMedia)
841 return;
842
843 if (m_state == QMediaPlayer::PlayingState)
844 return;
845
847
848 if (m_videoOutput && m_videoSink)
849 m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]);
850
851 // Reset media status if the current status is EndOfMedia
852 if (m_mediaStatus == QMediaPlayer::EndOfMedia)
853 setPosition(0);
854
855 if (m_mediaStatus == QMediaPlayer::LoadedMedia || m_mediaStatus == QMediaPlayer::BufferedMedia) {
856 // Setting the rate starts playback
857 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
858 }
859
862
863 Q_EMIT stateChanged(m_state);
864 m_playbackTimer.start(100);
865}
866
868{
869#ifdef QT_DEBUG_AVF
870 qDebug() << Q_FUNC_INFO << "currently: " << m_state;
871#endif
872
873 if (m_mediaStatus == QMediaPlayer::NoMedia)
874 return;
875
876 if (m_state == QMediaPlayer::PausedState)
877 return;
878
880
881 if (m_videoOutput && m_videoSink)
882 m_videoOutput->setLayer([static_cast<AVFMediaPlayerObserver*>(m_observer) playerLayer]);
883
884 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
885
886 // Reset media status if the current status is EndOfMedia
887 if (m_mediaStatus == QMediaPlayer::EndOfMedia)
888 setPosition(0);
889
891 Q_EMIT stateChanged(m_state);
892 m_playbackTimer.stop();
893}
894
896{
897#ifdef QT_DEBUG_AVF
898 qDebug() << Q_FUNC_INFO << "currently: " << m_state;
899#endif
900
901 if (m_state == QMediaPlayer::StoppedState)
902 return;
903
904 // AVPlayer doesn't have stop(), only pause() and play().
905 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] pause];
906 setPosition(0);
907
908 if (m_videoOutput)
909 m_videoOutput->setLayer(nullptr);
910
911 if (m_mediaStatus == QMediaPlayer::BufferedMedia)
913
915 m_playbackTimer.stop();
916}
917
919{
920#ifdef QT_DEBUG_AVF
921 qDebug() << Q_FUNC_INFO << volume;
922#endif
923
924 AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
925 if (player)
926 player.volume = volume;
927}
928
930{
931#ifdef QT_DEBUG_AVF
932 qDebug() << Q_FUNC_INFO << muted;
933#endif
934
935 AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
936 if (player)
937 player.muted = muted;
938}
939
941{
942#ifdef Q_OS_MACOS
943 AVPlayer *player = [static_cast<AVFMediaPlayerObserver*>(m_observer) player];
945 player.audioOutputDeviceUniqueID = nil;
946 if (!m_audioOutput)
947 player.muted = true;
948 } else {
949 NSString *str = QString::fromUtf8(m_audioOutput->device.id()).toNSString();
950 player.audioOutputDeviceUniqueID = str;
951 }
952#endif
953}
954
956{
957 if (doLoop()) {
958 setPosition(0);
959 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
960 return;
961 }
962
963 //AVPlayerItem has reached end of track/stream
964#ifdef QT_DEBUG_AVF
965 qDebug() << Q_FUNC_INFO;
966#endif
968 m_mediaStatus = QMediaPlayer::EndOfMedia;
970
971 if (m_videoOutput)
972 m_videoOutput->setLayer(nullptr);
973
974 Q_EMIT mediaStatusChanged(m_mediaStatus);
975 Q_EMIT stateChanged(m_state);
976}
977
979{
980 AVPlayerStatus currentStatus = [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] status];
981
982#ifdef QT_DEBUG_AVF
983 qDebug() << Q_FUNC_INFO << currentStatus << ", " << m_mediaStatus << ", " << newState;
984#endif
985
986 if (m_mediaStatus == QMediaPlayer::NoMedia)
987 return;
988
989 if (currentStatus == AVPlayerStatusReadyToPlay) {
990
991 QMediaPlayer::MediaStatus newStatus = m_mediaStatus;
992
993 AVPlayerItem *playerItem = [m_observer playerItem];
994
995 // get the meta data
996 m_metaData = AVFMetaData::fromAsset(playerItem.asset);
998 updateTracks();
999
1000 if (playerItem) {
1001 setSeekable([[playerItem seekableTimeRanges] count] > 0);
1002
1003 // Get the native size of the video, and reset the bounds of the player layer
1004 AVPlayerLayer *playerLayer = [m_observer playerLayer];
1005 if (m_observer.videoTrack && 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);
1010 }
1011 }
1012
1013 if (m_requestedPosition != -1) {
1014 setPosition(m_requestedPosition);
1015 m_requestedPosition = -1;
1016 }
1017 }
1018
1021
1022 if (newStatus != m_mediaStatus)
1023 Q_EMIT mediaStatusChanged((m_mediaStatus = newStatus));
1024
1025 }
1026
1027 if (newState == QMediaPlayer::PlayingState && [static_cast<AVFMediaPlayerObserver*>(m_observer) player]) {
1028 // Setting the rate is enough to start playback, no need to call play()
1029 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
1030 m_playbackTimer.start();
1031 }
1032}
1033
1034
1039
1040
1045
1047{
1048 if (bufferProgress == m_bufferProgress)
1049 return;
1050
1051 auto status = m_mediaStatus;
1052 // Buffered -> unbuffered.
1053 if (!bufferProgress) {
1055 } else if (status == QMediaPlayer::StalledMedia) {
1057 // Resume playback.
1058 if (m_state == QMediaPlayer::PlayingState) {
1059 [[static_cast<AVFMediaPlayerObserver*>(m_observer) player] setRate:m_rate];
1060 m_playbackTimer.start();
1061 }
1062 }
1063
1064 if (m_mediaStatus != status)
1065 Q_EMIT mediaStatusChanged(m_mediaStatus = status);
1066
1067 m_bufferProgress = bufferProgress;
1069}
1070
1072{
1073 if (duration == m_duration)
1074 return;
1075
1076 m_duration = duration;
1078}
1079
1081{
1082 if (m_state == QMediaPlayer::StoppedState)
1083 return;
1084
1086}
1087
1089{
1090 if (m_requestedPosition != -1) {
1091 m_requestedPosition = -1;
1093 }
1094
1096
1097 Q_EMIT error(QMediaPlayer::FormatError, tr("Failed to load media"));
1098}
1099
1101{
1102 setStreamURL(m_observer, m_resources.toEncoded());
1103}
1104
1106{
1107 resetStream(nullptr);
1108}
1109
1111{
1112 bool firstLoad = true;
1113 for (int i = 0; i < QPlatformMediaPlayer::NTrackTypes; ++i) {
1114 if (tracks[i].count())
1115 firstLoad = false;
1116 tracks[i].clear();
1117 nativeTracks[i].clear();
1118 }
1119 AVPlayerItem *playerItem = [m_observer playerItem];
1120 if (playerItem) {
1121 // Check each track for audio and video content
1122 NSArray *tracks = playerItem.tracks;
1123 for (AVPlayerItemTrack *track in tracks) {
1124 AVAssetTrack *assetTrack = track.assetTrack;
1125 if (assetTrack) {
1126 int qtTrack = -1;
1127 if ([assetTrack.mediaType isEqualToString:AVMediaTypeAudio]) {
1129 setAudioAvailable(true);
1130 } else if ([assetTrack.mediaType isEqualToString:AVMediaTypeVideo]) {
1132 setVideoAvailable(true);
1133 if (m_observer.videoTrack != track) {
1134 m_observer.videoTrack = track;
1135 bool isMirrored = false;
1137 videoOrientationForAssetTrack(assetTrack, orientation, isMirrored);
1138 orientationChanged(orientation, isMirrored);
1139 }
1140 }
1141 else if ([assetTrack.mediaType isEqualToString:AVMediaTypeSubtitle]) {
1143 }
1144 if (qtTrack != -1) {
1146 this->tracks[qtTrack].append(metaData);
1147 nativeTracks[qtTrack].append(track);
1148 }
1149 }
1150 }
1151 // subtitles are disabled by default
1152 if (firstLoad)
1154 }
1156}
1157
1159{
1160 const auto &t = nativeTracks[type];
1162 // subtitle streams are not always automatically enabled on macOS/iOS.
1163 // this hack ensures they get enables and we actually get the text
1164 AVPlayerItem *playerItem = m_observer.m_playerItem;
1165 if (playerItem) {
1166 AVAsset *asset = playerItem.asset;
1167 if (!asset)
1168 return;
1169 AVMediaSelectionGroup *group = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1170 if (!group)
1171 return;
1172 auto *options = group.options;
1173 if (options.count)
1174 [playerItem selectMediaOption:options.firstObject inMediaSelectionGroup:group];
1175 }
1176 }
1177 for (int i = 0; i < t.count(); ++i)
1178 t.at(i).enabled = (i == index);
1180}
1181
1183{
1184 const auto &t = nativeTracks[type];
1185 for (int i = 0; i < t.count(); ++i)
1186 if (t.at(i).enabled)
1187 return i;
1188 return -1;
1189}
1190
1195
1197{
1198 const auto &t = tracks[type];
1199 if (trackNumber < 0 || trackNumber >= t.count())
1200 return QMediaMetaData();
1201 return t.at(trackNumber);
1202}
1203
1204void AVFMediaPlayer::resetStream(QIODevice *stream)
1205{
1206 if (m_mediaStream) {
1209 }
1210
1211 m_mediaStream = stream;
1212
1213 if (m_mediaStream) {
1216 }
1217}
1218
1220{
1221 if (!m_videoSink)
1222 return;
1223 m_videoSink->setNativeSize(size);
1224}
1225
1226void AVFMediaPlayer::orientationChanged(QtVideo::Rotation rotation, bool mirrored)
1227{
1228 if (!m_videoOutput)
1229 return;
1230
1231 m_videoOutput->setVideoRotation(rotation);
1232 m_videoOutput->setVideoMirrored(mirrored);
1233}
1234
1237 bool &mirrored)
1238{
1240 if (videoTrack) {
1241 CGAffineTransform transform = videoTrack.preferredTransform;
1242 if (CGAffineTransformIsIdentity(transform))
1243 return;
1244 qreal delta = transform.a * transform.d - transform.b * transform.c;
1245 qreal radians = qAtan2(transform.b, transform.a);
1246 qreal degrees = qRadiansToDegrees(radians);
1247 qreal scaleX = (transform.a/qAbs(transform.a)) * qSqrt(qPow(transform.a, 2) + qPow(transform.c, 2));
1248 qreal scaleY = (transform.d/abs(transform.d)) * qSqrt(qPow(transform.b, 2) + qPow(transform.d, 2));
1249
1250 if (delta < 0.0) { // flipped
1251 if (scaleX < 0.0) {
1252 // vertical flip
1253 degrees = -degrees;
1254 } else if (scaleY < 0.0) {
1255 // horizontal flip
1256 degrees = (180 + (int)degrees) % 360;
1257 }
1258 mirrored = true;
1259 }
1260
1261 if (qFuzzyCompare(degrees, qreal(90)) || qFuzzyCompare(degrees, qreal(-270))) {
1263 } else if (qFuzzyCompare(degrees, qreal(-90)) || qFuzzyCompare(degrees, qreal(270))) {
1265 } else if (qFuzzyCompare(degrees, qreal(180)) || qFuzzyCompare(degrees, qreal(-180))) {
1267 }
1268 }
1269}
1270
1271#include "moc_avfmediaplayer_p.cpp"
QMediaPlayer player
Definition audio.cpp:213
static void * AVFMediaPlayerObserverStatusObservationContext
static NSString *const AVF_RATE_KEY
static void * AVFMediaPlayerObserverCurrentItemObservationContext
static NSString *const AVF_PLAYABLE_KEY
static NSString *const AVF_STATUS_KEY
static void * AVFMediaPlayerObserverRateObservationContext
static NSString *const AVF_CURRENT_ITEM_KEY
static void setURL(AVFMediaPlayerObserver *observer, const QByteArray &url, const QString &mimeType=QString())
static void * AVFMediaPlayerObserverPresentationSizeContext
static void * AVFMediaPlayerObserverBufferLikelyToKeepUpContext
NSURL * m_URL
static QT_USE_NAMESPACE NSString *const AVF_TRACKS_KEY
static NSString *const AVF_BUFFER_LIKELY_KEEP_UP_KEY
static NSString *const AVF_CURRENT_ITEM_DURATION_KEY
NSString * m_mimeType
static void * AVFMediaPlayerObserverTracksContext
static void setStreamURL(AVFMediaPlayerObserver *observer, const QByteArray &url)
static void * AVFMediaPlayerObserverCurrentItemDurationObservationContext
BOOL m_bufferIsLikelyToKeepUp
NSData * m_data
IOBluetoothDevice * device
QMediaMetaData trackMetaData(TrackType type, int trackNumber) override
qint64 duration() const override
virtual ~AVFMediaPlayer()
void setVolume(float volume)
void processLoadStateChange()
int trackCount(TrackType) override
void setPosition(qint64 pos) override
void setVideoSink(QVideoSink *sink) override
QMediaTimeRange availablePlaybackRanges() const override
void nativeSizeChanged(QSize size)
static void videoOrientationForAssetTrack(AVAssetTrack *track, QtVideo::Rotation &angle, bool &mirrored)
bool isAudioAvailable() const override
QMediaMetaData metaData() const override
void setVideoOutput(AVFVideoRendererControl *output)
void stop() override
float bufferProgress() const override
void setMedia(const QUrl &content, QIODevice *stream) override
qint64 position() const override
int activeTrack(QPlatformMediaPlayer::TrackType type) override
QMediaPlayer::PlaybackState state() const override
void setMuted(bool muted)
void pause() override
QPlatformAudioOutput * m_audioOutput
void play() override
void processDurationChange(qint64 duration)
QUrl media() const override
QList< AVPlayerItemTrack * > nativeTracks[QPlatformMediaPlayer::NTrackTypes]
void processLoadStateFailure()
void updateAudioOutputDevice()
qreal playbackRate() const override
QMediaPlayer::MediaStatus mediaStatus() const override
QIODevice * mediaStream() const override
AVAsset * currentAssetHandle()
bool isVideoAvailable() const override
bool isSeekable() const override
void setAudioOutput(QPlatformAudioOutput *output) override
void setActiveTrack(QPlatformMediaPlayer::TrackType type, int index) override
AVFMediaPlayer(QMediaPlayer *parent)
QList< QMediaMetaData > tracks[QPlatformMediaPlayer::NTrackTypes]
void processBufferStateChange(int bufferProgress)
void setPlaybackRate(qreal rate) override
static QMediaMetaData fromAssetTrack(AVAssetTrack *asset)
static QMediaMetaData fromAsset(AVAsset *asset)
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 deviceChanged()
void mutedChanged(bool muted)
void volumeChanged(float volume)
\inmodule QtCore
Definition qbytearray.h:57
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
\inmodule QtCore \reentrant
Definition qiodevice.h:34
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
Definition qlist.h:399
void append(parameter_type t)
Definition qlist.h:459
void clear()
Definition qlist.h:435
\inmodule QtMultimedia
Q_INVOKABLE bool isEmpty() const
\qmlmethod bool QtMultimedia::mediaMetaData::isEmpty() Returns true if the meta data contains no item...
Q_INVOKABLE void clear()
\qmlmethod void QtMultimedia::mediaMetaData::clear() Removes all data from the MediaMetaData object.
The QMediaPlayer class allows the playing of a media files.
MediaStatus
\qmlproperty enumeration QtMultimedia::MediaPlayer::playbackState
PlaybackState
Defines the current state of a media player.
The QMediaTimeRange class represents a set of zero or more disjoint time intervals.
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
void durationChanged(std::chrono::milliseconds ms)
void positionChanged(std::chrono::milliseconds ms)
void seekableChanged(bool seekable)
void playbackRateChanged(qreal rate)
void audioAvailableChanged(bool audioAvailable)
void stateChanged(QMediaPlayer::PlaybackState newState)
void videoAvailableChanged(bool videoAvailable)
void mediaStatusChanged(QMediaPlayer::MediaStatus status)
void bufferProgressChanged(float progress)
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6028
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
void stop()
Stops the timer.
Definition qtimer.cpp:267
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
\inmodule QtCore
Definition qurl.h:94
QString url(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2817
QByteArray toEncoded(FormattingOptions options=FullyEncoded) const
Returns the encoded representation of the URL if it's valid; otherwise an empty QByteArray is returne...
Definition qurl.cpp:2967
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
The QVideoSink class represents a generic sink for video data.
Definition qvideosink.h:22
QString str
[2]
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
AVFMediaPlayer * m_session
void setURL:mimeType:(NSURL *url,[mimeType] NSString *mimeType)
AVPlayerLayer * m_playerLayer
AVPlayerItem * m_playerItem
AVPlayerItemTrack * videoTrack
@ AutoConnection
QString self
Definition language.cpp:58
qint64 startTime
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
long NSInteger
#define Q_FUNC_INFO
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
EGLStreamKHR stream
const char * mimeType
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
#define qDebug
[1]
Definition qlogging.h:165
#define qWarning
Definition qlogging.h:167
constexpr float qRadiansToDegrees(float radians)
Definition qmath.h:281
auto qAtan2(T1 y, T2 x)
Definition qmath.h:90
auto qPow(T1 x, T2 y)
Definition qmath.h:180
QT_BEGIN_NAMESPACE constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:19
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:21
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
n void setPosition(void) \n\
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLenum GLsizei count
GLuint object
[3]
GLenum GLuint buffer
GLenum type
GLboolean GLuint group
GLfloat angle
GLuint GLenum GLenum transform
GLuint GLenum * rate
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint in
GLsizei GLenum GLboolean sink
GLenum GLsizei len
#define tr(X)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_EMIT
#define emit
#define Q_UNUSED(x)
unsigned long long quint64
Definition qtypes.h:61
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QT_BEGIN_NAMESPACE typedef uchar * output
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
myObject disconnect()
[26]
QReadWriteLock lock
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...