2011년 11월 25일 금요일

x264 인코더 사용법 - how to use x264 encoder



#include "libavcodec/avcodec.h"
#include "common/common.h"


// define structure
typedef struct {
int width;
int height;
int bitrate;
int framerate;
int i_interval;
} OUTPUT_PARAMS;


typedef struct {
OUTPUT_PARAMS OutParams;

x264_t *pX264EncHandle;
x264_param_t X264InitParam;
x264_picture_t X264Picture;

} ENCODER_OBJ;


typedef struct {
int width;
int height;
int stride;

unsigned char *py;
unsigned char *pcb;
unsigned char *pcr;

unsigned int pts;
} IMG_INFO;



ENCODER_OBJ g_Encoder;

// user setting
void X264_Params_UserSetting( x264_param_t *param )
{

    param->i_threads = X264_THREADS_AUTO;
    param->b_deterministic = 0;
    param->i_sync_lookahead = X264_SYNC_LOOKAHEAD_AUTO;

// param->i_level_idc = 31;
    /* Video properties */
    param->i_csp           = X264_CSP_I420;
    param->i_width         = 0;
    param->i_height         = 0;
    param->vui.i_sar_width = 0;
    param->vui.i_sar_height = 0;
    param->vui.i_overscan   = 0;  /* undef */
    param->vui.i_vidformat = 5;  /* undef */
    param->vui.b_fullrange = 0;  /* off */
    param->vui.i_colorprim = 2;  /* undef */
    param->vui.i_transfer   = 2;  /* undef */
    param->vui.i_colmatrix = 2;  /* undef */
    param->vui.i_chroma_loc = 0;  /* left center */
    param->i_fps_num       = 30;
    param->i_fps_den       = 1;
    param->i_level_idc     = -1;
    param->i_slice_max_size = 0;
    param->i_slice_max_mbs = 0;
    param->i_slice_count = 0;

    /* Encoder parameters */
    param->i_frame_reference = 1;
    param->i_keyint_max = 300;
    param->i_keyint_min = X264_KEYINT_MIN_AUTO;
    param->i_bframe = 0; // i_delay += i_bframe
    param->i_scenecut_threshold = 40; // rarely needs to be adjusted. default=40
    param->i_bframe_adaptive = X264_B_ADAPT_NONE; // recommand(X264_B_ADAPT_FAST==1)
    param->i_bframe_bias = 0;
    param->i_bframe_pyramid = X264_B_PYRAMID_NONE;
    param->b_interlaced = 0;
    param->b_constrained_intra = 0;

    param->b_deblocking_filter = 1;
    param->i_deblocking_filter_alphac0 = 0;
    param->i_deblocking_filter_beta = 0;

    param->b_cabac = 0;
    param->i_cabac_init_idc = 0;

    param->rc.i_rc_method = 0;//X264_RC_CRF;
    param->rc.i_bitrate = 0;
    param->rc.f_rate_tolerance = 1.0;
    param->rc.i_vbv_max_bitrate = 0;
    param->rc.i_vbv_buffer_size = 0;
    param->rc.f_vbv_buffer_init = 0.9;
    param->rc.i_qp_constant = 23 + QP_BD_OFFSET;
    param->rc.f_rf_constant = 23;
    param->rc.i_qp_min = 10;
    param->rc.i_qp_max = 51; //QP_MAX;
    param->rc.i_qp_step = 4;
    param->rc.f_ip_factor = 1.4; //default, 1.4;
    param->rc.f_pb_factor = 1.3;
    param->rc.i_aq_mode = X264_AQ_VARIANCE;
    param->rc.f_aq_strength = 1.0;
    param->rc.i_lookahead = 1;

    param->rc.b_stat_write = 0;
//    param->rc.psz_stat_out = "x264_2pass.log";
    param->rc.b_stat_read = 0;
//    param->rc.psz_stat_in = "x264_2pass.log";
    param->rc.f_qcompress = 0.6;
    param->rc.f_qblur = 0.5;
    param->rc.f_complexity_blur = 20;
    param->rc.i_zones = 0;
    param->rc.b_mb_tree = 1;

    /* Log */
//    param->pf_log = x264_log_default;
    param->p_log_private = NULL;
    param->i_log_level = X264_LOG_NONE; //X264_LOG_INFO;

    /* */
    param->analyse.intra = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8;
    param->analyse.inter = X264_ANALYSE_I4x4 | X264_ANALYSE_I8x8 | X264_ANALYSE_PSUB16x16 | X264_ANALYSE_BSUB16x16;
    param->analyse.i_direct_mv_pred = X264_DIRECT_PRED_SPATIAL;
    param->analyse.i_me_method = X264_ME_DIA;
    param->analyse.f_psy_rd = 1.0;
    param->analyse.b_psy = 1;
    param->analyse.f_psy_trellis = 0;
    param->analyse.i_me_range = 16;
    param->analyse.i_subpel_refine = 5;
    param->analyse.b_mixed_references = 1;
    param->analyse.b_chroma_me = 1;
    param->analyse.i_mv_range_thread = -1;
    param->analyse.i_mv_range = -1; // set from level_idc
    param->analyse.i_chroma_qp_offset = 0;
    param->analyse.b_fast_pskip = 1;
    param->analyse.b_weighted_bipred = 1;
    param->analyse.i_weighted_pred = X264_WEIGHTP_NONE; //X264_WEIGHTP_SMART;
    param->analyse.b_dct_decimate = 1;
    param->analyse.b_transform_8x8 = 0;
    param->analyse.i_trellis = 1;
    param->analyse.i_luma_deadzone[0] = 21;
    param->analyse.i_luma_deadzone[1] = 11;
    param->analyse.b_psnr = 0;
    param->analyse.b_ssim = 0;

    param->i_cqm_preset = X264_CQM_FLAT;

    memset( param->cqm_4iy, 16, sizeof( param->cqm_4iy ) );
    memset( param->cqm_4ic, 16, sizeof( param->cqm_4ic ) );
    memset( param->cqm_4py, 16, sizeof( param->cqm_4py ) );
    memset( param->cqm_4pc, 16, sizeof( param->cqm_4pc ) );
    memset( param->cqm_8iy, 16, sizeof( param->cqm_8iy ) );
    memset( param->cqm_8py, 16, sizeof( param->cqm_8py ) );

    param->b_repeat_headers = 1;
    param->b_annexb = 1;
    param->b_aud = 0;
    param->b_vfr_input = 0; // i_delay +=
    param->i_nal_hrd = X264_NAL_HRD_NONE;
    param->b_tff = 1;
    param->b_pic_struct = 0;
    param->b_fake_interlaced = 0;
    param->i_frame_packing = -1;
}

