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 int window_width, window_height;
00040 int overlay_width, overlay_height;
00041 int overlay_x, overlay_y;
00042 int overlay_fmt;
00043 int sdl_was_already_inited;
00044 } SDLContext;
00045
00046 static const 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
00062 if (sdl->overlay) {
00063 SDL_FreeYUVOverlay(sdl->overlay);
00064 sdl->overlay = NULL;
00065 }
00066 if (!sdl->sdl_was_already_inited)
00067 SDL_Quit();
00068
00069 return 0;
00070 }
00071
00072 static int sdl_write_header(AVFormatContext *s)
00073 {
00074 SDLContext *sdl = s->priv_data;
00075 AVStream *st = s->streams[0];
00076 AVCodecContext *encctx = st->codec;
00077 AVRational sar, dar;
00078 int i, ret;
00079
00080 if (!sdl->window_title)
00081 sdl->window_title = av_strdup(s->filename);
00082 if (!sdl->icon_title)
00083 sdl->icon_title = av_strdup(sdl->window_title);
00084
00085 if (SDL_WasInit(SDL_INIT_VIDEO)) {
00086 av_log(s, AV_LOG_ERROR,
00087 "SDL video subsystem was already inited, aborting\n");
00088 sdl->sdl_was_already_inited = 1;
00089 ret = AVERROR(EINVAL);
00090 goto fail;
00091 }
00092
00093 if (SDL_Init(SDL_INIT_VIDEO) != 0) {
00094 av_log(s, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError());
00095 ret = AVERROR(EINVAL);
00096 goto fail;
00097 }
00098
00099 if ( s->nb_streams > 1
00100 || encctx->codec_type != AVMEDIA_TYPE_VIDEO
00101 || encctx->codec_id != AV_CODEC_ID_RAWVIDEO) {
00102 av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
00103 ret = AVERROR(EINVAL);
00104 goto fail;
00105 }
00106
00107 for (i = 0; sdl_overlay_pix_fmt_map[i].pix_fmt != PIX_FMT_NONE; i++) {
00108 if (sdl_overlay_pix_fmt_map[i].pix_fmt == encctx->pix_fmt) {
00109 sdl->overlay_fmt = sdl_overlay_pix_fmt_map[i].overlay_fmt;
00110 break;
00111 }
00112 }
00113
00114 if (!sdl->overlay_fmt) {
00115 av_log(s, AV_LOG_ERROR,
00116 "Unsupported pixel format '%s', choose one of yuv420p, yuyv422, or uyvy422\n",
00117 av_get_pix_fmt_name(encctx->pix_fmt));
00118 ret = AVERROR(EINVAL);
00119 goto fail;
00120 }
00121
00122
00123 sar = st->sample_aspect_ratio.num ? st->sample_aspect_ratio : (AVRational){ 1, 1 };
00124 dar = av_mul_q(sar, (AVRational){ encctx->width, encctx->height });
00125
00126
00127 if (sdl->window_width && sdl->window_height) {
00128
00129 if (av_cmp_q(dar, (AVRational){ sdl->window_width, sdl->window_height }) > 0) {
00130
00131 sdl->overlay_width = sdl->window_width;
00132 sdl->overlay_height = av_rescale(sdl->overlay_width, dar.den, dar.num);
00133 } else {
00134
00135 sdl->overlay_height = sdl->window_height;
00136 sdl->overlay_width = av_rescale(sdl->overlay_height, dar.num, dar.den);
00137 }
00138 } else {
00139 if (sar.num > sar.den) {
00140 sdl->overlay_width = encctx->width;
00141 sdl->overlay_height = av_rescale(sdl->overlay_width, dar.den, dar.num);
00142 } else {
00143 sdl->overlay_height = encctx->height;
00144 sdl->overlay_width = av_rescale(sdl->overlay_height, dar.num, dar.den);
00145 }
00146 sdl->window_width = sdl->overlay_width;
00147 sdl->window_height = sdl->overlay_height;
00148 }
00149 sdl->overlay_x = (sdl->window_width - sdl->overlay_width ) / 2;
00150 sdl->overlay_y = (sdl->window_height - sdl->overlay_height) / 2;
00151
00152 SDL_WM_SetCaption(sdl->window_title, sdl->icon_title);
00153 sdl->surface = SDL_SetVideoMode(sdl->window_width, sdl->window_height,
00154 24, SDL_SWSURFACE);
00155 if (!sdl->surface) {
00156 av_log(s, AV_LOG_ERROR, "Unable to set video mode: %s\n", SDL_GetError());
00157 ret = AVERROR(EINVAL);
00158 goto fail;
00159 }
00160
00161 sdl->overlay = SDL_CreateYUVOverlay(encctx->width, encctx->height,
00162 sdl->overlay_fmt, sdl->surface);
00163 if (!sdl->overlay || sdl->overlay->pitches[0] < encctx->width) {
00164 av_log(s, AV_LOG_ERROR,
00165 "SDL does not support an overlay with size of %dx%d pixels\n",
00166 encctx->width, encctx->height);
00167 ret = AVERROR(EINVAL);
00168 goto fail;
00169 }
00170
00171 av_log(s, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s sar:%d/%d -> w:%d h:%d\n",
00172 encctx->width, encctx->height, av_get_pix_fmt_name(encctx->pix_fmt), sar.num, sar.den,
00173 sdl->overlay_width, sdl->overlay_height);
00174 return 0;
00175
00176 fail:
00177 sdl_write_trailer(s);
00178 return ret;
00179 }
00180
00181 static int sdl_write_packet(AVFormatContext *s, AVPacket *pkt)
00182 {
00183 SDLContext *sdl = s->priv_data;
00184 AVCodecContext *encctx = s->streams[0]->codec;
00185 SDL_Rect rect = { sdl->overlay_x, sdl->overlay_y, sdl->overlay_width, sdl->overlay_height };
00186 AVPicture pict;
00187 int i;
00188
00189 avpicture_fill(&pict, pkt->data, encctx->pix_fmt, encctx->width, encctx->height);
00190
00191 SDL_FillRect(sdl->surface, &sdl->surface->clip_rect,
00192 SDL_MapRGB(sdl->surface->format, 0, 0, 0));
00193 SDL_LockYUVOverlay(sdl->overlay);
00194 for (i = 0; i < 3; i++) {
00195 sdl->overlay->pixels [i] = pict.data [i];
00196 sdl->overlay->pitches[i] = pict.linesize[i];
00197 }
00198 SDL_DisplayYUVOverlay(sdl->overlay, &rect);
00199 SDL_UnlockYUVOverlay(sdl->overlay);
00200
00201 SDL_UpdateRect(sdl->surface, rect.x, rect.y, rect.w, rect.h);
00202
00203 return 0;
00204 }
00205
00206 #define OFFSET(x) offsetof(SDLContext,x)
00207
00208 static const AVOption options[] = {
00209 { "window_title", "set SDL window title", OFFSET(window_title), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
00210 { "icon_title", "set SDL iconified window title", OFFSET(icon_title) , AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
00211 { "window_size", "set SDL window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE,{.str=NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
00212 { NULL },
00213 };
00214
00215 static const AVClass sdl_class = {
00216 .class_name = "sdl outdev",
00217 .item_name = av_default_item_name,
00218 .option = options,
00219 .version = LIBAVUTIL_VERSION_INT,
00220 };
00221
00222 AVOutputFormat ff_sdl_muxer = {
00223 .name = "sdl",
00224 .long_name = NULL_IF_CONFIG_SMALL("SDL output device"),
00225 .priv_data_size = sizeof(SDLContext),
00226 .audio_codec = AV_CODEC_ID_NONE,
00227 .video_codec = AV_CODEC_ID_RAWVIDEO,
00228 .write_header = sdl_write_header,
00229 .write_packet = sdl_write_packet,
00230 .write_trailer = sdl_write_trailer,
00231 .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
00232 .priv_class = &sdl_class,
00233 };