サンプル: サムネイル画像を作る 前編
それでは、早速FFmpegの共有ライブラリを利用してみましょう。例として、任意の動画ファイルから初めのフレームを画像として出力するプログラムを作ってみます。このサンプルプログラムがやっていることは単なるファイルフォーマットの変換であり、わざわざC言語のAPIを利用しなくともffmpegコマンドで実行できてしまうことです。しかし、実装していくと分かりますが、共有ライブラリを利用すると、動画の1コマ1コマに直接触ることができます。これによって、ffmpegコマンドだけではできなかった動画のフィルタリングや編集と言ったことまでが実装できるようになります。
前編では、動画の最初の画像を1枚読み込む処理を作成します。必要な処理は、以下の3つです。
- FFmpegの初期化
- サムネイルの受け取り場所を確保
- 動画から1フレームを得る
では、早速コードを書いていきましょう。
FFmpegの初期化
FFmpegのAPIを利用するにあたり、一番始めにしなくてはならないことはFFmpegの初期化です。FFmpegを初期化するには、次のように記述します。
/* FFmpegの初期化 */
av_register_all();
av_register_all()は、「libavformat/avformat.h」で宣言されています。この関数を呼ぶと、FFmpegで利用できるすべてのファイルフォーマットとコーデックが初期化され、利用できる状態になります。
また、今回は利用しませんが、通常のファイルフォーマットに加えて外部機器やウィンドウサーバーなどからの入出力を有効にしたい場合は、次のようにavdevice_register_all()も呼びます。
avdevice_register_all();
サムネイルの受け取り場所を確保
実際に動画の読み込みを始める前に、動画ファイルから読み込む1フレーム分の格納場所を作っておきます。今回のプログラムでは、前編では確保した領域に最初のフレームを保存し、これを後編でJPEG画像としてエンコードして出力することになります。
/* 画像を保存するフレームの初期化 */
AVFrame *frame;
init_frame(&frame);
AVFrameは、「libavcodec/avcodec.h」で定義されている構造体で、動画の1コマ分を表します。この構造体には、1コマ分の画像の他に、PTS(Presentation Time Stamp)というその画像はいつ表示されるべきかを示す値や、その他動画ならではの情報が含まれています。ただし、ここでは単に画像の保存場所として利用します。
なお、PTSに関してはエンコードを行う後編の記事で詳しく説明します。
フレームを初期化するinit_frame()はFFmpegのAPIではなく自前で定義した関数です。init_frame()の部分コードは、次のようになります。
/** * 出力するフレームの情報 */ #define PIC_WIDTH 640 #define PIC_HEIGHT 480 #define PIC_FORMAT PIX_FMT_YUV420P /** * フレームを初期化する * @param[out] frame 初期化したいAVFrameポインタへのポインタ */ int init_frame(AVFrame **frame){ /* フレーム領域を確保 */ *frame = avcodec_alloc_frame(); if(*frame == NULL) error("can't allocate frame."); /* 保存用バッファを確保 */ int numBytes = avpicture_get_size(PIC_FORMAT, PIC_WIDTH, PIC_HEIGHT); buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t)); if(buffer == NULL) return -1; /* バッファをフレームへセット */ avpicture_fill((AVPicture *)( *frame ), buffer, PIC_FORMAT, PIC_WIDTH, PIC_HEIGHT); return 0; }
avcodec_alloc_frame()で領域を確保し、avpicture_get_size()で必要なバッファのサイズを調べて確保しています。画像フォーマットによって必要なバッファのサイズは異なるため、この関数を呼び出して計算する必要があります。
最後に、avpicture_fill()を利用して確保したAVFrameに作業用バッファを割り当てています。avpicture_fill()はAVPictureを引数に取るため、キャストして渡しています。構造体AVFrameと構造体AVPictureは先頭部分の定義が一緒になっているため、キャストが可能です。AVPictureは純粋に画像データのみを含む構造体です。
