0

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?

Carlito
  • 805
  • 10
  • 20
  • Apparently there are a number of platforms or situations (such as ATI/nVidia driver settings) that can cause vsync to be turned off or not used by SDL_Flip; suggest you search on "sdl_flip vsync" a bit. You might be hitting any one of these things. – leander May 05 '13 at 19:25

3 Answers3

2

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);
  • I tried this, but it didn't work. The video was still flickering. Are you also drawing a video on a `SDL_Overlay`? – Carlito May 07 '13 at 07:41
  • No, I write to the pixels directly. Like this: memcpy((int*)screen->pixels, image, size*4); –  May 07 '13 at 09:11
  • I'm still using a single thread, but I plan on using multiple threads later on. Do you load your video frame into an image? Or do you just load an image? – Carlito May 07 '13 at 10:14
  • Not sure exactly what you mean. I read in a targa file since the format is easy to use and I write directly to screen->pixels. You can try SDL_BlitSurface instead of memcpy but memcpy is fast enough for me. –  May 07 '13 at 10:44
  • If I understand it correctly, targa is an image format. But I am loading an mp4 video and trying to display the videoframes on a SDL_Overlay. So I think there is a difference between just drawing an image, or drawing videoframes on an overlay, but I am not sure. – Carlito May 07 '13 at 11:00
  • I don't know about SDL_Overlay. But I generating images (using a ray tracer) at more than 60fps and showing them in real-time with no flicker. I copy to the new image to the pixel buffer and do SDL_Update each frame. –  May 07 '13 at 11:17
  • anyway, since I have not used SDL_Overlay I'm not sure I can help you. I would check out http://sdl.beuc.net/sdl.wiki/SDL_Overlay . It appears to have an example on how to use it using e.g. memcpy. –  May 07 '13 at 12:11
  • I will check that link later today and update my post if I have found a working solution. Thanks for your time! – Carlito May 07 '13 at 12:19
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);
Scintillo
  • 1,634
  • 1
  • 15
  • 29
  • I should have mentioned this before, but I tried this already but it had no effect, the video still flickers. – Carlito May 06 '13 at 10:13
0

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.

Carlito
  • 805
  • 10
  • 20