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 로 할 것.

2011년 10월 18일 화요일

윈도우에서 IOCP 를 사용해야하는 이유

네트웍 프로그램에서 대량의 트래픽이나 클라이언트/서버를 상대로 데이터를 송수신할때 보통 단일쓰레드로 select 함수를 사용해서 처리한다.
리눅스에서는 이것이 보통 합당한 방식인데 왜냐하면 멀티코어 cpu 상에서 cpu 사용률을 보면 각 cpu가 골고루 사용되는것을 볼수 있다. 이것은 이더넷 카드의 인터럽트가 각 cpu 에 골고루 신호를 보내기때문이다.
하지만 윈도우에서 같은 방식으로 싱글쓰레드 select 함수를 사용하면 cpu 1개가 100% 로 치솟고 나머지 cpu 들은 거의 0% 로 놀고있는 현상을 볼수있다. 이것은 이더넷 카드의 인터럽트가 쿼드코어기준으로 4개의 cpu 에 골고루 인터럽트를 보내지않고 1개의 cpu 에게만 집중적으로 보내기 때문이다. 이것을 막기위해 레지스트리 수정등 여러방법을 써봤지만 다 효과가 없었고 오직 IOCP 기술을 사용해서 부하분산을 할 수 있었다.
IO Completion Port 는 일종의 IO 큐로써 등록된 각 소켓 디스크립터들의 이벤트들을 담고있고 초기에 사용자가 만들었던 쓰레드풀에서 각 쓰레드들이 랜덤하게 큐의 이벤트들을 pop 해서 가져가서 처리하는 방식이다.
IO Completion Port 가 일종의 select 함수 역할을 하는 것이고 이벤트에 대한 처리는 각 쓰레드들이 알아서 하는 방식이다. 이렇게 하면 쓰레드들이 골고루 일을 하기때문에 cpu 부하분산을 시킬수 있다.

2011년 10월 14일 금요일

IOCP로 UDP 데이터 수신 - how to use IOCP WSARecvFrom


< 변수들 - variables >

// IOCP 컨텍스트 정의
class IOContext {
WSAOVERLAPPED wsaOverlapped;
int socket;
WSABUF wsaBuf;
char buf[10240];
HANDLE hCompletionPort;
    HANDLE hCloseEvent;
    BOOL bClosed;

    IOContext(int sock);
    ~IOContext();

};


IOContext::IOContext(int sock)
{
socket = sock;
wsaBuf.len = sizeof(buf);
wsaBuf.buf = buf;
SecureZeroMemory((PVOID)&wsaOverlapped, sizeof(WSAOVERLAPPED));

hCloseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
bClosed = FALSE;
}

IOContext::~IOContext()
{
CloseHandle(hCloseEvent);
}


List<IOContext> contextList;    // IOCP 컨텍스트들 링크드 리스트로 관리
HANDLE hCompletionPort;
SYSTEM_INFO systemInfo;
DWORD dwThreadCount;
HANDLE hThreads[16];


< IOCP 초기화 및 쓰레드 생성 - initialize IOCP Handle and create worker threads >

void init()
{

    hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    GetSystemInfo(&systemInfo);
    dwThreadCount = systemInfo.dwNumberOfProcessors*2;
 
    start();
}

void start()
{
    for (int i=0; i < dwThreadCount; i++)
    {
  hThreads[i] = (HANDLE)_beginthreadex(NULL, 0, WorkerThread, this, 0, NULL);
    }
}

< IOCP 에 소켓등록 - register socket to IOCP Handle >

void registerSocket(int socketNum)
{
     IOContext *context = new IOContext(socketNum);

     contextList.insert(context);
     context->hCompletionPort = CreateIoCompletionPort((HANDLE)socketNum, hCompletionPort, (ULONG_PTR)context, 0);

int err;
if (!PostQueuedCompletionStatus(hCompletionPort, 0, (ULONG_PTR)context, &context->wsaOverlapped)) {
  if ((err = WSAGetLastError()) != WSA_IO_PENDING)
    printf("[%s] PostQueuedCompletionStatus error: %d\r\n", err);
}
}


< IOCP 에 소켓등록해제 - unregister socket from IOCP Handle >

void unregisterSocket(int socketNum)
{
     IOContext *context = (IOContext *)contextList.search(socketNum);
     closesocket(socketNum);
     if (WaitForSingleObject(context->hCloseEvent, 1000*5) == WAIT_TIMEOUT) {
         printf("CloseEvent Wait Timeout !!!\n");
     }
     contextList.remove(context);
}


