以前の記事「CFNetworkフレームワークを用いた入力HTTPストリームの作成」でネットワークからデータを取得する方法について述べました。今回はここから、Audio File Stream ServicesとAudio Queue Servicesを用いてオーディオ再生を行う方法について説明します。なお、詳細はAppleのドキュメント「Audio Queue Services Programming Guide」を参照してください。
概要
ストリーミングオーディオを再生するには次の手順を踏みます。
- CFNetworkを用いてストリームデータを取得
- Audio File Stream Servicesを用いてネットワークパケットを解析
- Audio Queue Servicesを用いてそのオーディオを再生
この流れを次に示します。矩形はオブジェクト、丸四角はコールバックを示し、実線はオブジェクトなどの生成、破線はオーディオデータの流れを示しています。そして、破線上の赤い文字は発生条件、黒い文字は呼び出し関数を示しています。
1. CFReadSream (CFNetwork)
CFNetworkのCFReadSreamを用いてネットワーク越しにデータを取得します。基本的な部分は以前の記事「CFNetworkフレームワークを用いた入力HTTPストリームの作成」と同様なので、ここでは詳細は述べません。
データを取得したときにイベントkCFStreamEventHasBytesAvailableが発生で、AudioFileStreamParseBytesによってAudioFileStreamにデータを渡すようにします。具体的には次のように書きます。
switch(event) { case kCFStreamEventHasBytesAvailable: { UInt8 buf[4096]; CFIndex bytesRead = CFReadStreamRead(stream, buf, sizeof(buf)); AudioFileStreamParseBytes(globalAudioFileStream, bytesRead, buf, 0); } break;
2. Audio File Stream Services
ネットワークから得られたデータはオーディオパケット単位で得られるわけではない上、オーディオパケット以外のデータとしてプロパティが混じっています。次の図はこれを概念化したものです。
オーディオデータは(上図の状態0)のようにオーディオパケット(青)とプロパティ(紫)が混じっています。さらに、ネットワークから得られるデータ場合はオーディオパケットなどの境界で適切に分割されていません(上図の状態1)。そのため、Audio File Stream Servicesによってオーディオデータをパケット単位で取得することと、ストリーム中のプロパティを取得することを行います(上図の状態2)。
Audio File Stream Servicesで行うべき処理について次に示します。
- 関数AudioFileStreamOpenによって新規オーディオストリーム解析器を作成
オーディオデータとメタデータ (プロパティ) のためにコールバック関数AudioFileStreamPacketsProcとAudioFileStreamPropertyListenerProcを渡す。 - 先ほどのCFReadSreamからストリームデータを取得
実際には可能な限りギャップなしでデータを解析器に継続的に渡すようにする必要があります。 - ストリームの解析が終了時に関数AudioFileStreamCloseを呼び出して、解析器のクローズとデアロケートを行う
また、2つのコールバックでは次のような処理を行います。
- プロパティコールバック
プロパティ値を取得することができます (関数AudioFileStreamGetPropertyInfoとAudioFileStreamGetPropertyを呼ぶ) 今回は再生可能な状態になった時点でAudio Queueを作成します。 - オーディオデータコールバック
データの再生、ファイルへの保存といった処理を行うとよいです。
今回はバッファを作成して、それをオーディオキューに追加します。
次の2つのコード片はこれらコールバックの重要な部分だけの抜き出したものです。
// myAudioFileStream_PropertyListenerProc switch(inPropertyID){ case kAudioFileStreamProperty_ReadyToProducePackets: { AudioStreamBasicDescription asbd; UInt32 size = sizeof(asbd); AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &size, (void*) &asbd); AudioQueueNewOutput(&asbd, &myAudioQueueOutputCallback, NULL, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &globalAudioQueue); AudioQueueSetParameter(globalAudioQueue, kAudioQueueParam_Volume, 1.0); AudioQueueStart(globalAudioQueue, NULL); break; // myAudioFileStream_PacketsProc AudioQueueBufferRef aqbuf; OSStatus err = AudioQueueAllocateBufferWithPacketDescriptions(globalAudioQueue, inNumberBytes, inNumberPackets, &aqbuf); memcpy(aqbuf->mAudioData, inInputData, inNumberBytes); aqbuf->mAudioDataByteSize = inNumberBytes; AudioQueueEnqueueBuffer(globalAudioQueue, aqbuf, inNumberPackets, inPacketDescriptions);
3. Audio Queue Services
通常、オーディオキューコールバック関数ではバッファを再利用するようなコードを書きますが、今回の例では作成したバッファを削除しています。
AudioQueueFreeBuffer(inAQ, inBuffer);
まとめ
Audio FileStream ServicesとAudio Queue Servicesを用いてオーディオ再生を行う方法について説明しました。なお、この例ではコードを簡潔にするためにオーディオバッファを毎回作成、破棄を行っていますが、実際にはメモリ効率のため使い回す必要があります。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。