SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

FFmpeg APIで、さまざまな動画を操る

FFmpeg APIで、さまざまな動画を操る - 前編

自作アプリケーションに動画の読み込み処理を組み込む


動画から1フレームを得る(load_frame関数の実装)

 ここまでで、前処理が完了しましたので、いよいよ動画を読み込む処理を見て行きましょう。main()関数からは、先ほど確保したフレームを出力先として利用し、次のように呼び出すこととします。

1フレーム読み込み処理の呼び出し部
/**
 * 動画から最初のフレームを読み込む
 * @param[out] output_frame 読み込んだフレームの出力先
 * @param[in]  filename     動画ファイルのPATH
 */
void load_frame(AVFrame *output_frame, const char *filename);

/*main関数 */
int main(int argc, char *argv[]){
    /* ... FFmpegの初期化(略) ...*/

    /* ... サムネイルの受け取り場所を確保(略) ...*/

    /* フレームのロード  */
    load_frame(frame, argv[1]);

    /* ... 後略(後編で作成) ...*/
}

 load_frame()の処理は、おおよそ次のような流れで実行していきます。

  1. 動画ファイルのフォーマットをFFmpegに解析させる
  2. 動画ファイルの中から、動画ストリームを探し出す
  3. コーデックを開き、読み込みの準備を行う
  4. パケットを読み込む
  5. パケットからフレームを復元する
  6. 目的のフレームが得られるまで、4と5を繰り返す
  7. 利用したメモリを解放し、終了する

動画ファイルのフォーマットをFFmpegに解析させる

 動画ファイルを読み込むには、当然対象となる動画ファイルのフォーマットやコーデックを正確に把握し、それに合致した手順で読み込まなくてはなりません。FFmpegでは、動画ファイルからこれらの情報を予想するための関数が提供されています。

 以下のコードを見てください。

動画ファイルを解析する
/* ファイルのヘッダを読み、フォーマットを得る */
AVFormatContext *formatCtx;
ret = av_open_input_file(&formatCtx, filename, NULL, 0, NULL);
if(ret != 0) error("can't open input file.");

/* ファイルの中身からストリーム情報を得る */
ret = av_find_stream_info(formatCtx);
if(ret < 0) error("can't find stream info.");

 構造体AVFormatContextは「libavformat/avformat.h」で定義されています。この構造体には、対象となるファイルの入出力フォーマットとI/Oにかかわる情報が保存されています。具体的には、ファイル名や入出力ストリーム、メタ情報として動画のタイトルや著者など、となります。

 av_open_input_file()も「libavformat/avformat.h」に宣言があります。この関数では、動画ファイルのヘッダ部を読み込むことで、動画フォーマットを判定します。開いたファイルハンドルや判明したフォーマット情報は、すべてformatCtxへ保存されます。

 続くav_find_stream_info()も「libavformat/avformat.h」で宣言される関数で、パケットを先読みして動画ファイルに含まれるストリームの情報を判別します。動画ファイルには動画だけではなく、音声など、種々のストリームが含まれています。この関数を呼ぶことで、含まれるストリームとその種類が分かるようになります。

動画ファイルの中から、動画ストリームを探し出す

 今回のサンプルではサムネイルを作成するため、実際に読み込みたいのは動画情報を含むストリームです。複数ある(かもしれない)ストリームの中から、サムネイル作成の対象とするフレームをあらかじめ見つけておきます。

 この部分のコードは、次のようになります。

動画を含むストリームの検索
/* ビデオストリームを探す */
int streamIndex = -1;
for(i = 0; i < formatCtx->nb_streams; i++){
    if(formatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO){
        /* ビデオストリームを発見 */
        streamIndex = i;
        break;
    }
}
if(streamIndex < 0) error("can't find video stream.");
AVCodecContext *codecCtx = formatCtx->streams[streamIndex]->codec;

 formatCtx->nb_streamsにはこの動画ファイルが含んでいるストリームの本数が含まれており、formatCtx->streamsには実際のストリーム(AVStream)が含まれています。これらを利用し、ストリームを0から順番に調べています。ビデオ情報を含んでいるかどうかは、streams[i]->codecに含まれる、ストリーム内のコーデック情報(AVCodecContext)を参照し、コーデックの種類で判定しています。コーデックの種類はcodec->codec_typeに含まれるenum CodecTypeという列挙型です。ストリームが動画を含む場合、コーデックの種類はCODEC_TYPE_VIDEOとなります。

 目的のストリームが見つかったら、次の処理のためにそのコーデック情報をcodecCtxへ保存しておきます。

コーデックの種類
 enum CodecTypeは「libavcodec/avcodec.h」で定義されており、他に以下の種類があります。
  • CODEC_TYPE_UNKNOWN
  • CODEC_TYPE_VIDEO
  • CODEC_TYPE_AUDIO
  • CODEC_TYPE_DATA
  • CODEC_TYPE_SUBTITLE
  • CODEC_TYPE_ATTACHMENT
 他に、列挙型の最後の値としてCODEC_TYPE_NBが定義されていますが、これはコーデック種類の個数を表すのに利用されています(_NBはnumberの略だと推測されます)。例えば、「ffmpeg.c」ではCODEC_TYPE_NBを次のように利用しています。
ffmpeg.c より引用
static AVCodecContext *avctx_opts[CODEC_TYPE_NB];

...

for(i=0; i<CODEC_TYPE_NB; i++){
    avctx_opts[i]= avcodec_alloc_context2(i);
}

次のページ

修正履歴

この記事は参考になりましたか?

FFmpeg APIで、さまざまな動画を操る連載記事一覧
この記事の著者

hiratara(ヒラタラ)

1977年に苫小牧市で生まれる。北海道大学理学部数学科卒。小学生の頃、両親に買い与えられたMZ-2500でプログラミングを始めた。学生時代、CGIの自作に没頭し、それ以降WEB開発の魅力に憑かれる。社会人になっても数学好きは変わらず、専門書を買い集めるのが最近の趣味。id:hirataraにてblogを執筆...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/2444 2008/06/26 11:25

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー