How to use libavcodecs
FFmpegが提供する、libavcodecsについて説明を行っていく。
libavodec概要
open input movie
入力されるファイルを開く前に、libavcodec(ffmpeg)が持つ機能を有効にするために、おまじないをする必要がある。そして、その後で入力されるファイルを、libavcodecが提供する関数で開く。
- 有効なコーデックをアクティベートする
- 入力ファイルを開き、基本的な情報にアクセスする
有効なコーデックをアクティベートする
avcodec_register_all(); // おまじない その1 av_register_all(); // おまじない その2
入力ファイルを開き基本的な情報にアクセスする
AVFormatContext *avFormatCtx;
const char *filename = "キャプチャ.m2p";
AVFormatParameters = *avFormatPrm;
av_open_input_file(&avFormatCtx,
filename,
NULL, // AVInputFormat: NULLでOK
0, // buf_size: 0でOK
avFormatPrm);
av_find_stream_info(avFormatCtx); // 簡単なパース
で開く。これは、メディアを開いた状態で、デコードに必要なコーデック等はまだ開いておらず、この後で徐々に必要な情報にアクセスし、コーデックを有効にしていく。また、メディア情報のダンプが成功するようになっており、ターミナルにムービーの情報を出力することができる。
dump_format(avFormatCtx, 0, filename, false);
ターミナルへの出力
以下工事中
open codecs
入力ファイルにアクセスし、どのコーデックをオープンすれば良いかを得て、コーデックをオープンする過程を説明する。繁雑なので、特にビデオコーデックのみを開くコードを記述していく。
- 入力ストリームのビデオストリームへアクセスする
- 最適なビデオデコーダーを得る
- いよいよコーデックをオープンする
入力ストリームのビデオストリームへアクセスする
for(int i=0; i< avFormatCtx->nb_streams; i++) {
int type = avFormatCtx->streams[i]->codec->codec_type;
if (type == CODEC_TYPE_VIDEO) {
videoStream = i; // 映像
} else if (type == CODEC_TYPE_AUDIO) {
audioStream = i; // 音声
} else {
; // なんでしょ
}
}
先ほど得られた、AVFormatContextのメンバ nb_streamsの回数だけループし、int type =,,,,のようにしてそのメディアが、ビデオなのか、オーディオなのかを判定する。コード中のvideoStreamが映像のトラック番号を保持することになる。
最適なビデオデコーダーを得る
vCodecCtx = avFormatCtx->streams[videoStream]->codec; vCodec = avcodec_find_decoder(vCodecCtx->codec_id);
実際に、vCodecCtxにビデオメディアの情報(サイズ、FPS等)がセットされ、vCodecがコーデックそのものを指すようになる。
いよいよコーデックをオープンする
avcodec_open(vCodecCtx, vCodec);
だけ。対応するコーデックがない場合、エラーで落ちる。
これで、デコードをするためのコーデックを得ることができた。後は、ムービーをリードし、正しくデコードしてあげるだけだ。
read packet
通常ムービーのパケットを読み込む方法には、2種類ある。1つ目は、1パケットずつ読み込み、必要なパケット数がたまった段階で、デコードを行う方法である。2つ目は、ビデオ1フレームに必要なパケットを一気に読み込みデコードを行う方法である。この区別はlibavcodecが提供する関数に基づく。
2つ目の方法の方が簡単で制御がしやすい半面、パケットの正確で高精度なタイムコード、すなわち(多分)PTSを得ることが不可能であるが、DTSは簡単に得ることができる。
私は手抜きで、この2つ目の方法を用いている。
典型的な流れは以下のとおり、
int size;
int dts;
uint8_t* data;
// if <= -1 : end of file
while (av_read_frame(avFormatCtx, &packet) > -1) {
dts = packet.duration;
data = packet.data;
size = packet.size;
if (packet.stream_index == videoStream) {
decodeVideo(data);
} else if (packet.stream_index == audioStream) {
decodeAudio(data);
}
}
つまり、av_read_frame()で、ビデオまたはオーディオ1フレーム(ビデオなら1枚、オーディオなら2048サンプルとか)を1度で準備し、デコードルーチンへそのデータを渡す。
デコードの基本動作はこの処理を繰り返すことにある。
decode
デコードを行う際は、前の工程、すなわちリードしたパケットを入力する。ここでは、映像、音声それぞれのデコードの説明を行う。
映像のデコード
avcodec_decode_video(vCodecCtx,
YUVData,
&doneVideo,
inputVideoData,
inputVideoDataSize);
第1引数 vCodecCtxは、初期に開いたコーデック。第4引数 inputVideoDataは、packet.data つまりリードされたデータである。イメージとして、デコードされていないmpeg2のパケットデータなどである。第5引数 inputVideoDataSizeは、そのmpeg2パケットは一体何バイトであるのかという情報をデコードコーデックに引きわたす。
さて、説明を飛ばした第2・3引数であるが、簡単な第3引数から。第3引数 doneVideo は、ビデオパケットをデコードした結果、YUV形式の画像をデコードできたかを教えてくれる。通常、画像を得るまでには、2〜3枚ほどの入力フレームが必要になる。よって、この値が0以外であった場合に、デコードできた、と判定できる。
最後に、第2引数であるが、BMPファイルはみなさんごぞんじであろう。あれと同じで、人間が目で判別できる形式で、このYUVDataにデコード結果がセットされる。いわゆるrawデータである。
このYUVDataを得ることができるので、libx264を用いたエンコードの入力画像として渡してあげることができる。
音声のデコード
avcodec_decode_audio(aCodecCtx,
(short *)PCMData,
&decodedSamples,
inputAudioData,
inputAudioDataSize);
第1・4・5引数は、先ほどのビデオと同じ。
第3引数 decodedSamples は先ほどの doneVideoとは異なり、この変数にデコードされたサンプル数がセットされる。0であった場合は当然、デコードしたがまだ結果を得られていない状態である。
第2引数 PCMData にWAVEでお馴染のエンコードされていない、生データがセットされる。(shot *)でバッファを渡すにはわけがあり、ステレオ2チャンネルのとき、32bitデータを考えた場合に、上位16bitに右の音声、下位16bitに左の音声が記録される。そのため、キャストをしている。
deinterlace
libavcodecが提供する関数を用いることで、容易にインターレースの解除を行うことが出来る。デコードの結果得られたYUVデータと、縦横、そしてフォーマットを指定すれば良い。
AVPicture deinterlacedYUVData;
avpicture_deinterlace(&deinterlacedYUVData,
(AVPicture*)YUVData,
PIX_FMT_YUV420P,
width,
height);
