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;
}

윈도우/리눅스 non-blocking socket 만들기 - making windows/linux non-blocking socket


Boolean makeSocketNonBlocking(int sock) {
#if defined(WIN32) || defined(_WIN32) || defined(IMN_PIM)
  unsigned long arg = 1;
  return ioctlsocket(sock, FIONBIO, &arg) == 0;
#elif defined(VXWORKS)
  int arg = 1;
  return ioctl(sock, FIONBIO, (int)&arg) == 0;
#else
  int curFlags = fcntl(sock, F_GETFL, 0);
  return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0;
#endif
}

윈도우는 ioctlsocket, 리눅스는 fcntl 사용

2011년 9월 29일 목요일

윈도우 소켓버퍼 사이즈 늘리기 - increasing windows socket buffer size

< 현재 소켓 버퍼 사이즈 읽어오기 >

unsigned getBufferSize(int bufOptName,
     int socket) {
  unsigned curSize;
  SOCKLEN_T sizeSize = sizeof curSize;
  if (getsockopt(socket, SOL_SOCKET, bufOptName,
(char*)&curSize, &sizeSize) < 0) {
    socketErr("getBufferSize() error: ");
    return 0;
  }

  return curSize;
}

< 소켓 버퍼 사이즈 늘리기 설정 >

#ifndef SOCKLEN_T
#define SOCKLEN_T socklen_t
#endif

unsigned increaseBufferTo( int bufOptName,
int socket, unsigned requestedSize) {
  // First, get the current buffer size.  If it's already at least
  // as big as what we're requesting, do nothing.
  unsigned curSize = getBufferSize(bufOptName, socket);

  // Next, try to increase the buffer to the requested size,
  // or to some smaller size, if that's not possible:
  while (requestedSize > curSize) {
    SOCKLEN_T sizeSize = sizeof requestedSize;
    if (setsockopt(socket, SOL_SOCKET, bufOptName,
  (char*)&requestedSize, sizeSize) >= 0) {
      // success
      return requestedSize;
    }
    requestedSize = (requestedSize+curSize)/2;
  }

  return getBufferSize( bufOptName, socket);
}

< 함수 사용 >

// 송신버퍼 사이즈 늘리기
unsigned increaseSendBufferTo(int socket, unsigned requestedSize) {
  return increaseBufferTo( SO_SNDBUF, socket, requestedSize);
}

// 수신버퍼 사이즈 늘리기
unsigned increaseReceiveBufferTo( int socket, unsigned requestedSize) {
  return increaseBufferTo( SO_RCVBUF, socket, requestedSize);
}

< 함수 사용 >

int ret, size = 1024*1024;

if ((ret=increaseSendBufferTo(fsock, size)) != size)
printf("%s failed to increase send buffer size (size:%d, ret:%d)\r\n", __FUNCTION__, size, ret);


*** 버퍼 사이즈를 원하는 만큼 늘릴 수 없으면 윈도우 레지스트리 설정을 추가/수정해야한다.

[HKEY_LOCAL_MACHINE \SYSTEM \CurrentControlSet \Services \Afd \Parameters]
DefaultReceiveWindow = 16384
DefaultSendWindow = 16384

* 리부팅할 필요없고 위 레지스트리 값만 넣어주면된다.






winsock socket 연결 64개 이상으로 늘리는 법 - increasing winsock 64 sockets limitation

윈속 소켓 연결시 한 쓰레드에 64개까지만 연결을 제한한다. (서버측 accept 혹은 select 할 소켓 디스크립터 갯수)
이것은 winsock2.h 에 FD_SET 이 64 로 정의되어있기 때문인데 FD_SET 을 아래 코드와 같이 정의해서 연결을 늘려줄 수 있다.


#define FD_SETSIZE 1024
#include <winsock2.h>

< 출처 : http://tangentsoft.net/wskfaq/advanced.html >

c++ 에서 객체 delete 했는데 소멸자 호출안되는 경우

c++ 에서 클래스를 동적으로 생성/소멸해서 쓰는 때가 많은데 delete 로 객체를 메모리에서 해제 시켜도 해당 객체의 소멸자가 호출되지않는 귀신이 곡할때가 있다.
바로 아래의 경우에 해당한다.


