ffmpegインタフェースを使用してYUVをh.264に符号化する


目次

  • 1. ステップ1:解析データパラメータ
  • 2. ステップ2:ffmpeg構造を要求通りに初期化
  • 2.1. ステップ1:コーデックIDに基づいてコーデックポインタ
  • を取得する.
  • 2.2. ステップ2:AVCodecContextインスタンスを取得します.コーデックAVCodecポインタによりコーデックコンテキスト
  • を取得する.
  • 2.3. ステップ3:エンコーダパラメータ
  • を設定する
  • 2.4. ステップ4:AVCodecおよびAVCodecContextの例に従ってエンコーダ
  • を開く.
  • 2.5. ステップ5:符号化入力データ
  • の構成
  • 3. ステップ3:エンコーディングプロセスサイクル
  • 3.1. アルゴリズムフレームワーク
  • 3.2. ステップ1:YUV画素値をAVFrameに
  • 保存する
  • 3.3. ステップ2:符号化前に別途必要な操作があった場合、AVPacketオブジェクトを初期化する.
  • 3.4. ステップ3:符号化
  • 3.5. 完全な符号化サイクル抽出は、
  • のコードを使用して実現することができる.
  • 4. ステップ4:コードストリームデータ
  • を書き出す
  • 5. ステップ5:終了作業
  • 5.1. エンコーダに残る最後のフレーム
  • を出力する.
  • 5.2. リソースの解放

  • ステップ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_idxdata[color_plane]+row_idx*frame_stride,frame_width,1,frame_width,io_param.pFin);}
    return frame_size; }

    ステップ2:符号化前に別途必要な操作があった場合、AVPacketオブジェクトを初期化する。


    このオブジェクトは、符号化された符号ストリームデータを保存する.
  • で初期化する操作は非常に簡単で、av_を呼び出すだけです.init_Packetは、AVPacketオブジェクトのポインタを入力します.
  • AVPacket::dataをNULL、AVPacket::size付与0とする.

  • ステップ3:エンコーディング


    完全な符号化サイクル抽出は、次のコードを使用して実現できます。


    *encode 1 second of video*for(frameIdx=0;frameIdxfflush(stdout);
    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));