2011년 7월 8일 금요일

윈도우에서 리얼타임(realtime) 구현

네트웍으로 수신되는 라이브 비디오 영상을 플레이하는 플레이어를 개발하는 경우 정확하게  33ms 마다 한 프레임을 그려줘야하는데 directshow 를 쓰지않고 win32 쓰레드로 구현하는 경우 정확하게 33ms 를 지키기가 어렵다. Sleep() 함수의 경우 틱값이 10ms 이기 때문에 거의 무용지물에 가깝고 네트웍 트래픽 상태가 않좋다면 다 채널 영상의 경우 제멋데로 플레이가 되는것을 볼 수 있다. 윈도우 타이머에 관한 모든 방법을 다 사용해봤는데 윈도우 멀티미디어 타이머를 사용하는 아래 방법이 가장 적절한 방법이었고 HD 다채널(9채널이상) 상황에서도 타이밍이 정확하게 동작하는 것을 볼수 있었다.


static void CALLBACK TimeProc(UINT _timerId, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
// KLog log;
CstreamMedia* rtspClient = (CstreamMedia*)dwUser;
    rtspClient->tcount++;

if (rtspClient->frameQueue.count >= MAX_TIMER_Q_COUNT)
rtspClient->timerCount = rtspClient->timerCountOrg/2;
else if (rtspClient->frameQueue.count <= MIN_TIMER_Q_COUNT)
rtspClient->timerCount = rtspClient->timerCountOrg;

if (rtspClient->tcount >= rtspClient->timerCount) {
//log.WriteFileLog("[%s] q count: %d\n", rtspClient->szURL, rtspClient->frameQueue.count);
SetEvent(rtspClient->hTimerEvent);
rtspClient->tcount = 0;
}
}

DWORD WINAPI timerThread( LPVOID lpParam )
{
CstreamMedia* rtspClient = (CstreamMedia*)lpParam;
int sleepTime = 33;
int count = 0;

long last_time = 0, curr_time = 0, diff_time;

KLog log;

if (rtspClient->videoFps > 0)
sleepTime = 1000/rtspClient->videoFps;

err("timerThread begin\n");

    TIMECAPS tc;
    UINT wTimerRes;

    // 타이머의 최소/최대 해상도를 얻어옵니다.
    if (timeGetDevCaps (&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
        // 에러발생
        return -1;
    }

if (timeBeginPeriod(1) != TIMERR_NOERROR) {
log.WriteFileLog("TIMERR_NOERROR\n");
}

// 1ms의 타이머 이벤트를 시작합니다.
    UINT interval = 1;  // 단위: ms

    DWORD timerID = timeSetEvent (1, 1, TimeProc, (DWORD)rtspClient, TIME_PERIODIC);
    if (timerID == NULL) {
        // 에러발생
        return -1;
    }

//log.WriteFileLog("tcount: %d\n", tcount);

SetEvent(rtspClient->hTimerThreadReady);

while (rtspClient->m_recvThreadFlag)
{
Sleep(1);
}

// 타이머를 해제 합니다.
    if (timeKillEvent (timerID) != TIMERR_NOERROR) {
        // 에러발생
        return -1;
    }

timeEndPeriod(1);

rtspClient->frameQueue.reset();

err("timerThread end\n");

return 0;
}

간단히 설명하면

1. timeBeginPeriod(1) 해서 윈도우 틱값을 1ms 로 변경.
2. timeSetEvent 함수로 1ms 마다 주기적으로 TimeProc 콜백함수를 호출하도록 등록.
3. TimeProc 함수에서 tcount 값을 1씩 증가.
4. TimeProc 함수에서 tcount 값이 원하는 시간값(33)이 되면 SetEvent 함수로 실제 일을 하는 (decoding/draw) 쓰레드(WaitForSingleObject로 기다리는)를 깨워줌.

위 코드를 조금만 수정하면 리얼타임에 관련된 코드에 적용가능할듯 합니다.

댓글 없음:

댓글 쓰기