// open x264 encoder - need mutex lock (not thread-safe)
int X264_EncOpen(OUTPUT_PARAMS *params)
{
int t_x264_thread;

t_x264_thread = x264_threading_init();

x264_param_default(&g_Encoder.X264InitParam);
X264_Params_UserSetting(&g_Encoder.X264InitParam);

g_Encoder.X264InitParam.i_width = params->width;
g_Encoder.X264InitParam.i_height = params->height;
g_Encoder.X264InitParam.i_fps_num = params->framerate;
g_Encoder.X264InitParam.i_keyint_max = params->i_interval;
g_Encoder.X264InitParam.rc.i_bitrate = params->bitrate;

g_Encoder.pX264EncHandle = x264_encoder_open(&g_Encoder.X264InitParam);
if (g_Encoder.pX264EncHandle == NULL)
{
printf("open encoder failed \r\n");
return -1;
}

g_Encoder.X264Picture.img.i_csp = X264_CSP_I420;
g_Encoder.X264Picture.img.i_plane = 3;

return 0;
}

// encode frame
int Encode_frame(x264_t *h, x264_picture_t *pic, unsigned char *streamEncoded, int *streamLength)
{
    x264_picture_t pic_out;
    x264_nal_t *nal;
    int i_nal;
int t_encodedsize;

t_encodedsize = x264_encoder_encode(h, &nal, &i_nal, pic, &pic_out);
if (t_encodedsize < 0)
{
printf("encode frame error: %d\r\n", t_encodedsize);
return -1;
}
else
{
if (t_encodedsize!=0)
memcpy(streamEncoded, nal[0].p_payload, t_encodedsize);

*streamLength = t_encodedsize;
}
    return t_encodedsize;
}


