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>
18constexpr static
int MAX_BUFFERS_IN_QUEUE = 5;
26 CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
27 if (!formatDescription)
29 const AudioStreamBasicDescription*
const asbd = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription);
33 if (!qtFormat.isValid())
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];
69 abuf.push_back(
QByteArray((
const char*)audioBuffer.mData, audioBuffer.mDataByteSize));
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];
264 const QString url =
"iodevice:///iodevice." + ext;
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()
506 if (m_buffersCounter >= MAX_BUFFERS_IN_QUEUE) {
511 while (m_buffersCounter >= MAX_BUFFERS_IN_QUEUE)
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.
\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
QObject * parent() const
Returns a pointer to the parent object.
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]
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#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