#include "precompile.h" #include "videoclock.h" #ifdef RVC_OS_WIN #else #include #include #endif #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; #ifdef RVC_OS_WIN HANDLE exit_evt; HANDLE thread_run; #else sem_t exit_sem; pthread_t thread_id; #endif volatile int *nVideoSendFreq; }; static void clockDbg(videoclock_t clock, const char* fmt, ...) { int n; va_list arg; va_start(arg, fmt); if (clock->dbglog) { (clock->dbglog)(clock, fmt, arg); } else { 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); } } va_end(arg); } #ifdef RVC_OS_WIN 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; } #else void* local_video_sendfunc(void* param) { struct videoclock* clock = (struct videoclock*)param; video_frame frame; int dwTimeout = 1000/clock->fps_num; if (video_frame_alloc(clock->frame_width, clock->frame_height, clock->frame_format, &frame) != 0){ goto on_error; } for (;;) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); long unsec = ts.tv_nsec + (1000 * 1000 * dwTimeout * 2); ts.tv_sec += (unsec / 1000000000); ts.tv_nsec = (unsec % 1000000000); if (0 != sem_timedwait(&clock->exit_sem, &ts) && (ETIMEDOUT == errno)) { clock->get_frame(clock->get_user_data, &frame); clock->put_frame(clock->put_user_data, &frame); } else { break; } } video_frame_free(&frame); on_error: return 0; } #endif 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)); #ifdef RVC_OS_WIN clock->exit_evt = CreateEvent(NULL, FALSE, FALSE, NULL); if (!clock->exit_evt) goto on_error; #else if (0 != sem_init(&clock->exit_sem, 0, 0)){ goto on_error; } #endif 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)); free(clock); return -1; } void videoclock_destroy(videoclock_t vc) { if (vc) { #ifdef RVC_OS_WIN CloseHandle(vc->exit_evt); #else sem_destroy(&vc->exit_sem); #endif free(vc); } } int videoclock_start(videoclock_t vc) { if (!vc) return -1; #ifdef RVC_OS_WIN ResetEvent(vc->exit_evt); vc->thread_run = (HANDLE)_beginthreadex(NULL, 0, &worker_thread_proc, vc, 0, NULL); if (!vc->thread_run) return -1; #else pthread_attr_t attr; pthread_attr_init(&attr); struct sched_param param; param.sched_priority = sched_get_priority_max(SCHED_RR); pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); int err = pthread_create(&vc->thread_id, NULL, local_video_sendfunc, vc); if (0 != err) { clockDbg(vc,"pthread create failed for %s.", strerror(errno)); return -1; } #endif return 0; } int videoclock_stop(videoclock_t vc) { if (!vc) return -1; #ifdef RVC_OS_WIN 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; } #else sem_post(&vc->exit_sem); if (vc->thread_id > 0){ pthread_join(vc->thread_id, NULL); } #endif return 0; }