#include "precompile.h" #include "videoview.h" #include #define MAX_VIEW_ENTRY 8 #define BUFFER_COUNT 4 #define CMB_VIDEOVIEW _T("cmb_videoview") #define WM_VIDEO (WM_APP+0x03) struct videoview { HWND hwnd; int own; WNDPROC lpfnOrigin;/* use when hwnd != NULL, for subclass window */ char *buf[BUFFER_COUNT]; /* backgournd buffer */ volatile LONG buf_use[BUFFER_COUNT]; int buf_size; int offset_x; int offset_y; int height; int width; HDRAWDIB hDrawDib; /* use when own == TRUE */ HANDLE hevtnotify; /* set when user call videoview_create with hwnd == null */ HANDLE uithread; }; static struct { HWND hwnd; struct videoview *pvv; } _map[MAX_VIEW_ENTRY] = {0}; static LRESULT CALLBACK SubclassWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { int i; struct videoview *pvv = NULL; for (i = 0; i < MAX_VIEW_ENTRY; ++i) { if (_map[i].hwnd == hWnd) { pvv = _map[i].pvv; break; } } if (i == MAX_VIEW_ENTRY) { /* bug */ return DefWindowProc(hWnd, msg, wParam, lParam); } if (msg == WM_VIDEO) { int idx = (int)wParam; if (idx >= 0 && idx < BUFFER_COUNT && pvv->hDrawDib) { HDC hdc = GetDC(hWnd); BITMAPINFOHEADER bmiHeader = {sizeof(BITMAPINFOHEADER)}; bmiHeader.biBitCount = 24; bmiHeader.biWidth = pvv->width; bmiHeader.biHeight = pvv->height; bmiHeader.biPlanes = 1; bmiHeader.biCompression = BI_RGB; DrawDibDraw(pvv->hDrawDib, hdc, pvv->offset_x, pvv->offset_y, -1, -1, &bmiHeader, pvv->buf[idx], 0, 0, pvv->width, pvv->height, DDF_SAME_DRAW); InterlockedExchange(&pvv->buf_use[idx], 0);/* clear use flag */ ReleaseDC(hWnd, hdc); } else if (idx == -1) { /* create drawdib */ if (pvv->hDrawDib == NULL) { if ((pvv->hDrawDib = DrawDibOpen()) != NULL) { HDC hdc = GetDC(hWnd); BITMAPINFOHEADER bmiHeader = {sizeof(BITMAPINFOHEADER)}; bmiHeader.biBitCount = 24; bmiHeader.biWidth = pvv->width; bmiHeader.biHeight = pvv->height; bmiHeader.biPlanes = 1; bmiHeader.biCompression = BI_RGB; if (DrawDibBegin(pvv->hDrawDib, hdc, -1, -1, &bmiHeader, pvv->width, pvv->height, 0) == FALSE) { DrawDibClose(pvv->hDrawDib); pvv->hDrawDib = NULL; } else { UINT u = DrawDibRealize(pvv->hDrawDib, hdc, 0); pvv->hDrawDib; } ReleaseDC(hWnd, hdc); } } return (LRESULT)pvv->hDrawDib; } else if (idx == -2) { /* destroy drawdib */ if (pvv->hDrawDib) { DrawDibEnd(pvv->hDrawDib); DrawDibClose(pvv->hDrawDib); pvv->hDrawDib = NULL; } } else if (idx == -3) { /* destroy window */ DestroyWindow(hWnd); PostQuitMessage(0); } return 0; } if (pvv->own) { if (msg == WM_CLOSE) return 0; } if (pvv->lpfnOrigin) return (*pvv->lpfnOrigin)(hWnd, msg, wParam, lParam); return DefWindowProc(hWnd, msg, wParam, lParam); } static BOOL InitDrawDib(videoview_t vv) { if (vv && vv->hwnd) return (BOOL)SendMessage(vv->hwnd, WM_VIDEO, (WPARAM)-1, 0); /* -1 for init */ return -1; } static void DeinitDrawDib(videoview_t vv) { SendMessage(vv->hwnd, WM_VIDEO, (WPARAM)-2, 0); /* -2 for destroy */ } static unsigned __stdcall VideoViewUIThreadProc(void *param) { MSG msg; WNDCLASS wc; struct videoview * pvv = (struct videoview*)param; 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 = &SubclassWndProc; RegisterClass(&wc); /* create window */ height = pvv->height + 2*GetSystemMetrics(SM_CYBORDER) + 2*GetSystemMetrics(SM_CYEDGE) + GetSystemMetrics(SM_CYCAPTION); width = pvv->width + 2*GetSystemMetrics(SM_CXBORDER) + 2*GetSystemMetrics(SM_CXEDGE); pvv->hwnd = CreateWindow(CMB_VIDEOVIEW, _T("cmb video view"), WS_MINIMIZE|WS_CAPTION|WS_POPUPWINDOW|WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, NULL, NULL); /* notify caller's thread, ui thread window is ready */ SetEvent(pvv->hevtnotify); while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } CoUninitialize(); _endthreadex(0); return 0; } int videoview_create(videoview_t *pvv, HWND hWnd, int off_x, int off_y, int pic_width, int pic_height) { int i; struct videoview *p; if (!pvv) return -1; for (i = 0; i < MAX_VIEW_ENTRY; ++i) if (_map[i].hwnd == NULL) break; if (i == MAX_VIEW_ENTRY) return -1; p = (struct videoview*)malloc(sizeof(struct videoview)); if (p == NULL) return -1; ZeroMemory(p, sizeof(struct videoview)); p->height = pic_height; p->width = pic_width; p->buf_size = pic_width * pic_height * 3; p->offset_x = off_x; p->offset_y = off_y; p->buf[0] = malloc(BUFFER_COUNT*p->buf_size); if (p->buf[0] == NULL) { free(p); return -1; } for (i = 1; i < BUFFER_COUNT; ++i) p->buf[i] = p->buf[0] + i*p->buf_size; if (hWnd) { if (!IsWindow(hWnd)) { free(p->buf[0]); free(p); return -1; } p->own = FALSE; p->hwnd = hWnd; p->lpfnOrigin = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)SubclassWndProc); _map[i].hwnd = p->hwnd; *pvv = _map[i].pvv = p; if (!InitDrawDib(p)) { SetWindowLong(hWnd, GWL_WNDPROC, (LONG)p->lpfnOrigin); free(p->buf[0]); free(p); _map[i].hwnd = NULL; _map[i].pvv = NULL; return -1; } } else { p->own = TRUE; p->hevtnotify = CreateEvent(NULL, FALSE, FALSE, NULL); p->uithread = (HANDLE)_beginthreadex(0, 0, &VideoViewUIThreadProc, p, 0, NULL); WaitForSingleObject(p->hevtnotify, INFINITE); _map[i].hwnd = p->hwnd; *pvv = _map[i].pvv = p; if (!InitDrawDib(p)) { PostMessage(p->hwnd, WM_CLOSE, 0, 0); WaitForSingleObject(p->uithread, INFINITE); CloseHandle(p->uithread); CloseHandle(p->hevtnotify); free(p->buf[0]); free(p); _map[i].hwnd = NULL; _map[i].pvv = NULL; return -1; } } return 0; } void videoview_destroy(videoview_t vv) { if (!vv || !vv->hwnd) return; if (vv->hDrawDib) DeinitDrawDib(vv); if (vv->own) { int i; for (i = 0; i < MAX_VIEW_ENTRY; ++i) if (_map[i].pvv == vv) break; if (i == MAX_VIEW_ENTRY) return; PostMessage(vv->hwnd, WM_VIDEO, (WPARAM)-3, 0); WaitForSingleObject(vv->uithread, INFINITE); CloseHandle(vv->uithread); CloseHandle(vv->hevtnotify); _map[i].hwnd = NULL; _map[i].pvv = NULL; } else { if (vv->lpfnOrigin) SetWindowLong(vv->hwnd, GWL_WNDPROC, (LONG)vv->lpfnOrigin); } free(vv->buf[0]); free(vv); } int videoview_put(videoview_t vv, void *buf, int len, unsigned int ts_rtp/*for later usage*/) { int i; if (!vv || !vv->hwnd || !buf || len != vv->buf_size) return -1; for (i = 0; i < BUFFER_COUNT; ++i) { if (vv->buf_use[i] == 0) { /* first try */ if (InterlockedCompareExchange(&vv->buf_use[i], 1, 0) == 0) { /* second try, ok */ memcpy(vv->buf[i], buf, len); PostMessage(vv->hwnd, WM_VIDEO, (WPARAM)i, (LPARAM)ts_rtp); return 0; } } } return -1; /* not enough slot */ }