ESP @Wiki

P-FFMPEG1

最終更新:

匿名ユーザー

- view
メンバー限定 登録/ログイン

How to use libavcodecs


FFmpegが提供する、libavcodecsについて説明を行っていく。

libavodec概要


open input movie


入力されるファイルを開く前に、libavcodec(ffmpeg)が持つ機能を有効にするために、おまじないをする必要がある。そして、その後で入力されるファイルを、libavcodecが提供する関数で開く。

  1. 有効なコーデックをアクティベートする
  2. 入力ファイルを開き、基本的な情報にアクセスする

有効なコーデックをアクティベートする

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


入力ファイルにアクセスし、どのコーデックをオープンすれば良いかを得て、コーデックをオープンする過程を説明する。繁雑なので、特にビデオコーデックのみを開くコードを記述していく。

  1. 入力ストリームのビデオストリームへアクセスする
  2. 最適なビデオデコーダーを得る
  3. いよいよコーデックをオープンする

入力ストリームのビデオストリームへアクセスする

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);

resample


まとめのコード