SHOEISHA iD

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

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

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

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

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

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

ストリームを開き、動画コーデックを割り当てる

ストリームの追加

 前編で説明したように、動画ファイルには音声、動画等、複数のストリームが含まれます。読み込みの場合には、複数のストリームの中から動画を扱うストリームだけを選び出す処理を書き、そのストリームから来たパケットを復号してフレームを得ました。

 書き出しでもパケットの書き込みはストリームを指定して行うため、そのためのストリームを用意しておく必要があります。先ほど用意したAVFormatContextへパケットを書き込むためのAVStreamを追加します。

ストリームを追加
/* 新たなストリームを追加 */
AVStream *stream = av_new_stream(formatCtx, 0);
if(stream == NULL) error("can't alloc stream.");

 av_new_streamはlibavformat/avformat.hで宣言されている関数です。第1引数にAVFormatContextを渡し、第2引数にストリームID(今回は0を利用)を渡します。

コーデックの取得

 符号化に利用すべきコーデックを決定し、そのIDを得ます。コーデックは先ほど追加したストリームにセットされます。

コーデックを探す
/* codecを予測 */
enum CodecID codec_id = av_guess_codec(
    formatCtx->oformat, NULL, formatCtx->filename, NULL, 
    CODEC_TYPE_VIDEO
    );

/* codecを得る */
AVCodec *codec = avcodec_find_encoder(codec_id);
if(codec == NULL) error("can't find codec(encoder).");

 av_guess_codecはlibavformat/avformat.hで宣言されており、ファイルフォーマットやファイル名等の情報から適切なコーデックを予想します。引数は5つあり、順に「出力フォーマット」「コーデック名」「ファイル名」「MIMEタイプ」「コーデック種別」となっています。

 コーデックのIDがわかったら、AVCodec構造体を入手します。avcodec_find_encoderは、読み込みの時に登場したavcodec_find_decoderと同じように、コーデックIDを元にAVCodecを返す関数です。ただし今度はデコーダではなく、エンコーダを返してきます。

av_guess_codec使用時の注意
 av_guess_codecの引数としてコーデック名も直接指定できるのですが、r13235の実装では利用されてないようです。よって、この関数を利用してもファイルフォーマットのデフォルトのコーデックしか得られません。
 
 利用したいコーデックが決まっている場合はavcodec_find_encoder_by_nameを利用する必要があります。ffmpeg.cでも、-vcodecオプションなどでコーデック名が入力されている場合はこちらを利用しています。
ストリームにコーデックの情報を設定

 コーデックがわかったので、これらの情報をAVStreamへ教えてあげます。具体的には、AVStreamが保持しているAVCodecContext構造体へ、コーデックに関する情報を詰め込みます。

 まず、ほとんどの情報は単純にAVCodecContextへ詰め込むだけです。次のような処理になります。

ストリームへコーデックの情報を知らせる
/* ストリームに、対応するコーデック情報を指定 */
AVCodecContext *codecCtx  = stream->codec;
codecCtx->codec_id   = codec->id;
codecCtx->codec_type = CODEC_TYPE_VIDEO;
codecCtx->width  = PIC_WIDTH;
codecCtx->height = PIC_HEIGHT;
codecCtx->time_base.num = 1;
codecCtx->time_base.den = 60;

 しかし、ちょっと下ごしらえが必要な情報もありますので、次はそれらについて対応していきます。

 まず、ピクセルフォーマットです。これは、コーデックによっては対応されているものとされていない物がありますので、自分が指定したいフォーマットがコーデックで利用できるかを調べる必要があります。

コーデックに対応しているピクセルフォーマットを指定
codecCtx->pix_fmt = PIX_FMT_NONE;
/* ピクセルフォーマットは、codecで対応されているかチェック */
if(codec && codec->pix_fmts){
    const enum PixelFormat *p = codec->pix_fmts;
    while(*p++ != -1){
        if(*p == codecCtx->pix_fmt) break;
    }
    /* 非対応の場合は対応する物に差し替え */
    if(*p == -1) codecCtx->pix_fmt = codec->pix_fmts[0];
}

 今回は特に指定したいフォーマットはないので、PIX_FMT_NONE(指定なし)の状態にしておき、コーデックのデフォルト(対応する一覧の先頭)のピクセルフォーマットを利用します。AVCodec構造体 のpix_fmtsフィールドに対応するピクセルフォーマットの一覧がありますので、この中に自分が指定した物があるかを探します。pix_fmtsフィールドの要素は終端が-1であるというルールになっているので、whileで-1が現れるまでループします。

 自分が指定した物がpix_fmtsフィールドに見つからなかった場合は、pix_fmtsの最初の要素をフォーマットとします。

 もう1つ、必要に応じて指定しなければならないのがグローバルヘッダーフラグです。"image2"フォーマットでは必要のない処理ですが、ファイルフォーマットによってはコーデックにこの指定をするよう要求される場合があります。AVOutputFormatflagsフィールドを調べ、必要であればAVCodecContextのフラグを立てます。

グローバルヘッダフラグの指定
/* フォーマットにより、グローバルヘッダが要求される場合は
   codecにも伝える */
if(format->flags & AVFMT_GLOBALHEADER)
    codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
ファイル出力パラメータを指定

 フォーマット指定処理の締めくくりとして、ファイル出力のパラメータを設定します。今回は特に設定しないので、NULLを渡しておきます。

パラメータを指定
/* 出力のパラメータを指定(空でも必要) */
ret = av_set_parameters(formatCtx, NULL);
if(ret < 0) error("can't set parameters.");

次のページ

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

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

hiratara(ヒラタラ)

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/2622 2008/07/14 14:00

イベント

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

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

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

メールバックナンバー