FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sdl2.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016 Josh de Kock
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  * libSDL2 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_Window *window;
40  SDL_Renderer *renderer;
41  char *window_title;
42  int window_width, window_height; /**< size of the window */
43  int window_x, window_y; /**< position of the window */
47 
48  SDL_Texture *texture;
50  SDL_Rect texture_rect;
51 
52  int inited;
53 } SDLContext;
54 
55 static const struct sdl_texture_format_entry {
58  /*
59  * Not implemented in FFmpeg, but leaving here for completeness.
60  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_ARGB4444 },
61  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_RGBA4444 },
62  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_ABGR4444 },
63  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_BGRA4444 },
64  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_ARGB1555 },
65  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_RGBA5551 },
66  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_ABGR1555 },
67  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_BGRA5551 },
68  * { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_ARGB2101010 },
69  */
70  { AV_PIX_FMT_RGB8, SDL_PIXELFORMAT_RGB332 },
71  { AV_PIX_FMT_RGB444, SDL_PIXELFORMAT_RGB444 },
72  { AV_PIX_FMT_RGB555, SDL_PIXELFORMAT_RGB555 },
73  { AV_PIX_FMT_BGR555, SDL_PIXELFORMAT_BGR555 },
74  { AV_PIX_FMT_RGB565, SDL_PIXELFORMAT_RGB565 },
75  { AV_PIX_FMT_BGR565, SDL_PIXELFORMAT_BGR565 },
76  { AV_PIX_FMT_RGB24, SDL_PIXELFORMAT_RGB24 },
77  { AV_PIX_FMT_BGR24, SDL_PIXELFORMAT_BGR24 },
78  { AV_PIX_FMT_0RGB32, SDL_PIXELFORMAT_RGB888 },
79  { AV_PIX_FMT_0BGR32, SDL_PIXELFORMAT_BGR888 },
80 #if HAVE_BIGENDIAN
81  { AV_PIX_FMT_RGB0, SDL_PIXELFORMAT_RGBX8888 },
82  { AV_PIX_FMT_BGR0, SDL_PIXELFORMAT_BGRX8888 },
83 #else
84  { AV_PIX_FMT_0BGR, SDL_PIXELFORMAT_RGBX8888 },
85  { AV_PIX_FMT_0RGB, SDL_PIXELFORMAT_BGRX8888 },
86 #endif
87  { AV_PIX_FMT_RGB32, SDL_PIXELFORMAT_ARGB8888 },
88  { AV_PIX_FMT_RGB32_1, SDL_PIXELFORMAT_RGBA8888 },
89  { AV_PIX_FMT_BGR32, SDL_PIXELFORMAT_ABGR8888 },
90  { AV_PIX_FMT_BGR32_1, SDL_PIXELFORMAT_BGRA8888 },
91  { AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV },
92  { AV_PIX_FMT_YUYV422, SDL_PIXELFORMAT_YUY2 },
93  { AV_PIX_FMT_UYVY422, SDL_PIXELFORMAT_UYVY },
94  { AV_PIX_FMT_NONE, 0 },
95 };
96 
98 {
99  AVRational sar, dar; /* sample and display aspect ratios */
100  SDLContext *sdl = s->priv_data;
101  AVStream *st = s->streams[0];
102  AVCodecParameters *codecpar = st->codecpar;
103  SDL_Rect *texture_rect = &sdl->texture_rect;
104 
105  /* compute texture width and height from the codec context information */
106  sar = st->sample_aspect_ratio.num ? st->sample_aspect_ratio : (AVRational){ 1, 1 };
107  dar = av_mul_q(sar, (AVRational){ codecpar->width, codecpar->height });
108 
109  /* we suppose the screen has a 1/1 sample aspect ratio */
110  if (sdl->window_width && sdl->window_height) {
111  /* fit in the window */
112  if (av_cmp_q(dar, (AVRational){ sdl->window_width, sdl->window_height }) > 0) {
113  /* fit in width */
114  texture_rect->w = sdl->window_width;
115  texture_rect->h = av_rescale(texture_rect->w, dar.den, dar.num);
116  } else {
117  /* fit in height */
118  texture_rect->h = sdl->window_height;
119  texture_rect->w = av_rescale(texture_rect->h, dar.num, dar.den);
120  }
121  } else {
122  if (sar.num > sar.den) {
123  texture_rect->w = codecpar->width;
124  texture_rect->h = av_rescale(texture_rect->w, dar.den, dar.num);
125  } else {
126  texture_rect->h = codecpar->height;
127  texture_rect->w = av_rescale(texture_rect->h, dar.num, dar.den);
128  }
129  sdl->window_width = texture_rect->w;
130  sdl->window_height = texture_rect->h;
131  }
132 
133  texture_rect->x = (sdl->window_width - texture_rect->w) / 2;
134  texture_rect->y = (sdl->window_height - texture_rect->h) / 2;
135 }
136 
138 {
139  SDLContext *sdl = s->priv_data;
140 
141  if (sdl->texture)
142  SDL_DestroyTexture(sdl->texture);
143  sdl->texture = NULL;
144 
145  if (sdl->renderer)
146  SDL_DestroyRenderer(sdl->renderer);
147  sdl->renderer = NULL;
148 
149  if (sdl->window)
150  SDL_DestroyWindow(sdl->window);
151  sdl->window = NULL;
152 
153  if (!sdl->inited)
154  SDL_Quit();
155 
156  return 0;
157 }
158 
160 {
161  SDLContext *sdl = s->priv_data;
162  AVStream *st = s->streams[0];
163  AVCodecParameters *codecpar = st->codecpar;
164  int i, ret = 0;
165  int flags = 0;
166 
167  if (!sdl->window_title)
168  sdl->window_title = av_strdup(s->url);
169 
170  if (SDL_WasInit(SDL_INIT_VIDEO)) {
172  "SDL video subsystem was already inited, you could have multiple SDL outputs. This may cause unknown behaviour.\n");
173  sdl->inited = 1;
174  }
175 
176  if ( s->nb_streams > 1
177  || codecpar->codec_type != AVMEDIA_TYPE_VIDEO
178  || codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) {
179  av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
180  goto fail;
181  }
182 
183  for (i = 0; sdl_texture_format_map[i].format != AV_PIX_FMT_NONE; i++) {
184  if (sdl_texture_format_map[i].format == codecpar->format) {
186  break;
187  }
188  }
189 
190  if (!sdl->texture_fmt) {
191  av_log(s, AV_LOG_ERROR,
192  "Unsupported pixel format '%s'.\n",
193  av_get_pix_fmt_name(codecpar->format));
194  goto fail;
195  }
196 
197  /* resize texture to width and height from the codec context information */
198  flags = SDL_WINDOW_HIDDEN |
199  (sdl->window_fullscreen ? SDL_WINDOW_FULLSCREEN : 0) |
200  (sdl->window_borderless ? SDL_WINDOW_BORDERLESS : SDL_WINDOW_RESIZABLE);
201 
202  /* initialization */
203  if (!sdl->inited){
204  if (SDL_Init(SDL_INIT_VIDEO) != 0) {
205  av_log(s, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError());
206  goto fail;
207  }
208  }
209 
211 
212  if (SDL_CreateWindowAndRenderer(sdl->window_width, sdl->window_height,
213  flags, &sdl->window, &sdl->renderer) != 0){
214  av_log(sdl, AV_LOG_ERROR, "Couldn't create window and renderer: %s\n", SDL_GetError());
215  goto fail;
216  }
217 
218  SDL_SetWindowTitle(sdl->window, sdl->window_title);
219  SDL_SetWindowPosition(sdl->window, sdl->window_x, sdl->window_y);
220  SDL_ShowWindow(sdl->window);
221 
222  sdl->texture = SDL_CreateTexture(sdl->renderer, sdl->texture_fmt, SDL_TEXTUREACCESS_STREAMING,
223  codecpar->width, codecpar->height);
224 
225  if (!sdl->texture) {
226  av_log(sdl, AV_LOG_ERROR, "Unable to set create mode: %s\n", SDL_GetError());
227  goto fail;
228  }
229 
230  av_log(s, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s -> w:%d h:%d\n",
231  codecpar->width, codecpar->height, av_get_pix_fmt_name(codecpar->format),
232  sdl->window_width, sdl->window_height);
233 
234  sdl->inited = 1;
235 
236  return 0;
237 fail:
239  return ret;
240 }
241 
243 {
244  int ret, quit = 0;
245  SDLContext *sdl = s->priv_data;
246  AVCodecParameters *codecpar = s->streams[0]->codecpar;
247  uint8_t *data[4];
248  int linesize[4];
249 
250  SDL_Event event;
251  if (SDL_PollEvent(&event)){
252  switch (event.type) {
253  case SDL_KEYDOWN:
254  switch (event.key.keysym.sym) {
255  case SDLK_ESCAPE:
256  case SDLK_q:
257  quit = 1;
258  break;
259  default:
260  break;
261  }
262  break;
263  case SDL_QUIT:
264  quit = 1;
265  break;
266  case SDL_WINDOWEVENT:
267  switch(event.window.event){
268  case SDL_WINDOWEVENT_RESIZED:
269  case SDL_WINDOWEVENT_SIZE_CHANGED:
270  sdl->window_width = event.window.data1;
271  sdl->window_height = event.window.data2;
273  break;
274  default:
275  break;
276  }
277  break;
278  default:
279  break;
280  }
281  }
282 
283  if (quit && sdl->enable_quit_action) {
285  return AVERROR(EIO);
286  }
287 
288  av_image_fill_arrays(data, linesize, pkt->data, codecpar->format, codecpar->width, codecpar->height, 1);
289  switch (sdl->texture_fmt) {
290  /* case SDL_PIXELFORMAT_ARGB4444:
291  * case SDL_PIXELFORMAT_RGBA4444:
292  * case SDL_PIXELFORMAT_ABGR4444:
293  * case SDL_PIXELFORMAT_BGRA4444:
294  * case SDL_PIXELFORMAT_ARGB1555:
295  * case SDL_PIXELFORMAT_RGBA5551:
296  * case SDL_PIXELFORMAT_ABGR1555:
297  * case SDL_PIXELFORMAT_BGRA5551:
298  * case SDL_PIXELFORMAT_ARGB2101010:
299  */
300  case SDL_PIXELFORMAT_IYUV:
301  case SDL_PIXELFORMAT_YUY2:
302  case SDL_PIXELFORMAT_UYVY:
303  ret = SDL_UpdateYUVTexture(sdl->texture, NULL,
304  data[0], linesize[0],
305  data[1], linesize[1],
306  data[2], linesize[2]);
307  break;
308  case SDL_PIXELFORMAT_RGB332:
309  case SDL_PIXELFORMAT_RGB444:
310  case SDL_PIXELFORMAT_RGB555:
311  case SDL_PIXELFORMAT_BGR555:
312  case SDL_PIXELFORMAT_RGB565:
313  case SDL_PIXELFORMAT_BGR565:
314  case SDL_PIXELFORMAT_RGB24:
315  case SDL_PIXELFORMAT_BGR24:
316  case SDL_PIXELFORMAT_RGB888:
317  case SDL_PIXELFORMAT_RGBX8888:
318  case SDL_PIXELFORMAT_BGR888:
319  case SDL_PIXELFORMAT_BGRX8888:
320  case SDL_PIXELFORMAT_ARGB8888:
321  case SDL_PIXELFORMAT_RGBA8888:
322  case SDL_PIXELFORMAT_ABGR8888:
323  case SDL_PIXELFORMAT_BGRA8888:
324  ret = SDL_UpdateTexture(sdl->texture, NULL, data[0], linesize[0]);
325  break;
326  default:
327  av_log(NULL, AV_LOG_FATAL, "Unsupported pixel format\n");
328  ret = -1;
329  break;
330  }
331  SDL_RenderClear(sdl->renderer);
332  SDL_RenderCopy(sdl->renderer, sdl->texture, NULL, &sdl->texture_rect);
333  SDL_RenderPresent(sdl->renderer);
334  return ret;
335 }
336 
337 #define OFFSET(x) offsetof(SDLContext,x)
338 
339 static const AVOption options[] = {
340  { "window_title", "set SDL window title", OFFSET(window_title), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
341  { "window_size", "set SDL window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
342  { "window_x", "set SDL window x position", OFFSET(window_x), AV_OPT_TYPE_INT, { .i64 = SDL_WINDOWPOS_CENTERED }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
343  { "window_y", "set SDL window y position", OFFSET(window_y), AV_OPT_TYPE_INT, { .i64 = SDL_WINDOWPOS_CENTERED }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
344  { "window_fullscreen", "set SDL window fullscreen", OFFSET(window_fullscreen), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
345  { "window_borderless", "set SDL window border off", OFFSET(window_borderless), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
346  { "window_enable_quit", "set if quit action is available", OFFSET(enable_quit_action), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
347  { NULL },
348 };
349 
350 static const AVClass sdl2_class = {
351  .class_name = "sdl2 outdev",
352  .item_name = av_default_item_name,
353  .option = options,
354  .version = LIBAVUTIL_VERSION_INT,
356 };
357 
359  .name = "sdl,sdl2",
360  .long_name = NULL_IF_CONFIG_SMALL("SDL2 output device"),
361  .priv_data_size = sizeof(SDLContext),
362  .audio_codec = AV_CODEC_ID_NONE,
363  .video_codec = AV_CODEC_ID_RAWVIDEO,
368  .priv_class = &sdl2_class,
369 };
packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
Definition: pixfmt.h:81
static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
Definition: ffmpeg.c:689
#define NULL
Definition: coverity.c:32
int enable_quit_action
Definition: sdl2.c:46
static const char * format[]
Definition: af_aiir.c:330
int texture_fmt
Definition: sdl2.c:56
AVOption.
Definition: opt.h:246
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:101
misc image utilities
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition: pixfmt.h:68
static int sdl2_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition: sdl2.c:242
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: avcodec.h:3900
AVRational sample_aspect_ratio
sample aspect ratio (0 if unknown)
Definition: avformat.h:936
int num
Numerator.
Definition: rational.h:59
static int sdl2_write_header(AVFormatContext *s)
Definition: sdl2.c:159
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:411
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
static int sdl2_write_trailer(AVFormatContext *s)
Definition: sdl2.c:137
packed BGR 8:8:8, 32bpp, XBGRXBGR... X=unused/undefined
Definition: pixfmt.h:239
static AVPacket pkt
#define AV_PIX_FMT_RGB444
Definition: pixfmt.h:368
This struct describes the properties of an encoded stream.
Definition: avcodec.h:3892
Format I/O context.
Definition: avformat.h:1351
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: sdl2.c:44
uint8_t
SDL_Renderer * renderer
Definition: sdl2.c:40
int width
Video only.
Definition: avcodec.h:3966
packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined
Definition: pixfmt.h:238
AVOptions.
AVStream ** streams
A list of all streams in the file.
Definition: avformat.h:1419
uint8_t * data
Definition: avcodec.h:1445
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:192
#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:276
SDL_Window * window
Definition: sdl2.c:39
Main libavdevice API header.
static void compute_texture_rect(AVFormatContext *s)
Definition: sdl2.c:97
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
#define AV_PIX_FMT_BGR32_1
Definition: pixfmt.h:355
#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:186
char * url
input or output URL.
Definition: avformat.h:1447
static const AVOption options[]
Definition: sdl2.c:339
enum AVMediaType codec_type
General type of the encoded data.
Definition: avcodec.h:3896
#define AV_PIX_FMT_0BGR32
Definition: pixfmt.h:357
#define OFFSET(x)
Definition: sdl2.c:337
#define fail()
Definition: checkasm.h:117
unsigned int nb_streams
Number of elements in AVFormatContext.streams.
Definition: avformat.h:1407
SDL_Rect texture_rect
Definition: sdl2.c:50
enum AVPixelFormat format
Definition: sdl2.c:56
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
static int write_trailer(AVFormatContext *s1)
Definition: v4l2enc.c:94
const char * name
Definition: avformat.h:507
#define s(width, name)
Definition: cbs_vp9.c:257
int window_height
size of the window
Definition: sdl2.c:42
packed RGB 8:8:8, 24bpp, BGRBGR...
Definition: pixfmt.h:69
static const AVClass sdl2_class
Definition: sdl2.c:350
Definition: sdl2.c:55
int inited
Definition: sdl2.c:52
SDL_Texture * texture
Definition: sdl2.c:48
Stream structure.
Definition: avformat.h:874
#define AVFMT_NOTIMESTAMPS
Format does not need / have any timestamps.
Definition: avformat.h:469
AVOutputFormat ff_sdl2_muxer
Definition: sdl2.c:358
int window_width
Definition: sdl2.c:42
#define AV_PIX_FMT_BGR555
Definition: pixfmt.h:372
#define AV_PIX_FMT_BGR32
Definition: pixfmt.h:354
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:251
int window_y
position of the window
Definition: sdl2.c:43
#define AV_PIX_FMT_RGB32
Definition: pixfmt.h:352
packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
Definition: pixfmt.h:67
static const char * window_title
Definition: ffplay.c:312
Describe the class of an AVClass context structure.
Definition: log.h:67
int window_borderless
Definition: sdl2.c:45
Rational number (pair of numerator and denominator).
Definition: rational.h:58
packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
Definition: pixfmt.h:240
offset must point to two consecutive integers
Definition: opt.h:233
misc parsing utilities
#define AV_PIX_FMT_BGR565
Definition: pixfmt.h:371
#define flags(name, subs,...)
Definition: cbs_av1.c:596
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition: rational.h:89
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition: pixfmt.h:66
int window_x
Definition: sdl2.c:43
#define AVFMT_NOFILE
Demuxer will use avio_open, no opened file should be provided by the caller.
Definition: avformat.h:465
packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb)
Definition: pixfmt.h:86
#define AV_PIX_FMT_RGB555
Definition: pixfmt.h:367
int den
Denominator.
Definition: rational.h:60
#define AVFMT_VARIABLE_FPS
Format allows variable fps.
Definition: avformat.h:472
char * window_title
Definition: sdl2.c:41
#define AV_PIX_FMT_RGB32_1
Definition: pixfmt.h:353
void * priv_data
Format private data.
Definition: avformat.h:1379
static void write_header(FFV1Context *f)
Definition: ffv1enc.c:337
#define AV_PIX_FMT_RGB565
Definition: pixfmt.h:366
AVRational av_mul_q(AVRational b, AVRational c)
Multiply two rationals.
Definition: rational.c:80
static const struct sdl_texture_format_entry sdl_texture_format_map[]
int texture_fmt
Definition: sdl2.c:49
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
Definition: log.h:170
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1021
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:2362
packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined
Definition: pixfmt.h:237
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
This structure stores compressed data.
Definition: avcodec.h:1422
#define AV_PIX_FMT_0RGB32
Definition: pixfmt.h:356