在基于位图的视频播放器上闪烁
Posted
技术标签:
【中文标题】在基于位图的视频播放器上闪烁【英文标题】:Flickering on bitmap-based video player 【发布时间】:2017-07-06 14:35:15 【问题描述】:我正在尝试为以 RAW 格式存储的视频组合一个简单的视频播放器。这些视频可选地通过卷积过滤器。我的 CWinApp 类有三个相关的方法。首先,当一个视频被选中并放在指针viewingVideo
中时,我分配了两个位图:
void CMyApp::setBitmaps()
if (viewingVideo == NULL)
return;
bmp1 = CreateDIBSection(dc, viewingVideo->bmi, DIB_RGB_COLORS, NULL, NULL, 0);
bmp2 = CreateDIBSection(dc, viewingVideo->bmi, DIB_RGB_COLORS, NULL, NULL, 0);
然后,当用户按下播放键时,我会初始化每 33 毫秒调用以下两种方法的计时器,以实现 30 fps 的播放:
void CMyApp::updateVideoFrame()
if (viewingVideo == NULL)
return;
if (viewingKernel != NULL)
applyKernelFFT(viewingKernel, viewingVideo, currentFrame);
if (viewingKernel == NULL)
SetDIBits(dc, bmpToggle ? bmp2 : bmp1, 0, viewingVideo->height, (char*)(viewingVideo->data) + currentFrame*viewingVideo->bpp*viewingVideo->width*viewingVideo->height, viewingVideo->bmi, DIB_RGB_COLORS);
else
SetDIBits(dc, bmpToggle ? bmp2 : bmp1, 0, viewingVideo->height, (char*)(viewingVideo->filtered_data), viewingVideo->bmi, DIB_RGB_COLORS);
bmpToggle = !bmpToggle;
void CMyApp::updateVideoScreen()
CMyView* view = CMyView::GetView(); //returns the active view
view->m_Video.SetBitmap(bmpToggle ? bmp1 : bmp2);
CRect update;
view->m_Video.GetWindowRect(&update);
view->ScreenToClient(&update);
//update.DeflateRect(update.Width()/2-1, update.Height()/2 - 1);
InvalidateRect(*AfxGetMainWnd(), update, FALSE);
在任何给定时间,这些方法都应该使用不同的位图。当我启动这些计时器时,屏幕上有相当多的闪烁。当我取消注释 DeflateRect
调用,使 update
成为 2x2 像素矩形时,很明显实际上有两个闪烁源:有时整个图片闪烁,有时只有中心的 2x2 矩形。
我已经尝试过其他地方建议的常见修复方法,例如将WM_ERASEBKGND
交给CMyView
。这没有帮助。如何防止这种闪烁?
【问题讨论】:
你的 WM_PAINT 处理程序是什么样的? 也许这就是问题所在,我目前没有处理 WM_PAINT。我需要双缓冲吗?如果是这样,在 MFC 中这样做的正确方法是什么? 您正在使 updateVideoScreen 中的主窗口无效,这将触发 WM_PAINT。我以为那是你真正将位图blit到屏幕上的时候。 【参考方案1】:对于其他想要从他们的软件中删除癫痫警告的人来说,这里是修复闪烁的方法。 Adrian McCarthy 的建议完全正确。在updateVideoScreen
中,我将view->m_Video.SetBitmap(bmpToggle ? bmp1 : bmp2);
替换为SelectObject(dc, bmpToggle ? bmp1 : bmp2);
,因此位图数据被发送到我的内存DC 而不是控件。然后我覆盖OnPaint()
调用BitBlt
将位图写入视频控件所在的屏幕:
void CMTFBoostView::OnPaint()
CRect rect;
m_Video.GetWindowRect(&rect);
ScreenToClient(&rect);
video* v = ((CMTFBoostApp*)AfxGetApp())->viewingVideo;
CDC* src = ((CMTFBoostApp*)AfxGetApp())->cdc;
CDC* dc = GetDC();
if (v != NULL && dc->RectVisible(rect))
dc->BitBlt(rect.left, rect.top, v->width, v->height, src, 0, 0, SRCCOPY);
ReleaseDC(dc);
CFormView::OnPaint();
前几行有点像把视频放在我之前放的地方——从视图中完全删除 CStatic 控件可能会更干净。无论如何,这会显示没有闪烁的视频。
【讨论】:
有什么原因没有在OnPaint
处理程序中使用 CPaintDC
?
没有特别的原因 - 也许CPaintDC
在语义上更正确,或者只是更好的做法。更重要的是忘记打ReleaseDC(dc)
,导致显示几千帧后出现问题。我已经使用此更改编辑了代码。
使用CPaintDC
不仅在语义上更正确。它在功能上是正确的。不使用CPaintDC
无法验证无效区域,导致不断生成多余的WM_PAINT
消息流。除此之外,您更新的代码不会编译。对ReleaseDC
的调用在函数体之外。请不要发布假代码。
感谢您在 ReleaseDC
上发现我的错字。我试图用CPaintDC
来解决这个问题,但要更新图片,我必须更改我的InvalidateRect
调用,这会导致闪烁回来(所以它感觉不是一个合适的答案问题)。如果您可以让我知道如何正确地使该区域无效以与CPaintDC
一起使用,我很乐意更新答案。否则,这似乎是我目前能做的最好的事情,尽管有多余的 WM_PAINT
消息。以上是关于在基于位图的视频播放器上闪烁的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 平板电脑上使用 StageVideo 闪烁?