00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00026 #include <SDL.h>
00027 #include "libavutil/avstring.h"
00028 #include "libavutil/opt.h"
00029 #include "libavutil/parseutils.h"
00030 #include "libavutil/pixdesc.h"
00031 #include "avdevice.h"
00032
00033 typedef struct {
00034 AVClass *class;
00035 SDL_Surface *surface;
00036 SDL_Overlay *overlay;
00037 char *window_title;
00038 char *icon_title;
00039 char *window_size;
00040 int window_width, window_height;
00041 int overlay_width, overlay_height;
00042 int overlay_fmt;
00043 int sdl_was_already_inited;
00044 } SDLContext;
00045
00046 struct sdl_overlay_pix_fmt_entry {
00047 enum PixelFormat pix_fmt; int overlay_fmt;
00048 } sdl_overlay_pix_fmt_map[] = {
00049 { PIX_FMT_YUV420P, SDL_IYUV_OVERLAY },
00050 { PIX_FMT_YUYV422, SDL_YUY2_OVERLAY },
00051 { PIX_FMT_UYVY422, SDL_UYVY_OVERLAY },
00052 { PIX_FMT_NONE, 0 },
00053 };
00054
00055 static int sdl_write_trailer(AVFormatContext *s)
00056 {
00057 SDLContext *sdl = s->priv_data;
00058
00059 av_freep(&sdl->window_title);
00060 av_freep(&sdl->icon_title);
00061 av_freep(&sdl->window_size);
00062
00063 if (sdl->overlay) {
00064 SDL_FreeYUVOverlay(sdl->overlay);
00065 sdl->overlay = NULL;
00066 }
00067 if (!sdl->sdl_was_already_inited)
00068 SDL_Quit();
00069
00070 return 0;
00071 }
00072
00073 static int sdl_write_header(AVFormatContext *s)
00074 {
00075 SDLContext *sdl = s->priv_data;
00076 AVStream *st = s->streams[0];
00077 AVCodecContext *encctx = st->codec;
00078 float sar, dar;
00079 int i, ret;
00080
00081 if (!sdl->window_title)
00082 sdl->window_title = av_strdup(s->filename);
00083 if (!sdl->icon_title)
00084 sdl->icon_title = av_strdup(sdl->window_title);
00085
00086 if (SDL_WasInit(SDL_INIT_VIDEO)) {
00087 av_log(s, AV_LOG_ERROR,
00088 "SDL video subsystem was already inited, aborting.\n");
00089 sdl->sdl_was_already_inited = 1;
00090 ret = AVERROR(EINVAL);
00091 goto fail;
00092 }
00093
00094 if (SDL_Init(SDL_INIT_VIDEO) != 0) {
00095 av_log(s, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError());
00096 ret = AVERROR(EINVAL);
00097 goto fail;
00098 }
00099
00100 if ( s->nb_streams > 1
00101 || encctx->codec_type != AVMEDIA_TYPE_VIDEO
00102 || encctx->codec_id != CODEC_ID_RAWVIDEO) {
00103 av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
00104 ret = AVERROR(EINVAL);
00105 goto fail;
00106 }
00107
00108 for (i = 0; sdl_overlay_pix_fmt_map[i].pix_fmt != PIX_FMT_NONE; i++) {
00109 if (sdl_overlay_pix_fmt_map[i].pix_fmt == encctx->pix_fmt) {
00110 sdl->overlay_fmt = sdl_overlay_pix_fmt_map[i].overlay_fmt;
00111 break;
00112 }
00113 }
00114
00115 if (!sdl->overlay_fmt) {
00116 av_log(s, AV_LOG_ERROR,
00117 "Unsupported pixel format '%s', choose one of yuv420p, yuyv422, or uyvy422.\n",
00118 av_get_pix_fmt_name(encctx->pix_fmt));
00119 ret = AVERROR(EINVAL);
00120 goto fail;
00121 }
00122
00123 if (sdl->window_size) {
00124 if (av_parse_video_size(&sdl->window_width, &sdl->window_height,
00125 sdl->window_size) < 0) {
00126 av_log(s, AV_LOG_ERROR, "Invalid window size '%s'\n", sdl->window_size);
00127 ret = AVERROR(EINVAL);
00128 goto fail;
00129 }
00130 }
00131
00132
00133 sar = st->sample_aspect_ratio.num ? av_q2d(st->sample_aspect_ratio) : 1;
00134 dar = sar * (float)encctx->width / (float)encctx->height;
00135
00136
00137 sdl->overlay_height = encctx->height;
00138 sdl->overlay_width = ((int)rint(sdl->overlay_height * dar));
00139 if (sdl->overlay_width > encctx->width) {
00140 sdl->overlay_width = encctx->width;
00141 sdl->overlay_height = ((int)rint(sdl->overlay_width / dar));
00142 }
00143
00144 if (!sdl->window_width || !sdl->window_height) {
00145 sdl->window_width = sdl->overlay_width;
00146 sdl->window_height = sdl->overlay_height;
00147 }
00148
00149 SDL_WM_SetCaption(sdl->window_title, sdl->icon_title);
00150 sdl->surface = SDL_SetVideoMode(sdl->window_width, sdl->window_height,
00151 24, SDL_SWSURFACE);
00152 if (!sdl->surface) {
00153 av_log(s, AV_LOG_ERROR, "Unable to set video mode: %s\n", SDL_GetError());
00154 ret = AVERROR(EINVAL);
00155 goto fail;
00156 }
00157
00158 sdl->overlay = SDL_CreateYUVOverlay(sdl->overlay_width, sdl->overlay_height,
00159 sdl->overlay_fmt, sdl->surface);
00160 if (!sdl->overlay || sdl->overlay->pitches[0] < sdl->overlay_width) {
00161 av_log(s, AV_LOG_ERROR,
00162 "SDL does not support an overlay with size of %dx%d pixels.\n",
00163 sdl->overlay_width, sdl->overlay_height);
00164 ret = AVERROR(EINVAL);
00165 goto fail;
00166 }
00167
00168 av_log(s, AV_LOG_INFO, "w:%d h:%d fmt:%s sar:%f -> w:%d h:%d\n",
00169 encctx->width, encctx->height, av_get_pix_fmt_name(encctx->pix_fmt), sar,
00170 sdl->window_width, sdl->window_height);
00171 return 0;
00172
00173 fail:
00174 sdl_write_trailer(s);
00175 return ret;
00176 }
00177
00178 static int sdl_write_packet(AVFormatContext *s, AVPacket *pkt)
00179 {
00180 SDLContext *sdl = s->priv_data;
00181 AVCodecContext *encctx = s->streams[0]->codec;
00182 SDL_Rect rect = { 0, 0, sdl->window_width, sdl->window_height };
00183 AVPicture pict;
00184 int i;
00185
00186 avpicture_fill(&pict, pkt->data, encctx->pix_fmt, encctx->width, encctx->height);
00187
00188 SDL_FillRect(sdl->surface, &sdl->surface->clip_rect,
00189 SDL_MapRGB(sdl->surface->format, 0, 0, 0));
00190 SDL_LockYUVOverlay(sdl->overlay);
00191 for (i = 0; i < 3; i++) {
00192 sdl->overlay->pixels [i] = pict.data [i];
00193 sdl->overlay->pitches[i] = pict.linesize[i];
00194 }
00195 SDL_DisplayYUVOverlay(sdl->overlay, &rect);
00196 SDL_UnlockYUVOverlay(sdl->overlay);
00197
00198 SDL_UpdateRect(sdl->surface, 0, 0, sdl->overlay_width, sdl->overlay_height);
00199
00200 return 0;
00201 }
00202
00203 #define OFFSET(x) offsetof(SDLContext,x)
00204
00205 static const AVOption options[] = {
00206 { "window_title", "SDL window title", OFFSET(window_title), FF_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
00207 { "icon_title", "SDL iconified window title", OFFSET(icon_title) , FF_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
00208 { "window_size", "SDL window forced size", OFFSET(window_size) , FF_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
00209 { NULL },
00210 };
00211
00212 static const AVClass sdl_class = {
00213 .class_name = "sdl outdev",
00214 .item_name = av_default_item_name,
00215 .option = options,
00216 .version = LIBAVUTIL_VERSION_INT,
00217 };
00218
00219 AVOutputFormat ff_sdl_muxer = {
00220 .name = "sdl",
00221 .long_name = NULL_IF_CONFIG_SMALL("SDL output device"),
00222 .priv_data_size = sizeof(SDLContext),
00223 .audio_codec = CODEC_ID_NONE,
00224 .video_codec = CODEC_ID_RAWVIDEO,
00225 .write_header = sdl_write_header,
00226 .write_packet = sdl_write_packet,
00227 .write_trailer = sdl_write_trailer,
00228 .flags = AVFMT_NOFILE,
00229 .priv_class = &sdl_class,
00230 };