123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- #include "precompile.h"
- #include "videorender.h"
- #define av_always_inline __inline
- #define inline __inline
- #include <libswscale\swscale.h>
- #include <vfw.h>
- #include "video_common/ffmpeg_api_adapter.h"
- //#pragma comment(lib, "ffmpeg\\libswscale.lib")
- //#pragma comment(lib, "Winmm.lib")
- //#pragma comment(lib, "Vfw32.lib")
- #define DEFAULT_MAX_QUEUE_SIZE 16
- #define TIMER_RESOLUTION 1
- #define CMB_VIDEOVIEW _T("cmb_videoview")
- struct render_window
- {
- volatile HWND hWnd;
- HANDLE ui_thread;
- struct videorender *render;
- };
- struct videorender
- {
- volatile LONG quit;
- HANDLE thread_run;
- struct SwsContext *sws_context;
- CRITICAL_SECTION q_cs;
- HANDLE q_full_sem;
- HANDLE q_empty_sem;
- HANDLE q_exit_evt;
- video_frame** frame;
- video_timestamp* time;
- int q_head;
- int q_tail;
- int q_size;
- struct render_window *own_hwnd; /* if hWnd is null, we will create our own window */
- HWND hWnd;
- int top;
- int left;
- int width;
- int height;
- unsigned char *rgb;
- void (*free_frame_cb)(video_frame *frame);
- HDRAWDIB hdib;
- };
- static void display_frame(struct videorender *pr, video_frame *frame)
- {
- int dstFormat;
- int srcFormat;
- int dstStride[4] = {0};
- if (frame->format == VIDEO_FORMAT_RGB24) {
- srcFormat = PIX_FMT_RGB24;
- } else if (frame->format == VIDEO_FORMAT_I420) {
- srcFormat = PIX_FMT_YUV420P;
- } else {
- return;
- }
- dstFormat = PIX_FMT_RGB24;
- dstStride[0] = pr->width * 3;
- /* prepare sws_context */
- pr->sws_context = sws_getCachedContext(pr->sws_context, frame->width, frame->height, srcFormat,
- pr->width, pr->height, dstFormat, SWS_FAST_BILINEAR, NULL, NULL, NULL);
- if (!pr->sws_context)
- return;
- /* color space convert and image scale */
- {
- unsigned char *data[4] = {pr->rgb, 0, 0, 0};
- sws_scale(pr->sws_context, frame->data, frame->linesize, 0, frame->height, data, dstStride);
- }
- /* draw */
- {
- HDC hdc;
- BITMAPINFOHEADER bmpheader = {sizeof(BITMAPINFOHEADER)};
- bmpheader.biPlanes = 1;
- bmpheader.biBitCount = 24;//pr->bpp;
- bmpheader.biCompression = BI_RGB;
- bmpheader.biHeight = pr->height;
- bmpheader.biWidth = pr->width;
- hdc = GetDC(pr->hWnd);
- DrawDibDraw(pr->hdib, hdc, 0, 0, -1, -1, &bmpheader, pr->rgb, 0, 0, pr->width, pr->height, DDF_SAME_DRAW|DDF_SAME_HDC);
- ReleaseDC(pr->hWnd, hdc);
- }
- }
- static LRESULT CALLBACK UIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- switch (msg) {
- case WM_DESTROY:
- PostQuitMessage(0);
- break;
- case WM_CLOSE:
- DestroyWindow(hWnd);
- break;
- default:
- return DefWindowProc(hWnd, msg, wParam, lParam);
- }
- return 0;
- }
- static unsigned __stdcall ui_proc(void *param)
- {
- struct render_window *win = (struct render_window *)param;
- MSG msg;
- WNDCLASS wc;
- int width, height;
- //CoInitialize(NULL);
- /* register class */
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
- wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
- wc.hInstance = NULL;
- wc.style = CS_HREDRAW|CS_VREDRAW|CS_NOCLOSE;
- wc.lpszMenuName = NULL;
- wc.lpszClassName = CMB_VIDEOVIEW;
- wc.lpfnWndProc = &UIWndProc;
- RegisterClass(&wc);
- height = win->render->height + 2*GetSystemMetrics(SM_CYBORDER) +
- 2*GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CYCAPTION);
- width = win->render->width + 2*GetSystemMetrics(SM_CXBORDER) +
- 2*GetSystemMetrics(SM_CXEDGE);
- win->hWnd = CreateWindowEx(WS_EX_TOPMOST,
- CMB_VIDEOVIEW, _T("cmb video view"),
- WS_CAPTION|WS_POPUPWINDOW|WS_VISIBLE,
- CW_USEDEFAULT, CW_USEDEFAULT, width, height,
- NULL, NULL, NULL, NULL);
- //SetWindowPos(win->hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
- while (GetMessage(&msg, 0, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- //CoUninitialize();
- _endthreadex(0);
- return 0;
- }
- unsigned __stdcall run(void *param)
- {
- struct videorender *pr;
- video_timestamp ts_freq;
- HANDLE wait_evt;
- HANDLE hwait[3];
- if (video_get_timestamp_freq(&ts_freq) != 0) {
- _endthreadex(0);
- return 0;
- }
- if (!(wait_evt = CreateEvent(NULL, FALSE, FALSE, NULL))) {
- _endthreadex(0);
- return 0;
- }
- /* set time resolution */
- {
- TIMECAPS tc;
- timeGetDevCaps(&tc, sizeof(TIMECAPS));
- timeBeginPeriod(min(max(TIMER_RESOLUTION ,tc.wPeriodMin) ,tc.wPeriodMax));
- }
- /* set affinity mask */
- {
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- if (si.dwNumberOfProcessors > 1)
- SetThreadAffinityMask(GetCurrentThread(), 0x01 << rand()%si.dwNumberOfProcessors);
- }
- pr = (struct videorender*)param;
- hwait[0] = pr->q_full_sem;
- hwait[1] = pr->q_exit_evt;
- hwait[2] = wait_evt;
- while (!pr->quit) {
- DWORD dwResult = WaitForMultipleObjects(2, &hwait[0], FALSE, INFINITE) - WAIT_OBJECT_0;
- if (dwResult == 0) {
- video_timestamp now;
- video_frame *disp_frame;
- video_timestamp disp_ts;
- EnterCriticalSection(&pr->q_cs);
- disp_frame = pr->frame[pr->q_head];
- disp_ts = pr->time[pr->q_head];
- pr->q_head = (pr->q_head+1)%pr->q_size;
- LeaveCriticalSection(&pr->q_cs);
- video_get_timestamp(&now);
- if (disp_ts.u64 > now.u64) { /* too early */
- UINT delay = (UINT)(1000*(disp_ts.u64 - now.u64)/ts_freq.u64);
- timeSetEvent(delay, TIMER_RESOLUTION, (LPTIMECALLBACK)wait_evt, 0, TIME_CALLBACK_EVENT_SET|TIME_ONESHOT);
- dwResult = WaitForMultipleObjects(2, &hwait[1], FALSE, INFINITE) - WAIT_OBJECT_0;
- if (dwResult == 1) /* wait finished */
- display_frame(pr, disp_frame);
- } else {
- display_frame(pr, disp_frame);
- }
- if (pr->free_frame_cb) {
- pr->free_frame_cb(disp_frame);
- }
- ReleaseSemaphore(pr->q_empty_sem, 1, NULL);
- }
- }
- /* exit, need to clear all queue frames */
- if (pr->free_frame_cb) {
- while (WaitForSingleObject(pr->q_full_sem, 0) == WAIT_OBJECT_0) {
- pr->free_frame_cb(pr->frame[pr->q_head]);
- pr->q_head = (pr->q_head + 1)%pr->q_size;
- }
- }
- /* end timer period */
- {
- TIMECAPS tc;
- timeGetDevCaps(&tc, sizeof(TIMECAPS));
- timeEndPeriod(min(max(TIMER_RESOLUTION ,tc.wPeriodMin) ,tc.wPeriodMax));
- }
- CloseHandle(wait_evt);
- _endthreadex(0);
- return 0;
- }
- int videorender_create(HWND hWnd,
- int top,
- int left,
- int width,
- int height,
- int q_max_size,
- void (*free_frame_cb)(video_frame *frame),
- videorender_t *p_render)
- {
- struct videorender *pr;
-
- if (width%2 != 0)
- return -1;
- if (height%2 != 0)
- return -1;
- if (hWnd && !IsWindow(hWnd))
- return -1;
- pr = (struct videorender *)malloc(sizeof(struct videorender));
- if (!pr)
- return -1;
- memset(pr, 0, sizeof(struct videorender));
- pr->top = top;
- pr->width = width;
- pr->height = height;
- pr->q_size = q_max_size;
- if (!hWnd) {
- struct render_window *win = malloc(sizeof(struct render_window));
- if (!win)
- goto on_error;
- win->hWnd = NULL;
- win->render = pr;
- win->ui_thread = (HANDLE)_beginthreadex(NULL, 0, &ui_proc, win, 0, 0);
- if (!win->ui_thread) {
- free(win);
- goto on_error;
- }
- while (win->hWnd == NULL)
- Sleep(1);
- pr->own_hwnd = win;
- hWnd = win->hWnd;
- }
- pr->hWnd = hWnd;
- pr->frame = malloc(sizeof(video_frame*)*q_max_size);
- if (!pr->frame)
- goto on_error;
- pr->time = malloc(sizeof(video_timestamp)*q_max_size);
- if (!pr->time)
- goto on_error;
- pr->q_full_sem = CreateSemaphore(NULL, 0, q_max_size, NULL);
- if (!pr->q_full_sem)
- goto on_error;
- pr->q_empty_sem = CreateSemaphore(NULL, q_max_size, q_max_size, NULL);
- if (!pr->q_empty_sem)
- goto on_error;
- pr->q_exit_evt = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (!pr->q_exit_evt)
- goto on_error;
- pr->rgb = malloc(3 * pr->width * pr->height);
- if (!pr->rgb)
- goto on_error;
- /* setup drawdib */
- {
- HDC hdc;
- BITMAPINFOHEADER bmpheader = {sizeof(BITMAPINFOHEADER)};
- bmpheader.biPlanes = 1;
- bmpheader.biBitCount = 24;
- bmpheader.biCompression = BI_RGB;
- bmpheader.biHeight = pr->height;
- bmpheader.biWidth = pr->width;
- pr->hdib = DrawDibOpen();
- if (!pr->hdib)
- goto on_error;
- hdc = GetDC(hWnd);
- if (!DrawDibBegin(pr->hdib, hdc, pr->width, pr->height, &bmpheader,
- pr->width, pr->height, DDF_SAME_DRAW|DDF_SAME_HDC)) {
- DrawDibClose(pr->hdib);
- pr->hdib = NULL;
- ReleaseDC(hWnd, hdc);
- goto on_error;
- }
- ReleaseDC(hWnd, hdc);
- }
-
- pr->free_frame_cb = free_frame_cb;
- /* start working thread */
- pr->thread_run = (HANDLE)_beginthreadex(NULL, 0, &run, pr, 0, 0);
- if (!pr->thread_run)
- goto on_error;
- InitializeCriticalSection(&pr->q_cs);
- *p_render = pr;
- return 0;
- on_error:
- if (pr->frame)
- free(pr->frame);
- if (pr->time)
- free(pr->time);
- if (pr->q_full_sem)
- CloseHandle(pr->q_full_sem);
- if (pr->q_empty_sem)
- CloseHandle(pr->q_empty_sem);
- if (pr->q_exit_evt)
- CloseHandle(pr->q_exit_evt);
- if (pr->rgb)
- free(pr->rgb);
- if (pr->hdib) {
- DrawDibEnd(pr->hdib);
- DrawDibClose(pr->hdib);
- }
- if (pr->own_hwnd) {
- PostMessage(pr->own_hwnd->hWnd, WM_CLOSE, 0, 0);
- WaitForSingleObject(pr->own_hwnd->ui_thread, INFINITE);
- CloseHandle(pr->own_hwnd->ui_thread);
- free(pr->own_hwnd);
- }
- if (pr->thread_run)
- CloseHandle(pr->thread_run);
- free(pr);
- return -1;
- }
- void videorender_destroy(videorender_t render)
- {
- if (!render)
- return;
- /* change quit flag to true and force mem fence */
- InterlockedExchange(&render->quit, 1);
- /* signal exit event */
- SetEvent(render->q_exit_evt);
- WaitForSingleObject(render->thread_run, INFINITE);
- CloseHandle(render->q_exit_evt);
- CloseHandle(render->thread_run);
- CloseHandle(render->q_full_sem);
- CloseHandle(render->q_empty_sem);
- DeleteCriticalSection(&render->q_cs);
- free(render->frame);
- free(render->time);
- if (render->sws_context)
- sws_freeContext(render->sws_context);
- free(render->rgb);
- DrawDibEnd(render->hdib);
- DrawDibClose(render->hdib);
- if (render->own_hwnd) {
- PostMessage(render->own_hwnd->hWnd, WM_CLOSE, 0, 0);
- WaitForSingleObject(render->own_hwnd->ui_thread, INFINITE);
- CloseHandle(render->own_hwnd->ui_thread);
- free(render->own_hwnd);
- }
- free(render);
- return;
- }
- int videorender_queue_frame(videorender_t render, video_frame *frame, video_timestamp *disp_ts)
- {
- DWORD dwResult;
- if (!frame || !disp_ts)
- return 0;
- if (render->quit)
- return -1;
- dwResult = WaitForSingleObject(render->q_empty_sem, 0);
- if (dwResult == WAIT_OBJECT_0) {
- EnterCriticalSection(&render->q_cs);
- render->frame[render->q_tail] = frame;
- render->time[render->q_tail].u64 = disp_ts->u64;
- render->q_tail = (render->q_tail+1)%render->q_size;
- LeaveCriticalSection(&render->q_cs);
- ReleaseSemaphore(render->q_full_sem, 1, NULL);
- return 0;
- } else {
- return -1; /* no empty slot */
- }
- }
|