フレームを返却用の領域にコピーする
load_frame関数では、output_frameにフレームを戻すことになっていました。ここまでで、avcodec_decode_videoで復号したフレームはコーデックが管理する領域に保持されているので、これをこのまま返すことはできません。そこで、先ほど入手できたフレームをavcodec_decode_videoへコピーする必要があります。
今回は画像のサイズも変更するため、静止画編集のAPIであるlibswscaleを利用します。コードは次のようになります。
while(パケット読み出し){ /* ... フレームを復号する処理(略) ... */ /* フレームが得られたので、画像サイズを変換し、 出力先フレームへ保存 */ /* 変換の設定 */ struct SwsContext *swsCtx = sws_getContext( codecCtx->width, codecCtx->height, codecCtx->pix_fmt, PIC_WIDTH , PIC_HEIGHT , PIC_FORMAT, 0, NULL, NULL, NULL); /* 画像変換する */ sws_scale(swsCtx, frame->data, frame->linesize, 0, codecCtx->height, output_frame->data, output_frame->linesize); /* 変換設定の破棄 */ sws_freeContext(swsCtx); /* ... 後略 ... */ }
sws_getContext、sws_scale、sws_freeContextはすべて「libswscale/swscale.h」にて宣言されています。sws_getContextでどんな変換をしたいかを指定し、SwsContext構造体を作ってもらいます。それをsws_scaleに渡すと、画像の変換を行ってくれます。ここでは、frameを変換してoutput_frameへ保存させています。最後に、sws_freeContextで確保した領域を解放して完了です。
1フレームを入手したらループを抜ける
一般的な動画の読み込みでは、ループさせてパケットをどんどん読んでいきますが、今回の例では最初のフレームのサムネイルを作るだけなので、1フレーム入手できればそれ以上パケットを読む必要はありません。ループの最後に次のようにbreakを書き、さっさとループを抜けてしまいましょう。
while(パケット読み出し){ /* ... 復号+サイズ変更処理(略) ... */ /* 今回は最初のフレームだけが必要なので、 フレームが得られていれば終了 */ if(isFinish) break; }
load_frame関数内で利用したメモリを解放し、終了する
これで目的の処理はすべて完了しました。最後にコーデックとストリームを閉じ、確保した領域を解放しましょう
/* コーデックを閉じる */ avcodec_close(codecCtx); /* ファイルを閉じる */ av_close_input_file(formatCtx); /* 確保したメモリを解放する */ av_freep(&frame);
avcodec_closeは「libavcodec/avcodec.h」、av_close_input_fileは「libavformat/avformat.h」、av_freepは「libavutil/mem.h」にそれぞれ宣言されています。
これで読み込み処理の説明は終わりです。次の書き込み処理に関しては、後編で解説します。
FFmpegの構造体のまとめ
デコード処理の説明が一通り終わったところで、一度FFmpeg APIで登場する構造体をまとめておきましょう。
入出力操作を表す構造体
動画を扱うという性質上、FFmpegには入出力にかかわる構造体が多く登場します。その関係を見てみましょう。
その前に、AVStreamについてまだ説明していなかったので、簡単に説明します。この構造体は「libavfromat/avformat.h」で定義されており、codecフィールドにAVCodecContextを保持しています。
FFmpegの入出力を取り扱う各構造体の関係を表すと、次のようなイメージになります。
AVFormatContext - AVInputFormat (又は AVOutputFormat) | +-AVStream | | | +-AVCodecContext - AVCodec | +-AVStream | | | +-AVCodecContext - AVCodec | ... 以下動画内にあるストリームの数だけ続く ...
AVCodecはコーデックであり、音声データや動画データをどのように符号化・復号するのかを表す部分です。コーデックがあれば、音声や動画を適切に符号化することはできますが、音声と動画をうまく同じファイルに保存するためにはそのためのファイル形式が必要です。その部分を扱うのがAVInputFormatやAVOutputFormatです。これはいわゆるコンテナの部分で、複数の音声や動画などのストリームをどのようにファイルにまとめるのかを表したデータです。
入出力されるデータを表す構造体
ここまでで、入出力されるデータを表す構造体としてはAVPicture、AVFrame、AVPacketが登場しました。
AVPacketはファイルに保存されている音声や動画の1つ分のデータを表しており、AVInputFormatやAVOutputFormatによって扱われます。FFmpegのデータ解析ループは、パケットを単位にして行われます。
AVPictureとAVFrameはどちらも動画の"画"を表すデータで、主に動画のAVCodecによって扱われます。両者の違いは、AVPictureは単純に画像だけのデータであることに対して、AVFrameには"動"の側面が含まれることです。具体的には、何コマ目の画像であるとか表示される時間などの情報を含んでいます。
これらの関係を簡単に説明すると、ファイルからAVPacketを一つずつ読み出し、それをコーデックで処理するとAVFrame(動画)や音声を得られるイメージとなります。
まとめ
本稿では、以下の内容について説明しました。
- FFmpeg APIで登場する構造体、関数
- 動画を読み込むのに必要な手続き
後編では、動画の書き込み処理について説明します。
参考文献
- 『FFmpegで作る動画共有サイト』 月村潤・本間雅洋・堀田直孝・原一浩・足立健誌・尾花衣美・堀内康弘・寺田学 著、毎日コミュニケーションズ、2008年1月
- 『An FFmpeg and SDL Tutorial』
- パイオニア株式会社 『技術開発【MPEG技術解説】』

