2 * Copyright (c) 2011 Stefano Sabatini
4 * This file is part of FFmpeg.
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.
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.
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
23 * libSDL output device
27 #include <SDL_thread.h>
29 #include "libavutil/avstring.h"
30 #include "libavutil/opt.h"
31 #include "libavutil/parseutils.h"
32 #include "libavutil/pixdesc.h"
33 #include "libavutil/time.h"
42 int window_width
, window_height
; /**< size of the window */
43 int window_fullscreen
;
45 SDL_Rect overlay_rect
;
48 int sdl_was_already_inited
;
49 SDL_Thread
*event_thread
;
52 int init_ret
; /* return code used to signal initialization errors */
57 static const struct sdl_overlay_pix_fmt_entry
{
58 enum AVPixelFormat pix_fmt
; int overlay_fmt
;
59 } sdl_overlay_pix_fmt_map
[] = {
60 { AV_PIX_FMT_YUV420P
, SDL_IYUV_OVERLAY
},
61 { AV_PIX_FMT_YUYV422
, SDL_YUY2_OVERLAY
},
62 { AV_PIX_FMT_UYVY422
, SDL_UYVY_OVERLAY
},
63 { AV_PIX_FMT_NONE
, 0 },
66 static int sdl_write_trailer(AVFormatContext
*s
)
68 SDLContext
*sdl
= s
->priv_data
;
73 SDL_FreeYUVOverlay(sdl
->overlay
);
75 if (sdl
->event_thread
)
76 SDL_WaitThread(sdl
->event_thread
, NULL
);
77 sdl
->event_thread
= NULL
;
79 SDL_DestroyMutex(sdl
->mutex
);
82 SDL_DestroyCond(sdl
->init_cond
);
83 sdl
->init_cond
= NULL
;
85 if (!sdl
->sdl_was_already_inited
)
91 static void compute_overlay_rect(AVFormatContext
*s
)
93 AVRational sar
, dar
; /* sample and display aspect ratios */
94 SDLContext
*sdl
= s
->priv_data
;
95 AVStream
*st
= s
->streams
[0];
96 AVCodecContext
*encctx
= st
->codec
;
97 SDL_Rect
*overlay_rect
= &sdl
->overlay_rect
;
99 /* compute overlay width and height from the codec context information */
100 sar
= st
->sample_aspect_ratio
.num
? st
->sample_aspect_ratio
: (AVRational
){ 1, 1 };
101 dar
= av_mul_q(sar
, (AVRational
){ encctx
->width
, encctx
->height
});
103 /* we suppose the screen has a 1/1 sample aspect ratio */
104 if (sdl
->window_width
&& sdl
->window_height
) {
105 /* fit in the window */
106 if (av_cmp_q(dar
, (AVRational
){ sdl
->window_width
, sdl
->window_height
}) > 0) {
108 overlay_rect
->w
= sdl
->window_width
;
109 overlay_rect
->h
= av_rescale(overlay_rect
->w
, dar
.den
, dar
.num
);
112 overlay_rect
->h
= sdl
->window_height
;
113 overlay_rect
->w
= av_rescale(overlay_rect
->h
, dar
.num
, dar
.den
);
116 if (sar
.num
> sar
.den
) {
117 overlay_rect
->w
= encctx
->width
;
118 overlay_rect
->h
= av_rescale(overlay_rect
->w
, dar
.den
, dar
.num
);
120 overlay_rect
->h
= encctx
->height
;
121 overlay_rect
->w
= av_rescale(overlay_rect
->h
, dar
.num
, dar
.den
);
123 sdl
->window_width
= overlay_rect
->w
;
124 sdl
->window_height
= overlay_rect
->h
;
127 overlay_rect
->x
= (sdl
->window_width
- overlay_rect
->w
) / 2;
128 overlay_rect
->y
= (sdl
->window_height
- overlay_rect
->h
) / 2;
131 #define SDL_BASE_FLAGS (SDL_SWSURFACE|SDL_RESIZABLE)
133 static int event_thread(void *arg
)
135 AVFormatContext
*s
= arg
;
136 SDLContext
*sdl
= s
->priv_data
;
137 int flags
= SDL_BASE_FLAGS
| (sdl
->window_fullscreen
? SDL_FULLSCREEN
: 0);
138 AVStream
*st
= s
->streams
[0];
139 AVCodecContext
*encctx
= st
->codec
;
142 if (SDL_Init(SDL_INIT_VIDEO
) != 0) {
143 av_log(s
, AV_LOG_ERROR
, "Unable to initialize SDL: %s\n", SDL_GetError());
144 sdl
->init_ret
= AVERROR(EINVAL
);
148 SDL_WM_SetCaption(sdl
->window_title
, sdl
->icon_title
);
149 sdl
->surface
= SDL_SetVideoMode(sdl
->window_width
, sdl
->window_height
,
152 av_log(sdl
, AV_LOG_ERROR
, "Unable to set video mode: %s\n", SDL_GetError());
153 sdl
->init_ret
= AVERROR(EINVAL
);
157 sdl
->overlay
= SDL_CreateYUVOverlay(encctx
->width
, encctx
->height
,
158 sdl
->overlay_fmt
, sdl
->surface
);
159 if (!sdl
->overlay
|| sdl
->overlay
->pitches
[0] < encctx
->width
) {
160 av_log(s
, AV_LOG_ERROR
,
161 "SDL does not support an overlay with size of %dx%d pixels\n",
162 encctx
->width
, encctx
->height
);
163 sdl
->init_ret
= AVERROR(EINVAL
);
168 av_log(s
, AV_LOG_VERBOSE
, "w:%d h:%d fmt:%s -> w:%d h:%d\n",
169 encctx
->width
, encctx
->height
, av_get_pix_fmt_name(encctx
->pix_fmt
),
170 sdl
->overlay_rect
.w
, sdl
->overlay_rect
.h
);
173 SDL_LockMutex(sdl
->mutex
);
175 SDL_UnlockMutex(sdl
->mutex
);
176 SDL_CondSignal(sdl
->init_cond
);
178 if (sdl
->init_ret
< 0)
179 return sdl
->init_ret
;
186 ret
= SDL_PeepEvents(&event
, 1, SDL_GETEVENT
, SDL_ALLEVENTS
);
188 av_log(s
, AV_LOG_ERROR
, "Error when getting SDL event: %s\n", SDL_GetError());
196 switch (event
.type
) {
198 switch (event
.key
.keysym
.sym
) {
209 case SDL_VIDEORESIZE
:
210 sdl
->window_width
= event
.resize
.w
;
211 sdl
->window_height
= event
.resize
.h
;
213 SDL_LockMutex(sdl
->mutex
);
214 sdl
->surface
= SDL_SetVideoMode(sdl
->window_width
, sdl
->window_height
, 24, SDL_BASE_FLAGS
);
216 av_log(s
, AV_LOG_ERROR
, "Failed to set SDL video mode: %s\n", SDL_GetError());
219 compute_overlay_rect(s
);
221 SDL_UnlockMutex(sdl
->mutex
);
232 static int sdl_write_header(AVFormatContext
*s
)
234 SDLContext
*sdl
= s
->priv_data
;
235 AVStream
*st
= s
->streams
[0];
236 AVCodecContext
*encctx
= st
->codec
;
239 if (!sdl
->window_title
)
240 sdl
->window_title
= av_strdup(s
->filename
);
241 if (!sdl
->icon_title
)
242 sdl
->icon_title
= av_strdup(sdl
->window_title
);
244 if (SDL_WasInit(SDL_INIT_VIDEO
)) {
245 av_log(s
, AV_LOG_ERROR
,
246 "SDL video subsystem was already inited, aborting\n");
247 sdl
->sdl_was_already_inited
= 1;
248 ret
= AVERROR(EINVAL
);
252 if ( s
->nb_streams
> 1
253 || encctx
->codec_type
!= AVMEDIA_TYPE_VIDEO
254 || encctx
->codec_id
!= AV_CODEC_ID_RAWVIDEO
) {
255 av_log(s
, AV_LOG_ERROR
, "Only supports one rawvideo stream\n");
256 ret
= AVERROR(EINVAL
);
260 for (i
= 0; sdl_overlay_pix_fmt_map
[i
].pix_fmt
!= AV_PIX_FMT_NONE
; i
++) {
261 if (sdl_overlay_pix_fmt_map
[i
].pix_fmt
== encctx
->pix_fmt
) {
262 sdl
->overlay_fmt
= sdl_overlay_pix_fmt_map
[i
].overlay_fmt
;
267 if (!sdl
->overlay_fmt
) {
268 av_log(s
, AV_LOG_ERROR
,
269 "Unsupported pixel format '%s', choose one of yuv420p, yuyv422, or uyvy422\n",
270 av_get_pix_fmt_name(encctx
->pix_fmt
));
271 ret
= AVERROR(EINVAL
);
275 /* compute overlay width and height from the codec context information */
276 compute_overlay_rect(s
);
278 sdl
->init_cond
= SDL_CreateCond();
279 if (!sdl
->init_cond
) {
280 av_log(s
, AV_LOG_ERROR
, "Could not create SDL condition variable: %s\n", SDL_GetError());
281 ret
= AVERROR_EXTERNAL
;
284 sdl
->mutex
= SDL_CreateMutex();
286 av_log(s
, AV_LOG_ERROR
, "Could not create SDL mutex: %s\n", SDL_GetError());
287 ret
= AVERROR_EXTERNAL
;
290 sdl
->event_thread
= SDL_CreateThread(event_thread
, s
);
291 if (!sdl
->event_thread
) {
292 av_log(s
, AV_LOG_ERROR
, "Could not create SDL event thread: %s\n", SDL_GetError());
293 ret
= AVERROR_EXTERNAL
;
297 /* wait until the video system has been inited */
298 SDL_LockMutex(sdl
->mutex
);
299 while (!sdl
->inited
) {
300 SDL_CondWait(sdl
->init_cond
, sdl
->mutex
);
302 SDL_UnlockMutex(sdl
->mutex
);
303 if (sdl
->init_ret
< 0) {
310 sdl_write_trailer(s
);
314 static int sdl_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
316 SDLContext
*sdl
= s
->priv_data
;
317 AVCodecContext
*encctx
= s
->streams
[0]->codec
;
322 sdl_write_trailer(s
);
325 avpicture_fill(&pict
, pkt
->data
, encctx
->pix_fmt
, encctx
->width
, encctx
->height
);
327 SDL_LockMutex(sdl
->mutex
);
328 SDL_FillRect(sdl
->surface
, &sdl
->surface
->clip_rect
,
329 SDL_MapRGB(sdl
->surface
->format
, 0, 0, 0));
330 SDL_LockYUVOverlay(sdl
->overlay
);
331 for (i
= 0; i
< 3; i
++) {
332 sdl
->overlay
->pixels
[i
] = pict
.data
[i
];
333 sdl
->overlay
->pitches
[i
] = pict
.linesize
[i
];
335 SDL_DisplayYUVOverlay(sdl
->overlay
, &sdl
->overlay_rect
);
336 SDL_UnlockYUVOverlay(sdl
->overlay
);
338 SDL_UpdateRect(sdl
->surface
,
339 sdl
->overlay_rect
.x
, sdl
->overlay_rect
.y
,
340 sdl
->overlay_rect
.w
, sdl
->overlay_rect
.h
);
341 SDL_UnlockMutex(sdl
->mutex
);
346 #define OFFSET(x) offsetof(SDLContext,x)
348 static const AVOption options
[] = {
349 { "window_title", "set SDL window title", OFFSET(window_title
), AV_OPT_TYPE_STRING
, { .str
= NULL
}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM
},
350 { "icon_title", "set SDL iconified window title", OFFSET(icon_title
) , AV_OPT_TYPE_STRING
, { .str
= NULL
}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM
},
351 { "window_size", "set SDL window forced size", OFFSET(window_width
), AV_OPT_TYPE_IMAGE_SIZE
, { .str
= NULL
}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM
},
352 { "window_fullscreen", "set SDL window fullscreen", OFFSET(window_fullscreen
), AV_OPT_TYPE_INT
, { .i64
= 0 }, INT_MIN
, INT_MAX
, AV_OPT_FLAG_ENCODING_PARAM
},
356 static const AVClass sdl_class
= {
357 .class_name
= "sdl outdev",
358 .item_name
= av_default_item_name
,
360 .version
= LIBAVUTIL_VERSION_INT
,
361 .category
= AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT
,
364 AVOutputFormat ff_sdl_muxer
= {
366 .long_name
= NULL_IF_CONFIG_SMALL("SDL output device"),
367 .priv_data_size
= sizeof(SDLContext
),
368 .audio_codec
= AV_CODEC_ID_NONE
,
369 .video_codec
= AV_CODEC_ID_RAWVIDEO
,
370 .write_header
= sdl_write_header
,
371 .write_packet
= sdl_write_packet
,
372 .write_trailer
= sdl_write_trailer
,
373 .flags
= AVFMT_NOFILE
| AVFMT_VARIABLE_FPS
| AVFMT_NOTIMESTAMPS
,
374 .priv_class
= &sdl_class
,