// encoder process
unsigned int g_X264_EncProcess = 0;
int X264_EncProcess(unsigned char *pstream, int *streamsize, IMG_INFO *img)
{
int t_encodedStreamLength;
g_Encoder.X264Picture.img.plane[0] = img->py;
g_Encoder.X264Picture.img.plane[1] = img->pcb;
g_Encoder.X264Picture.img.plane[2] = img->pcr;

g_Encoder.X264Picture.img.i_stride[0] = img->stride;
g_Encoder.X264Picture.img.i_stride[1] = img->stride/2;
g_Encoder.X264Picture.img.i_stride[2] = img->stride/2;
g_Encoder.X264Picture.i_type = X264_TYPE_AUTO;
g_Encoder.X264Picture.i_qpplus1 = X264_QP_AUTO;
    g_Encoder.X264Picture.i_pic_struct = PIC_STRUCT_AUTO;
g_Encoder.X264Picture.i_pts = g_X264_EncProcess++;

t_encodedStreamLength = Encode_frame(g_Encoder.pX264EncHandle, &g_Encoder.X264Picture, pstream, streamsize);

return t_encodedStreamLength;
}

// close encoder
int X264_Close(int chan)
{
if (g_Encoder.pX264EncHandle)
x264_encoder_close(g_Encoder.pX264EncHandle);

return 0;
}

1. X264_EncOpen 함수인자인 OUTPUT_PARAMS 에 원하는 인코딩 설정값을 넣고 함수호출
2. 인코딩된 데이터를 담을 버퍼 포인터(pstream)와 yuv(YCrCb) 값이 담긴 프레임 버퍼 포인터를 지정하고 X264_EncProcess 함수호출
3. X264_EncProcess 함수의 pstream 에 인코딩된 프레임의 데이터가 저장됨
4. 인코더 종료시 X264_Close 호출

* 인코딩 성능을 높이려면 아래 ffmpeg 과 같이 x264 빌드시 enable-win32thread 옵션을 넣지말고 pthreadVC2.dll 을 사용할것