< Worker Thread >

unsigned int __stdcall WorkerThread(LPVOID lpParam)
{
    BOOL bSuccess = FALSE;
    DWORD dwIoSize;
    LPOVERLAPPED lpOverlapped = NULL;
    IOContext *context;

    while (1)
    {
        bSuccess = GetQueuedCompletionStatus(hCompletionPort, &dwIoSize, (PULONG_PTR)&context, &lpOverlapped, INFINITE);

if (context->bClosed) continue;

  if (!bSuccess) {
    err = GetLastError();
    if (err == WSA_OPERATION_ABORTED) {
                context->bClosed = TRUE;
SetEvent(context->hCloseEvent);         
            } else if (err == WSAENOTSOCK) {
                context->bClosed = TRUE;
                SetEvent(context->hCloseEvent);
            }
            continue;
        }

        // DO SOMETHING
        ......
  struct sockaddr_in fromAddress;
  int len = sizeof(context->buf);
  int addressSize = sizeof(fromAddress);
  int bytesRead;
  DWORD flag = 0;
  int err;

  context->wsaBuf.len = sizeof(buf);
  context->wsaBuf.buf = context->buf;
  if (WSARecvFrom(context->socket, &context->wsaBuf, 1, (LPDWORD)&bytesRead, (LPDWORD)&flag, (struct sockaddr*)&fromAddress, (socklen_t *)&addressSize, &context->wsaOverlapped, NULL) == SOCKET_ERROR) {
    if ((err=WSAGetLastError()) != WSA_IO_PENDING) {
      printf("[%s] WSARecvFrom error:%d, sock:%d, bytesRead:%d\r\n", __FUNCTION__, err, context->socket, bytesRead);
    }

            if (err == WSAENOTSOCK) { // invalid socket (Socket operation on nonsocket.)
                context->bClosed = TRUE;
                SetEvent(context->hCloseEvent);
            }
  }
    }
    return 0;
}


2011년 10월 11일 화요일

MySQL 접속권한 주기

mysql> GRANT ALL ON new_aces_server.* TO root@'172.16.1.15' IDENTIFIED BY 'acest';

2011년 9월 30일 금요일

윈도우에서 gettimeofday 함수구현 - windows gettimeofday function

< 상대시간 >

int gettimeofday(struct timeval* tp, int* tz)
{
LARGE_INTEGER tickNow;
static LARGE_INTEGER tickFrequency;
static BOOL tickFrequencySet = FALSE;
if (tickFrequencySet == FALSE) {
QueryPerformanceFrequency(&tickFrequency);
tickFrequencySet = TRUE;
}
QueryPerformanceCounter(&tickNow);
tp->tv_sec = (long) (tickNow.QuadPart / tickFrequency.QuadPart);
tp->tv_usec = (long) (((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);

return 0;
}


< 절대시간 - unix 타임 >

#include <stdio.h>
#include < time.h >
#include <windows.h>

#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
#else
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
#endif

struct timezone
{
  int  tz_minuteswest; /* minutes W of Greenwich */
  int  tz_dsttime;     /* type of dst correction */
};

int GetTimeOfDay(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
uint64_t tmpres = 0;
static int tzflag;

if (NULL != tv)
{
// system time을 구하기
GetSystemTimeAsFileTime(&ft);

// unsigned 64 bit로 만들기
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;

// 100nano를 1micro로 변환하기
tmpres /= 10;

// epoch time으로 변환하기
tmpres -= DELTA_EPOCH_IN_MICROSECS;  

// sec와 micorsec으로 맞추기
tv->tv_sec = (tmpres / 1000000UL);
tv->tv_usec = (tmpres % 1000000UL);
}

// timezone 처리
if (NULL != tz)
{
if (!tzflag)
{
_tzset();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}

return 0;
}

int64_t GetTimeOfDay()
{
struct timeval now;
GetTimeOfDay(&now, NULL);

int64_t millisecond = (int64_t)(now.tv_sec)*1000 + (int64_t)(now.tv_usec)/1000;

return millisecond;
}

윈도우에서 writev 함수구현 - windows writev function implementation


#include <winsock2.h>

int writev(int sock, struct iovec *iov, int nvecs)
{
DWORD ret;
if (WSASend(sock, (LPWSABUF)iov, nvecs, &ret, 0, NULL, NULL) == 0) {
return ret;
}
return -1;
}