如果您打开skype并单击“共享屏幕”,则它将向您显示将要流式传输的视频的预览。
到目前为止,我有以下代码:
获取屏幕:
HBITMAP screenshot()
{
// get the device context of the screen
HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetDeviceCaps(hScreenDC, HORZRES);
int height = GetDeviceCaps(hScreenDC, VERTRES);
// maybe worth checking these are positive values
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
return hBitmap;
要在表单上呈现:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
case WM_CREATE:
//hBitmap = (HBITMAP)LoadImage(NULL, LPCSTR("c:/users/they/documents/file.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
case WM_PAINT:
hBitmap = screenshot();
PAINTSTRUCT ps;
HDC hdc;
BITMAP bitmap;
HDC hdcMem;
HGDIOBJ oldBitmap;
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
oldBitmap = SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(bitmap), &bitmap);
BitBlt(hdc, 200, 50, bitmap.bmWidth,bitmap.bmHeight,
hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
if(millis % 70) RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
}
问题是,我已经阅读了有关计时器队列和标准计时器的时间“毫秒%70”,但听说它们在快速运行时不可靠, 还可以像不使用库那样逐帧渲染“视频”的最佳方法一样进行重新绘制吗?
答案 0 :(得分:0)
这里有几个不同的问题:
GDI可能不够快,无法每秒完成60或70帧。注释中还建议了其他技术(如@Richard Critten),这些技术旨在与硬件紧密协作来进行此类操作。不可否认,这些API可能更难学习和使用。
您说对了,这些计时器不会可靠地触发每帧代码。人们可以使用一些技巧来提高这些计时器的分辨率,但是这样做有很多弊端,尤其是如果您对此不太谨慎的话。
话虽如此,有可能将一个程序捆绑在一起,以较低的帧速率使用GDI复制视频帧。我已经在“ Windows视频”这一天做完了。我设法以接近30 fps的速度从网络摄像头视频预览窗口中捕捉640x480帧,偶尔掉帧。
为此,您可以使用“游戏循环”而不是依赖计时器事件。游戏循环是一个紧密的循环,它会监视时钟直到需要处理下一帧为止。这将消耗大量的CPU并消耗笔记本电脑的电池,但这实际上是大多数Windows视频游戏在玩时的样子。 (暂停游戏后,好的游戏将停止“旋转”。)
典型的基于事件的Windows程序具有如下消息循环:
while (GetMessage(&msg, NULL, 0, 0) > 0) {
DispatchMessage(&msg);
}
(实际代码可能稍微复杂一些,但这是我们关心的胆量。)
GetMessage
调用将等待,直到有消息可供您的程序响应为止。
游戏循环不会等待。它检查是否有消息准备就绪而无需等待。它使用PeekMessage来做到这一点。它要做的另一件事是跟踪时间。如果无事可做,它将立即循环。在半伪代码中,它看起来像这样:
SomeType next_frame_time = now;
for (;;) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) break;
DispatchMessage(msg);
}
if (current time >= next_frame_time) {
HandleNextFrame();
next_frame_time += frame interval;
}
}
请注意,除非WM_QUIT消息到达,否则循环只会永远运行。而且它运行得最快。不必在响应WM_TIMER和WM_PAINT的情况下进行工作,而是在调用HandleNextFrame时进行处理。
剩下的技巧是使用高分辨率时钟。您可以为此使用Windows API QueryPeformanceCounter。请注意,您必须在运行时使用QueryPerformanceFrequency确定QueryPerformanceCounter使用的单位。