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 을 사용할것

2011년 11월 18일 금요일

mingw 로 ffmpeg 빌드시 pthread 연동

윈도우에서 minGW 를 이용해서 ffmpeg 을 빌드할때 보통 멀티쓰레드를 지원하기위해

configure 옵션으로 --enable-w32threads 옵션을 주는데 이 옵션으로 빌드를 하면

성능이 제대로 나오지않는다. 네트웍 플레이어를 개발한다고 했을때 보통 vlc 와 성능

비교를 하는데 위 옵션으로 빌드시 vlc 와 비교해서 60~70% 정도의 성능밖에 나오지않는다.

이것을 개선하기 위해 위 옵션을 제외하고 빌드를 했는데 이렇게 하면 vc++로 개발한

프로그램에서 링크를 할때 pthread 관련한 링크에러가 줄줄이 발생한다. 이것을 처리하기

위해 http://sourceware.org/pthreads-win32/ 에서 pthreadVC2.dll, pthreadVC2.lib 을 다운받아

pthreadVC2.lib 을 링크로 걸어주면 링크에러가 해결되고 vlc 와 거의 동급의 성능을 낼 수 있

었다.

실제로 여러 채널에 대해 디코딩 시간을 측정해보면 훨씬더 빠른것을 볼 수 있다.

*** 위 방식으로 빌드후 다양한 방식으로 테스트해본 결과 기존 win32 thread 와 성능차이가 없는것으로 결론 !!! => 그냥 --enable-w32threads 로 할 것.