6#include <QtCore/qmutex.h>
7#include <QtCore/qiodevice.h>
8#include <QMimeDatabase>
10#include "private/qcoreaudioutils_p.h"
11#include <QtCore/qloggingcategory.h>
13#include <AVFoundation/AVFoundation.h>
26 CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
27 if (!formatDescription)
29 const AudioStreamBasicDescription*
const asbd = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription);
37 size_t audioBufferListSize = 0;
38 OSStatus err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer,
44 kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
49 CMBlockBufferRef blockBuffer = NULL;
50 AudioBufferList* audioBufferList = (AudioBufferList*) malloc(audioBufferListSize);
52 err = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer,
58 kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
61 free(audioBufferList);
66 for (UInt32
i = 0;
i < audioBufferList->mNumberBuffers;
i++)
68 AudioBuffer audioBuffer = audioBufferList->mBuffers[
i];
72 free(audioBufferList);
73 CFRelease(blockBuffer);
75 CMTime sampleStartTime = (CMSampleBufferGetPresentationTimeStamp(sampleBuffer));
76 float sampleStartTimeSecs = CMTimeGetSeconds(sampleStartTime);
86- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader
87 shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
95 if (!(self = [super
init]))
103-(BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader
104 shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
108 if (![loadingRequest.request.URL.scheme isEqualToString:
@"iodevice"])
117 device->seek(loadingRequest.dataRequest.requestedOffset);
118 if (loadingRequest.contentInformationRequest) {
119 loadingRequest.contentInformationRequest.contentLength =
device->size();
120 loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
123 if (loadingRequest.dataRequest) {
124 NSInteger requestedLength = loadingRequest.dataRequest.requestedLength;
125 int maxBytes =
qMin(32 * 1024,
int(requestedLength));
128 while (submitted < requestedLength) {
133 [loadingRequest.dataRequest respondWithData:[NSData dataWithBytes:buffer length:len]];
138 [loadingRequest finishLoading];
150 float sampleRate =
format.sampleRate();
151 int nChannels =
format.channelCount();
152 int sampleSize =
format.bytesPerSample() * 8;
155 NSDictionary *audioSettings = [NSDictionary dictionaryWithObjectsAndKeys:
156 [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
157 [NSNumber numberWithFloat:sampleRate], AVSampleRateKey,
158 [NSNumber numberWithInt:nChannels], AVNumberOfChannelsKey,
159 [NSNumber numberWithInt:sampleSize], AVLinearPCMBitDepthKey,
160 [NSNumber numberWithBool:isFloat], AVLinearPCMIsFloatKey,
161 [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
162 [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
165 return audioSettings;
168QAudioFormat qt_format_for_audio_track(AVAssetTrack *track)
171 CMFormatDescriptionRef
desc = (__bridge CMFormatDescriptionRef)track.formatDescriptions[0];
172 const AudioStreamBasicDescription*
const asbd =
173 CMAudioFormatDescriptionGetStreamBasicDescription(
desc);
177 if (asbd->mBitsPerChannel == 0)
192 [m_reader cancelReading];
196 [m_readerOutput release];
203 m_readingQueue = dispatch_queue_create(
"reader_queue", DISPATCH_QUEUE_SERIAL);
204 m_decodingQueue = dispatch_queue_create(
"decoder_queue", DISPATCH_QUEUE_SERIAL);
216 dispatch_release(m_readingQueue);
217 dispatch_release(m_decodingQueue);
227 if (!m_device && m_source ==
fileName)
238 NSURL *nsURL = m_source.toNSURL();
239 m_asset = [[AVURLAsset alloc] initWithURL:nsURL options:nil];
265 NSString *urlString =
url.toNSString();
266 NSURL *nsURL = [NSURL URLWithString:urlString];
268 m_asset = [[AVURLAsset alloc] initWithURL:nsURL options:nil];
272 [m_asset.resourceLoader setDelegate:m_readerDelegate queue:m_decodingQueue];
280 if (m_decodingContext) {
281 qCDebug(qLcAVFAudioDecoder()) <<
"AVFAudioDecoder has been already started";
292 m_decodingContext = std::make_shared<DecodingContext>();
293 std::weak_ptr<DecodingContext> weakContext(m_decodingContext);
295 auto handleLoadingResult = [=]() {
296 NSError *
error = nil;
297 AVKeyValueStatus status = [m_asset statusOfValueForKey:@"tracks" error:&error];
299 if (status == AVKeyValueStatusFailed) {
300 if (
error.domain == NSURLErrorDomain)
302 QString::fromNSString(
error.localizedDescription));
305 tr(
"Could not load media source's tracks"));
306 }
else if (status != AVKeyValueStatusLoaded) {
307 qWarning() <<
"Unexpected AVKeyValueStatus:" << status;
315 [m_asset loadValuesAsynchronouslyForKeys:@[ @"tracks" ]
316 completionHandler:[=]() {
317 invokeWithDecodingContext(weakContext, handleLoadingResult);
321void AVFAudioDecoder::decBuffersCounter(
uint val)
325 m_buffersCounter -=
val;
330 m_buffersCounterCondition.
wakeAll();
335 qCDebug(qLcAVFAudioDecoder()) <<
"stop decoding";
337 m_decodingContext.reset();
338 decBuffersCounter(m_cachedBuffers.
size());
339 m_cachedBuffers.
clear();
363 if (m_cachedBuffers.
empty())
368 decBuffersCounter(1);
378 qCDebug(qLcAVFAudioDecoder()) <<
"Invalid media. Error code:" << errorCode
392void AVFAudioDecoder::onFinished()
394 m_decodingContext.reset();
400void AVFAudioDecoder::initAssetReader()
402 qCDebug(qLcAVFAudioDecoder()) <<
"Init asset reader";
407 NSArray<AVAssetTrack *> *tracks = [m_asset tracksWithMediaType:AVMediaTypeAudio];
413 AVAssetTrack *track = [tracks objectAtIndex:0];
422 NSError *
error = nil;
423 NSDictionary *audioSettings = av_audio_settings_for_format(
format);
425 AVAssetReaderTrackOutput *readerOutput =
426 [[AVAssetReaderTrackOutput alloc] initWithTrack:track outputSettings:audioSettings];
427 AVAssetReader *
reader = [[AVAssetReader alloc] initWithAsset:m_asset error:&error];
432 if (![reader canAddOutput:readerOutput]) {
437 [reader addOutput:readerOutput];
440 m_decodingContext->m_reader =
reader;
441 m_decodingContext->m_readerOutput = readerOutput;
446void AVFAudioDecoder::startReading()
449 Q_ASSERT(m_decodingContext->m_reader);
453 if (![m_decodingContext->m_reader startReading]) {
460 std::weak_ptr<DecodingContext> weakContext = m_decodingContext;
465 auto copyNextSampleBuffer = [=]() {
466 auto decodingContext = weakContext.lock();
467 if (!decodingContext)
470 CMSampleBufferRef sampleBuffer = [decodingContext->m_readerOutput copyNextSampleBuffer];
474 dispatch_async(m_decodingQueue, [=]() {
475 if (!weakContext.expired() && CMSampleBufferDataIsReady(sampleBuffer)) {
478 if (audioBuffer.isValid())
479 invokeWithDecodingContext(weakContext,
480 [=]() { handleNewAudioBuffer(audioBuffer); });
483 CFRelease(sampleBuffer);
489 dispatch_async(m_readingQueue, [=]() {
490 qCDebug(qLcAVFAudioDecoder()) <<
"start reading thread";
496 waitUntilBuffersCounterLessMax();
497 }
while (copyNextSampleBuffer());
500 invokeWithDecodingContext(weakContext, [
this]() { onFinished(); });
504void AVFAudioDecoder::waitUntilBuffersCounterLessMax()
512 m_buffersCounterCondition.
wait(&m_buffersCounterMutex);
533void AVFAudioDecoder::invokeWithDecodingContext(std::weak_ptr<DecodingContext> weakContext, F &&
f)
535 if (!weakContext.expired())
544#include "moc_avfaudiodecoder_p.cpp"
QAudioBuffer handleNextSampleBuffer(CMSampleBufferRef sampleBuffer)
IOBluetoothDevice * device
QAudioBuffer read() override
void setAudioFormat(const QAudioFormat &format) override
AVFAudioDecoder(QAudioDecoder *parent)
QUrl source() const override
QIODevice * sourceDevice() const override
QAudioFormat audioFormat() const override
void setSource(const QUrl &fileName) override
void setSourceDevice(QIODevice *device) override
virtual ~AVFAudioDecoder()
static Q_MULTIMEDIA_EXPORT QAudioFormat toQAudioFormat(const AudioStreamBasicDescription &streamFormat)
The QAudioDecoder class implements decoding audio.
Error
Defines a media player error condition.
void push_back(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore \reentrant
bool isOpen() const
Returns true if the device is open; otherwise returns false.
bool isReadable() const
Returns true if data can be read from the device; otherwise returns false.
qsizetype size() const noexcept
bool empty() const noexcept
QMimeType mimeTypeForData(const QByteArray &data) const
Returns a MIME type for data.
QString preferredSuffix
the preferred suffix for the MIME type
QThread * thread() const
Returns the thread in which the object lives.
void enqueue(const T &t)
Adds value t to the tail of the queue.
T dequeue()
Removes the head item in the queue and returns it.
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QThread * currentThread()
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
void clear()
Resets the content of the QUrl.
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
AVFAudioDecoder * m_decoder
QImageReader reader("image.png")
[1]
#define MAX_BUFFERS_IN_QUEUE
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
GLint GLsizei GLsizei GLenum format
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
QUrl url("example.com")
[constructor-url-reference]
AVAssetReaderTrackOutput * m_readerOutput
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent