私が自分のアプリケーションに利用していたffmpegのライブラリなんですが、そろそろ最新のffmpegに入れ替えようとしました。
すると、今まで符号付き16ビットとして利用出来ていた動画の音声パケットフォーマット(s16xx)が、FLTP(?)とかになっていました
このタイプをチェックしていたためアプリはパケットを処理せず終了していました。
そこで、
「そんなチェックハズしゃええやん」
とチェックを単に外すと音パケットが全然足りなくなりました。google先生に尋ねるとフォーラムとかで私と同じ境遇の人々がいて質問していました。
その回答で英語で
You need to remember that AV_SAMPLE_FMT_FLTP is a planar mode. If your code is expecting an AV_SAMPLE_FMT_S16 (interleaved mode) output, you need to reorder the samples after converting. Considering 2 audio channels and using interleaved mode, the samples are ordered as “c0, c1, c0, c1, c0, c1, …”. Planar mode is “c0, c0, c0, …, c1, c1, c1, …”.
プラナーって映像とプラナーしてるわけ?
だとしたらどうしたら良い?
と思っていたら。どうやらこれは(例えばステレオなら)
ch0(R)0,1,2,3,4,5,6,7,8…….
ch1(L)0,1,2,3,4,5,6,7,8…….
と音声チェネルがプラナーしてるってことが分かった。そしてさらに、
音声の値もフローティングポイント、すなわちfloat型で値の範囲が-1.0~1.0であることが分かった
フロートの値は符号付き16ビットに変換するのでそれぞれ32768を乗算してやれば、
-32768~32768
になる。そして、
右の1個めを最初に、左の1個めを次に、右の2個めをその次、左の2個めをその次・・・・・・・
とやって元の16bitのフォーマットが取り返せる
で、そのプラナーな要素は以下に格納されている
デコード結果(AVFrame)のextended_data[n] //チャネルnの音声パケット
ということで以下にコード例(ここではステレオ限定で)
//デコードが終わってから avcodec_decode_audio4(audioCodecCtx, decodedAudio, &decodedSize, &packet); if(audioCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP){ short *audioPkt = NULL; // パケット格納域 size_t packetSize = 0; // パケットバイト長 int sampleCnt = audioCodecCtx->decodedAudio->nb_samples; // サンプル数 float *channel0 = (float*)decodedAudio->extended_data[0]; // 元の型はuint8_tな float *channel1 = (float*)decodedAudio->extended_data[1]; packetSize = sampleCnt * 2 * sizeof(short); // バイトサイズ(ステレオ2ch*sizeof(short)*サンプル数) if(packetSize){ audioPkt = (short *)av_mallocz(packetSize); // ffmpegなんで・・・ for(int i=0;i < sampleCnt;i++){ // サンプル数分 audioPkt[i*2] = (short)(channel0[i] * 32767.0f); audioPkt[i*2+1] = (short)(channel1[i] * 32767.0f); } } // この'audioPkt'が今までの16ビットデータなので。これを使う } else { .................. }
※エラーチェックとか、領域の開放とかが抜けてますからね。このままコピペはダメですよ
これ、最初
audioPkt[i*2] = (short)(channel0[i] * 32767.0f);
のところを
audioPkt[i*2] = (short)channel0[i] * 32767.0f;
としてたら全然音が出なかった。
良い子の皆さんなら何故だかわかるよね(私は馬鹿でした)
なぜffmpegがこの様に変更したかとかは知りませんが、以下参考までに
ffmpegを新しくするのに私が遭遇したのは、その他では
・フレームのアロケーションで
avcodec_alloc_frame()を使わないでav_frame_alloc()を使う
・フレームの初期値設定
avcodec_get_frame_defaults()を使わないでav_frame_unref()を使う
・AVCODEC_MAX_AUDIO_FRAME_SIZEの定義が無くなった
→1秒分を想定した長さを入れとけってことで別名で(ここでは)192000を定義しました
以上で動くには動きました。