< MyClass.h 파일 >

class MyClass {
public:
   MyClass();
   ~MyClass();
};

< MyClass.cpp 파일 >

MyClass::MyClass()
{
   printf("contructor\n");
}

MyClass::~MyClass()
{
   printf("destructor\n");
}

< MainClass.h 파일 >

class MyClass;

class MainClass {
...
   MyClass *myClass;
   void free();
};

< MainClass.cpp 파일 >

#include "MainClass.h"

MainClass::MainClass()
{
   myClass = new MyClass();
}
...
void MainClass::free()
{
   delete myClass;   => myClass 객체변수는 분명히 메모리해제되지만 MyClass::~MyClass() 소멸자 함수는 호출되지않는다
}

위와 같은 상황은 MainClass 쪽에서 MyClass 헤더파일을 include 해주지않고
MainClass.h 에 class MyClass; 와 같이 선언만 해주었기때문에 발생한다.
MainClass 에서 myClass 인스턴스의 어떤 변수/함수 필드도 엑세스하지않기때문에
컴파일 에러도 발생하지않으며 delete 시 메모리에서도 정상해제된다. 단지 소멸자 함수만
호출되지않는다.
이런 경우 반드시 MyClass.h 파일을 include 해주어야한다.

< MainClass.cpp >

#include "MyClass.h"   <= !!!

2011년 9월 26일 월요일

vc++에서 winsock2 헤더파일 include 및 link(링크)

< winsock2 include >

#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

빌드에러가 발생하면 프로젝트 속성에서 C/C++ -> Preprocessor -> Preprocessor Definitions 에
_WINSOCKAPI_ 추가


< winsock2 library 링크 >

프로젝트 속성 -> Librarian -> General -> Additional Dependencies 에
ws2_32.lib 추가

WSAStartup() 함수로 winsock 초기화


윈도우에서 winsock 사용시 WSAStartup 함수로 winsock 을 초기화해야한다.


#if defined(__WIN32__) || defined(_WIN32)
#ifndef IMN_PIM
#define WS_VERSION_CHOICE1 0x202/*MAKEWORD(2,2)*/
#define WS_VERSION_CHOICE2 0x101/*MAKEWORD(1,1)*/
int initializeWinsockIfNecessary(void) {
/* We need to call an initialization routine before
* we can do anything with winsock.  (How fucking lame!):
*/
static int _haveInitializedWinsock = 0;
WSADATA wsadata;

if (!_haveInitializedWinsock) {
if ((WSAStartup(WS_VERSION_CHOICE1, &wsadata) != 0)
   && ((WSAStartup(WS_VERSION_CHOICE2, &wsadata)) != 0)) {
return 0; /* error in initialization */
}
    if ((wsadata.wVersion != WS_VERSION_CHOICE1)
       && (wsadata.wVersion != WS_VERSION_CHOICE2)) {
        WSACleanup();
return 0; /* desired Winsock version was not available */
}
_haveInitializedWinsock = 1;
}

return 1;
}
#else
int initializeWinsockIfNecessary(void) { return 1; }
#endif
#else
#define initializeWinsockIfNecessary() 1
#endif

...

// 윈속 초기화

  if (!initializeWinsockIfNecessary()) {
    socketErr(env, "Failed to initialize 'winsock': ");
    return -1;
  }


windows 에서 winsock select 함수사용


