44 #include <sys/ioctl.h>
49 #include "console_video.h"
123 #if !defined(HAVE_VIDEO_CONSOLE) || !defined(HAVE_FFMPEG)
130 int console_video_cli(
struct video_desc *
env,
const char *var,
int fd)
135 int console_video_config(
struct video_desc **penv,
const char *var,
const char *
val)
140 void console_video_start(
struct video_desc *
env,
struct ast_channel *owner)
142 ast_log(LOG_NOTICE,
"voice only, console video support not present\n");
145 void console_video_uninit(
struct video_desc *
env)
149 int get_gui_startup(
struct video_desc*
env)
154 int console_video_formats = 0;
159 int console_video_formats =
166 static void my_scale(
struct fbuf_t *in, AVPicture *p_in,
167 struct fbuf_t *out, AVPicture *p_out);
174 struct video_device {
180 struct timeval last_frame;
200 struct video_out_desc {
213 struct fbuf_t loc_src_geometry;
219 AVFrame *enc_in_frame;
226 struct video_device
devices[MAX_VIDEO_SOURCES];
229 int device_secondary;
231 int picture_in_picture;
257 char keypad_file[256];
258 char keypad_font[256];
260 char sdl_videodriver[256];
266 struct fbuf_t src_dpy[MAX_VIDEO_SOURCES];
273 struct video_out_desc out;
276 static AVPicture *fill_pict(
struct fbuf_t *b, AVPicture *p);
278 void fbuf_free(
struct fbuf_t *b)
282 if (b->data && b->size)
284 memset(b,
'\0',
sizeof(*b));
288 b->pix_fmt = x.pix_fmt;
294 int get_gui_startup(
struct video_desc*
env)
296 return env ? env->stayopen : 0;
305 used_mem(
const char *msg)
309 pid_t pid = getpid();
310 sprintf(in,
"ps -o vsz= -o rss= %d", pid);
311 ast_log(LOG_WARNING,
"used mem (vsize, rss) %s ", msg);
318 #include "console_gui.c"
332 static int grabber_open(
struct video_out_desc *v)
339 for (i = 0; i < v->device_num; i++) {
341 if (v->devices[i].grabber)
344 for (j = 0; (g = console_grabbers[j]); j++) {
346 g_data = g->open(v->devices[i].name, &v->loc_src_geometry, v->fps);
349 v->devices[i].grabber = g;
350 v->devices[i].grabber_data = g_data;
351 v->devices[i].status_index |= IS_ON;
355 for (i = 0; i < v->device_num; i++) {
356 if (!v->devices[i].grabber)
358 v->device_primary = i;
359 v->device_secondary = i;
376 static struct fbuf_t *grabber_read(
struct video_device *dev,
int fps)
380 if (dev->grabber == NULL)
389 dev->last_frame = now;
390 return dev->grabber->read(dev->grabber_data);
397 static void grabber_move(
struct video_device *dev,
int dx,
int dy)
399 if (dev->grabber && dev->grabber->move) {
400 dev->grabber->move(dev->grabber_data, dx, dy);
412 for (i = 0; supported_codecs[i]; i++)
413 if (!strcasecmp(name, supported_codecs[i]->name))
415 if (supported_codecs[i] == NULL) {
416 ast_log(LOG_WARNING,
"Cannot find codec for '%s'\n", name);
418 strcpy(name, supported_codecs[i]->name);
420 ast_log(LOG_WARNING,
"Using codec '%s'\n", name);
421 return supported_codecs[i];
426 static int video_out_uninit(
struct video_desc *
env)
428 struct video_out_desc *v = &env->out;
433 AVCodecContext *enc_ctx = (AVCodecContext *)v->enc_ctx;
434 avcodec_close(enc_ctx);
438 if (v->enc_in_frame) {
439 av_free(v->enc_in_frame);
440 v->enc_in_frame = NULL;
444 fbuf_free(&env->enc_in);
445 fbuf_free(&v->enc_out);
447 for (i = 0; i < v->device_num; i++) {
448 if (v->devices[i].grabber){
449 v->devices[i].grabber_data =
450 v->devices[i].grabber->close(v->devices[i].grabber_data);
451 v->devices[i].grabber = NULL;
453 v->devices[i].dev_buf = NULL;
455 v->devices[i].status_index = 0;
457 v->picture_in_picture = 0;
458 env->frame_freeze = 0;
469 static int video_out_init(
struct video_desc *env)
474 struct video_out_desc *v = &env->out;
478 v->enc_in_frame = NULL;
479 v->enc_out.data = NULL;
481 codec = map_video_format(v->enc->format, CM_WR);
482 v->codec = avcodec_find_encoder(codec);
484 ast_log(LOG_WARNING,
"Cannot find the encoder for format %d\n",
494 enc_in = &env->enc_in;
495 enc_in->pix_fmt = PIX_FMT_YUV420P;
496 enc_in->size = (enc_in->w * enc_in->h * 3)/2;
499 ast_log(LOG_WARNING,
"Cannot allocate encoder input buffer\n");
500 return video_out_uninit(env);
503 v->enc_in_frame = avcodec_alloc_frame();
504 if (!v->enc_in_frame) {
505 ast_log(LOG_WARNING,
"Unable to allocate the encoding video frame\n");
506 return video_out_uninit(env);
510 size = enc_in->w * enc_in->h;
511 v->enc_in_frame->data[0] = enc_in->data;
512 v->enc_in_frame->data[1] = v->enc_in_frame->data[0] + size;
513 v->enc_in_frame->data[2] = v->enc_in_frame->data[1] + size/4;
514 v->enc_in_frame->linesize[0] = enc_in->w;
515 v->enc_in_frame->linesize[1] = enc_in->w/2;
516 v->enc_in_frame->linesize[2] = enc_in->w/2;
522 AVCodecContext *enc_ctx = avcodec_alloc_context();
523 v->enc_ctx = enc_ctx;
524 enc_ctx->pix_fmt = enc_in->pix_fmt;
525 enc_ctx->width = enc_in->w;
526 enc_ctx->height = enc_in->h;
530 enc_ctx->rtp_mode = 1;
531 enc_ctx->rtp_payload_size = v->mtu / 2;
532 enc_ctx->bit_rate = v->bitrate;
533 enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate/2;
534 enc_ctx->qmin = v->qmin;
535 enc_ctx->time_base = (AVRational){1, v->fps};
536 enc_ctx->gop_size = v->fps*5;
538 v->enc->enc_init(v->enc_ctx);
540 if (avcodec_open(enc_ctx, v->codec) < 0) {
541 ast_log(LOG_WARNING,
"Unable to initialize the encoder %d\n", codec);
544 return video_out_uninit(env);
551 v->enc_out.data =
ast_calloc(1, enc_in->size);
552 v->enc_out.size = enc_in->size;
568 void console_video_uninit(
struct video_desc *env)
571 if (env->stayopen == 0) {
573 for (i=0; env->shutdown && i < 10; i++) {
575 ast_channel_unlock(env->owner);
579 ast_channel_lock(env->owner);
591 static AVPicture *fill_pict(
struct fbuf_t *b, AVPicture *p)
594 int l4 = b->w * b->h/4;
599 memset(p,
'\0',
sizeof(*p));
600 switch (b->pix_fmt) {
610 case PIX_FMT_YUYV422:
617 p->data[0] = b->data;
618 p->linesize[0] = len;
620 p->data[1] = luv ? b->data + 4*l4 : b->data+len;
621 p->data[2] = luv ? b->data + 5*l4 : b->data+len;
622 p->linesize[1] = luv;
623 p->linesize[2] = luv;
627 p->data[0] += len*b->win_y + b->win_x*sample_size;
629 p->data[1] += luv*(b->win_y/2) + (b->win_x/2) * sample_size;
630 p->data[2] += luv*(b->win_y/2) + (b->win_x/2) * sample_size;
639 static void my_scale(
struct fbuf_t *in, AVPicture *p_in,
640 struct fbuf_t *out, AVPicture *p_out)
642 AVPicture my_p_in, my_p_out;
643 int eff_w=out->w, eff_h=out->h;
646 p_in = fill_pict(in, &my_p_in);
648 p_out = fill_pict(out, &my_p_out);
659 img_convert(p_out, out->pix_fmt,
660 p_in, in->pix_fmt, in->w, in->h);
663 struct SwsContext *convert_ctx;
665 convert_ctx = sws_getContext(in->w, in->h, in->pix_fmt,
666 eff_w, eff_h, out->pix_fmt,
667 SWS_BICUBIC, NULL, NULL, NULL);
668 if (convert_ctx == NULL) {
669 ast_log(LOG_ERROR,
"FFMPEG::convert_cmodel : swscale context initialization failed\n");
673 ast_log(LOG_WARNING,
"in %d %dx%d out %d %dx%d\n",
674 in->pix_fmt, in->w, in->h, out->pix_fmt, eff_w, eff_h);
675 sws_scale(convert_ctx,
676 p_in->data, p_in->linesize,
678 p_out->data, p_out->linesize);
680 sws_freeContext(convert_ctx);
685 struct video_desc *get_video_desc(
struct ast_channel *c);
700 struct video_desc *env = get_video_desc(chan);
706 env->in = v = dec_init(f->
subclass & ~1);
709 ast_log(LOG_WARNING,
"Cannot initialize input decoder\n");
713 if (v->dec_in_cur == NULL)
715 #if defined(DROP_PACKETS) && DROP_PACKETS > 0
717 if ((random() % 10000) <= 100*DROP_PACKETS) {
718 ast_log(LOG_NOTICE,
"Packet lost [%d]\n", f->
seqno);
731 v->dec_in_cur->used = 0;
732 v->dec_in_cur->ebit = 0;
733 v->next_seq = f->
seqno + 1;
735 ast_log(LOG_WARNING,
"out of discard mode, frame %d\n", f->
seqno);
745 if (v->next_seq != f->
seqno) {
746 ast_log(LOG_WARNING,
"discarding frame out of order, %d %d\n",
747 v->next_seq, f->
seqno);
754 ast_log(LOG_WARNING,
"empty video frame, discard\n");
757 if (v->d_callbacks->dec_decap(v->dec_in_cur, f->
data.ptr, f->
datalen)) {
758 ast_log(LOG_WARNING,
"error in dec_decap, enter discard\n");
763 struct fbuf_t *tmp = v->dec_in_cur;
764 ast_mutex_lock(&env->dec_lock);
765 if (++v->dec_in_cur == &v->dec_in[N_DEC_IN])
766 v->dec_in_cur = &v->dec_in[0];
767 if (v->dec_in_dpy == NULL) {
769 }
else if (v->dec_in_dpy == v->dec_in_cur) {
770 v->dec_in_cur = NULL;
772 ast_mutex_unlock(&env->dec_lock);
790 static struct ast_frame *get_video_frames(
struct video_desc *env,
struct ast_frame **tail)
792 struct video_out_desc *v = &env->out;
794 struct fbuf_t *loc_src_primary = NULL, *p_read;
797 if (!env->out.device_num)
801 for (i = 0; i < env->out.device_num; i++) {
802 p_read = grabber_read(&env->out.devices[i], env->out.fps);
805 env->out.devices[i].dev_buf = p_read;
808 loc_src_primary = env->out.devices[env->out.device_primary].dev_buf;
811 if (loc_src_primary) {
814 my_scale(loc_src_primary, NULL, &env->enc_in, NULL);
816 if (env->out.picture_in_picture) {
817 struct fbuf_t *loc_src_secondary;
819 loc_src_secondary = env->out.devices[env->out.device_secondary].dev_buf;
820 if (loc_src_secondary) {
821 env->enc_in.win_x = env->out.pip_x;
822 env->enc_in.win_y = env->out.pip_y;
823 env->enc_in.win_w = env->enc_in.w/3;
824 env->enc_in.win_h = env->enc_in.h/3;
827 my_scale(loc_src_secondary, NULL, &env->enc_in, NULL);
829 env->enc_in.win_x = 0;
830 env->enc_in.win_y = 0;
831 env->enc_in.win_w = 0;
832 env->enc_in.win_h = 0;
837 env->out.picture_in_picture = 0;
840 show_frame(env, WIN_LOCAL);
841 for (i = 0; i < env->out.device_num; i++)
842 show_frame(env, i+WIN_SRC1);
847 if (!env->owner || !loc_src_primary || !v->sendvideo)
849 if (v->enc_out.data == NULL) {
850 static volatile int a = 0;
852 ast_log(LOG_WARNING,
"fail, no encoder output buffer\n");
856 return v->enc->enc_encap(&v->enc_out, v->mtu, tail);
865 static void *video_thread(
void *arg)
867 struct video_desc *env = arg;
869 char save_display[128] =
"";
876 if (!ast_strlen_zero(env->sdl_videodriver)) {
877 const char *s = getenv(
"DISPLAY");
878 setenv(
"SDL_VIDEODRIVER", env->sdl_videodriver, 1);
879 if (s && !strcasecmp(env->sdl_videodriver,
"aalib-console")) {
885 if (!ast_strlen_zero(save_display)) {
886 setenv(
"DISPLAY", save_display, 1);
889 ast_mutex_init(&env->dec_lock);
891 if (grabber_open(&env->out)) {
892 ast_log(LOG_WARNING,
"cannot open local video source\n");
895 if (env->out.device_num) {
896 env->out.devices[env->out.device_primary].status_index |= IS_PRIMARY | IS_SECONDARY;
907 for (i = 0; i < env->out.device_num; i++) {
908 print_message(env->gui->thumb_bd_array[i].board,
909 src_msgs[env->out.devices[i].status_index]);
914 struct timespec t = { 0, 50000000 };
918 char *caption = NULL, buf[160];
921 if (count++ % 10 == 0) {
922 if (env->out.sendvideo && env->out.devices) {
923 snprintf(buf,
sizeof(buf),
"%s %s %dx%d @@ %dfps %dkbps",
924 env->out.devices[env->out.device_primary].name, env->codec_name,
925 env->enc_in.w, env->enc_in.h,
926 env->out.fps, env->out.bitrate / 1000);
928 sprintf(buf,
"hold");
937 eventhandler(env, caption);
949 while (v->dec_in_dpy) {
950 struct fbuf_t *tmp = v->dec_in_dpy;
953 if (v->d_callbacks->dec_run(v, tmp) && !env->frame_freeze)
954 show_frame(env, WIN_REMOTE);
957 ast_mutex_lock(&env->dec_lock);
958 if (++v->dec_in_dpy == &v->dec_in[N_DEC_IN])
959 v->dec_in_dpy = &v->dec_in[0];
961 if (v->dec_in_cur == NULL)
963 else if (v->dec_in_dpy == v->dec_in_cur)
964 v->dec_in_dpy = NULL;
965 ast_mutex_unlock(&env->dec_lock);
971 f = get_video_frames(env, &p);
984 ast_channel_lock(chan);
987 if (ast_channel_readq(chan).first == NULL) {
988 ast_channel_readq(chan).first = f;
990 ast_channel_readq(chan).last->
frame_list.next = f;
992 ast_channel_readq(chan).last = p;
997 if (ast_channel_alertable(chan)) {
999 if (ast_channel_alert(chan)) {
1000 ast_log(LOG_WARNING,
"Unable to write to alert pipe on %s, frametype/subclass %d/%d: %s!\n",
1004 ast_channel_unlock(chan);
1008 env->in = dec_uninit(env->in);
1009 video_out_uninit(env);
1012 env->gui = cleanup_sdl(env->gui, env->out.device_num);
1013 ast_mutex_destroy(&env->dec_lock);
1030 static void init_env(
struct video_desc *env)
1032 struct fbuf_t *c = &(env->out.loc_src_geometry);
1033 struct fbuf_t *ei = &(env->enc_in);
1034 struct fbuf_t *ld = &(env->loc_dpy);
1035 struct fbuf_t *rd = &(env->rem_dpy);
1038 c->pix_fmt = PIX_FMT_YUV420P;
1039 ei->pix_fmt = PIX_FMT_YUV420P;
1040 if (ei->w == 0 || ei->h == 0) {
1044 ld->pix_fmt = rd->pix_fmt = PIX_FMT_YUV420P;
1046 copy_geometry(ei, c);
1047 copy_geometry(ei, rd);
1048 copy_geometry(rd, ld);
1051 for (i = 0; i < env->out.device_num; i++) {
1052 env->src_dpy[i].pix_fmt = PIX_FMT_YUV420P;
1053 env->src_dpy[i].w = SRC_WIN_W;
1054 env->src_dpy[i].h = SRC_WIN_H;
1059 env->out.pip_x = ei->w - ei->w/3;
1060 env->out.pip_y = ei->h - ei->h/3;
1070 void console_video_start(
struct video_desc *env,
struct ast_channel *owner)
1072 ast_log(LOG_WARNING,
"env %p chan %p\n", env, owner);
1079 env->out.enc = map_config_video_format(env->codec_name);
1081 ast_log(LOG_WARNING,
"start video out %s %dx%d\n",
1082 env->codec_name, env->enc_in.w, env->enc_in.h);
1089 avcodec_register_all();
1090 av_log_set_level(AV_LOG_ERROR);
1092 if (env->out.fps == 0) {
1094 ast_log(LOG_WARNING,
"fps unset, forcing to %d\n", env->out.fps);
1096 if (env->out.bitrate == 0) {
1097 env->out.bitrate = 65000;
1098 ast_log(LOG_WARNING,
"bitrate unset, forcing to %d\n", env->out.bitrate);
1101 ast_pthread_create_detached_background(&env->vthread,
1102 NULL, video_thread, env);
1111 static int video_geom(
struct fbuf_t *b,
const char *s)
1116 const char *s;
int w;
int h;
1118 {
"16cif", 1408, 1152 },
1119 {
"xga", 1024, 768 },
1120 {
"4cif", 704, 576 },
1123 {
"qvga", 320, 240 },
1124 {
"qcif", 176, 144 },
1125 {
"sqcif", 128, 96 },
1128 if (*s ==
'<' || *s ==
'>')
1129 sscanf(s+1,
"%dx%d", &w, &h);
1130 for (fp = formats; fp->s; fp++) {
1137 }
else if (*s ==
'<') {
1140 }
else if (!strcasecmp(s, fp->s)) {
1144 if (*s ==
'<' && fp->s == NULL)
1149 }
else if (sscanf(s,
"%dx%d", &b->w, &b->h) != 2) {
1150 ast_log(LOG_WARNING,
"Invalid video_size %s, using 352x288\n", s);
1173 static int device_table_fill(
struct video_device *
devices,
int *device_num_p,
const char *s)
1176 struct video_device *p;
1179 if (*device_num_p >= 9)
1182 for (i = 0; i < *device_num_p; i++) {
1183 if (!strcmp(devices[i].name, s))
1187 p = &devices[*device_num_p];
1193 p->grabber_data = NULL;
1196 p->status_index = 0;
1202 int console_video_cli(
struct video_desc *env,
const char *var,
int fd)
1207 if (!strcasecmp(var,
"videodevice")) {
1208 ast_cli(fd,
"videodevice is [%s]\n", env->out.devices[env->out.device_primary].name);
1209 }
else if (!strcasecmp(var,
"videocodec")) {
1210 ast_cli(fd,
"videocodec is [%s]\n", env->codec_name);
1211 }
else if (!strcasecmp(var,
"sendvideo")) {
1212 ast_cli(fd,
"sendvideo is [%s]\n", env->out.sendvideo ?
"on" :
"off");
1213 }
else if (!strcasecmp(var,
"video_size")) {
1214 int in_w = 0, in_h = 0;
1216 in_w = env->in->dec_out.w;
1217 in_h = env->in->dec_out.h;
1219 ast_cli(fd,
"sizes: video %dx%d camera %dx%d local %dx%d remote %dx%d in %dx%d\n",
1220 env->enc_in.w, env->enc_in.h,
1221 env->out.loc_src_geometry.w, env->out.loc_src_geometry.h,
1222 env->loc_dpy.w, env->loc_dpy.h,
1223 env->rem_dpy.w, env->rem_dpy.h,
1225 }
else if (!strcasecmp(var,
"bitrate")) {
1226 ast_cli(fd,
"bitrate is [%d]\n", env->out.bitrate);
1227 }
else if (!strcasecmp(var,
"qmin")) {
1228 ast_cli(fd,
"qmin is [%d]\n", env->out.qmin);
1229 }
else if (!strcasecmp(var,
"fps")) {
1230 ast_cli(fd,
"fps is [%d]\n", env->out.fps);
1231 }
else if (!strcasecmp(var,
"startgui")) {
1233 console_video_start(env, NULL);
1234 }
else if (!strcasecmp(var,
"stopgui") && env->stayopen != 0) {
1236 if (env->gui && env->owner)
1239 console_video_uninit(env);
1247 int console_video_config(
struct video_desc **penv,
1248 const char *var,
const char *
val)
1250 struct video_desc *env;
1253 ast_log(LOG_WARNING,
"bad argument penv=NULL\n");
1259 env = *penv =
ast_calloc(1,
sizeof(
struct video_desc));
1261 ast_log(LOG_WARNING,
"fail to allocate video_desc\n");
1266 env->out.device_primary = 0;
1267 env->out.device_secondary = 0;
1269 env->out.bitrate = 65000;
1270 env->out.sendvideo = 1;
1272 env->out.device_num = 0;
1275 CV_F(
"videodevice", device_table_fill(env->out.devices, &env->out.device_num, val));
1276 CV_BOOL(
"sendvideo", env->out.sendvideo);
1277 CV_F(
"video_size", video_geom(&env->enc_in, val));
1278 CV_F(
"camera_size", video_geom(&env->out.loc_src_geometry, val));
1279 CV_F(
"local_size", video_geom(&env->loc_dpy, val));
1280 CV_F(
"remote_size", video_geom(&env->rem_dpy, val));
1281 CV_STR(
"keypad", env->keypad_file);
1282 CV_F(
"region", keypad_cfg_read(env->gui, val));
1283 CV_UINT(
"startgui", env->stayopen);
1284 CV_STR(
"keypad_font", env->keypad_font);
1285 CV_STR(
"sdl_videodriver", env->sdl_videodriver);
1286 CV_UINT(
"fps", env->out.fps);
1287 CV_UINT(
"bitrate", env->out.bitrate);
1288 CV_UINT(
"qmin", env->out.qmin);
1289 CV_STR(
"videocodec", env->codec_name);
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
struct ast_frame::@225 frame_list
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
#define ast_strdup(str)
A wrapper for strdup()
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
#define CV_END
close a variable parsing block
#define CV_START(__in_var, __in_val)
the macro to open a block for variable parsing
struct ast_frame_subclass subclass
#define CV_F(__pattern, __body)
call a generic function if the name matches.
General Asterisk PBX channel definitions.
ast_cli_command
calling arguments for new-style handlers.
#define CV_BOOL(__x, __dst)
helper macros to assign the value to a BOOL, UINT, static string and dynamic string ...
union ast_frame::@224 data
#define ast_calloc(num, len)
A wrapper for calloc()
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Data structure associated with a single frame of data.
enum ast_frame_type frametype
Structure for mutex and tracking information.