From 7de3b1394b71118279e8f997be490633bc3f5d46 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini Date: Sun, 24 Nov 2013 19:32:59 +0100 Subject: [PATCH] lavd/sdl: add event handler thread SDL_Init() is called on the event handler thread, as required by SDL in Windows to avoid deadlocks as discovered by Roger Pack. Fix trac ticket #1743 and #1744. --- doc/outdevs.texi | 10 ++++ libavdevice/sdl.c | 154 +++++++++++++++++++++++++++++++++++++++++--------- libavdevice/version.h | 2 +- 3 files changed, 138 insertions(+), 28 deletions(-) diff --git a/doc/outdevs.texi b/doc/outdevs.texi index 8038defe67..6c8422cca4 100644 --- a/doc/outdevs.texi +++ b/doc/outdevs.texi @@ -209,6 +209,16 @@ Set fullscreen mode when non-zero value is provided. Default value is zero. @end table +@subsection Interactive commands + +The window created by the device can be controlled through the +following interactive commands. + +@table @key +@item q, ESC +Quit the device immediately. +@end table + @subsection Examples The following command shows the @command{ffmpeg} output is an diff --git a/libavdevice/sdl.c b/libavdevice/sdl.c index 0210ad26d9..8606958ad8 100644 --- a/libavdevice/sdl.c +++ b/libavdevice/sdl.c @@ -24,10 +24,13 @@ */ #include +#include + #include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" +#include "libavutil/time.h" #include "avdevice.h" typedef struct { @@ -42,6 +45,12 @@ typedef struct { int overlay_x, overlay_y; int overlay_fmt; int sdl_was_already_inited; + SDL_Thread *event_thread; + SDL_mutex *mutex; + SDL_cond *init_cond; + int init_ret; /* return code used to signal initialization errors */ + int inited; + int quit; } SDLContext; static const struct sdl_overlay_pix_fmt_entry { @@ -57,16 +66,102 @@ static int sdl_write_trailer(AVFormatContext *s) { SDLContext *sdl = s->priv_data; - if (sdl->overlay) { + sdl->quit = 1; + + if (sdl->overlay) SDL_FreeYUVOverlay(sdl->overlay); - sdl->overlay = NULL; - } + if (sdl->event_thread) + SDL_WaitThread(sdl->event_thread, NULL); + if (sdl->mutex) + SDL_DestroyMutex(sdl->mutex); + if (sdl->init_cond) + SDL_DestroyCond(sdl->init_cond); + if (!sdl->sdl_was_already_inited) SDL_Quit(); return 0; } +static int event_thread(void *arg) +{ + AVFormatContext *s = arg; + SDLContext *sdl = s->priv_data; + int flags = SDL_SWSURFACE | (sdl->window_fullscreen ? SDL_FULLSCREEN : 0); + AVStream *st = s->streams[0]; + AVCodecContext *encctx = st->codec; + + /* initialization */ + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + av_log(s, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError()); + sdl->init_ret = AVERROR(EINVAL); + goto init_end; + } + + SDL_WM_SetCaption(sdl->window_title, sdl->icon_title); + sdl->surface = SDL_SetVideoMode(sdl->window_width, sdl->window_height, + 24, flags); + if (!sdl->surface) { + av_log(sdl, AV_LOG_ERROR, "Unable to set video mode: %s\n", SDL_GetError()); + sdl->init_ret = AVERROR(EINVAL); + goto init_end; + } + + sdl->overlay = SDL_CreateYUVOverlay(encctx->width, encctx->height, + sdl->overlay_fmt, sdl->surface); + if (!sdl->overlay || sdl->overlay->pitches[0] < encctx->width) { + av_log(s, AV_LOG_ERROR, + "SDL does not support an overlay with size of %dx%d pixels\n", + encctx->width, encctx->height); + sdl->init_ret = AVERROR(EINVAL); + goto init_end; + } + + sdl->init_ret = 0; + av_log(s, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s -> w:%d h:%d\n", + encctx->width, encctx->height, av_get_pix_fmt_name(encctx->pix_fmt), + sdl->overlay_width, sdl->overlay_height); + +init_end: + SDL_LockMutex(sdl->mutex); + sdl->inited = 1; + SDL_UnlockMutex(sdl->mutex); + SDL_CondSignal(sdl->init_cond); + + if (sdl->init_ret < 0) + return sdl->init_ret; + + /* event loop */ + while (!sdl->quit) { + int ret; + SDL_Event event; + SDL_PumpEvents(); + ret = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_ALLEVENTS); + if (ret < 0) + av_log(s, AV_LOG_ERROR, "Error when getting SDL event: %s\n", SDL_GetError()); + if (ret <= 0) + continue; + + switch (event.type) { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + case SDLK_q: + sdl->quit = 1; + break; + } + break; + case SDL_QUIT: + sdl->quit = 1; + break; + default: + break; + } + } + + return 0; +} + static int sdl_write_header(AVFormatContext *s) { SDLContext *sdl = s->priv_data; @@ -74,7 +169,6 @@ static int sdl_write_header(AVFormatContext *s) AVCodecContext *encctx = st->codec; AVRational sar, dar; /* sample and display aspect ratios */ int i, ret; - int flags = SDL_SWSURFACE | sdl->window_fullscreen ? SDL_FULLSCREEN : 0; if (!sdl->window_title) sdl->window_title = av_strdup(s->filename); @@ -89,12 +183,6 @@ static int sdl_write_header(AVFormatContext *s) goto fail; } - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - av_log(s, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError()); - ret = AVERROR(EINVAL); - goto fail; - } - if ( s->nb_streams > 1 || encctx->codec_type != AVMEDIA_TYPE_VIDEO || encctx->codec_id != AV_CODEC_ID_RAWVIDEO) { @@ -148,28 +236,35 @@ static int sdl_write_header(AVFormatContext *s) sdl->overlay_x = (sdl->window_width - sdl->overlay_width ) / 2; sdl->overlay_y = (sdl->window_height - sdl->overlay_height) / 2; - SDL_WM_SetCaption(sdl->window_title, sdl->icon_title); - sdl->surface = SDL_SetVideoMode(sdl->window_width, sdl->window_height, - 24, flags); - if (!sdl->surface) { - av_log(s, AV_LOG_ERROR, "Unable to set video mode: %s\n", SDL_GetError()); - ret = AVERROR(EINVAL); + sdl->init_cond = SDL_CreateCond(); + if (!sdl->init_cond) { + av_log(s, AV_LOG_ERROR, "Could not create SDL condition variable: %s\n", SDL_GetError()); + ret = AVERROR_EXTERNAL; goto fail; } - - sdl->overlay = SDL_CreateYUVOverlay(encctx->width, encctx->height, - sdl->overlay_fmt, sdl->surface); - if (!sdl->overlay || sdl->overlay->pitches[0] < encctx->width) { - av_log(s, AV_LOG_ERROR, - "SDL does not support an overlay with size of %dx%d pixels\n", - encctx->width, encctx->height); - ret = AVERROR(EINVAL); + sdl->mutex = SDL_CreateMutex(); + if (!sdl->mutex) { + av_log(s, AV_LOG_ERROR, "Could not create SDL mutex: %s\n", SDL_GetError()); + ret = AVERROR_EXTERNAL; + goto fail; + } + sdl->event_thread = SDL_CreateThread(event_thread, s); + if (!sdl->event_thread) { + av_log(s, AV_LOG_ERROR, "Could not create SDL event thread: %s\n", SDL_GetError()); + ret = AVERROR_EXTERNAL; goto fail; } - av_log(s, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s sar:%d/%d -> w:%d h:%d\n", - encctx->width, encctx->height, av_get_pix_fmt_name(encctx->pix_fmt), sar.num, sar.den, - sdl->overlay_width, sdl->overlay_height); + /* wait until the video system has been inited */ + SDL_LockMutex(sdl->mutex); + if (!sdl->inited) { + SDL_CondWait(sdl->init_cond, sdl->mutex); + } + SDL_UnlockMutex(sdl->mutex); + if (sdl->init_ret < 0) { + ret = sdl->init_ret; + goto fail; + } return 0; fail: @@ -185,10 +280,15 @@ static int sdl_write_packet(AVFormatContext *s, AVPacket *pkt) AVPicture pict; int i; + if (sdl->quit) + return AVERROR(EIO); avpicture_fill(&pict, pkt->data, encctx->pix_fmt, encctx->width, encctx->height); + SDL_LockMutex(sdl->mutex); SDL_FillRect(sdl->surface, &sdl->surface->clip_rect, SDL_MapRGB(sdl->surface->format, 0, 0, 0)); + SDL_UnlockMutex(sdl->mutex); + SDL_LockYUVOverlay(sdl->overlay); for (i = 0; i < 3; i++) { sdl->overlay->pixels [i] = pict.data [i]; diff --git a/libavdevice/version.h b/libavdevice/version.h index 4f4ec99d94..8e3ff7f2ca 100644 --- a/libavdevice/version.h +++ b/libavdevice/version.h @@ -29,7 +29,7 @@ #define LIBAVDEVICE_VERSION_MAJOR 55 #define LIBAVDEVICE_VERSION_MINOR 5 -#define LIBAVDEVICE_VERSION_MICRO 100 +#define LIBAVDEVICE_VERSION_MICRO 101 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ LIBAVDEVICE_VERSION_MINOR, \ -- 2.11.0