44 #include <sys/ioctl.h> 117 #if !defined(HAVE_VIDEO_CONSOLE) || !defined(HAVE_FFMPEG) 160 static void my_scale(
struct fbuf_t *
in, AVPicture *p_in,
168 struct video_device {
174 struct timeval last_frame;
194 struct video_out_desc {
207 struct fbuf_t loc_src_geometry;
213 AVFrame *enc_in_frame;
223 int device_secondary;
225 int picture_in_picture;
251 char keypad_file[256];
252 char keypad_font[256];
254 char sdl_videodriver[256];
265 struct gui_info *gui;
267 struct video_out_desc
out;
270 static AVPicture *fill_pict(
struct fbuf_t *
b, AVPicture *p);
278 memset(b,
'\0',
sizeof(*b));
290 return env ? env->stayopen : 0;
299 used_mem(
const char *msg)
303 pid_t pid = getpid();
304 sprintf(in,
"ps -o vsz= -o rss= %d", pid);
326 static int grabber_open(
struct video_out_desc *v)
333 for (i = 0; i < v->device_num; i++) {
335 if (v->devices[i].grabber)
340 g_data = g->
open(v->devices[i].name, &v->loc_src_geometry, v->fps);
343 v->devices[i].grabber = g;
344 v->devices[i].grabber_data = g_data;
345 v->devices[i].status_index |= IS_ON;
349 for (i = 0; i < v->device_num; i++) {
350 if (!v->devices[i].grabber)
352 v->device_primary = i;
353 v->device_secondary = i;
370 static struct fbuf_t *grabber_read(
struct video_device *dev,
int fps)
374 if (dev->grabber ==
NULL)
383 dev->last_frame = now;
384 return dev->grabber->read(dev->grabber_data);
391 static void grabber_move(
struct video_device *dev,
int dx,
int dy)
393 if (dev->grabber && dev->grabber->move) {
394 dev->grabber->move(dev->grabber_data, dx, dy);
420 static int video_out_uninit(
struct video_desc *
env)
422 struct video_out_desc *v = &env->out;
427 AVCodecContext *enc_ctx = (AVCodecContext *)v->enc_ctx;
428 avcodec_close(enc_ctx);
432 if (v->enc_in_frame) {
433 av_free(v->enc_in_frame);
434 v->enc_in_frame =
NULL;
441 for (i = 0; i < v->device_num; i++) {
442 if (v->devices[i].grabber){
443 v->devices[i].grabber_data =
444 v->devices[i].grabber->close(v->devices[i].grabber_data);
445 v->devices[i].grabber =
NULL;
447 v->devices[i].dev_buf =
NULL;
449 v->devices[i].status_index = 0;
451 v->picture_in_picture = 0;
452 env->frame_freeze = 0;
463 static int video_out_init(
struct video_desc *env)
468 struct video_out_desc *v = &env->out;
472 v->enc_in_frame =
NULL;
473 v->enc_out.data =
NULL;
476 v->codec = avcodec_find_encoder(codec);
488 enc_in = &env->enc_in;
489 enc_in->
pix_fmt = PIX_FMT_YUV420P;
490 enc_in->
size = (enc_in->
w * enc_in->
h * 3)/2;
494 return video_out_uninit(env);
497 v->enc_in_frame = avcodec_alloc_frame();
498 if (!v->enc_in_frame) {
500 return video_out_uninit(env);
504 size = enc_in->
w * enc_in->
h;
505 v->enc_in_frame->data[0] = enc_in->
data;
506 v->enc_in_frame->data[1] = v->enc_in_frame->data[0] + size;
507 v->enc_in_frame->data[2] = v->enc_in_frame->data[1] + size/4;
508 v->enc_in_frame->linesize[0] = enc_in->
w;
509 v->enc_in_frame->linesize[1] = enc_in->
w/2;
510 v->enc_in_frame->linesize[2] = enc_in->
w/2;
516 AVCodecContext *enc_ctx = avcodec_alloc_context();
517 v->enc_ctx = enc_ctx;
518 enc_ctx->pix_fmt = enc_in->
pix_fmt;
519 enc_ctx->width = enc_in->
w;
520 enc_ctx->height = enc_in->
h;
524 enc_ctx->rtp_mode = 1;
525 enc_ctx->rtp_payload_size = v->mtu / 2;
526 enc_ctx->bit_rate = v->bitrate;
527 enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate/2;
528 enc_ctx->qmin = v->qmin;
529 enc_ctx->time_base = (AVRational){1, v->fps};
530 enc_ctx->gop_size = v->fps*5;
532 v->enc->enc_init(v->enc_ctx);
534 if (avcodec_open(enc_ctx, v->codec) < 0) {
538 return video_out_uninit(env);
546 v->enc_out.size = enc_in->
size;
565 if (env->stayopen == 0) {
567 for (i=0; env->shutdown && i < 10; i++) {
585 static AVPicture *fill_pict(
struct fbuf_t *b, AVPicture *p)
588 int l4 = b->
w * b->
h/4;
593 memset(p,
'\0',
sizeof(*p));
604 case PIX_FMT_YUYV422:
611 p->data[0] = b->
data;
612 p->linesize[0] =
len;
616 p->linesize[1] = luv;
617 p->linesize[2] = luv;
621 p->data[0] += len*b->
win_y + b->
win_x*sample_size;
623 p->data[1] += luv*(b->
win_y/2) + (b->
win_x/2) * sample_size;
624 p->data[2] += luv*(b->
win_y/2) + (b->
win_x/2) * sample_size;
633 static void my_scale(
struct fbuf_t *
in, AVPicture *p_in,
634 struct fbuf_t *out, AVPicture *p_out)
636 AVPicture my_p_in, my_p_out;
637 int eff_w=out->
w, eff_h=out->
h;
640 p_in = fill_pict(in, &my_p_in);
642 p_out = fill_pict(out, &my_p_out);
653 img_convert(p_out, out->
pix_fmt,
657 struct SwsContext *convert_ctx;
659 convert_ctx = sws_getContext(in->
w, in->
h, in->
pix_fmt,
662 if (convert_ctx ==
NULL) {
663 ast_log(
LOG_ERROR,
"FFMPEG::convert_cmodel : swscale context initialization failed\n");
669 sws_scale(convert_ctx,
670 p_in->data, p_in->linesize,
672 p_out->data, p_out->linesize);
674 sws_freeContext(convert_ctx);
709 #if defined(DROP_PACKETS) && DROP_PACKETS > 0 711 if ((random() % 10000) <= 100*DROP_PACKETS) {
784 static struct ast_frame *get_video_frames(
struct video_desc *env,
struct ast_frame **tail)
786 struct video_out_desc *v = &env->out;
788 struct fbuf_t *loc_src_primary =
NULL, *p_read;
791 if (!env->out.device_num)
795 for (i = 0; i < env->out.device_num; i++) {
796 p_read = grabber_read(&env->out.devices[i], env->out.fps);
799 env->out.devices[i].dev_buf = p_read;
802 loc_src_primary = env->out.devices[env->out.device_primary].dev_buf;
805 if (loc_src_primary) {
808 my_scale(loc_src_primary,
NULL, &env->enc_in,
NULL);
810 if (env->out.picture_in_picture) {
811 struct fbuf_t *loc_src_secondary;
813 loc_src_secondary = env->out.devices[env->out.device_secondary].dev_buf;
814 if (loc_src_secondary) {
815 env->enc_in.win_x = env->out.pip_x;
816 env->enc_in.win_y = env->out.pip_y;
817 env->enc_in.win_w = env->enc_in.w/3;
818 env->enc_in.win_h = env->enc_in.h/3;
821 my_scale(loc_src_secondary,
NULL, &env->enc_in,
NULL);
823 env->enc_in.win_x = 0;
824 env->enc_in.win_y = 0;
825 env->enc_in.win_w = 0;
826 env->enc_in.win_h = 0;
831 env->out.picture_in_picture = 0;
835 for (i = 0; i < env->out.device_num; i++)
841 if (!env->owner || !loc_src_primary || !v->sendvideo)
843 if (v->enc_out.data ==
NULL) {
844 static volatile int a = 0;
850 return v->enc->enc_encap(&v->enc_out, v->mtu, tail);
859 static void *video_thread(
void *arg)
861 struct video_desc *env = arg;
863 char save_display[128] =
"";
871 const char *s = getenv(
"DISPLAY");
872 setenv(
"SDL_VIDEODRIVER", env->sdl_videodriver, 1);
873 if (s && !strcasecmp(env->sdl_videodriver,
"aalib-console")) {
880 setenv(
"DISPLAY", save_display, 1);
885 if (grabber_open(&env->out)) {
889 if (env->out.device_num) {
890 env->out.devices[env->out.device_primary].status_index |= IS_PRIMARY | IS_SECONDARY;
901 for (i = 0; i < env->out.device_num; i++) {
903 src_msgs[env->out.devices[i].status_index]);
908 struct timespec t = { 0, 50000000 };
912 char *caption =
NULL,
buf[160];
915 if (count++ % 10 == 0) {
916 if (env->out.sendvideo && env->out.devices) {
917 snprintf(buf,
sizeof(buf),
"%s %s %dx%d @@ %dfps %dkbps",
918 env->out.devices[env->out.device_primary].name, env->codec_name,
919 env->enc_in.w, env->enc_in.h,
920 env->out.fps, env->out.bitrate / 1000);
922 sprintf(buf,
"hold");
965 f = get_video_frames(env, &p);
991 if (ast_channel_alertable(chan)) {
993 if (ast_channel_alert(chan)) {
994 ast_log(
LOG_WARNING,
"Unable to write to alert pipe on %s, frametype/subclass %d/%d: %s!\n",
1003 video_out_uninit(env);
1006 env->gui =
cleanup_sdl(env->gui, env->out.device_num);
1024 static void init_env(
struct video_desc *env)
1026 struct fbuf_t *
c = &(env->out.loc_src_geometry);
1027 struct fbuf_t *ei = &(env->enc_in);
1028 struct fbuf_t *ld = &(env->loc_dpy);
1029 struct fbuf_t *rd = &(env->rem_dpy);
1033 ei->
pix_fmt = PIX_FMT_YUV420P;
1034 if (ei->
w == 0 || ei->
h == 0) {
1040 copy_geometry(ei, c);
1041 copy_geometry(ei, rd);
1042 copy_geometry(rd, ld);
1045 for (i = 0; i < env->out.device_num; i++) {
1046 env->src_dpy[i].pix_fmt = PIX_FMT_YUV420P;
1053 env->out.pip_x = ei->
w - ei->
w/3;
1054 env->out.pip_y = ei->
h - ei->
h/3;
1073 env->out.enc = map_config_video_format(env->codec_name);
1076 env->codec_name, env->enc_in.w, env->enc_in.h);
1083 avcodec_register_all();
1084 av_log_set_level(AV_LOG_ERROR);
1086 if (env->out.fps == 0) {
1090 if (env->out.bitrate == 0) {
1091 env->out.bitrate = 65000;
1096 NULL, video_thread, env);
1105 static int video_geom(
struct fbuf_t *b,
const char *s)
1110 const char *s;
int w;
int h;
1112 {
"16cif", 1408, 1152 },
1113 {
"xga", 1024, 768 },
1114 {
"4cif", 704, 576 },
1117 {
"qvga", 320, 240 },
1118 {
"qcif", 176, 144 },
1119 {
"sqcif", 128, 96 },
1122 if (*s ==
'<' || *s ==
'>')
1123 sscanf(s+1,
"%dx%d", &w, &h);
1124 for (fp = formats; fp->s; fp++) {
1131 }
else if (*s ==
'<') {
1134 }
else if (!strcasecmp(s, fp->s)) {
1138 if (*s ==
'<' && fp->s ==
NULL)
1143 }
else if (sscanf(s,
"%dx%d", &b->
w, &b->
h) != 2) {
1167 static int device_table_fill(
struct video_device *
devices,
int *device_num_p,
const char *s)
1170 struct video_device *p;
1173 if (*device_num_p >= 9)
1176 for (i = 0; i < *device_num_p; i++) {
1177 if (!strcmp(devices[i].name, s))
1181 p = &devices[*device_num_p];
1187 p->grabber_data =
NULL;
1190 p->status_index = 0;
1201 if (!strcasecmp(var,
"videodevice")) {
1202 ast_cli(fd,
"videodevice is [%s]\n", env->out.devices[env->out.device_primary].name);
1203 }
else if (!strcasecmp(var,
"videocodec")) {
1204 ast_cli(fd,
"videocodec is [%s]\n", env->codec_name);
1205 }
else if (!strcasecmp(var,
"sendvideo")) {
1206 ast_cli(fd,
"sendvideo is [%s]\n", env->out.sendvideo ?
"on" :
"off");
1207 }
else if (!strcasecmp(var,
"video_size")) {
1208 int in_w = 0, in_h = 0;
1210 in_w = env->in->dec_out.w;
1211 in_h = env->in->dec_out.h;
1213 ast_cli(fd,
"sizes: video %dx%d camera %dx%d local %dx%d remote %dx%d in %dx%d\n",
1214 env->enc_in.w, env->enc_in.h,
1215 env->out.loc_src_geometry.w, env->out.loc_src_geometry.h,
1216 env->loc_dpy.w, env->loc_dpy.h,
1217 env->rem_dpy.w, env->rem_dpy.h,
1219 }
else if (!strcasecmp(var,
"bitrate")) {
1220 ast_cli(fd,
"bitrate is [%d]\n", env->out.bitrate);
1221 }
else if (!strcasecmp(var,
"qmin")) {
1222 ast_cli(fd,
"qmin is [%d]\n", env->out.qmin);
1223 }
else if (!strcasecmp(var,
"fps")) {
1224 ast_cli(fd,
"fps is [%d]\n", env->out.fps);
1225 }
else if (!strcasecmp(var,
"startgui")) {
1228 }
else if (!strcasecmp(var,
"stopgui") && env->stayopen != 0) {
1230 if (env->gui && env->owner)
1242 const char *var,
const char *
val)
1244 struct video_desc *env;
1253 env = *penv =
ast_calloc(1,
sizeof(
struct video_desc));
1260 env->out.device_primary = 0;
1261 env->out.device_secondary = 0;
1263 env->out.bitrate = 65000;
1264 env->out.sendvideo = 1;
1266 env->out.device_num = 0;
1269 CV_F(
"videodevice", device_table_fill(env->out.devices, &env->out.device_num, val));
1270 CV_BOOL(
"sendvideo", env->out.sendvideo);
1271 CV_F(
"video_size", video_geom(&env->enc_in, val));
1272 CV_F(
"camera_size", video_geom(&env->out.loc_src_geometry, val));
1273 CV_F(
"local_size", video_geom(&env->loc_dpy, val));
1274 CV_F(
"remote_size", video_geom(&env->rem_dpy, val));
1275 CV_STR(
"keypad", env->keypad_file);
1277 CV_UINT(
"startgui", env->stayopen);
1278 CV_STR(
"keypad_font", env->keypad_font);
1279 CV_STR(
"sdl_videodriver", env->sdl_videodriver);
1281 CV_UINT(
"bitrate", env->out.bitrate);
1282 CV_UINT(
"qmin", env->out.qmin);
1283 CV_STR(
"videocodec", env->codec_name);
static void sdl_setup(struct video_desc *env)
#define ast_channel_lock(chan)
Main Channel structure associated with a channel.
int print_message(struct board *b, const char *s)
Asterisk main include file. File version handling, generic pbx functions.
void *(* open)(const char *name, struct fbuf_t *geom, int fps)
void fbuf_free(struct fbuf_t *)
struct fbuf_t dec_in[N_DEC_IN]
#define CV_UINT(__x, __dst)
static void dummy(char *unused,...)
struct video_codec_desc * d_callbacks
struct ast_frame::@264 frame_list
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
void console_video_start(struct video_desc *env, struct ast_channel *owner)
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define ast_mutex_lock(a)
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.
static struct gui_info * cleanup_sdl(struct gui_info *g, int n)
int console_video_formats
#define CV_END
close a variable parsing block
void ast_cli(int fd, const char *fmt,...)
struct grab_desc * console_grabbers[]
#define CV_START(__in_var, __in_val)
the macro to open a block for variable parsing
#define ast_pthread_create_detached_background(a, b, c, d)
#define CV_STR(__x, __dst)
int console_write_video(struct ast_channel *chan, struct ast_frame *f)
struct ast_frame_subclass subclass
static void show_frame(struct video_desc *env, int out)
#define ast_strlen_zero(foo)
static enum CodecID map_video_format(uint32_t ast_format, int rw)
map an asterisk format into an ffmpeg one
int console_video_config(struct video_desc **penv, const char *var, const char *val)
#define CV_F(__pattern, __body)
call a generic function if the name matches.
struct ast_readq_list * ast_channel_readq(struct ast_channel *chan)
General Asterisk PBX channel definitions.
#define MAX_VIDEO_SOURCES
static struct video_dec_desc * dec_init(uint32_t the_ast_format)
ast_cli_command
calling arguments for new-style handlers.
struct fbuf_t * dec_in_dpy
struct fbuf_t * dec_in_cur
#define CV_BOOL(__x, __dst)
helper macros to assign the value to a BOOL, UINT, static string and dynamic string ...
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
struct sla_ringing_trunk * first
#define ast_channel_unlock(chan)
#define ast_calloc(num, len)
A wrapper for calloc()
static struct video_dec_desc * dec_uninit(struct video_dec_desc *v)
uninitialize the descriptor for remote video stream
static int keypad_cfg_read(struct gui_info *gui, const char *val)
int console_video_cli(struct video_desc *env, const char *var, int fd)
static void eventhandler(struct video_desc *env, const char *caption)
void console_video_uninit(struct video_desc *env)
Standard Command Line Interface.
int get_gui_startup(struct video_desc *env)
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
const char * ast_channel_name(const struct ast_channel *chan)
Data structure associated with a single frame of data.
int setenv(const char *name, const char *value, int overwrite)
union ast_frame::@263 data
enum ast_frame_type frametype
#define ast_mutex_init(pmutex)
#define ast_mutex_destroy(a)
static const struct video_codec_desc * supported_codecs[]
decoder_decap_f dec_decap
struct video_desc * get_video_desc(struct ast_channel *c)
return the pointer to the video descriptor
int unsetenv(const char *name)
Structure for mutex and tracking information.
#define ast_mutex_unlock(a)