winsock select 함수 사용시 fd_set 이 비어있으면 select 함수에서 에러발생
이때 WSAGetLastError() 함수로 에러값을 보고 dummy 소켓을 생성해서 fd_set에
넣어줘야함. 아래 소스 참조


  fd_set readSet = fReadSet; // make a copy for this select() call

  struct timeval tv_timeToDelay;
  tv_timeToDelay.tv_sec = 1;
  tv_timeToDelay.tv_usec = 0;


  int selectResult = select(fMaxNumSockets, &readSet, NULL, NULL,
   &tv_timeToDelay);
  if (selectResult < 0) {
#if defined(__WIN32__) || defined(_WIN32)
    int err = WSAGetLastError();
    // For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if
    // it was called with no entries set in "readSet".  If this happens, ignore it:
    if (err == WSAEINVAL && readSet.fd_count == 0) {
      err = 0;
      // To stop this from happening again, create a dummy readable socket:
      int dummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
      FD_SET((unsigned)dummySocketNum, &fReadSet);
    }
    if (err != 0) {
#else
    if (errno != EINTR && errno != EAGAIN) {
#endif
// Unexpected error - treat this as fatal:
#if !defined(_WIN32_WCE)
perror("BasicTaskScheduler::SingleStep(): select() fails");
#endif
// exit(0);
      }
  }

2011년 8월 26일 금요일

MySQL Connector/Net 에서 utf-8 적용

접속시 connectionString에 "charset=utf8" 추가

MySqlConnection conn = null;
string connStr = "server=" + ip + ";user=" + id + ";database=" + database + ";port=" + port + ";password=" + passwd + ";charset=utf8;";
conn = new MySqlConnection(connStr);

2011년 8월 4일 목요일

미리할당된 버퍼큐(Buffer Queue) 사용하기

링크드리스트로 구성된 메모리가 미리 할당된 버퍼큐
push() 하면 memcpy 가 일어나지만 getBuffer() 함수를 사용하면 push할 버퍼의 포인터를 가져와 엑세스하므로 퍼포먼스를 향상시킬수 있다.

< BufferQueue.h >


#ifndef __BUFFER_QUEUE_H__
#define __BUFFER_QUEUE_H__

#include <windows.h>

#define BUF_SIZE (1024*1024*8)
#define BUF_ELEM_SIZE (30)

typedef struct BufferElement_t {
int len;
char buffer[BUF_SIZE];
BufferElement_t *next;
} BufferElement;

class BufferQueue
{
public:
BufferQueue();
~BufferQueue();

int count;

int push(char *buffer, int len);
BufferElement* pop();
void clear();
BufferElement* getBuffer();
void releaseBuffer();

protected:
BufferElement *head, *tail;
HANDLE hBufferLock;
};

#endif


< BufferQueue.cpp >
#include "BufferQueue.h"

BufferQueue::BufferQueue()
{
count = 0;

hBufferLock = CreateMutex(NULL, FALSE, NULL);

BufferElement *prev = new BufferElement;
prev->len = 0;
head = tail = prev;

for (int i=0; i < BUF_ELEM_SIZE; i++)
{
BufferElement *elem = new BufferElement;
elem->len = 0;
prev->next = elem;
prev = elem;
}

prev->next = head;
}

BufferQueue::~BufferQueue()
{
WaitForSingleObject(hBufferLock, INFINITE);

BufferElement *tmp;
BufferElement *elem = head;

for (int i=0; i < BUF_ELEM_SIZE; i++) {
tmp = elem;
elem = elem->next;
if (tmp)
delete tmp;
}

if (elem)
delete elem;

count = 0;

ReleaseMutex(hBufferLock);

CloseHandle(hBufferLock);
}

BufferElement* BufferQueue::getBuffer()
{
WaitForSingleObject(hBufferLock, INFINITE);

if (count >= BUF_ELEM_SIZE) {
ReleaseMutex(hBufferLock);
return NULL;
}

BufferElement *elem = tail;
tail = tail->next;
count++;

//ReleaseMutex(hBufferLock);

return elem;
}

void BufferQueue::releaseBuffer()
{
ReleaseMutex(hBufferLock);
}

int BufferQueue::push(char *buffer, int len)
{
WaitForSingleObject(hBufferLock, INFINITE);

if (count >= BUF_ELEM_SIZE) {
ReleaseMutex(hBufferLock);
return -1;
}

if (len >= BUF_SIZE) {
ReleaseMutex(hBufferLock);
return -2;
}
tail->len = len;
memcpy(tail->buffer, buffer, len);
tail = tail->next;

count++;

ReleaseMutex(hBufferLock);

return count;
}

BufferElement* BufferQueue::pop()
{
WaitForSingleObject(hBufferLock, INFINITE);

if (count <= 0) {
ReleaseMutex(hBufferLock);
return NULL;
}

BufferElement *elem = head;
head = head->next;
count--;

ReleaseMutex(hBufferLock);

return elem;
}

void BufferQueue::clear()
{
WaitForSingleObject(hBufferLock, INFINITE);
count = 0;
tail = head;
ReleaseMutex(hBufferLock);
}

멀티모니터 열거하기 - Multiple Monitor Enumeration

모니터가 여러 개인 경우 directdraw api 를 사용해 모니터별로 열거할수 있다.
getScreen() 함수는 지정한 RECT 에 해당하는 모니터를 찾고 모니터 오프셋 값을 RECT 에 넣어줌

//----------------------------------------------------------------------------------
#define  MAX_MONITOR (4)

struct Monitor {
    HMONITOR hMon;
    GUID guid; 
};

class MonitorManager {
    Monitor monitor[MAX_MONITOR];
    int nScreen;
    ...
    int getScreen(RECT *rect);
};

// Enumerate Monitor
struct EnumInfo
{
    BOOL bMultimonSupported;
    HRESULT hr;
};

BOOL WINAPI DDEnumCallbackEx( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName,
                              VOID* pContext, HMONITOR hmon )
{
MONITORINFO mi;
if (pGuid != NULL && hmon != NULL) {
mi.cbSize = sizeof(mi);
GetMonitorInfo(hmon, &mi);

MonitorManager *manager = (MonitorManager *)pContext;
manager->monitor[manager->nScreen].guid = *pGuid;
manager->monitor[manager->nScreen].hMon = hmon;
manager->nScreen++;
}
    return TRUE; // Keep enumerating
}

BOOL WINAPI DDEnumCallback( GUID* pGuid, LPTSTR pszDesc, LPTSTR pszDriverName,
                            VOID* pContext )
{
    DDEnumCallbackEx(pGuid, pszDesc, pszDriverName, pContext, NULL);
    return FALSE; // Stop enumerating -- For this sample, we don't want any non-display devices
}

HRESULT EnumerateScreens(MonitorManager *manager)
{
    HRESULT hr;
    HMODULE hModule = NULL;
    LPDIRECTDRAWENUMERATEEX pDDEnumEx = NULL;
    EnumInfo enumInfo;

    ZeroMemory(&enumInfo, sizeof(enumInfo));

    hModule = LoadLibrary( TEXT("ddraw.dll") );
    // If ddraw.dll doesn't exist in the search path,
    // then DirectX probably isn't installed, so fail.
    if (hModule == NULL)
        return E_FAIL;
 
    pDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hModule,
#ifdef UNICODE
        "DirectDrawEnumerateExW"
#else
        "DirectDrawEnumerateExA"
#endif
        );

manager->nScreen = 0;

    if (pDDEnumEx == NULL)
    {
        // We must be running on an old version of DirectDraw.
        // Therefore MultiMon isn't supported. Fall back on
        // DirectDrawEnumerate to enumerate standard devices on a
        // single-monitor system.
        enumInfo.bMultimonSupported = FALSE;
        hr = DirectDrawEnumerate(DDEnumCallback, manager);
    }
    else
    {
        enumInfo.bMultimonSupported = TRUE;
        hr = pDDEnumEx(DDEnumCallbackEx, manager, DDENUM_ATTACHEDSECONDARYDEVICES);
    }

    // If something failed inside the enumeration, be sure to return that HRESULT

    FreeLibrary(hModule);
    return hr;
}

