Я пишу приложение для iOS, которое передает видео и аудио по сети.
Я использую AVCaptureSession для захвата необработанных видеокадров с помощью AVCaptureVideoDataOutput и кодирую их в программном обеспечении используя x264. Это прекрасно работает.
Я хотел сделать то же самое для звука, только мне не нужно было так много контроля над звуком, поэтому я хотел использовать встроенный аппаратный кодировщик для создания потока AAC. Это означало использование Audio Converter из слоя Audio Toolbox. Для этого я добавил обработчик аудиокадров AVCaptudeAudioDataOutput:
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
// get the audio samples into a common buffer _pcmBuffer
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &_pcmBufferSize, &_pcmBuffer);
// use AudioConverter to
UInt32 ouputPacketsCount = 1;
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mNumberChannels = 1;
bufferList.mBuffers[0].mDataByteSize = sizeof(_aacBuffer);
bufferList.mBuffers[0].mData = _aacBuffer;
OSStatus st = AudioConverterFillComplexBuffer(_converter, converter_callback, (__bridge void *) self, &ouputPacketsCount, &bufferList, NULL);
if (0 == st) {
// ... send bufferList.mBuffers[0].mDataByteSize bytes from _aacBuffer...
}
}
В этом случае функция обратного вызова для аудио конвертера довольно проста (при условии, что размеры и количество пакетов настроены правильно):
- (void) putPcmSamplesInBufferList:(AudioBufferList *)bufferList withCount:(UInt32 *)count
{
bufferList->mBuffers[0].mData = _pcmBuffer;
bufferList->mBuffers[0].mDataByteSize = _pcmBufferSize;
}
И настройка аудио конвертера выглядит так:
{
// ...
AudioStreamBasicDescription pcmASBD = {0};
pcmASBD.mSampleRate = ((AVAudioSession *) [AVAudioSession sharedInstance]).currentHardwareSampleRate;
pcmASBD.mFormatID = kAudioFormatLinearPCM;
pcmASBD.mFormatFlags = kAudioFormatFlagsCanonical;
pcmASBD.mChannelsPerFrame = 1;
pcmASBD.mBytesPerFrame = sizeof(AudioSampleType);
pcmASBD.mFramesPerPacket = 1;
pcmASBD.mBytesPerPacket = pcmASBD.mBytesPerFrame * pcmASBD.mFramesPerPacket;
pcmASBD.mBitsPerChannel = 8 * pcmASBD.mBytesPerFrame;
AudioStreamBasicDescription aacASBD = {0};
aacASBD.mFormatID = kAudioFormatMPEG4AAC;
aacASBD.mSampleRate = pcmASBD.mSampleRate;
aacASBD.mChannelsPerFrame = pcmASBD.mChannelsPerFrame;
size = sizeof(aacASBD);
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &aacASBD);
AudioConverterNew(&pcmASBD, &aacASBD, &_converter);
// ...
}
Это кажется довольно простым, только ЭТО НЕ РАБОТАЕТ. После запуска AVCaptureSession аудиопреобразователь (в частности, AudioConverterFillComplexBuffer) возвращает ошибку «hwiu» (используемое оборудование). Преобразование работает нормально, если сеанс остановлен, но тогда я ничего не могу зафиксировать...
Мне было интересно, есть ли способ получить поток AAC из AVCaptureSession. Варианты, которые я рассматриваю, следующие:
Каким-то образом использовать AVAssetWriterInput для кодирования аудиосэмплов в AAC, а затем каким-то образом получать закодированные пакеты (не через AVAssetWriter, который будет записывать только в файл).
Реорганизация моего приложения таким образом, чтобы оно использовало AVCaptureSession только на стороне видео и использовало Очереди аудио на стороне аудио. Это усложнит управление потоком (запуск и остановка записи, реагирование на прерывания) и, боюсь, может вызвать проблемы с синхронизацией аудио и видео. Кроме того, это просто не похоже на хороший дизайн.
Кто-нибудь знает, возможно ли получить AAC из AVCaptureSession? Должен ли я использовать здесь аудиоочереди? Может ли это привести к проблемам с синхронизацией или управлением?