#include "precompile.h" #include "videoclock.h" #define TIMER_RESOLUTION 1 struct videoclock { get_frame_cb get_frame; put_frame_cb put_frame; clockdbg_cb dbglog; int fps_num; int fps_den; void *put_user_data; void *get_user_data; int frame_width; int frame_height; int frame_format; HANDLE exit_evt; HANDLE thread_run; volatile int *nVideoSendFreq; }; static void clockDbg(videoclock_t clock, const char* fmt, ...) { va_list arg; va_start(arg, fmt); if (clock->dbglog) { (clock->dbglog)(clock, fmt, arg); } else { int n = _vscprintf(fmt, arg); if (n > 0) { char* buf = (char*)malloc((size_t)(n + 3)); vsprintf(buf, fmt, arg); strcat(buf, "\r\n"); printf(buf); free(buf); } } va_end(arg); } static __inline void GetTick(LARGE_INTEGER *last, LARGE_INTEGER *lt) { DWORD dwNow = GetTickCount(); if (last->LowPart > dwNow) { lt->LowPart = dwNow; lt->HighPart = last->HighPart + 1; } else { lt->LowPart = dwNow; lt->HighPart = last->HighPart; } } static unsigned int __stdcall worker_thread_proc(void* param) { struct videoclock *clock = (struct videoclock*)param; video_frame frame; LARGE_INTEGER start_tick; DWORD dwTimeout = 0; LARGE_INTEGER seq = {0}; LARGE_INTEGER tick = {0}; SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); if (video_frame_alloc(clock->frame_width, clock->frame_height, clock->frame_format, &frame) != 0) goto on_error; start_tick.LowPart = GetTickCount(); start_tick.HighPart = 0; seq.LowPart = 1; for (;;) { DWORD dwRet = WaitForSingleObject(clock->exit_evt, dwTimeout); if (dwRet == WAIT_TIMEOUT) { clock->get_frame(clock->get_user_data, &frame); clock->put_frame(clock->put_user_data, &frame); seq.LowPart++; if (seq.LowPart == 0) { seq.HighPart ++; } GetTick(&tick, &tick); //no support VideoSendFreq(req dynamics fps) #if 1 if (seq.QuadPart * clock->fps_den * 1000 / clock->fps_num < tick.QuadPart - start_tick.QuadPart) { dwTimeout = 0; } else { dwTimeout = (DWORD)(seq.QuadPart * clock->fps_den * 1000 / clock->fps_num - (tick.QuadPart - start_tick.QuadPart)); } #else if (*clock->nVideoSendFreq!= 0) { if (seq.QuadPart * clock->fps_den * 1000 / (*clock->nVideoSendFreq)< tick.QuadPart - start_tick.QuadPart) { dwTimeout = 0; } else { dwTimeout = (DWORD)(seq.QuadPart * clock->fps_den * 1000 / (*clock->nVideoSendFreq)- (tick.QuadPart - start_tick.QuadPart)); } } else if(*clock->nVideoSendFreq== -1) { //暂停发送视频 dwTimeout = 200000; } else if(*clock->nVideoSendFreq== 0) { if (seq.QuadPart * clock->fps_den * 1000 / clock->fps_num < tick.QuadPart - start_tick.QuadPart) { dwTimeout = 0; } else { dwTimeout = (DWORD)(seq.QuadPart * clock->fps_den * 1000 / clock->fps_num - (tick.QuadPart - start_tick.QuadPart)); } } #endif } else { break; } } video_frame_free(&frame); on_error: _endthreadex(0); return 0; } int videoclock_create(int fps_num, int fps_den, int video_width, int video_height, int frame_format, put_frame_cb put, void *put_user_data, get_frame_cb get, void *get_user_data, videoclock_t *p_clock, volatile int*nFps, clockdbg_cb dbgfunc) { struct videoclock *clock; /* check ... */ if (!p_clock) return -1; if (!put || !get) return -1; if (frame_format != VIDEO_FORMAT_I420 && frame_format != VIDEO_FORMAT_RGB24) return -1; if (NULL == dbgfunc) { return -1; } clock = malloc(sizeof(struct videoclock)); if (!clock) return -1; memset(clock, 0, sizeof(struct videoclock)); clock->exit_evt = CreateEvent(NULL, FALSE, FALSE, NULL); if (!clock->exit_evt) goto on_error; clock->fps_num = fps_num; clock->nVideoSendFreq = fps_num; clock->fps_den = fps_den; clock->frame_format = frame_format; clock->frame_width = video_width; clock->frame_height = video_height; clock->get_frame = get; clock->get_user_data = get_user_data; clock->put_frame = put; clock->put_user_data = put_user_data; clock->nVideoSendFreq = nFps; clock->dbglog = dbgfunc; *p_clock = clock; return 0; on_error: clockDbg(clock,"videoclock_create failed for %s.", strerror(errno)); CloseHandle(clock->exit_evt); free(clock); return -1; } void videoclock_destroy(videoclock_t vc) { if (vc) { CloseHandle(vc->exit_evt); free(vc); } } int videoclock_start(videoclock_t vc) { if (!vc) return -1; ResetEvent(vc->exit_evt); vc->thread_run = (HANDLE)_beginthreadex(NULL, 0, &worker_thread_proc, vc, 0, NULL); if (!vc->thread_run) return -1; return 0; } int videoclock_stop(videoclock_t vc) { if (!vc) return -1; if (vc->exit_evt) { SetEvent(vc->exit_evt); } if (vc->thread_run) { WaitForSingleObject(vc->thread_run, INFINITE); CloseHandle(vc->thread_run); vc->thread_run = NULL; } return 0; }