int MonitorManager::getScreen(RECT *rect)
{
MONITORINFO mi;
HMONITOR hMon = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST);
for (int i=0; i < nScreen; i++) {
if (hMon == monitor[i].hMon) {
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMon, &mi);
OffsetRect(rect, -mi.rcMonitor.left, -mi.rcMonitor.top);
return i;
}
}

return -1;
}

2011년 7월 29일 금요일

ActiveX 확실히 제거하는법

보통 익스플로러에서 도구 -> 추가기능관리 -> (화면왼쪽아래 콤보박스) 모든 추가 기능 -> 더블클릭 -> 제거
하는데 더 확실한 방법

탐색기에서
C:\WINDOWS\Downloaded Program Files
폴더에 들어가면 설치된 엑티브엑스들 볼수있음. 선택->우클릭->제거

IIS 설치후 ASP.NET 엑세스 권한 설정

IIS 설치후 asp.net(asmx) 스크립트 엑세스 권한 없는경우 .net framework 디렉토리에서
아래 실행

aspnet_regiis -ga ASPNET

관련링크 : http://msdn.microsoft.com/ko-kr/library/k6h9cz8h(v=vs.80).aspx

2011년 7월 19일 화요일

.NET MySQL Connector/Net - "out of sync with server" Exception 문제

