【问题标题】:SDL video overlay flickers with SDL_FlipSDL 视频覆盖闪烁 SDL_Flip
【发布时间】:2013-04-29 14:04:49
【问题描述】:

我使用 C++ 中的 SDL 和 libav 在 Linux 的屏幕上绘制视频。我的大部分视频打开代码都基于this tutorial,但我更改了一些已弃用的功能。我像这样初始化 SDL:

SDL_Init(SDL_INIT_EVERYTHING);
const SDL_VideoInfo * info = SDL_GetVideoInfo();
screen = SDL_SetVideoMode(info->current_w, info->current_h, 0, SDL_SWSURFACE | SDL_FULLSCREEN);

我不打算发布整个代码,因为它非常大,但以下显示了我如何尝试显示视频叠加层。请注意,一些变量是我的 Video 类中的类成员,例如 formatCtxpacket

void Video::GetOverlay(SDL_Overlay * overlay) {
    int frameFinished;
    while (av_read_frame(formatCtx, &packet) >= 0) {
        if (packet.stream_index == videoStream) {
            avcodec_decode_video2(codecCtx, frame, &frameFinished, &packet);
            if (frameFinished) {
                SDL_LockYUVOverlay(overlay);
                AVPicture pict;
                pict.data[0] = overlay->pixels[0];
                pict.data[1] = overlay->pixels[2];
                pict.data[2] = overlay->pixels[1];
                pict.linesize[0] = overlay->pitches[0];
                pict.linesize[1] = overlay->pitches[2];
                pict.linesize[2] = overlay->pitches[1];
                SwsContext * ctx = sws_getContext (codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
                                   codecCtx->width, codecCtx->height,  PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
                sws_scale(ctx, frame->data, frame->linesize, 0, codecCtx->height, pict.data, pict.linesize);
                sws_freeContext(ctx);
                SDL_UnlockYUVOverlay(overlay);
                ++frameIndex;
                return;
            }   
        }   
        av_free_packet(&packet);
    }   
}

然后在我的主循环中:

SDL_Overlay * overlay = SDL_CreateYUVOverlay(video->GetWidth(), video->GetHeight(), SDL_YV12_OVERLAY, screen);
while (true) {
    video->GetOverlay(overlay);
    SDL_Rect rect = { 400, 200, video->GetWidth(), video->GetHeight() };
    SDL_DisplayYUVOverlay(overlay, &rect);
    SDL_Flip(screen);
}

这有效,视频播放,但闪烁很多。就像它试图在每一帧的同一位置绘制图像一样。当我取消对SDL_Flip(screen) 的呼叫时,视频播放正常。太快了,我还没有进行视频超时,但是当我添加一个临时的SDL_Delay(10) 时,它看起来还不错。 但是当我删除SDL_Flip 以显示我的视频时,我无法在屏幕上绘制任何其他内容。 SDL_BlitSurfaceSDL_FillRect 都无法在屏幕上绘制任何内容。我已经尝试将SDL_DOUBLEBUF 添加到标志中,但这并没有改变这种情况。

如果需要,我可以提供更多代码,但我认为问题出在我发布的代码中,因为其他一切正常(绘制图像或显示没有SDL_Flip 的视频)。

我做错了什么?

【问题讨论】:

  • 显然有许多平台或情况(例如 ATI/nVidia 驱动程序设置)会导致 Vsync 被关闭或不被 SDL_Flip 使用;建议您搜索一下“sdl_flip vsync”。你可能会遇到这些事情中的任何一个。

标签: c++ video sdl libav


【解决方案1】:

您应该使用双缓冲来防止闪烁。

screen = SDL_SetVideoMode(info->current_w, info->current_h, 0, SDL_SWSURFACE | SDL_FULLSCREEN | SDL_DOUBLEBUF);

【讨论】:

  • 我之前应该提过这个,但我已经试过了,但没有效果,视频还在闪烁。
【解决方案2】:

由于您使用的是 SWSURFACE,因此不要使用 SDL_Flip(screen),而是使用 SDL_UpdateRect。你不需要设置 SDL_DOUBLEBUF

http://sdl.beuc.net/sdl.wiki/SDL_UpdateRect

这就是我所做的,我不会闪烁。

我的屏幕是这样设置的

screen = SDL_SetVideoMode(width, height, 32, SDL_SWSURFACE | SDL_FULLSCREEN);

在我的主循环中我调用

SDL_UpdateRect(screen, 0, 0, 0, 0);

【讨论】:

  • 我试过了,但是没有用。视频还在闪烁。你还在SDL_Overlay上画视频吗?
  • 不,我直接写入像素。像这样:memcpy((int*)screen->pixels, image, size*4);
  • 我仍在使用单线程,但我计划稍后使用多线程。您是否将视频帧加载到图像中?还是只加载一张图片?
  • 不确定您的意思。我读入了一个 targa 文件,因为该格式易于使用,并且我直接写入屏幕->像素。您可以尝试使用 SDL_BlitSurface 代替 memcpy,但 memcpy 对我来说已经足够快了。
  • 如果我理解正确的话,targa 是一种图像格式。但我正在加载一个 mp4 视频并尝试在 SDL_Overlay 上显示视频帧。所以我认为仅绘制图像或在叠加层上绘制视频帧之间存在区别,但我不确定。
【解决方案3】:

我仍然认为这不是最好的解决方案,但我发现了一些可行的方法! 命令SDL_UpdateRect(screen, 0, 0, 0, 0); 将更新整个屏幕。如果我只更新没有绘制视频的屏幕部分,视频将不再闪烁。我认为这可能与 SDL_Overlays 的处理方式与普通表面不同有关。

【讨论】:

    猜你喜欢
    • 2021-04-02
    • 1970-01-01
    • 2020-04-29
    • 2011-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-30
    相关资源
    最近更新 更多