ストリームを開き、動画コーデックを割り当てる
ストリームの追加
前編で説明したように、動画ファイルには音声、動画等、複数のストリームが含まれます。読み込みの場合には、複数のストリームの中から動画を扱うストリームだけを選び出す処理を書き、そのストリームから来たパケットを復号してフレームを得ました。
書き出しでもパケットの書き込みはストリームを指定して行うため、そのためのストリームを用意しておく必要があります。先ほど用意した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を返す関数です。ただし今度はデコーダではなく、エンコーダを返してきます。
利用したいコーデックが決まっている場合は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"フォーマットでは必要のない処理ですが、ファイルフォーマットによってはコーデックにこの指定をするよう要求される場合があります。AVOutputFormatのflagsフィールドを調べ、必要であれば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.");