linux mysql-5.0.77 서버 설치된 환경에서 윈도우 C# 으로 만든 클라이언트 접속시
Connector/Net 6.4.3 으로 접속시도하면 아래 Exception 발생

out of sync with server

=> Connector/Net 6.3.7 설치하면 해결

출처 : http://bugs.mysql.com/bug.php?id=61806

2011년 7월 8일 금요일

ffmpeg 비디오 디코더(decoder) API 사용법 - how to use ffmpeg video decoder API

#define FFMPEG_0_8   // ffmpeg-1.0 이전 버전

extern "C" {
#include "libavcodec\avcodec.h"
}

#define VIDEO_CODEC_H264 0
#define VIDEO_CODEC_MPEG4 1

// 코덱관련 변수들 선언
class VideoDecoder {
    AVCodec *m_pCodec;
    AVCodecContext *m_pCodecContext;
    AVFrame *m_pPicture;
    AVPacket m_avPacket;
...
};

static HANDLE hMutex;
static int g_numCPU = 1;

< 디코더 초기화 - initialize decoder >

void VideoDecoder::initCodec()
{
    static int init = 0;    // 코덱 초기화는 프로그램 돌때 한번만 해주면됨
    if (init == 0) {
        SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
g_numCPU = sysinfo.dwNumberOfProcessors;    // cpu 코어 갯수

#ifdef FFMPEG_0_8
avcodec_init();
#endif
avcodec_register_all();

        hMutex = CreateMutex(NULL, FALSE, NULL);
        init = 1;
    }
}

< 디코더 열기 - open decoder >

int VideoDecoder::openDecoder(int codec)
{
WaitForSingleObject(hMutex, INFINITE);    // 뮤텍스 처리 (thread-safe 하지않음)

av_init_packet(&m_avPacket);
if (codec == VIDEO_CODEC_H264)
m_pCodec = avcodec_find_decoder(CODEC_ID_H264); else if (codec == VIDEO_CODEC_MPEG4)
m_pCodec = avcodec_find_decoder(CODEC_ID_MPEG4);
else {
ReleaseMutex(hMutex);
return -1;
}

#ifdef FFMPEG_0_8
m_pCodecContext = avcodec_alloc_context();
#else
    m_pCodecContext = avcodec_alloc_context3(m_pCodec);
#endif

//m_pCodecContext->thread_type = FF_THREAD_FRAME; m_pCodecContext->thread_type = FF_THREAD_SLICE;
m_pCodecContext->thread_count = g_numCPU;

m_pPicture = avcodec_alloc_frame();

if (codec == VIDEO_CODEC_H264) // MPEG4('mp4v') 일때 영상깨짐 {
if (m_pCodec->capabilities&CODEC_CAP_TRUNCATED)
m_pCodecContext->flags |= CODEC_FLAG_TRUNCATED; }

#ifdef FFMPEG_0_8
if (avcodec_open(m_pCodecContext, m_pCodec) < 0) {
#else
    if (avcodec_open2(m_pCodecContext, m_pCodec, NULL) < 0)
#endif
  ReleaseMutex(hMutex);
  return -1;
 }

ReleaseMutex(hMutex);
    return 0;
}

< 디코더 닫기 - close decoder >

int VideoDecoder::closeDecoder()
{
WaitForSingleObject(hMutex, INFINITE);

if (m_pCodecContext)
  avcodec_close(m_pCodecContext);

av_free(m_pCodecContext);
av_free(m_pPicture);

ReleaseMutex(hMutex);

return 0;
}

< 디코더 사용 - decode >

