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 };