在基于位图的视频播放器上闪烁

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 闪烁?

将位图添加到视频中

在 x 秒后闪烁自动暂停视频并在选定选项后播放

如何从使用directx c# visual studio播放的视频中获取和处理每一帧(位图图像​​)

如何使用 MediaCodec 将位图编码为视频?

Jwplayer 闪烁“加载播放器时出错:无法加载播放器配置”