int VideoDecoder::Decode(char *inBuf, int inLen)
{
int retLen, got_picture;


    char *buf = (char *)malloc(inLen+FF_INPUT_BUFFER_PADDING_SIZE);
    memset(&buf[inLen], 0, FF_INPUT_BUFFER_PADDING_SIZE);
    memcpy(buf, inBuf, inLen);
    m_avPacket.data = (uint8_t *)buf;
    m_avPacket.size = inLen;

retLen = avcodec_decode_video2(m_pCodecContext, m_pPicture, &got_picture, &m_avPacket);    // 디코딩 => m_pPicture->data[0], data[1], data[2] 이 각각 YCbCr 값을 가짐 (YUV420)
if (retLen < 0) {
  goto exit;
 }

if (!got_picture) {    // 한 프레임을 만들지 못함 -> 한 프레임을 잘라서 주지 못할경우 여기에 대한 처리가 필요
  goto exit;
}
...
exit:

    if (buf)
        free(buf);

    return retLen;
}

사용법은 클래스 생성후 initCodec()(프로그램 실행처음 한번만 하면됨) -> openDecoder() -> Decode() -> closeDecoder() 해주면 된다.

ffmpeg 디코더 라이브러리 => ffmpeg 디코더 참고소스

ffmpeg 스케일러(scaler) 사용법 - how to use ffmpeg scaler

< VideoScaler.h >

#ifndef __VIDEO_SCALER_H__
#define __VIDEO_SCALER_H__

extern "C" {
#include "libavcodec\avcodec.h"
#include "libswscale\swscale.h"
}

#include "FFMPEGUtil.h"

class VideoScaler
{
public:
VideoScaler();
virtual ~VideoScaler();

int resetScaler(int srcWidth, int srcHeight, AVPixelFormat pixFmtSrc,
int targetWidth, int targetHeight, AVPixelFormat pixFmtDst);
void freeScaler();
int processScaler(AVFrame *frame);

int srcWidth() { return m_nSrcWidth; }
int srcHeight() { return m_nSrcHeight; }
int targetWidth() { return m_nTargetWidth; }
int targetHeight() { return m_nTargetHeight; }
int bitCount() { return GetBitPerPixel(m_pixFmtDst); }

AVPixelFormat srcPixFormat() { return m_pixFmtSrc; }
AVPixelFormat dstPixFormat() { return m_pixFmtDst; }

unsigned char* picture() { return m_pPicture; }
int size() { return m_nTargetWidth*m_nTargetHeight*(GetBitPerPixel(m_pixFmtDst)>>3); }

protected:
struct SwsContext* m_pScaler;
AVPixelFormat m_pixFmtSrc;
AVPixelFormat m_pixFmtDst;
int m_nSrcWidth, m_nSrcHeight;
int m_nTargetWidth, m_nTargetHeight;
unsigned char *m_pPicture;
};

#endif

< VideoScaler.cpp >

#include "VideoScaler.h"

VideoScaler::VideoScaler()
{
m_pScaler = NULL;
m_nSrcWidth = m_nSrcHeight = 0;
m_nTargetWidth = m_nTargetHeight = 0;
m_pPicture = NULL;
m_pixFmtSrc = m_pixFmtDst = AV_PIX_FMT_NONE;
}

VideoScaler::~VideoScaler()
{
freeScaler();
}

int VideoScaler::resetScaler(int srcWidth, int srcHeight, AVPixelFormat pixFmtSrc,
int targetWidth, int targetHeight, enum AVPixelFormat pixFmtDst)
{
if (srcWidth <= 0 || srcHeight <= 0) return -1;
if (targetWidth <= 0 || targetHeight <= 0) return -1;
if (pixFmtSrc == AV_PIX_FMT_NONE || pixFmtDst == AV_PIX_FMT_NONE) return -1;

if (m_pPicture) {
delete[] m_pPicture;
m_pPicture = NULL;
}

if (m_pScaler) {
sws_freeContext(m_pScaler);
m_pScaler = NULL;
}

int flags = SWS_BILINEAR;
//int flags = SWS_FAST_BILINEAR;
m_pScaler = sws_getContext(srcWidth, srcHeight, pixFmtSrc,
targetWidth, targetHeight, pixFmtDst, flags, NULL, NULL, NULL);

m_nSrcWidth = srcWidth;
m_nSrcHeight = srcHeight;
m_nTargetWidth = targetWidth;
m_nTargetHeight = targetHeight;

m_pixFmtSrc = pixFmtSrc;
m_pixFmtDst = pixFmtDst;

m_pPicture = new unsigned char[size()];

return 0;
}

