Я использую SDL и libav в C ++ для рисования видео на экране в Linux. Большая часть моего кода для открытия видео основана на этот урок, но я изменил некоторые функции, которые устарели. Я инициализирую 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, например formatCtx
а также 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);
}
}
А потом в моем основном цикле:
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_BlitSurface
а также SDL_FillRect
не удается нарисовать что-либо на экране. Я уже пытался добавить SDL_DOUBLEBUF
на флаги, но это не изменило ситуацию.
Я могу предоставить больше кода, если это необходимо, но я думаю, что проблема где-то в коде, который я опубликовал, так как все остальное работает нормально (рисование изображений или отображение видео без SDL_Flip
).
Что я делаю неправильно?
Поскольку вы используете SWSURFACE, не используйте SDL_Flip (экран), используйте 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);
Вы должны использовать двойную буферизацию, чтобы предотвратить мерцание.
screen = SDL_SetVideoMode(info->current_w, info->current_h, 0, SDL_SWSURFACE | SDL_FULLSCREEN | SDL_DOUBLEBUF);
Я все еще думаю, что это не лучшее решение, но я нашел то, что работает!
Команда SDL_UpdateRect(screen, 0, 0, 0, 0);
обновит весь экран. Если я обновлю только те части экрана, где видео не прорисовано, оно больше не будет мерцать. Я думаю, что это может быть связано с обработкой SDL_Overlays не так, как с обычными поверхностями.