댓글 15개:

  1. 안녕하세요!
    H.264 디코더 속도 향상을 위하여 ffmpeg-MT 및 enable-win32thread를 사용했습니다. 혹시나 해서 enable-win32thread를 사용하지 않고 성능 측정을 했더니 별로 큰 효과가 없어 보입니다. 저역시 다채널로 디코딩하는 과정중입니다. 제가 뭔가 빼먹고 했는지...궁금합니다.

    답글삭제
    답글
    1. ffmpeg 버전이 올라가면서 configure 옵션에 변경이 생겼습니다. enable-win32thread 옵션을 넣어야만 win32 쓰레드를 이용하는 버전과 configure 시 win32 쓰레드 사용여부를 자동 디텍트하는 버전이 있습니다. 자동 디텍트 버전의 경우 반드시 win32 쓰레드를 사용하지않고 pthread 를 쓰겠다고 해줘야합니다.
      어쨌든 빌드시 win32 쓰레드를 쓰지않고 pthread 를 사용하는 옵션으로 빌드를 해야합니다. 이럴경우 ffmpeg 라이브러리를 링크할때 pthread 관련 링크에러가 발생하는데 이것은 http://sourceware.org/pthreads-win32/ 에서 dll/lib 등을 다운받아서 링크해주면 해결됩니다.
      성능은 디코딩 시간측정/cpu 사용량 측정으로 해보면 pthread 쪽이 더 좋습니다.

      삭제
    2. 답변 감사합니다.
      16채널로 디코딩해봤지만 여전히 속도는 개선되어보지는 않습니다.
      사용전과 후 CPU점유량을 비교해보니 거의 90~95%를 차지하고 있습니다. 또한 채널당 디코딩 프레임율이 27~28프레임 정도...초당 30프레임을 다 디코딩하고 싶은데...
      어떻게하면 ffmpeg을 이용해서 디코더 속도를 개선할수 있을까요?

      삭제
  2. 그렇다면 해볼수 있는게 thread_type/thread_count 값을 바꿔보는것정도 남은거 같은데 그래도 성능개선이 안된다면 그정도가 한계가 아닐까합니다. 만약 해당 머신에서 그 이상 성능이 나와야한다는 벤치마킹 대상이 있다면 디코딩 성능외에 다른 파트의 성능이 어느정도인지 측정해보는것도 괜찮을거같네요. 디코딩 성능은 아마 그정도가 최적일거에요.

    답글삭제
  3. 답변 감사합니다.

    x264를 이용해서 트랜스코딩을 할려고하는데 다음과 같은 링크에러가 발생합니다.
    웹에서 찾아보니 프로젝트 셋팅 문제라해서 따라해보아도 잘 안됩니다.
    그리고 xlib264.dll.a를 xlib264.lib를 수정하여 lib를 호출해서 사용해봐도 에러는 아래와 같이 똑같이 나옵니다. 어떻게하면 해결될수 있을까요?

    error LNK2019: unresolved external symbol "struct x264_t * __cdecl x264_encoder_open_122(struct x264_param_t *)" (?x264_encoder_open_122@@YAPAUx264_t@@PAUx264_param_t@@@Z) referenced in function "int __cdecl X264_EncOpen(struct OUTPUT_PARAMS *)" (?X264_EncOpen@@YAHPAUOUTPUT_PARAMS@@@Z)
    error LNK2019: unresolved external symbol "void __cdecl x264_param_default(struct x264_param_t *)" (?x264_param_default@@YAXPAUx264_param_t@@@Z) referenced in function "int __cdecl X264_EncOpen(struct OUTPUT_PARAMS *)" (?X264_EncOpen@@YAHPAUOUTPUT_PARAMS@@@Z)
    error LNK2019: unresolved external symbol "int __cdecl x264_encoder_encode(struct x264_t *,struct x264_nal_t * *,int *,struct x264_picture_t *,struct x264_picture_t *)" (?x264_encoder_encode@@YAHPAUx264_t@@PAPAUx264_nal_t@@PAHPAUx264_picture_t@@3@Z) referenced in function "int __cdecl Encode_frame(struct x264_t *,struct x264_picture_t *,unsigned char *,int *)" (?Encode_frame@@YAHPAUx264_t@@PAUx264_picture_t@@PAEPAH@Z)
    error LNK2019: unresolved external symbol "void __cdecl x264_encoder_close(struct x264_t *)" (?x264_encoder_close@@YAXPAUx264_t@@@Z) referenced in function "int __cdecl X264_Close(void)" (?X264_Close@@YAHXZ)

    답글삭제
    답글
    1. 위 에러는 x264 라이브러리 빌드 버전과 include 하는 헤더의 버전이 맞지않아서 생기는 링크에러처럼 보입니다. 그게 아니면 링크하려는 프로젝트가 cpp 라서 생기는 링크에러 일수도 있습니다. extern "C" 로 inclue 를 묶어서 빌드해보세요. 그리고 트랜스코딩을 위해 x264를 사용한다면 ffmpeg과 x264 를 같이 빌드하는것이 좋습니다. ffmpeg configure 옵션에 x264 를 함께 빌드하는 옵션이 있습니다.

      삭제
    2. 답변 덕분에 해결했습니다. 그런데 컴파일 오류는 없는데 디버깅중에x264_encoder_encode() 실행시 오류가 발생합니다. tidtable.c 파일에서 "Unhandled exception at !!!:0x00000005:Access viloation reading lacation 0x000000000000. 으로 나오네요.
      어떻게 해야할까요? 항상 좋은 답변 감사합니다.

      삭제
    3. 정확히 tidtable.c의 부분을 알려드려야될것 같아서 이렇게 몇자 적어봅니다. 아래 tidtable.c 파일 내용중에서 #ifdef _M_IX86부분 다음부터 에러입니다.

      _CRTIMP PFLS_GETVALUE_FUNCTION __cdecl __set_flsgetvalue()
      {
      #ifdef _M_IX86
      PFLS_GETVALUE_FUNCTION flsGetValue = FLS_GETVALUE;
      if (!flsGetValue)
      {
      flsGetValue = _decode_pointer(gpFlsGetValue);
      TlsSetValue(__getvalueindex, flsGetValue);
      }
      return flsGetValue;
      #else /* _M_IX86 */
      return NULL;
      #endif /* _M_IX86 */
      }

      삭제
    4. access violation ... 0x0000 이면 포인터로 null 값이 전달되네요. x264_encoder_encode 입력값 중에 null 이 들어가는 상황같은데 이 부분을 봐야할것같네요.

      삭제
  4. 방금 글을 올리면서 이전 글이 지워졌네요.
    다시 올려야겠네요.

    주인장님 덕분에 컴파일 오류를 고쳤습니다. 하지만 디버깅 중 x264_encoder_encode() 오류가 발생합니다. 발생한 파일은 tidtable.c 이고 위치는 이전 글에 남겼습니다. 이때 디버깅 오류는 Unhandled exception at 0x61798cba in FFMPEG-kkkk.exe: 0xC0000005: Access violation reading location 0x00000000. 입니다.
    x264_encoder_encode() 파라메터 문제인것 같아서 웹에서 검색해봤지만 문제는 아닌것 같은데. 어떻게 해결하면 좋을까요?

    답글삭제
  5. 저는 ffmpeg에서 입력된 첫번째 비트스트림은 I picture로 sps, pps, coded slice of an IDR picture가 들어 있는 정보를 디코딩하고 바로 x264를 이용해서 인코딩할려고 합니다. 그런데 디버깅중에 x264_encoder_encode의 리턴값이 0입니다. 0인 경우에는 리턴된 NAL unit이 없다고 하는데...x264_encoder_headers()를 사용하면 i_nal에는 3개(SPS,PPs, coded slice of an IDR picture)가 존재하고, nal값들이 있습니다.
    조언 기다리고 있겠습니다.

    답글삭제
  6. 주인장님! 해결했습니다. x264.c 소스를 보고 몇가지 함수를 추가하였더니 무리 없이 인코딩이 되었습니다. 고맙습니다.

    답글삭제
  7. 주인장님!
    x264문서 및 소스를 보면 입력된 영상이 YUY2도 인코딩되는데 실제 되지 않습니다. swscale을 이용해서 YUY2를 YV12로 바꿔서 인코딩해야하는지 아니면 다른 방법이 있는지 궁금합니다. 만약 YUY2->YV12 컨버트없이 바로 인코딩할수 있다면 어떻게 해야할지...답변 부탁드립니다.

    답글삭제
  8. 안녕하세요.
    이 글 올리신지 3년이 지났는데 요즘 최신의 ffmpeg 빌드에서도
    성능적인 이유로 w32thread 보다는 pthread를 권장하시나요?

    답글삭제
    답글
    1. 윈도우에서 pthread 방식과 w32thread 방식과 성능차이는 없습니다. w32thread 쓰시면됩니다.

      삭제