void VideoScaler::freeScaler()
{
if (m_pScaler) {
sws_freeContext(m_pScaler);
m_pScaler = NULL;
}
if (m_pPicture) {
delete[] m_pPicture;
m_pPicture = NULL;
}

m_nSrcWidth = m_nSrcHeight = 0;
m_nTargetWidth = m_nTargetHeight = 0;
m_pixFmtSrc = m_pixFmtDst = AV_PIX_FMT_NONE;
}

int VideoScaler::processScaler(AVFrame *frame)
{
unsigned char *ptr = m_pPicture;

unsigned char *src_buf[4];
src_buf[0] = frame->data[0];
src_buf[1] = frame->data[1];
src_buf[2] = frame->data[2];
src_buf[3] = NULL;

int src_stride[4];
src_stride[0] = frame->linesize[0];
src_stride[1] = frame->linesize[1];
src_stride[2] = frame->linesize[2];
src_stride[3] = 0;

unsigned char *dst_buf[4];
dst_buf[0] = ptr;
dst_buf[1] = NULL;
dst_buf[2] = NULL;
dst_buf[3] = NULL;

int pitch = m_nTargetWidth * (GetBitPerPixel(m_pixFmtDst) >> 3);
int dst_stride[4];
dst_stride[0] = pitch;
dst_stride[1] = pitch;
dst_stride[2] = pitch;
dst_stride[3] = pitch;

int ret = -1;
if (m_pScaler) {
ret = sws_scale(m_pScaler, src_buf, src_stride, 0, m_nSrcHeight, dst_buf, dst_stride);
#if 0
FILE *fp = fopen("d:\\test.ppm", "wb");
fprintf(fp, "P6\n%d %d\n255\n", m_nTargetWidth, m_nTargetHeight);
fwrite(ptr, m_nTargetWidth*m_nTargetHeight*(m_nBitCount/8), 1, fp);
fclose(fp);
#endif
}

return ret;
}

< FFMPEGUtil.cpp >

int GetBitPerPixel(AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
return av_get_bits_per_pixel(desc);
}

< 사용 >
...
m_pScaler = new VideoScaler();
...
int scaleFrame(AVFrame *frame, int targetWidth, int targetHeight, AVPixelFormat pix_fmt)
{
if (frame->width != m_pScaler->srcWidth() || frame->height != m_pScaler->srcHeight() || 
targetWidth != m_pScaler->targetWidth() || targetHeight != m_pScaler->targetHeight() ||
frame->format != m_pScaler->srcPixFormat() || pix_fmt != m_pScaler->dstPixFormat()) 
{
if (m_pScaler->resetScaler(frame->width, frame->height, (AVPixelFormat)frame->format,
targetWidth, targetHeight, pix_fmt) < 0) 
{
printf("failed to reset scaler\n");
return -1;
}
}

if (m_pScaler->processScaler(frame) < 0) {
printf("failed to process scale\n");
return -1;
}
    ...
    doSomething(m_pScaler->picture(), m_pScaler->size());
}

VC++에서 ffmpeg 연동시 에러처리


libavutil/common.h:165: error: ‘UINT64_C’ was not declared in this scope
위와 같은 에러를 만나면 common.h 소스에 아래 코드추가
#ifndef INT64_C
#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)
#endif

DirectDraw를 이용한 YUV -> RGB 고속 변환출력 - YUV -> RGB Converting by using DirectDraw

< DirectDraw YUV Surface 생성 - creating directdraw surfaces >

DDPIXELFORMAT g_ddpfFormats[] = 
{{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('Y','V','1','2'),0,0,0,0,0}}; 
...
IDirectDraw7 *pDD;
IDirectDrawSurface7 *lpPrimary;
IDirectDrawClipper      *lpClipper;
IDirectDrawSurface7   *lpYUVBuffer;
int g_width, g_height;

