ネットワーク経由でビデオとオーディオをストリーミングする iOS アプリを作成しています。
私はAVCaptureSessionを使用して、 AVCaptureVideoDataOutputを使用して生のビデオ フレームを取得し、 x264 を使用してソフトウェアでエンコードしています。これはうまくいきます。
オーディオについても同じことをしたかったのですが、オーディオ側でそれほど多くの制御を必要としないため、組み込みのハードウェアエンコーダーを使用して AAC ストリームを生成したかっただけです。これは、オーディオ ツールボックス レイヤーからオーディオ コンバーターを使用することを意味していました。そのために、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);
// ...
}
これは、 IT DOES NOT WORKだけでかなり単純明快に思えます。AVCaptureSession が実行されると、オーディオ コンバーター (具体的には AudioConverterFillComplexBuffer) が「hwiu」(使用中のハードウェア) エラーを返します。セッションが停止している場合、変換は正常に機能しますが、何もキャプチャできません...
AVCaptureSession から AAC ストリームを取得する方法があるかどうか疑問に思っていました。私が検討しているオプションは次のとおりです。
AVAssetWriterInput を使用してオーディオ サンプルを AAC にエンコードし、エンコードされたパケットを何らかの方法で取得します (ファイルにのみ書き込む AVAssetWriter 経由ではありません)。
ビデオ側でのみ AVCaptureSession を使用し、オーディオ側でAudio Queuesを使用するように、アプリを再編成します。これにより、フロー制御 (録画の開始と停止、中断への対応) がより複雑になり、オーディオとビデオの間の同期の問題が発生する可能性があります。また、良いデザインとは思えません。
AVCaptureSession から AAC を取得できるかどうかは誰にもわかりませんか? ここで Audio Queues を使用する必要がありますか? これにより、同期または制御の問題が発生する可能性はありますか?