Question

I use SDL and libav in C++ to draw a video on the screen in Linux. Most of my code for video opening is based on this tutorial, but I changed some functions that were deprecated. I initialize SDL like this:

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);

I am not going to post the whole code since it is pretty big, but the following shows how I try to display a video overlay. Note that some variables are classmembers from my Video class, like formatCtx and packet.

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);
    }   
}

And then in my mainloop:

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);
}

This works, the video plays but it flickers a lot. Like it tries to draw an image on the same place each frame. When I remove the call to SDL_Flip(screen) the video plays fine. Too fast, I haven't worked videotiming out yet, but when I add a temporary SDL_Delay(10) it looks pretty good. But when I remove SDL_Flip to show my video, I can't draw anything else on the screen. Both SDL_BlitSurface and SDL_FillRect fail to draw anything on the screen. I already tried to add SDL_DOUBLEBUF to the flags, but this did not change the situation.

I can provide more code if that is needed, but I think the problem is somewhere in the code that I have posted, since everything else is working fine (drawing images, or displaying a video without SDL_Flip).

What am I doing wrong?

Was it helpful?

Solution 3

I still think this isn't the best solution, but I found something that works! The command SDL_UpdateRect(screen, 0, 0, 0, 0); will update the whole screen. If I only update the parts of the screen where no video is drawn, the video won't flicker anymore. I think it might have something to do with SDL_Overlays being handled differently than normal surfaces.

OTHER TIPS

Since you're using a SWSURFACE don't use SDL_Flip(screen) use SDL_UpdateRect. You don't need to set SDL_DOUBLEBUF

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

That's what I do and I don't get flicker.

I set my screen like this

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

In my main loop I call

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

You should use double buffering to prevent flickering.

screen = SDL_SetVideoMode(info->current_w, info->current_h, 0, SDL_SWSURFACE | SDL_FULLSCREEN | SDL_DOUBLEBUF);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top