initDraw(HWND hWnd, int width, int height)
{
HRESULT hr;

g_width = width;
g_height = height;

hr = DirectDrawCreateEx(NULL, (void **)&pDD, IID_IDirectDraw7, NULL);
if (FAILED(hr)) {
TRACE("failed to create directdraw device (hr:0x%x)\n", hr);
return -1;
}

hr = pDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
if(FAILED(hr)) {
TRACE("failed to SetCooperativeLevel (hr:0x%x)\n", hr);
return -1;
}


DDSURFACEDESC2 ddsd;

/* creating primary surface (RGB32) */
ZeroMemory( &ddsd, sizeof( ddsd ) );
ddsd.dwSize         = sizeof( ddsd );
ddsd.dwFlags        = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

if (hr=pDD->CreateSurface(&ddsd, &lpPrimary, NULL ) != DD_OK)
{
TRACE("failed to create create primary surface (hr:0x%x)\n", hr);
        return -1;
}

/* creating clipper */
hr = pDD->CreateClipper(0, &lpClipper, NULL);
if (hr != DD_OK)
{
TRACE("failed to create create clipper (hr:0x%x)\n", hr);
pDD->Release();
return -1;
}
hr = lpClipper->SetHWnd(0, hWnd);
if (hr != DD_OK)
{
TRACE("failed to clippter sethwnd (hr:0x%x)\n", hr);
lpClipper->Release();
lpPrimary->Release();
pDD->Release();
return -1;
}

hr = lpPrimary->SetClipper(lpClipper);
if (hr != DD_OK)
{
TRACE("failed to set clipper (hr:0x%x)\n", hr);
lpClipper->Release();
lpPrimary->Release();
pDD->Release();
return -1;
}


/* creating yuv420 surface */
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = width;
ddsd.dwHeight = height;

memcpy(&ddsd.ddpfPixelFormat, &g_ddpfFormats[0], sizeof(DDPIXELFORMAT));

if ((hr=pDD->CreateSurface(&ddsd, &lpYUVBuffer, NULL)) != DD_OK)
{
TRACE("failed to create yuv buffer surface\n");
return -1;
}
...
}


< Primary Surface 로 변환해서 출력 - yuv surface -> primary surface blt >

int draw(RECT *rectTarget, AVFrame *pPicture)
{
HRESULT hr;

int w = g_width;
int h = g_height;
...
DDSURFACEDESC2 ddsd;

ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
if ((hr=lpYUVBuffer->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)) == DD_OK)
{
LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;
LPBYTE ptr = lpSurf;
int t_stride = pPicture->linesize[0];
int i;
for (i=0; i < h; i++) {
memcpy(ptr+i*ddsd.lPitch, pPicture->data[0]+i*t_stride, w);
}
ptr += ddsd.lPitch*h;
for (i=0; i < h/2; i++) {
memcpy(ptr+i*ddsd.lPitch/2, pPicture->data[2]+i*t_stride/2, w/2);
}
ptr += ddsd.lPitch*h/4;
for (i=0; i < h/2; i++) {
memcpy(ptr+i*ddsd.lPitch/2, pPicture->data[1]+i*t_stride/2, w/2);
}

lpYUVBuffer->Unlock(NULL);
}

RECT rectSrc;
rectSrc.top = 0;
rectSrc.left = 0;
rectSrc.right = w;
rectSrc.bottom = h;

hr = lpPrimary->Blt(rectTarget, lpYUVBuffer, &rectSrc, DDBLT_ASYNC, NULL);
}

< ddraw resource release >


void release()
{
if (pDD != NULL)
{
pDD->Release();
pDD = NULL;
}

if (lpPrimary != NULL)
{
lpPrimary->Release();
lpPrimary = NULL;
}

if (lpYUVBuffer != NULL)
{
lpYUVBuffer->Release();
lpYUVBuffer = NULL;
}
}



위 소스에서 AVFrame 은 ffmpeg 에서 사용하는 구조체이고 data[0], data[2], data[1] 는 각각 yuv(yuv420) 값을 가진다.
분리된(non-interleaved) yuv 값을 가진 버퍼를 yuv surface에 memcpy 로 적절하게 복사한 다음 primary surface 로 blt 하면 하드웨어 가속기를 이용한 고속복사를 통해 영상 이미지를 출력할 수 있다.
여기서 primary surface의 픽셀포멧은 RGB32 인데 다른 픽셀포멧의 surface 에다 blt 시켜서 변환시킬수도 있을것같다.