FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sdl.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011 Stefano Sabatini
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file
23  * libSDL output device
24  */
25 
26 #include <SDL.h>
27 #include <SDL_thread.h>
28 
29 #include "libavutil/avstring.h"
30 #include "libavutil/imgutils.h"
31 #include "libavutil/opt.h"
32 #include "libavutil/parseutils.h"
33 #include "libavutil/pixdesc.h"
34 #include "libavutil/time.h"
35 #include "avdevice.h"
36 
37 typedef struct {
38  AVClass *class;
39  SDL_Surface *surface;
40  SDL_Overlay *overlay;
41  char *window_title;
42  char *icon_title;
43  int window_width, window_height; /**< size of the window */
45 
46  SDL_Rect overlay_rect;
48 
50  SDL_Thread *event_thread;
51  SDL_mutex *mutex;
52  SDL_cond *init_cond;
53  int init_ret; /* return code used to signal initialization errors */
54  int inited;
55  int quit;
56 } SDLContext;
57 
58 static const struct sdl_overlay_pix_fmt_entry {
61  { AV_PIX_FMT_YUV420P, SDL_IYUV_OVERLAY },
62  { AV_PIX_FMT_YUYV422, SDL_YUY2_OVERLAY },
63  { AV_PIX_FMT_UYVY422, SDL_UYVY_OVERLAY },
64  { AV_PIX_FMT_NONE, 0 },
65 };
66 
68 {
69  SDLContext *sdl = s->priv_data;
70 
71  sdl->quit = 1;
72 
73  if (sdl->overlay)
74  SDL_FreeYUVOverlay(sdl->overlay);
75  sdl->overlay = NULL;
76  if (sdl->event_thread)
77  SDL_WaitThread(sdl->event_thread, NULL);
78  sdl->event_thread = NULL;
79  if (sdl->mutex)
80  SDL_DestroyMutex(sdl->mutex);
81  sdl->mutex = NULL;
82  if (sdl->init_cond)
83  SDL_DestroyCond(sdl->init_cond);
84  sdl->init_cond = NULL;
85 
86  if (!sdl->sdl_was_already_inited)
87  SDL_Quit();
88 
89  return 0;
90 }
91 
93 {
94  AVRational sar, dar; /* sample and display aspect ratios */
95  SDLContext *sdl = s->priv_data;
96  AVStream *st = s->streams[0];
97  AVCodecContext *encctx = st->codec;
98  SDL_Rect *overlay_rect = &sdl->overlay_rect;
99 
100  /* compute overlay width and height from the codec context information */
101  sar = st->sample_aspect_ratio.num ? st->sample_aspect_ratio : (AVRational){ 1, 1 };
102  dar = av_mul_q(sar, (AVRational){ encctx->width, encctx->height });
103 
104  /* we suppose the screen has a 1/1 sample aspect ratio */
105  if (sdl->window_width && sdl->window_height) {
106  /* fit in the window */
107  if (av_cmp_q(dar, (AVRational){ sdl->window_width, sdl->window_height }) > 0) {
108  /* fit in width */
109  overlay_rect->w = sdl->window_width;
110  overlay_rect->h = av_rescale(overlay_rect->w, dar.den, dar.num);
111  } else {
112  /* fit in height */
113  overlay_rect->h = sdl->window_height;
114  overlay_rect->w = av_rescale(overlay_rect->h, dar.num, dar.den);
115  }
116  } else {
117  if (sar.num > sar.den) {
118  overlay_rect->w = encctx->width;
119  overlay_rect->h = av_rescale(overlay_rect->w, dar.den, dar.num);
120  } else {
121  overlay_rect->h = encctx->height;
122  overlay_rect->w = av_rescale(overlay_rect->h, dar.num, dar.den);
123  }
124  sdl->window_width = overlay_rect->w;
125  sdl->window_height = overlay_rect->h;
126  }
127 
128  overlay_rect->x = (sdl->window_width - overlay_rect->w) / 2;
129  overlay_rect->y = (sdl->window_height - overlay_rect->h) / 2;
130 }
131 
132 #define SDL_BASE_FLAGS (SDL_SWSURFACE|SDL_RESIZABLE)
133 
134 static int event_thread(void *arg)
135 {
136  AVFormatContext *s = arg;
137  SDLContext *sdl = s->priv_data;
138  int flags = SDL_BASE_FLAGS | (sdl->window_fullscreen ? SDL_FULLSCREEN : 0);
139  AVStream *st = s->streams[0];
140  AVCodecContext *encctx = st->codec;
141 
142  /* initialization */
143  if (SDL_Init(SDL_INIT_VIDEO) != 0) {
144  av_log(s, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError());
145  sdl->init_ret = AVERROR(EINVAL);
146  goto init_end;
147  }
148 
149  SDL_WM_SetCaption(sdl->window_title, sdl->icon_title);
150  sdl->surface = SDL_SetVideoMode(sdl->window_width, sdl->window_height,
151  24, flags);
152  if (!sdl->surface) {
153  av_log(sdl, AV_LOG_ERROR, "Unable to set video mode: %s\n", SDL_GetError());
154  sdl->init_ret = AVERROR(EINVAL);
155  goto init_end;
156  }
157 
158  sdl->overlay = SDL_CreateYUVOverlay(encctx->width, encctx->height,
159  sdl->overlay_fmt, sdl->surface);
160  if (!sdl->overlay || sdl->overlay->pitches[0] < encctx->width) {
161  av_log(s, AV_LOG_ERROR,
162  "SDL does not support an overlay with size of %dx%d pixels\n",
163  encctx->width, encctx->height);
164  sdl->init_ret = AVERROR(EINVAL);
165  goto init_end;
166  }
167 
168  sdl->init_ret = 0;
169  av_log(s, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s -> w:%d h:%d\n",
170  encctx->width, encctx->height, av_get_pix_fmt_name(encctx->pix_fmt),
171  sdl->overlay_rect.w, sdl->overlay_rect.h);
172 
173 init_end:
174  SDL_LockMutex(sdl->mutex);
175  sdl->inited = 1;
176  SDL_UnlockMutex(sdl->mutex);
177  SDL_CondSignal(sdl->init_cond);
178 
179  if (sdl->init_ret < 0)
180  return sdl->init_ret;
181 
182  /* event loop */
183  while (!sdl->quit) {
184  int ret;
185  SDL_Event event;
186  SDL_PumpEvents();
187  ret = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_ALLEVENTS);
188  if (ret < 0) {
189  av_log(s, AV_LOG_ERROR, "Error when getting SDL event: %s\n", SDL_GetError());
190  continue;
191  }
192  if (ret == 0) {
193  SDL_Delay(10);
194  continue;
195  }
196 
197  switch (event.type) {
198  case SDL_KEYDOWN:
199  switch (event.key.keysym.sym) {
200  case SDLK_ESCAPE:
201  case SDLK_q:
202  sdl->quit = 1;
203  break;
204  }
205  break;
206  case SDL_QUIT:
207  sdl->quit = 1;
208  break;
209 
210  case SDL_VIDEORESIZE:
211  sdl->window_width = event.resize.w;
212  sdl->window_height = event.resize.h;
213 
214  SDL_LockMutex(sdl->mutex);
215  sdl->surface = SDL_SetVideoMode(sdl->window_width, sdl->window_height, 24, SDL_BASE_FLAGS);
216  if (!sdl->surface) {
217  av_log(s, AV_LOG_ERROR, "Failed to set SDL video mode: %s\n", SDL_GetError());
218  sdl->quit = 1;
219  } else {
221  }
222  SDL_UnlockMutex(sdl->mutex);
223  break;
224 
225  default:
226  break;
227  }
228  }
229 
230  return 0;
231 }
232 
234 {
235  SDLContext *sdl = s->priv_data;
236  AVStream *st = s->streams[0];
237  AVCodecContext *encctx = st->codec;
238  int i, ret;
239 
240  if (!sdl->window_title)
241  sdl->window_title = av_strdup(s->filename);
242  if (!sdl->icon_title)
243  sdl->icon_title = av_strdup(sdl->window_title);
244 
245  if (SDL_WasInit(SDL_INIT_VIDEO)) {
246  av_log(s, AV_LOG_ERROR,
247  "SDL video subsystem was already inited, aborting\n");
248  sdl->sdl_was_already_inited = 1;
249  ret = AVERROR(EINVAL);
250  goto fail;
251  }
252 
253  if ( s->nb_streams > 1
254  || encctx->codec_type != AVMEDIA_TYPE_VIDEO
255  || encctx->codec_id != AV_CODEC_ID_RAWVIDEO) {
256  av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
257  ret = AVERROR(EINVAL);
258  goto fail;
259  }
260 
261  for (i = 0; sdl_overlay_pix_fmt_map[i].pix_fmt != AV_PIX_FMT_NONE; i++) {
262  if (sdl_overlay_pix_fmt_map[i].pix_fmt == encctx->pix_fmt) {
264  break;
265  }
266  }
267 
268  if (!sdl->overlay_fmt) {
269  av_log(s, AV_LOG_ERROR,
270  "Unsupported pixel format '%s', choose one of yuv420p, yuyv422, or uyvy422\n",
271  av_get_pix_fmt_name(encctx->pix_fmt));
272  ret = AVERROR(EINVAL);
273  goto fail;
274  }
275 
276  /* compute overlay width and height from the codec context information */
278 
279  sdl->init_cond = SDL_CreateCond();
280  if (!sdl->init_cond) {
281  av_log(s, AV_LOG_ERROR, "Could not create SDL condition variable: %s\n", SDL_GetError());
282  ret = AVERROR_EXTERNAL;
283  goto fail;
284  }
285  sdl->mutex = SDL_CreateMutex();
286  if (!sdl->mutex) {
287  av_log(s, AV_LOG_ERROR, "Could not create SDL mutex: %s\n", SDL_GetError());
288  ret = AVERROR_EXTERNAL;
289  goto fail;
290  }
291  sdl->event_thread = SDL_CreateThread(event_thread, s);
292  if (!sdl->event_thread) {
293  av_log(s, AV_LOG_ERROR, "Could not create SDL event thread: %s\n", SDL_GetError());
294  ret = AVERROR_EXTERNAL;
295  goto fail;
296  }
297 
298  /* wait until the video system has been inited */
299  SDL_LockMutex(sdl->mutex);
300  while (!sdl->inited) {
301  SDL_CondWait(sdl->init_cond, sdl->mutex);
302  }
303  SDL_UnlockMutex(sdl->mutex);
304  if (sdl->init_ret < 0) {
305  ret = sdl->init_ret;
306  goto fail;
307  }
308  return 0;
309 
310 fail:
312  return ret;
313 }
314 
316 {
317  SDLContext *sdl = s->priv_data;
318  AVCodecContext *encctx = s->streams[0]->codec;
319  uint8_t *data[4];
320  int linesize[4];
321  int i;
322 
323  if (sdl->quit) {
325  return AVERROR(EIO);
326  }
327  av_image_fill_arrays(data, linesize, pkt->data, encctx->pix_fmt, encctx->width, encctx->height, 1);
328 
329  SDL_LockMutex(sdl->mutex);
330  SDL_FillRect(sdl->surface, &sdl->surface->clip_rect,
331  SDL_MapRGB(sdl->surface->format, 0, 0, 0));
332  SDL_LockYUVOverlay(sdl->overlay);
333  for (i = 0; i < 3; i++) {
334  sdl->overlay->pixels [i] = data [i];
335  sdl->overlay->pitches[i] = linesize[i];
336  }
337  SDL_DisplayYUVOverlay(sdl->overlay, &sdl->overlay_rect);
338  SDL_UnlockYUVOverlay(sdl->overlay);
339 
340  SDL_UpdateRect(sdl->surface,
341  sdl->overlay_rect.x, sdl->overlay_rect.y,
342  sdl->overlay_rect.w, sdl->overlay_rect.h);
343  SDL_UnlockMutex(sdl->mutex);
344 
345  return 0;
346 }
347 
348 #define OFFSET(x) offsetof(SDLContext,x)
349 
350 static const AVOption options[] = {
351  { "window_title", "set SDL window title", OFFSET(window_title), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
352  { "icon_title", "set SDL iconified window title", OFFSET(icon_title) , AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
353  { "window_size", "set SDL window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
354  { "window_fullscreen", "set SDL window fullscreen", OFFSET(window_fullscreen), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
355  { NULL },
356 };
357 
358 static const AVClass sdl_class = {
359  .class_name = "sdl outdev",
360  .item_name = av_default_item_name,
361  .option = options,
362  .version = LIBAVUTIL_VERSION_INT,
364 };
365 
367  .name = "sdl",
368  .long_name = NULL_IF_CONFIG_SMALL("SDL output device"),
369  .priv_data_size = sizeof(SDLContext),
370  .audio_codec = AV_CODEC_ID_NONE,
371  .video_codec = AV_CODEC_ID_RAWVIDEO,
376  .priv_class = &sdl_class,
377 };
packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
Definition: pixfmt.h:83
#define NULL
Definition: coverity.c:32
const char * s
Definition: avisynth_c.h:631
Definition: sdl.c:37
static enum AVPixelFormat pix_fmt
int quit
Definition: sdl.c:55
int overlay_fmt
Definition: sdl.c:59
AVOption.
Definition: opt.h:245
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:101
misc image utilities
#define LIBAVUTIL_VERSION_INT
Definition: version.h:70
AVRational sample_aspect_ratio
sample aspect ratio (0 if unknown)
Definition: avformat.h:949
int num
numerator
Definition: rational.h:44
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align)
Setup the data pointers and linesizes based on the specified image parameters and the provided array...
Definition: imgutils.c:341
SDL_cond * init_cond
Definition: sdl.c:52
static int sdl_write_header(AVFormatContext *s)
Definition: sdl.c:233
enum AVPixelFormat pix_fmt
Definition: sdl.c:59
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:1752
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition: rational.h:66
static AVPacket pkt
int init_ret
Definition: sdl.c:53
SDL_Overlay * overlay
Definition: sdl.c:40
Format I/O context.
Definition: avformat.h:1314
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
int window_fullscreen
Definition: sdl.c:44
uint8_t
AVOptions.
SDL_Thread * event_thread
Definition: sdl.c:50
SDL_mutex * mutex
Definition: sdl.c:51
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1382
uint8_t * data
Definition: avcodec.h:1467
AVRational av_mul_q(AVRational b, AVRational c)
Multiply two rationals.
Definition: rational.c:80
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:192
SDL_Rect overlay_rect
Definition: sdl.c:46
#define av_log(a,...)
#define AV_OPT_FLAG_ENCODING_PARAM
a generic parameter which can be set by the user for muxing or encoding
Definition: opt.h:275
Main libavdevice API header.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
static void compute_overlay_rect(AVFormatContext *s)
Definition: sdl.c:92
Definition: sdl.c:58
av_default_item_name
#define AVERROR(e)
Definition: error.h:43
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:176
const char * arg
Definition: jacosubdec.c:66
#define fail()
Definition: checkasm.h:80
AVCodecContext * codec
Codec context associated with this stream.
Definition: avformat.h:896
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1370
static int sdl_write_trailer(AVFormatContext *s)
Definition: sdl.c:67
char filename[1024]
input or output filename
Definition: avformat.h:1390
int64_t av_rescale(int64_t a, int64_t b, int64_t c)
Rescale a 64-bit integer with rounding to nearest.
Definition: mathematics.c:129
char * icon_title
Definition: sdl.c:42
int width
picture width / height.
Definition: avcodec.h:1711
static int write_trailer(AVFormatContext *s1)
Definition: v4l2enc.c:94
const char * name
Definition: avformat.h:523
int sdl_was_already_inited
Definition: sdl.c:49
int window_height
size of the window
Definition: sdl.c:43
int inited
Definition: sdl.c:54
#define OFFSET(x)
Definition: sdl.c:348
Stream structure.
Definition: avformat.h:877
#define AVFMT_NOTIMESTAMPS
Format does not need / have any timestamps.
Definition: avformat.h:485
int window_width
Definition: sdl.c:43
enum AVMediaType codec_type
Definition: avcodec.h:1540
enum AVCodecID codec_id
Definition: avcodec.h:1549
static int event_thread(void *arg)
Definition: sdl.c:134
char * av_strdup(const char *s)
Duplicate the string s.
Definition: mem.c:267
main external API structure.
Definition: avcodec.h:1532
packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
Definition: pixfmt.h:64
static const char * window_title
Definition: ffplay.c:315
Describe the class of an AVClass context structure.
Definition: log.h:67
rational number numerator/denominator
Definition: rational.h:43
static int sdl_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition: sdl.c:315
static const struct sdl_overlay_pix_fmt_entry sdl_overlay_pix_fmt_map[]
static const AVOption options[]
Definition: sdl.c:350
offset must point to two consecutive integers
Definition: opt.h:232
misc parsing utilities
#define SDL_BASE_FLAGS
Definition: sdl.c:132
static int flags
Definition: cpu.c:47
AVOutputFormat ff_sdl_muxer
Definition: sdl.c:366
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition: pixfmt.h:63
int overlay_fmt
Definition: sdl.c:47
#define AVFMT_NOFILE
Demuxer will use avio_open, no opened file should be provided by the caller.
Definition: avformat.h:477
int den
denominator
Definition: rational.h:45
#define AVFMT_VARIABLE_FPS
Format allows variable fps.
Definition: avformat.h:488
SDL_Surface * surface
Definition: sdl.c:39
char * window_title
Definition: sdl.c:41
void * priv_data
Format private data.
Definition: avformat.h:1342
static void write_header(FFV1Context *f)
Definition: ffv1enc.c:497
static const AVClass sdl_class
Definition: sdl.c:358
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition: pixdesc.c:2078
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition: error.h:57
AVPixelFormat
Pixel format.
Definition: pixfmt.h:61
This structure stores compressed data.
Definition: avcodec.h:1444
static int write_packet(AVFormatContext *s1, AVPacket *pkt)
Definition: v4l2enc.c:86