various rearrangements and renaming of console_video stuff
[asterisk/asterisk.git] / channels / console_video.c
1 /*
2  * Experimental support for video sessions. We use SDL for rendering, ffmpeg
3  * as the codec library for encoding and decoding, and Video4Linux and X11
4  * to generate the local video stream.
5  *
6  * If one of these pieces is not available, either at compile time or at
7  * runtime, we do our best to run without it. Of course, no codec library
8  * means we can only deal with raw data, no SDL means we cannot do rendering,
9  * no V4L or X11 means we cannot generate data (but in principle we could
10  * stream from or record to a file).
11  *
12  * We need a recent (2007.07.12 or newer) version of ffmpeg to avoid warnings.
13  * Older versions might give 'deprecated' messages during compilation,
14  * thus not compiling in AST_DEVMODE, or don't have swscale, in which case
15  * you can try to compile #defining OLD_FFMPEG here.
16  *
17  * $Revision$
18  */
19
20 //#define DROP_PACKETS 5       /* if set, drop this % of video packets */
21 //#define OLD_FFMPEG    1       /* set for old ffmpeg with no swscale */
22
23 #include "asterisk.h"
24 #include <sys/ioctl.h>
25 #include "asterisk/cli.h"
26 #include "asterisk/file.h"
27 #include "asterisk/channel.h"
28
29 #include "console_video.h"
30
31 /*
32 The code is structured as follows.
33
34 When a new console channel is created, we call console_video_start()
35 to initialize SDL, the source, and the encoder/ decoder for the
36 formats in use (XXX the latter two should be done later, once the
37 codec negotiation is complete).  Also, a thread is created to handle
38 the video source and generate frames.
39
40 While communication is on, the local source is generated by the
41 video thread, which wakes up periodically, generates frames and
42 enqueues them in chan->readq.  Incoming rtp frames are passed to
43 console_write_video(), decoded and passed to SDL for display.
44
45 For as unfortunate and confusing as it can be, we need to deal with a
46 number of different video representations (size, codec/pixel format,
47 codec parameters), as follows:
48
49  loc_src        is the data coming from the camera/X11/etc.
50         The format is typically constrained by the video source.
51
52  enc_in         is the input required by the encoder.
53         Typically constrained in size by the encoder type.
54
55  enc_out        is the bitstream transmitted over RTP.
56         Typically negotiated while the call is established.
57
58  loc_dpy        is the format used to display the local video source.
59         Depending on user preferences this can have the same size as
60         loc_src_fmt, or enc_in_fmt, or thumbnail size (e.g. PiP output)
61
62  dec_in         is the incoming RTP bitstream. Negotiated
63         during call establishment, it is not necessarily the same as
64         enc_in_fmt
65
66  dec_out        the output of the decoder.
67         The format is whatever the other side sends, and the
68         buffer is allocated by avcodec_decode_... so we only
69         copy the data here.
70
71  rem_dpy        the format used to display the remote stream
72
73 We store the format info together with the buffer storing the data.
74 As a future optimization, a format/buffer may reference another one
75 if the formats are equivalent. This will save some unnecessary format
76 conversion.
77
78
79 In order to handle video you need to add to sip.conf (and presumably
80 iax.conf too) the following:
81
82         [general](+)
83                 videosupport=yes
84                 allow=h263      ; this or other video formats
85                 allow=h263p     ; this or other video formats
86
87  */
88
89 /*
90  * Codecs are absolutely necessary or we cannot do anything.
91  * SDL is optional (used for rendering only), so that we can still
92  * stream video withouth displaying it.
93  */
94 #if !defined(HAVE_VIDEO_CONSOLE) || !defined(HAVE_FFMPEG)
95 /* stubs if required pieces are missing */
96 int console_write_video(struct ast_channel *chan, struct ast_frame *f)
97 {
98         return 0;       /* writing video not supported */
99 }
100
101 int console_video_cli(struct video_desc *env, const char *var, int fd)
102 {
103         return 1;       /* nothing matched */
104 }
105
106 int console_video_config(struct video_desc **penv, const char *var, const char *val)
107 {
108         return 1;       /* no configuration */
109 }
110
111 void console_video_start(struct video_desc *env, struct ast_channel *owner)
112 {
113         ast_log(LOG_NOTICE, "voice only, console video support not present\n");
114 }
115
116 void console_video_uninit(struct video_desc *env)
117 {
118 }
119
120 int console_video_formats = 0;
121
122 #else /* defined(HAVE_FFMPEG) && defined(HAVE_SDL) */
123
124 /*! The list of video formats we support. */
125 int console_video_formats = 
126         AST_FORMAT_H263_PLUS | AST_FORMAT_H263 |
127         AST_FORMAT_MP4_VIDEO | AST_FORMAT_H264 | AST_FORMAT_H261 ;
128
129
130
131 static void my_scale(struct fbuf_t *in, AVPicture *p_in,
132         struct fbuf_t *out, AVPicture *p_out);
133
134 struct video_codec_desc;        /* forward declaration */
135 /*
136  * Descriptor of the local source, made of the following pieces:
137  *  + configuration info (geometry, device name, fps...). These are read
138  *    from the config file and copied here before calling video_out_init();
139  *  + the frame buffer (buf) and source pixel format, allocated at init time;
140  *  + the encoding and RTP info, including timestamps to generate
141  *    frames at the correct rate;
142  *  + source-specific info, i.e. fd for /dev/video, dpy-image for x11, etc,
143  *    filled in by video_open
144  * NOTE: loc_src.data == NULL means the rest of the struct is invalid, and
145  *      the video source is not available.
146  */
147 struct video_out_desc {
148         /* video device support.
149          * videodevice and geometry are read from the config file.
150          * At the right time we try to open it and allocate a buffer.
151          * If we are successful, webcam_bufsize > 0 and we can read.
152          */
153         /* all the following is config file info copied from the parent */
154         char            videodevice[64];
155         int             fps;
156         int             bitrate;
157         int             qmin;
158
159         int sendvideo;
160
161         struct fbuf_t   loc_src;        /* local source buffer, allocated in video_open() */
162         struct fbuf_t   enc_out;        /* encoder output buffer, allocated in video_out_init() */
163
164         struct video_codec_desc *enc;   /* encoder */
165         void            *enc_ctx;       /* encoding context */
166         AVCodec         *codec;
167         AVFrame         *enc_in_frame;  /* enc_in mapped into avcodec format. */
168                                         /* The initial part of AVFrame is an AVPicture */
169         int             mtu;
170         struct timeval  last_frame;     /* when we read the last frame ? */
171
172         /* device specific info */
173         int             fd;             /* file descriptor, for webcam */
174 #ifdef HAVE_X11
175         Display         *dpy;                   /* x11 grabber info */
176         XImage          *image;
177         int             screen_width;   /* width of X screen */
178         int             screen_height;  /* height of X screen */
179 #endif
180 };
181
182 /*
183  * The overall descriptor, with room for config info, video source and
184  * received data descriptors, SDL info, etc.
185  * This should be globally visible to all modules (grabber, vcodecs, gui)
186  * and contain all configurtion info.
187  */
188 struct video_desc {
189         char                    codec_name[64]; /* the codec we use */
190
191         pthread_t               vthread;        /* video thread */
192         ast_mutex_t             dec_lock;       /* sync decoder and video thread */
193         int                     shutdown;       /* set to shutdown vthread */
194         struct ast_channel      *owner;         /* owner channel */
195
196
197         struct fbuf_t   enc_in;         /* encoder input buffer, allocated in video_out_init() */
198
199         char                    keypad_file[256];       /* image for the keypad */
200         char                    keypad_font[256];       /* font for the keypad */
201
202         char                    sdl_videodriver[256];
203
204         struct fbuf_t           rem_dpy;        /* display remote video, no buffer (it is in win[WIN_REMOTE].bmp) */
205         struct fbuf_t           loc_dpy;        /* display local source, no buffer (managed by SDL in bmp[1]) */
206
207
208         /* local information for grabbers, codecs, gui */
209         struct gui_info         *gui;
210         struct video_dec_desc   *in;            /* remote video descriptor */
211         struct video_out_desc   out;            /* local video descriptor */
212 };
213
214 static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p);
215
216 static void fbuf_free(struct fbuf_t *b)
217 {
218         struct fbuf_t x = *b;
219
220         if (b->data && b->size)
221                 ast_free(b->data);
222         bzero(b, sizeof(*b));
223         /* restore some fields */
224         b->w = x.w;
225         b->h = x.h;
226         b->pix_fmt = x.pix_fmt;
227 }
228
229 /*
230  * Append a chunk of data to a buffer taking care of bit alignment
231  * Return 0 on success, != 0 on failure
232  */
233 static int fbuf_append(struct fbuf_t *b, uint8_t *src, int len,
234         int sbit, int ebit)
235 {
236         /*
237          * Allocate buffer. ffmpeg wants an extra FF_INPUT_BUFFER_PADDING_SIZE,
238          * and also wants 0 as a buffer terminator to prevent trouble.
239          */
240         int need = len + FF_INPUT_BUFFER_PADDING_SIZE;
241         int i;
242         uint8_t *dst, mask;
243
244         if (b->data == NULL) {
245                 b->size = need;
246                 b->used = 0;
247                 b->ebit = 0;
248                 b->data = ast_calloc(1, b->size);
249         } else if (b->used + need > b->size) {
250                 b->size = b->used + need;
251                 b->data = ast_realloc(b->data, b->size);
252         }
253         if (b->data == NULL) {
254                 ast_log(LOG_WARNING, "alloc failure for %d, discard\n",
255                         b->size);
256                 return 1;
257         }
258         if (b->used == 0 && b->ebit != 0) {
259                 ast_log(LOG_WARNING, "ebit not reset at start\n");
260                 b->ebit = 0;
261         }
262         dst = b->data + b->used;
263         i = b->ebit + sbit;     /* bits to ignore around */
264         if (i == 0) {   /* easy case, just append */
265                 /* do everything in the common block */
266         } else if (i == 8) { /* easy too, just handle the overlap byte */
267                 mask = (1 << b->ebit) - 1;
268                 /* update the last byte in the buffer */
269                 dst[-1] &= ~mask;       /* clear bits to ignore */
270                 dst[-1] |= (*src & mask);       /* append new bits */
271                 src += 1;       /* skip and prepare for common block */
272                 len --;
273         } else {        /* must shift the new block, not done yet */
274                 ast_log(LOG_WARNING, "must handle shift %d %d at %d\n",
275                         b->ebit, sbit, b->used);
276                 return 1;
277         }
278         memcpy(dst, src, len);
279         b->used += len;
280         b->ebit = ebit;
281         b->data[b->used] = 0;   /* padding */
282         return 0;
283 }
284
285 /*!
286  * Build an ast_frame for a given chunk of data, and link it into
287  * the queue, with possibly 'head' bytes at the beginning to
288  * fill in some fields later.
289  */
290 static struct ast_frame *create_video_frame(uint8_t *start, uint8_t *end,
291                        int format, int head, struct ast_frame *prev)
292 {
293         int len = end-start;
294         uint8_t *data;
295         struct ast_frame *f;
296
297         data = ast_calloc(1, len+head);
298         f = ast_calloc(1, sizeof(*f));
299         if (f == NULL || data == NULL) {
300                 ast_log(LOG_WARNING, "--- frame error f %p data %p len %d format %d\n",
301                                 f, data, len, format);
302                 if (f)
303                         ast_free(f);
304                 if (data)
305                         ast_free(data);
306                 return NULL;
307         }
308         memcpy(data+head, start, len);
309         f->data = data;
310         f->mallocd = AST_MALLOCD_DATA | AST_MALLOCD_HDR;
311         //f->has_timing_info = 1;
312         //f->ts = ast_tvdiff_ms(ast_tvnow(), out->ts);
313         f->datalen = len+head;
314         f->frametype = AST_FRAME_VIDEO;
315         f->subclass = format;
316         f->samples = 0;
317         f->offset = 0;
318         f->src = "Console";
319         f->delivery.tv_sec = 0;
320         f->delivery.tv_usec = 0;
321         f->seqno = 0;
322         AST_LIST_NEXT(f, frame_list) = NULL;
323
324         if (prev)
325                 AST_LIST_NEXT(prev, frame_list) = f;
326
327         return f;
328 }
329
330 #include "vcodecs.c"
331 #include "console_gui.c"
332
333 /*------ end codec specific code -----*/
334
335
336 /* Video4Linux stuff is only used in video_open() */
337 #ifdef HAVE_VIDEODEV_H
338 #include <linux/videodev.h>
339 #endif
340
341 /*!
342  * Open the local video source and allocate a buffer
343  * for storing the image. Return 0 on success, -1 on error
344  */
345 static int video_open(struct video_out_desc *v)
346 {
347         struct fbuf_t *b = &v->loc_src;
348         if (b->data)    /* buffer allocated means device already open */
349                 return v->fd;
350         v->fd = -1;
351         /*
352          * if the device is "X11", then open the x11 grabber
353          */
354     if (!strcasecmp(v->videodevice, "X11")) {
355         XImage *im;
356         int screen_num;
357
358         /* init the connection with the X server */
359         v->dpy = XOpenDisplay(NULL);
360         if (v->dpy == NULL) {
361                 ast_log(LOG_WARNING, "error opening display\n");
362                 goto error;
363         }
364
365         /* find width and height of the screen */
366         screen_num = DefaultScreen(v->dpy);
367         v->screen_width = DisplayWidth(v->dpy, screen_num);
368         v->screen_height = DisplayHeight(v->dpy, screen_num);
369
370         v->image = im = XGetImage(v->dpy,
371                 RootWindow(v->dpy, DefaultScreen(v->dpy)),
372                 b->x, b->y, b->w, b->h, AllPlanes, ZPixmap);
373         if (v->image == NULL) {
374                 ast_log(LOG_WARNING, "error creating Ximage\n");
375                 goto error;
376         }
377         switch (im->bits_per_pixel) {
378         case 32:
379                 b->pix_fmt = PIX_FMT_RGBA32;
380                 break;
381         case 16:
382                 b->pix_fmt = (im->green_mask == 0x7e0) ? PIX_FMT_RGB565 : PIX_FMT_RGB555;
383                 break;
384         }
385
386         ast_log(LOG_NOTICE, "image: data %p %d bpp fmt %d, mask 0x%lx 0x%lx 0x%lx\n",
387                 im->data,
388                 im->bits_per_pixel,
389                 b->pix_fmt,
390                 im->red_mask, im->green_mask, im->blue_mask);
391
392         /* set the pointer but not the size as this is not malloc'ed */
393         b->data = (uint8_t *)im->data;
394         v->fd = -2;
395     }
396 #ifdef HAVE_VIDEODEV_H
397     else {
398         /* V4L specific */
399         struct video_window vw = { 0 }; /* camera attributes */
400         struct video_picture vp;
401         int i;
402         const char *dev = v->videodevice;
403
404         v->fd = open(dev, O_RDONLY | O_NONBLOCK);
405         if (v->fd < 0) {
406                 ast_log(LOG_WARNING, "error opening camera %s\n", v->videodevice);
407                 return v->fd;
408         }
409
410         i = fcntl(v->fd, F_GETFL);
411         if (-1 == fcntl(v->fd, F_SETFL, i | O_NONBLOCK)) {
412                 /* non fatal, just emit a warning */
413                 ast_log(LOG_WARNING, "error F_SETFL for %s [%s]\n",
414                         dev, strerror(errno));
415         }
416         /* set format for the camera.
417          * In principle we could retry with a different format if the
418          * one we are asking for is not supported.
419          */
420         vw.width = v->loc_src.w;
421         vw.height = v->loc_src.h;
422         vw.flags = v->fps << 16;
423         if (ioctl(v->fd, VIDIOCSWIN, &vw) == -1) {
424                 ast_log(LOG_WARNING, "error setting format for %s [%s]\n",
425                         dev, strerror(errno));
426                 goto error;
427         }
428         if (ioctl(v->fd, VIDIOCGPICT, &vp) == -1) {
429                 ast_log(LOG_WARNING, "error reading picture info\n");
430                 goto error;
431         }
432         ast_log(LOG_WARNING,
433                 "contrast %d bright %d colour %d hue %d white %d palette %d\n",
434                 vp.contrast, vp.brightness,
435                 vp.colour, vp.hue,
436                 vp.whiteness, vp.palette);
437         /* set the video format. Here again, we don't necessary have to
438          * fail if the required format is not supported, but try to use
439          * what the camera gives us.
440          */
441         b->pix_fmt = vp.palette;
442         vp.palette = VIDEO_PALETTE_YUV420P;
443         if (ioctl(v->fd, VIDIOCSPICT, &vp) == -1) {
444                 ast_log(LOG_WARNING, "error setting palette, using %d\n",
445                         b->pix_fmt);
446         } else
447                 b->pix_fmt = vp.palette;
448         /* allocate the source buffer.
449          * XXX, the code here only handles yuv411, for other formats
450          * we need to look at pix_fmt and set size accordingly
451          */
452         b->size = (b->w * b->h * 3)/2;  /* yuv411 */
453         ast_log(LOG_WARNING, "videodev %s opened, size %dx%d %d\n",
454                 dev, b->w, b->h, b->size);
455         v->loc_src.data = ast_calloc(1, b->size);
456         if (!b->data) {
457                 ast_log(LOG_WARNING, "error allocating buffer %d bytes\n",
458                         b->size);
459                 goto error;
460         }
461         ast_log(LOG_WARNING, "success opening camera\n");
462     }
463 #endif /* HAVE_VIDEODEV_H */
464
465         if (v->image == NULL && v->fd < 0)
466                 goto error;
467         b->used = 0;
468         return 0;
469
470 error:
471         ast_log(LOG_WARNING, "fd %d dpy %p img %p data %p\n",
472                 v->fd, v->dpy, v->image, v->loc_src.data);
473         /* XXX maybe XDestroy (v->image) ? */
474         if (v->dpy)
475                 XCloseDisplay(v->dpy);
476         v->dpy = NULL;
477         if (v->fd >= 0)
478                 close(v->fd);
479         v->fd = -1;
480         fbuf_free(&v->loc_src);
481         return -1;
482 }
483
484 /*! \brief complete a buffer from the local video source.
485  * Called by get_video_frames(), in turn called by the video thread.
486  */
487 static int video_read(struct video_out_desc *v)
488 {
489         struct timeval now = ast_tvnow();
490         struct fbuf_t *b = &v->loc_src;
491
492         if (b->data == NULL)    /* not initialized */
493                 return 0;
494
495         /* check if it is time to read */
496         if (ast_tvzero(v->last_frame))
497                 v->last_frame = now;
498         if (ast_tvdiff_ms(now, v->last_frame) < 1000/v->fps)
499                 return 0;       /* too early */
500         v->last_frame = now; /* XXX actually, should correct for drift */
501
502 #ifdef HAVE_X11
503         if (v->image) {
504                 /* read frame from X11 */
505                 AVPicture p;
506                 XGetSubImage(v->dpy,
507                     RootWindow(v->dpy, DefaultScreen(v->dpy)),
508                         b->x, b->y, b->w, b->h, AllPlanes, ZPixmap, v->image, 0, 0);
509
510                 b->data = (uint8_t *)v->image->data;
511                 fill_pict(b, &p);
512                 return p.linesize[0] * b->h;
513         }
514 #endif
515         if (v->fd < 0)                  /* no other source */
516                 return 0;
517         for (;;) {
518                 int r, l = v->loc_src.size - v->loc_src.used;
519                 r = read(v->fd, v->loc_src.data + v->loc_src.used, l);
520                 // ast_log(LOG_WARNING, "read %d of %d bytes from webcam\n", r, l);
521                 if (r < 0)      /* read error */
522                         return 0;
523                 if (r == 0)     /* no data */
524                         return 0;
525                 v->loc_src.used += r;
526                 if (r == l) {
527                         v->loc_src.used = 0; /* prepare for next frame */
528                         return v->loc_src.size;
529                 }
530         }
531 }
532
533 /* Helper function to process incoming video.
534  * For each incoming video call invoke ffmpeg_init() to intialize
535  * the decoding structure then incoming video frames are processed
536  * by write_video() which in turn calls pre_process_data(), to extract
537  * the bitstream; accumulates data into a buffer within video_desc. When
538  * a frame is complete (determined by the marker bit in the RTP header)
539  * call decode_video() to decoding and if it successful call show_frame()
540  * to display the frame.
541  */
542
543 /*
544  * Map the codec name to the library. If not recognised, use a default.
545  * This is useful in the output path where we decide by name, presumably.
546  */
547 static struct video_codec_desc *map_config_video_format(char *name)
548 {
549         int i;
550
551         for (i = 0; supported_codecs[i]; i++)
552                 if (!strcasecmp(name, supported_codecs[i]->name))
553                         break;
554         if (supported_codecs[i] == NULL) {
555                 ast_log(LOG_WARNING, "Cannot find codec for '%s'\n", name);
556                 i = 0;
557                 strcpy(name, supported_codecs[i]->name);
558         }
559         ast_log(LOG_WARNING, "Using codec '%s'\n", name);
560         return supported_codecs[i];
561 }
562
563
564 /*! \brief uninitialize the descriptor for local video stream */
565 static int video_out_uninit(struct video_desc *env)
566 {
567         struct video_out_desc *v = &env->out;
568
569         /* XXX this should be a codec callback */
570         if (v->enc_ctx) {
571                 AVCodecContext *enc_ctx = (AVCodecContext *)v->enc_ctx;
572                 avcodec_close(enc_ctx);
573                 av_free(enc_ctx);
574                 v->enc_ctx = NULL;
575         }
576         if (v->enc_in_frame) {
577                 av_free(v->enc_in_frame);
578                 v->enc_in_frame = NULL;
579         }
580         v->codec = NULL;        /* only a reference */
581         
582         fbuf_free(&v->loc_src);
583         fbuf_free(&env->enc_in);
584         fbuf_free(&v->enc_out);
585         if (v->image) { /* X11 grabber */
586                 XCloseDisplay(v->dpy);
587                 v->dpy = NULL;
588                 v->image = NULL;
589         }
590         if (v->fd >= 0) {
591                 close(v->fd);
592                 v->fd = -1;
593         }
594         return -1;
595 }
596
597 /*
598  * Initialize the encoder for the local source:
599  * - AVCodecContext, AVCodec, AVFrame are used by ffmpeg for encoding;
600  * - encbuf is used to store the encoded frame (to be sent)
601  * - mtu is used to determine the max size of video fragment
602  * NOTE: we enter here with the video source already open.
603  */
604 static int video_out_init(struct video_desc *env)
605 {
606         int codec;
607         int size;
608         struct fbuf_t *enc_in;
609         struct video_out_desc *v = &env->out;
610
611         v->enc_ctx              = NULL;
612         v->codec                = NULL;
613         v->enc_in_frame         = NULL;
614         v->enc_out.data         = NULL;
615
616         if (v->loc_src.data == NULL) {
617                 ast_log(LOG_WARNING, "No local source active\n");
618                 return -1;      /* error, but nothing to undo yet */
619         }
620         codec = map_video_format(v->enc->format, CM_WR);
621         v->codec = avcodec_find_encoder(codec);
622         if (!v->codec) {
623                 ast_log(LOG_WARNING, "Cannot find the encoder for format %d\n",
624                         codec);
625                 return -1;      /* error, but nothing to undo yet */
626         }
627
628         v->mtu = 1400;  /* set it early so the encoder can use it */
629
630         /* allocate the input buffer for encoding.
631          * ffmpeg only supports PIX_FMT_YUV420P for the encoding.
632          */
633         enc_in = &env->enc_in;
634         enc_in->pix_fmt = PIX_FMT_YUV420P;
635         enc_in->size = (enc_in->w * enc_in->h * 3)/2;
636         enc_in->data = ast_calloc(1, enc_in->size);
637         if (!enc_in->data) {
638                 ast_log(LOG_WARNING, "Cannot allocate encoder input buffer\n");
639                 return video_out_uninit(env);
640         }
641         /* construct an AVFrame that points into buf_in */
642         v->enc_in_frame = avcodec_alloc_frame();
643         if (!v->enc_in_frame) {
644                 ast_log(LOG_WARNING, "Unable to allocate the encoding video frame\n");
645                 return video_out_uninit(env);
646         }
647
648         /* parameters for PIX_FMT_YUV420P */
649         size = enc_in->w * enc_in->h;
650         v->enc_in_frame->data[0] = enc_in->data;
651         v->enc_in_frame->data[1] = v->enc_in_frame->data[0] + size;
652         v->enc_in_frame->data[2] = v->enc_in_frame->data[1] + size/4;
653         v->enc_in_frame->linesize[0] = enc_in->w;
654         v->enc_in_frame->linesize[1] = enc_in->w/2;
655         v->enc_in_frame->linesize[2] = enc_in->w/2;
656
657         /* now setup the parameters for the encoder.
658          * XXX should be codec-specific
659          */
660     {
661         AVCodecContext *enc_ctx = avcodec_alloc_context();
662         v->enc_ctx = enc_ctx;
663         enc_ctx->pix_fmt = enc_in->pix_fmt;
664         enc_ctx->width = enc_in->w;
665         enc_ctx->height = enc_in->h;
666         /* XXX rtp_callback ?
667          * rtp_mode so ffmpeg inserts as many start codes as possible.
668          */
669         enc_ctx->rtp_mode = 1;
670         enc_ctx->rtp_payload_size = v->mtu / 2; // mtu/2
671         enc_ctx->bit_rate = v->bitrate;
672         enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate/2;
673         enc_ctx->qmin = v->qmin;        /* should be configured */
674         enc_ctx->time_base = (AVRational){1, v->fps};
675         enc_ctx->gop_size = v->fps*5; // emit I frame every 5 seconds
676
677         v->enc->enc_init(v->enc_ctx);
678  
679         if (avcodec_open(enc_ctx, v->codec) < 0) {
680                 ast_log(LOG_WARNING, "Unable to initialize the encoder %d\n",
681                         codec);
682                 av_free(enc_ctx);
683                 v->enc_ctx = NULL;
684                 return video_out_uninit(env);
685         }
686     }
687         /*
688          * Allocate enough for the encoded bitstream. As we are compressing,
689          * we hope that the output is never larger than the input size.
690          */
691         v->enc_out.data = ast_calloc(1, enc_in->size);
692         v->enc_out.size = enc_in->size;
693         v->enc_out.used = 0;
694
695         return 0;
696 }
697
698 /*! \brief uninitialize the entire environment.
699  * In practice, signal the thread and give it a bit of time to
700  * complete, giving up if it gets stuck. Because uninit
701  * is called from hangup with the channel locked, and the thread
702  * uses the chan lock, we need to unlock here. This is unsafe,
703  * and we should really use refcounts for the channels.
704  */
705 void console_video_uninit(struct video_desc *env)
706 {
707         int i, t = 100; /* initial wait is shorter, than make it longer */
708         env->shutdown = 1;
709         for (i=0; env->shutdown && i < 10; i++) {
710                 ast_channel_unlock(env->owner);
711                 usleep(t);
712                 t = 1000000;
713                 ast_channel_lock(env->owner);
714         }
715         env->owner = NULL;
716 }
717
718 /*! fill an AVPicture from our fbuf info, as it is required by
719  * the image conversion routines in ffmpeg.
720  * XXX This depends on the format.
721  */
722 static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p)
723 {
724         /* provide defaults for commonly used formats */
725         int l4 = b->w * b->h/4; /* size of U or V frame */
726         int len = b->w;         /* Y linesize, bytes */
727         int luv = b->w/2;       /* U/V linesize, bytes */
728
729         bzero(p, sizeof(*p));
730         switch (b->pix_fmt) {
731         case PIX_FMT_RGB555:
732         case PIX_FMT_RGB565:
733                 len *= 2;
734                 luv = 0;
735                 break;
736         case PIX_FMT_RGBA32:
737                 len *= 4;
738                 luv = 0;
739                 break;
740         case PIX_FMT_YUYV422:   /* Packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr */
741                 len *= 2;       /* all data in first plane, probably */
742                 luv = 0;
743                 break;
744         }
745         p->data[0] = b->data;
746         p->linesize[0] = len;
747         /* these are only valid for component images */
748         p->data[1] = luv ? b->data + 4*l4 : b->data+len;
749         p->data[2] = luv ? b->data + 5*l4 : b->data+len;
750         p->linesize[1] = luv;
751         p->linesize[2] = luv;
752         return p;
753 }
754
755 /*! convert/scale between an input and an output format.
756  * Old version of ffmpeg only have img_convert, which does not rescale.
757  * New versions use sws_scale which does both.
758  */
759 static void my_scale(struct fbuf_t *in, AVPicture *p_in,
760         struct fbuf_t *out, AVPicture *p_out)
761 {
762         AVPicture my_p_in, my_p_out;
763
764         if (p_in == NULL)
765                 p_in = fill_pict(in, &my_p_in);
766         if (p_out == NULL)
767                 p_out = fill_pict(out, &my_p_out);
768
769 #ifdef OLD_FFMPEG
770         /* XXX img_convert is deprecated, and does not do rescaling */
771         img_convert(p_out, out->pix_fmt,
772                 p_in, in->pix_fmt, in->w, in->h);
773 #else /* XXX replacement */
774     {
775         struct SwsContext *convert_ctx;
776
777         convert_ctx = sws_getContext(in->w, in->h, in->pix_fmt,
778                 out->w, out->h, out->pix_fmt,
779                 SWS_BICUBIC, NULL, NULL, NULL);
780         if (convert_ctx == NULL) {
781                 ast_log(LOG_ERROR, "FFMPEG::convert_cmodel : swscale context initialization failed");
782                 return;
783         }
784         if (0)
785                 ast_log(LOG_WARNING, "in %d %dx%d out %d %dx%d\n",
786                         in->pix_fmt, in->w, in->h, out->pix_fmt, out->w, out->h);
787         sws_scale(convert_ctx,
788                 p_in->data, p_in->linesize,
789                 in->w, in->h, /* src slice */
790                 p_out->data, p_out->linesize);
791
792         sws_freeContext(convert_ctx);
793     }
794 #endif /* XXX replacement */
795 }
796
797 struct video_desc *get_video_desc(struct ast_channel *c);
798
799 /*
800  * This function is called (by asterisk) for each video packet
801  * coming from the network (the 'in' path) that needs to be processed.
802  * We need to reconstruct the entire video frame before we can decode it.
803  * After a video packet is received we have to:
804  * - extract the bitstream with pre_process_data()
805  * - append the bitstream to a buffer
806  * - if the fragment is the last (RTP Marker) we decode it with decode_video()
807  * - after the decoding is completed we display the decoded frame with show_frame()
808  */
809 int console_write_video(struct ast_channel *chan, struct ast_frame *f);
810 int console_write_video(struct ast_channel *chan, struct ast_frame *f)
811 {
812         struct video_desc *env = get_video_desc(chan);
813         struct video_dec_desc *v = env->in;
814
815         if (!env->gui)  /* no gui, no rendering */
816                 return 0;
817         if (v == NULL)
818                 env->in = v = dec_init(f->subclass & ~1);
819         if (v == NULL) {
820                 /* This is not fatal, but we won't have incoming video */
821                 ast_log(LOG_WARNING, "Cannot initialize input decoder\n");
822                 return 0;
823         }
824
825         if (v->dec_in_cur == NULL)      /* no buffer for incoming frames, drop */
826                 return 0;
827 #if defined(DROP_PACKETS) && DROP_PACKETS > 0
828         /* Simulate lost packets */
829         if ((random() % 10000) <= 100*DROP_PACKETS) {
830                 ast_log(LOG_NOTICE, "Packet lost [%d]\n", f->seqno);
831                 return 0;
832         }
833 #endif
834         if (v->discard) {
835                 /*
836                  * In discard mode, drop packets until we find one with
837                  * the RTP marker set (which is the end of frame).
838                  * Note that the RTP marker flag is sent as the LSB of the
839                  * subclass, which is a  bitmask of formats. The low bit is
840                  * normally used for audio so there is no interference.
841                  */
842                 if (f->subclass & 0x01) {
843                         v->dec_in_cur->used = 0;
844                         v->dec_in_cur->ebit = 0;
845                         v->next_seq = f->seqno + 1;     /* wrap at 16 bit */
846                         v->discard = 0;
847                         ast_log(LOG_WARNING, "out of discard mode, frame %d\n", f->seqno);
848                 }
849                 return 0;
850         }
851
852         /*
853          * Only in-order fragments will be accepted. Remember seqno
854          * has 16 bit so there is wraparound. Also, ideally we could
855          * accept a bit of reordering, but at the moment we don't.
856          */
857         if (v->next_seq != f->seqno) {
858                 ast_log(LOG_WARNING, "discarding frame out of order, %d %d\n",
859                         v->next_seq, f->seqno);
860                 v->discard = 1;
861                 return 0;
862         }
863         v->next_seq++;
864
865         if (f->data == NULL || f->datalen < 2) {
866                 ast_log(LOG_WARNING, "empty video frame, discard\n");
867                 return 0;
868         }
869         if (v->d_callbacks->dec_decap(v->dec_in_cur, f->data, f->datalen)) {
870                 ast_log(LOG_WARNING, "error in dec_decap, enter discard\n");
871                 v->discard = 1;
872         }
873         if (f->subclass & 0x01) {       // RTP Marker
874                 /* prepare to decode: advance the buffer so the video thread knows. */
875                 struct fbuf_t *tmp = v->dec_in_cur;     /* store current pointer */
876                 ast_mutex_lock(&env->dec_lock);
877                 if (++v->dec_in_cur == &v->dec_in[N_DEC_IN])    /* advance to next, circular */
878                         v->dec_in_cur = &v->dec_in[0];
879                 if (v->dec_in_dpy == NULL) {    /* were not displaying anything, so set it */
880                         v->dec_in_dpy = tmp;
881                 } else if (v->dec_in_dpy == v->dec_in_cur) { /* current slot is busy */
882                         v->dec_in_cur = NULL;
883                 }
884                 ast_mutex_unlock(&env->dec_lock);
885         }
886         return 0;
887 }
888
889
890 /*! \brief read a frame from webcam or X11 through video_read(),
891  * display it,  then encode and split it.
892  * Return a list of ast_frame representing the video fragments.
893  * The head pointer is returned by the function, the tail pointer
894  * is returned as an argument.
895  */
896 static struct ast_frame *get_video_frames(struct video_desc *env, struct ast_frame **tail)
897 {
898         struct video_out_desc *v = &env->out;
899         struct ast_frame *dummy;
900
901         if (!v->loc_src.data) {
902                 static volatile int a = 0;
903                 if (a++ < 2)
904                         ast_log(LOG_WARNING, "fail, no loc_src buffer\n");
905                 return NULL;
906         }
907         if (!video_read(v))
908                 return NULL;    /* can happen, e.g. we are reading too early */
909
910         if (tail == NULL)
911                 tail = &dummy;
912         *tail = NULL;
913         /* Scale the video for the encoder, then use it for local rendering
914          * so we will see the same as the remote party.
915          */
916         my_scale(&v->loc_src, NULL, &env->enc_in, NULL);
917         show_frame(env, WIN_LOCAL);
918         if (!v->sendvideo)
919                 return NULL;
920         if (v->enc_out.data == NULL) {
921                 static volatile int a = 0;
922                 if (a++ < 2)
923                         ast_log(LOG_WARNING, "fail, no encbuf\n");
924                 return NULL;
925         }
926         v->enc->enc_run(v);
927         return v->enc->enc_encap(v, tail);
928 }
929
930 /*
931  * Helper thread to periodically poll the video source and enqueue the
932  * generated frames to the channel's queue.
933  * Using a separate thread also helps because the encoding can be
934  * computationally expensive so we don't want to starve the main thread.
935  */
936 static void *video_thread(void *arg)
937 {
938         struct video_desc *env = arg;
939         int count = 0;
940         char save_display[128] = "";
941
942         /* if sdl_videodriver is set, override the environment. Also,
943          * if it contains 'console' override DISPLAY around the call to SDL_Init
944          * so we use the console as opposed to the x11 version of aalib
945          */
946         if (!ast_strlen_zero(env->sdl_videodriver)) { /* override */
947                 const char *s = getenv("DISPLAY");
948                 setenv("SDL_VIDEODRIVER", env->sdl_videodriver, 1);
949                 if (s && !strcasecmp(env->sdl_videodriver, "aalib-console")) {
950                         ast_copy_string(save_display, s, sizeof(save_display));
951                         unsetenv("DISPLAY");
952                 }
953         }
954         sdl_setup(env);
955         if (!ast_strlen_zero(save_display))
956                 setenv("DISPLAY", save_display, 1);
957
958         /* initialize grab coordinates */
959         env->out.loc_src.x = 0;
960         env->out.loc_src.y = 0;
961
962         ast_mutex_init(&env->dec_lock); /* used to sync decoder and renderer */
963
964         if (video_open(&env->out)) {
965                 ast_log(LOG_WARNING, "cannot open local video source\n");
966         } else {
967                 /* try to register the fd. Unfortunately, if the webcam
968                  * driver does not support select/poll we are out of luck.
969                  */
970                 if (env->out.fd >= 0)
971                         ast_channel_set_fd(env->owner, 1, env->out.fd);
972                 video_out_init(env);
973         }
974
975         for (;;) {
976                 struct timeval t = { 0, 50000 };        /* XXX 20 times/sec */
977                 struct ast_frame *p, *f;
978                 struct ast_channel *chan = env->owner;
979                 int fd = chan->alertpipe[1];
980                 char *caption = NULL, buf[160];
981
982                 /* determine if video format changed */
983                 if (count++ % 10 == 0) {
984                         if (env->out.sendvideo)
985                             sprintf(buf, "%s %s %dx%d @@ %dfps %dkbps",
986                                 env->out.videodevice, env->codec_name,
987                                 env->enc_in.w, env->enc_in.h,
988                                 env->out.fps, env->out.bitrate/1000);
989                         else
990                             sprintf(buf, "hold");
991                         caption = buf;
992                 }
993
994                 /* manage keypad events */
995                 /* XXX here we should always check for events,
996                 * otherwise the drag will not work */ 
997                 if (env->gui)
998                         eventhandler(env, caption);
999  
1000                 /* sleep for a while */
1001                 ast_select(0, NULL, NULL, NULL, &t);
1002
1003             if (env->in) {
1004                 struct video_dec_desc *v = env->in;
1005                 
1006                 /*
1007                  * While there is something to display, call the decoder and free
1008                  * the buffer, possibly enabling the receiver to store new data.
1009                  */
1010                 while (v->dec_in_dpy) {
1011                         struct fbuf_t *tmp = v->dec_in_dpy;     /* store current pointer */
1012
1013                         if (v->d_callbacks->dec_run(v, tmp))
1014                                 show_frame(env, WIN_REMOTE);
1015                         tmp->used = 0;  /* mark buffer as free */
1016                         tmp->ebit = 0;
1017                         ast_mutex_lock(&env->dec_lock);
1018                         if (++v->dec_in_dpy == &v->dec_in[N_DEC_IN])    /* advance to next, circular */
1019                                 v->dec_in_dpy = &v->dec_in[0];
1020
1021                         if (v->dec_in_cur == NULL)      /* receiver was idle, enable it... */
1022                                 v->dec_in_cur = tmp;    /* using the slot just freed */
1023                         else if (v->dec_in_dpy == v->dec_in_cur) /* this was the last slot */
1024                                 v->dec_in_dpy = NULL;   /* nothing more to display */
1025                         ast_mutex_unlock(&env->dec_lock);
1026                 }
1027             }
1028
1029                 f = get_video_frames(env, &p);  /* read and display */
1030                 if (!f)
1031                         continue;
1032                 if (env->shutdown)
1033                         break;
1034                 chan = env->owner;
1035                 ast_channel_lock(chan);
1036
1037                 /* AST_LIST_INSERT_TAIL is only good for one frame, cannot use here */
1038                 if (chan->readq.first == NULL) {
1039                         chan->readq.first = f;
1040                 } else {
1041                         chan->readq.last->frame_list.next = f;
1042                 }
1043                 chan->readq.last = p;
1044                 /*
1045                  * more or less same as ast_queue_frame, but extra
1046                  * write on the alertpipe to signal frames.
1047                  */
1048                 if (fd > -1) {
1049                         int blah = 1, l = sizeof(blah);
1050                         for (p = f; p; p = AST_LIST_NEXT(p, frame_list)) {
1051                                 if (write(fd, &blah, l) != l)
1052                                         ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d: %s!\n",
1053                                             chan->name, f->frametype, f->subclass, strerror(errno));
1054                         }
1055                 }
1056                 ast_channel_unlock(chan);
1057         }
1058         /* thread terminating, here could call the uninit */
1059         /* uninitialize the local and remote video environments */
1060         env->in = dec_uninit(env->in);
1061         video_out_uninit(env);
1062
1063         if (env->gui)
1064                 env->gui = cleanup_sdl(env->gui);
1065         ast_mutex_destroy(&env->dec_lock);
1066         env->shutdown = 0;
1067         return NULL;
1068 }
1069
1070 static void copy_geometry(struct fbuf_t *src, struct fbuf_t *dst)
1071 {
1072         if (dst->w == 0)
1073                 dst->w = src->w;
1074         if (dst->h == 0)
1075                 dst->h = src->h;
1076 }
1077
1078 /*! initialize the video environment.
1079  * Apart from the formats (constant) used by sdl and the codec,
1080  * we use enc_in as the basic geometry.
1081  */
1082 static void init_env(struct video_desc *env)
1083 {
1084         struct fbuf_t *c = &(env->out.loc_src);         /* local source */
1085         struct fbuf_t *ei = &(env->enc_in);             /* encoder input */
1086         struct fbuf_t *ld = &(env->loc_dpy);    /* local display */
1087         struct fbuf_t *rd = &(env->rem_dpy);            /* remote display */
1088
1089         c->pix_fmt = PIX_FMT_YUV420P;   /* default - camera format */
1090         ei->pix_fmt = PIX_FMT_YUV420P;  /* encoder input */
1091         if (ei->w == 0 || ei->h == 0) {
1092                 ei->w = 352;
1093                 ei->h = 288;
1094         }
1095         ld->pix_fmt = rd->pix_fmt = PIX_FMT_YUV420P; /* sdl format */
1096         /* inherit defaults */
1097         copy_geometry(ei, c);   /* camera inherits from encoder input */
1098         copy_geometry(ei, rd);  /* remote display inherits from encoder input */
1099         copy_geometry(rd, ld);  /* local display inherits from remote display */
1100 }
1101
1102 /*!
1103  * The first call to the video code, called by oss_new() or similar.
1104  * Here we initialize the various components we use, namely SDL for display,
1105  * ffmpeg for encoding/decoding, and a local video source.
1106  * We do our best to progress even if some of the components are not
1107  * available.
1108  */
1109 void console_video_start(struct video_desc *env, struct ast_channel *owner)
1110 {
1111         if (env == NULL)        /* video not initialized */
1112                 return;
1113         if (owner == NULL)      /* nothing to do if we don't have a channel */
1114                 return;
1115         env->owner = owner;
1116         init_env(env);
1117         env->out.enc = map_config_video_format(env->codec_name);
1118
1119         ast_log(LOG_WARNING, "start video out %s %dx%d\n",
1120                 env->codec_name, env->enc_in.w,  env->enc_in.h);
1121         /*
1122          * Register all codecs supported by the ffmpeg library.
1123          * We only need to do it once, but probably doesn't
1124          * harm to do it multiple times.
1125          */
1126         avcodec_init();
1127         avcodec_register_all();
1128         av_log_set_level(AV_LOG_ERROR); /* only report errors */
1129
1130         if (env->out.fps == 0) {
1131                 env->out.fps = 15;
1132                 ast_log(LOG_WARNING, "fps unset, forcing to %d\n", env->out.fps);
1133         }
1134         if (env->out.bitrate == 0) {
1135                 env->out.bitrate = 65000;
1136                 ast_log(LOG_WARNING, "bitrate unset, forcing to %d\n", env->out.bitrate);
1137         }
1138
1139         ast_pthread_create_background(&env->vthread, NULL, video_thread, env);
1140 }
1141
1142 /*
1143  * Parse a geometry string, accepting also common names for the formats.
1144  * Trick: if we have a leading > or < and a numeric geometry,
1145  * return the larger or smaller one.
1146  * E.g. <352x288 gives the smaller one, 320x240
1147  */
1148 static int video_geom(struct fbuf_t *b, const char *s)
1149 {
1150         int w = 0, h = 0;
1151
1152         static struct {
1153                 const char *s; int w; int h;
1154         } *fp, formats[] = {
1155                 {"vga",         640, 480 },
1156                 {"cif",         352, 288 },
1157                 {"qvga",        320, 240 },
1158                 {"qcif",        176, 144 },
1159                 {"sqcif",       128, 96 },
1160                 {NULL,          0, 0 },
1161         };
1162         if (*s == '<' || *s == '>')
1163                 sscanf(s+1,"%dx%d", &w, &h);
1164         for (fp = formats; fp->s; fp++) {
1165                 if (*s == '>') {        /* look for a larger one */
1166                         if (fp->w <= w) {
1167                                 if (fp > formats)
1168                                         fp--; /* back one step if possible */
1169                                 break;
1170                         }
1171                 } else if (*s == '<') { /* look for a smaller one */
1172                         if (fp->w < w)
1173                                 break;
1174                 } else if (!strcasecmp(s, fp->s)) { /* look for a string */
1175                         break;
1176                 }
1177         }
1178         if (*s == '<' && fp->s == NULL) /* smallest */
1179                 fp--;
1180         if (fp->s) {
1181                 b->w = fp->w;
1182                 b->h = fp->h;
1183         } else if (sscanf(s, "%dx%d", &b->w, &b->h) != 2) {
1184                 ast_log(LOG_WARNING, "Invalid video_size %s, using 352x288\n", s);
1185                 b->w = 352;
1186                 b->h = 288;
1187         }
1188         return 0;
1189 }
1190
1191 /* extend ast_cli with video commands. Called by console_video_config */
1192 int console_video_cli(struct video_desc *env, const char *var, int fd)
1193 {
1194         if (env == NULL)
1195                 return 1;       /* unrecognised */
1196
1197         if (!strcasecmp(var, "videodevice")) {
1198                 ast_cli(fd, "videodevice is [%s]\n", env->out.videodevice);
1199         } else if (!strcasecmp(var, "videocodec")) {
1200                 ast_cli(fd, "videocodec is [%s]\n", env->codec_name);
1201         } else if (!strcasecmp(var, "sendvideo")) {
1202                 ast_cli(fd, "sendvideo is [%s]\n", env->out.sendvideo ? "on" : "off");
1203         } else if (!strcasecmp(var, "video_size")) {
1204                 int in_w = 0, in_h = 0;
1205                 if (env->in) {
1206                         in_w = env->in->dec_out.w;
1207                         in_h = env->in->dec_out.h;
1208                 }
1209                 ast_cli(fd, "sizes: video %dx%d camera %dx%d local %dx%d remote %dx%d in %dx%d\n",
1210                         env->enc_in.w, env->enc_in.h,
1211                         env->out.loc_src.w, env->out.loc_src.h,
1212                         env->loc_dpy.w, env->loc_dpy.h,
1213                         env->rem_dpy.w, env->rem_dpy.h,
1214                         in_w, in_h);
1215         } else if (!strcasecmp(var, "bitrate")) {
1216                 ast_cli(fd, "bitrate is [%d]\n", env->out.bitrate);
1217         } else if (!strcasecmp(var, "qmin")) {
1218                 ast_cli(fd, "qmin is [%d]\n", env->out.qmin);
1219         } else if (!strcasecmp(var, "fps")) {
1220                 ast_cli(fd, "fps is [%d]\n", env->out.fps);
1221         } else {
1222                 return 1;       /* unrecognised */
1223         }
1224         return 0;       /* recognised */
1225 }
1226
1227 /*! parse config command for video support. */
1228 int console_video_config(struct video_desc **penv,
1229         const char *var, const char *val)
1230 {
1231         struct video_desc *env;
1232
1233         if (penv == NULL) {
1234                 ast_log(LOG_WARNING, "bad argument penv=NULL\n");
1235                 return 1;       /* error */
1236         }
1237         /* allocate the video descriptor first time we get here */
1238         env = *penv;
1239         if (env == NULL) {
1240                 env = *penv = ast_calloc(1, sizeof(struct video_desc));
1241                 if (env == NULL) {
1242                         ast_log(LOG_WARNING, "fail to allocate video_desc\n");
1243                         return 1;       /* error */
1244                 
1245                 }
1246                 /* set default values */
1247                 ast_copy_string(env->out.videodevice, "X11", sizeof(env->out.videodevice));
1248                 env->out.fps = 5;
1249                 env->out.bitrate = 65000;
1250                 env->out.sendvideo = 1;
1251                 env->out.qmin = 3;
1252         }
1253         CV_START(var, val);
1254         CV_STR("videodevice", env->out.videodevice);
1255         CV_BOOL("sendvideo", env->out.sendvideo);
1256         CV_F("video_size", video_geom(&env->enc_in, val));
1257         CV_F("camera_size", video_geom(&env->out.loc_src, val));
1258         CV_F("local_size", video_geom(&env->loc_dpy, val));
1259         CV_F("remote_size", video_geom(&env->rem_dpy, val));
1260         CV_STR("keypad", env->keypad_file);
1261         CV_F("region", keypad_cfg_read(env->gui, val));
1262         CV_STR("keypad_font", env->keypad_font);
1263         CV_STR("sdl_videodriver", env->sdl_videodriver);
1264         CV_UINT("fps", env->out.fps);
1265         CV_UINT("bitrate", env->out.bitrate);
1266         CV_UINT("qmin", env->out.qmin);
1267         CV_STR("videocodec", env->codec_name);
1268         return 1;       /* nothing found */
1269
1270         CV_END;         /* the 'nothing found' case */
1271         return 0;               /* found something */
1272 }
1273
1274 #endif  /* video support */