ffmpegインタフェースを使用してYUVをh.264に符号化する
目次
ステップ1:データ・パラメータの解析
このステップでは、専用のプロファイルを設定し、パラメータを何かに従ってこのプロファイルに書き込み、プログラムでこのプロファイルを解析して符号化されたパラメータを得ることができます.パラメータが多くなければ、コマンドラインを直接使用して符号化パラメータを入力すればいいです.
ステップ2:ffmpeg構造を要求通りに初期化
ステップ1:コーデックIDに基づいてコーデックポインタを取得する
ステップ2:AVCodecContextインスタンスを取得します。コーデックAVCodecポインタによりコーデックコンテキストを取得
ステップ3:エンコーダパラメータの設定
ctx.c->bit_rate = io_param.nBitRate;
* resolution must be a multiple of two * ctx.c->width = io_param.nImageWidth; ctx.c->height = io_param.nImageHeight;
* frames per second * AVRational rational = {1,25}; ctx.c->time_base = rational;
ctx.c->gop_size = io_param.nGOPSize; ctx.c->max_b_frames = io_param.nMaxBFrames; ctx.c->pix_fmt = AV_PIX_FMT_YUV420P;
av_opt_set(ctx.c->priv_data, “preset”, “slow”, 0);//プライベートデータのカスタマイズ
ステップ4:AVCodecとAVCodecContextの例に従ってエンコーダを開く
これで、AVCodec、AVCodecContextのポインタが割り当てられ、この2つのオブジェクトのポインタをパラメータとしてエンコーダオブジェクトが開きます.呼び出された関数はavcodec_です.Open 2、宣言方法:
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
この関数の最初の2つのパラメータは、作成したばかりの2つのオブジェクトです.3番目のパラメータは、関数の実行プロセスで認識できなかったAVCodecContextと他のプライベート設定オプションを保存する辞書タイプのオブジェクトです.関数の戻り値は、エンコーダが正常に開いたかどうかを示し、正常に0を返すと、失敗して負の数を返します.呼び出し方法:
if(avcodec_open 2(ctx.c,ctx.codec,NULL)<0)/エンコーダコンテキストに従ってエンコーダ{fprintf(stderr,「Could not open codec」);exit(1);}を開く
ステップ5:エンコードされた入力データの構成
ステップ3:エンコーディングプロセスサイクル
アルゴリズムフレームワーク
while (numCoded < maxNumToCode) { read_yuv_data(); encode_video_frame(); write_out_h264(); }
ステップ1:YUV画素値をAVFrameに保存する
3つの色成分Y/U/Vのアドレスは、それぞれAVframe::data[0]、AVframe::data[1]およびAVframe::data[2]であり、画像の幅はそれぞれAVframe::linesize[0]、AVframe::linesize[1]およびAVframe::linesize[2]である.なお、linesizeの値は、widthではなくstrideを指すことが多い.すなわち、画素保存領域は、一定幅の無効なエッジ領域を有する可能性があり、データを読み出す際に注意しなければならない.AVframe::data[0]:輝度記憶空間AVframe::data[1]:色度記憶空間、サンプリング個数0の半分AVframe::data[2]:色度記憶空間、サンプリング個数0の半分
int Read_yuv_data(CodecCtx&ctx,IOParam&io_param,int color_plane){int frame_height=color_plane
= 0? ctx.frame->height : ctx.frame->height / 2;// *YUV420 1,2 0 *
int frame_width = color_plane =
0?ctx.frame->width:ctx.frame->width/2;//YUV 420 1,2色度成分のサンプリング個数は輝度成分0の半分int frame_size=frame_width*frame_height;int frame_stride=ctx.frame->linesizecolor[plane];if(frame_width==frame_stride){//幅とスパンが等しく、画素情報はfread_s(ctx.frame->data[color_plane],frame_size,1,frame_size,io_param.pFin)を連続的に格納する.else{//幅がスパンより小さく、画素情報格納空間間に間隔for(int row_idx=0;row_idx
return frame_size; }
ステップ2:符号化前に別途必要な操作があった場合、AVPacketオブジェクトを初期化する。
このオブジェクトは、符号化された符号ストリームデータを保存する.
ステップ3:エンコーディング
完全な符号化サイクル抽出は、次のコードを使用して実現できます。
*encode 1 second of video*for(frameIdx=0;frameIdx
Read_yuv_data(ctx, io_param, 0);//Y成分Read_yuv_data(ctx, io_param, 1);//U成分Read_yuv_data(ctx, io_param, 2);//V成分
ctx.frame->pts = frameIdx;
* encode the image * ret = avcodec_encode_video2(ctx.c, &(ctx.pkt), ctx.frame, &got_output);//AVFrameの画素情報をAVPacketの符号ストリームif(ret<0){fprintf(stderr,"Error encoding frame");exit(1);
if (got\_output)
{
//
printf("Write frame %3d (size=%5d)
", frameIdx, ctx.pkt.size);
fwrite(ctx.pkt.data, 1, ctx.pkt.size, io\_param.pFout);
av\_packet\_unref(&(ctx.pkt));
}
}//for (frameIdx = 0; frameIdx < io_param.nTotalFrames; frameIdx++)
ステップ4:コードストリームデータの書き出し
ステップ5:終了作業
エンコーダに残った最後のフレームを出力
エンコーダの実行プロセス全体を終了すると、符号化が完了した後の符号ストリームは、元のデータより1フレーム少ないことがわかります.これは、読み出し元の画素データの終了に基づいてサイクルの終了を判断し、最後のフレームがエンコーダに残って出力されていないためである.したがって、復号プロセス全体を閉じる前に、最後のフレームを出力するまで符号化の動作を継続しなければならない.この操作を実行してもavcodec_が呼び出されます.encode_ビデオ2関数は、AVFrameを表すパラメータをNULLにするだけです.
* get the delayed frames * for (got_output = 1; got_output; frameIdx++) { fflush(stdout);
ret = avcodec_encode_video2(ctx.c, &(ctx.pkt), NULL, &got_output);//出力エンコーダの残りの符号ストリームif(ret<0){fprintf(stderr,“Error encoding frame”);exit(1);
if (got\_output)
{
printf("Write frame %3d (size=%5d)
", frameIdx, ctx.pkt.size);
fwrite(ctx.pkt.data, 1, ctx.pkt.size, io\_param.pFout);
av\_packet\_unref(&(ctx.pkt));
}
}//for (got_output = 1; got_output; frameIdx++)
リソースの解放
エンコーダの各コンポーネントを計画通りに閉じ、符号化のプロセス全体を終了することができます.エンコーダコンポーネントの解放プロセスは、AVCocecをオフにし、AVCodecContextを解放し、AVFrameの画像キャッシュとオブジェクト自体を解放する必要がある確立プロセスと類比することができる.
avcodec_close(ctx.c); av_free(ctx.c); av_freep(&(ctx.frame->data[0])); av_frame_free(&(ctx.frame));