#objective-c #core-audio #avfoundation #void-pointers #core-media
#цель-c #core-audio #avfoundation #указатели на пустоту #core-media
Вопрос:
Я работал над чтением в аудиоресурсе с помощью AVAssetReader, чтобы позже я мог воспроизводить аудио с помощью AUGraph с обратным вызовом AudioUnit. У меня работают обратные вызовы AUGraph и AudioUnit, но они считывают файлы с диска, и если файл слишком большой, это займет слишком много памяти и приведет к сбою приложения. Поэтому я вместо этого читаю ресурс напрямую и только ограниченного размера. Затем я буду управлять им как двойным буфером и получать от AUGraph то, что ему нужно, когда это ему нужно.
(Примечание: Я хотел бы знать, могу ли я использовать службы очереди аудио и по-прежнему использовать AUGraph с обратным вызовом AudioUnit, чтобы память управлялась для меня фреймворками iOS.)
Моя проблема в том, что я плохо понимаю массивы, структуры и указатели в C. Часть, где мне нужна помощь, — это взять отдельный AudioBufferList, который содержит один аудиобуфер, и добавить эти данные в другой AudioBufferList, который содержит все данные, которые будут использоваться позже. Я считаю, что мне нужно использовать memcpy, но неясно, как его использовать или даже инициализировать AudioBufferList для моих целей. Я использую MixerHost для справки, который является образцом проекта от Apple, который считывает файл с диска.
Я загрузил свою незавершенную работу, если вы хотите загрузить ее в Xcode. Я выяснил большую часть того, что мне нужно для этого, и как только у меня будут собраны все данные в одном месте, я должен быть готов к работе.
Пример проекта:MyAssetReader.zip
В заголовке вы можете видеть, что я объявляю bufferList как указатель на структуру.
@interface MyAssetReader : NSObject {
BOOL reading;
signed long sampleTotal;
Float64 totalDuration;
AudioBufferList *bufferList; // How should this be handled?
}
Затем я выделяю bufferList таким образом, в значительной степени заимствуя у MixerHost…
UInt32 channelCount = [asset.tracks count];
if (channelCount > 1) {
NSLog(@"We have more than 1 channel!");
}
bufferList = (AudioBufferList *) malloc (
sizeof (AudioBufferList) sizeof (AudioBuffer) * (channelCount - 1)
);
if (NULL == bufferList) {NSLog (@"*** malloc failure for allocating bufferList memory"); return;}
// initialize the mNumberBuffers member
bufferList->mNumberBuffers = channelCount;
// initialize the mBuffers member to 0
AudioBuffer emptyBuffer = {0};
size_t arrayIndex;
for (arrayIndex = 0; arrayIndex < channelCount; arrayIndex ) {
// set up the AudioBuffer structs in the buffer list
bufferList->mBuffers[arrayIndex] = emptyBuffer;
bufferList->mBuffers[arrayIndex].mNumberChannels = 1;
// How should mData be initialized???
bufferList->mBuffers[arrayIndex].mData = malloc(sizeof(AudioUnitSampleType));
}
Наконец, я выполняю цикл чтения.
int frameCount = 0;
CMSampleBufferRef nextBuffer;
while (assetReader.status == AVAssetReaderStatusReading) {
nextBuffer = [assetReaderOutput copyNextSampleBuffer];
AudioBufferList localBufferList;
CMBlockBufferRef blockBuffer;
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(nextBuffer, NULL, amp;localBufferList, sizeof(localBufferList), NULL, NULL,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, amp;blockBuffer);
// increase the number of total bites
bufferList->mBuffers[0].mDataByteSize = localBufferList.mBuffers[0].mDataByteSize;
// carefully copy the data into the buffer list
memcpy(bufferList->mBuffers[0].mData frameCount, localBufferList.mBuffers[0].mData, sizeof(AudioUnitSampleType));
// get information about duration and position
//CMSampleBufferGet
CMItemCount sampleCount = CMSampleBufferGetNumSamples(nextBuffer);
Float64 duration = CMTimeGetSeconds(CMSampleBufferGetDuration(nextBuffer));
Float64 presTime = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(nextBuffer));
if (isnan(duration)) duration = 0.0;
if (isnan(presTime)) presTime = 0.0;
//NSLog(@"sampleCount: %ld", sampleCount);
//NSLog(@"duration: %f", duration);
//NSLog(@"presTime: %f", presTime);
self.sampleTotal = sampleCount;
self.totalDuration = duration;
frameCount ;
free(nextBuffer);
}
Я не уверен в том, что я обрабатываю mDataByteSize и mData, особенно с memcpy. Поскольку mData является указателем void, это очень сложная область.
memcpy(bufferList->mBuffers[0].mData frameCount, localBufferList.mBuffers[0].mData, sizeof(AudioUnitSampleType));
В этой строке, я думаю, следует скопировать значение из данных в localBufferList в позицию в bufferList плюс количество кадров, чтобы поместить указатель туда, куда он должен записать данные. У меня есть пара идей о том, что мне нужно изменить, чтобы это заработало.
- Поскольку указатель void равен всего лишь 1, а не размеру указателя для AudioUnitSampleType, мне может потребоваться умножить его также на sizeof (AudioUnitSampleType), чтобы вернуть memcpy в правильное положение
- Возможно, я неправильно использую malloc для подготовки mData, но поскольку я не уверен, сколько там будет фреймов, я не уверен, что делать для его инициализации
В настоящее время, когда я запускаю это приложение, оно завершает эту функцию недопустимым указателем для bufferList.
Я ценю вашу помощь в том, чтобы помочь мне лучше понять, как управлять списком аудиобуферов.
Комментарии:
1. Я узнал больше об указателях void и о том, как использовать malloc и memcpy, которые, похоже, являются ключевой частью понимания структуры AudioBufferList. Мне нужно будет знать способ сохранения значений в динамическом массиве в C. Я читаю, что подобные функции, возможно, были добавлены с помощью C99. Я продолжаю читать об этом.
2. Я думаю, что я буду накапливать значения из AudioBuffer в массив, который увеличивается с помощью ralloc, но я могу сделать это с блоками по 100, чтобы realloc не вызывался с каждой итерацией. Затем я могу создать массив динамического размера после цикла, использовать memcpy для заполнения этого массива накопленными значениями, а затем установить mData в буфер с помощью этого массива.
Ответ №1:
Я придумал свой собственный ответ. Я решил использовать объект NSMutableData, который позволяет мне добавлять байты из CMSampleBufferRef после вызова CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer, чтобы получить AudioBufferList.
[data appendBytes:localBufferList.mBuffers[0].mData length:localBufferList.mBuffers[0].mDataByteSize];
Как только цикл чтения завершен, у меня есть все данные в моем объекте NSMutableData. Затем я создаю и заполняю AudioBufferList таким образом.
audioBufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
if (NULL == audioBufferList) {
NSLog (@"*** malloc failure for allocating audioBufferList memory");
[data release];
return;
}
audioBufferList->mNumberBuffers = 1;
audioBufferList->mBuffers[0].mNumberChannels = channelCount;
audioBufferList->mBuffers[0].mDataByteSize = [data length];
audioBufferList->mBuffers[0].mData = (AudioUnitSampleType *)malloc([data length]);
if (NULL == audioBufferList->mBuffers[0].mData) {
NSLog (@"*** malloc failure for allocating mData memory");
[data release];
return;
}
memcpy(audioBufferList->mBuffers[0].mData, [data mutableBytes], [data length]);
[data release];
Я был бы признателен за небольшой обзор кода о том, как я использую malloc для создания структуры и ее заполнения. Я периодически получаю ошибку EXC_BAD_ACCESS, но пока не могу точно определить, где находится ошибка. Поскольку я использую malloc в структуре, мне не нужно нигде ее сохранять. Я вызываю «free», чтобы освободить дочерние элементы внутри структуры и, наконец, саму структуру везде, где я использую malloc.