45 #include <sys/ioctl.h>
118 #if !defined(HAVE_VIDEO_CONSOLE) || !defined(HAVE_FFMPEG)
154 int console_video_formats =
161 static void my_scale(
struct fbuf_t *in, AVPicture *p_in,
162 struct fbuf_t *out, AVPicture *p_out);
169 struct video_device {
175 struct timeval last_frame;
195 struct video_out_desc {
208 struct fbuf_t loc_src_geometry;
214 AVFrame *enc_in_frame;
224 int device_secondary;
226 int picture_in_picture;
252 char keypad_file[256];
253 char keypad_font[256];
255 char sdl_videodriver[256];
268 struct video_out_desc out;
271 static AVPicture *fill_pict(
struct fbuf_t *b, AVPicture *p);
279 memset(b,
'\0',
sizeof(*b));
291 return env ? env->stayopen : 0;
300 used_mem(
const char *msg)
304 pid_t pid = getpid();
305 sprintf(in,
"ps -o vsz= -o rss= %d", pid);
327 static int grabber_open(
struct video_out_desc *v)
334 for (i = 0; i < v->device_num; i++) {
336 if (v->devices[i].grabber)
341 g_data = g->
open(v->devices[i].name, &v->loc_src_geometry, v->fps);
344 v->devices[i].grabber = g;
345 v->devices[i].grabber_data = g_data;
346 v->devices[i].status_index |=
IS_ON;
350 for (i = 0; i < v->device_num; i++) {
351 if (!v->devices[i].grabber)
353 v->device_primary = i;
354 v->device_secondary = i;
371 static struct fbuf_t *grabber_read(
struct video_device *dev,
int fps)
375 if (dev->grabber == NULL)
384 dev->last_frame = now;
385 return dev->grabber->read(dev->grabber_data);
392 static void grabber_move(
struct video_device *dev,
int dx,
int dy)
394 if (dev->grabber && dev->grabber->move) {
395 dev->grabber->move(dev->grabber_data, dx, dy);
421 static int video_out_uninit(
struct video_desc *env)
423 struct video_out_desc *v = &env->out;
428 AVCodecContext *enc_ctx = (AVCodecContext *)v->enc_ctx;
429 avcodec_close(enc_ctx);
433 if (v->enc_in_frame) {
434 av_free(v->enc_in_frame);
435 v->enc_in_frame = NULL;
442 for (i = 0; i < v->device_num; i++) {
443 if (v->devices[i].grabber){
444 v->devices[i].grabber_data =
445 v->devices[i].grabber->close(v->devices[i].grabber_data);
446 v->devices[i].grabber = NULL;
448 v->devices[i].dev_buf = NULL;
450 v->devices[i].status_index = 0;
452 v->picture_in_picture = 0;
453 env->frame_freeze = 0;
464 static int video_out_init(
struct video_desc *env)
469 struct video_out_desc *v = &env->out;
473 v->enc_in_frame = NULL;
474 v->enc_out.data = NULL;
477 v->codec = avcodec_find_encoder(codec);
489 enc_in = &env->enc_in;
490 enc_in->
pix_fmt = PIX_FMT_YUV420P;
491 enc_in->
size = (enc_in->
w * enc_in->
h * 3)/2;
495 return video_out_uninit(env);
498 v->enc_in_frame = avcodec_alloc_frame();
499 if (!v->enc_in_frame) {
501 return video_out_uninit(env);
505 size = enc_in->
w * enc_in->
h;
506 v->enc_in_frame->data[0] = enc_in->
data;
507 v->enc_in_frame->data[1] = v->enc_in_frame->data[0] + size;
508 v->enc_in_frame->data[2] = v->enc_in_frame->data[1] + size/4;
509 v->enc_in_frame->linesize[0] = enc_in->
w;
510 v->enc_in_frame->linesize[1] = enc_in->
w/2;
511 v->enc_in_frame->linesize[2] = enc_in->
w/2;
517 AVCodecContext *enc_ctx = avcodec_alloc_context();
518 v->enc_ctx = enc_ctx;
519 enc_ctx->pix_fmt = enc_in->
pix_fmt;
520 enc_ctx->width = enc_in->
w;
521 enc_ctx->height = enc_in->
h;
525 enc_ctx->rtp_mode = 1;
526 enc_ctx->rtp_payload_size = v->mtu / 2;
527 enc_ctx->bit_rate = v->bitrate;
528 enc_ctx->bit_rate_tolerance = enc_ctx->bit_rate/2;
529 enc_ctx->qmin = v->qmin;
530 enc_ctx->time_base = (AVRational){1, v->fps};
531 enc_ctx->gop_size = v->fps*5;
533 v->enc->enc_init(v->enc_ctx);
535 if (avcodec_open(enc_ctx, v->codec) < 0) {
539 return video_out_uninit(env);
547 v->enc_out.size = enc_in->
size;
566 if (env->stayopen == 0) {
568 for (i=0; env->shutdown && i < 10; i++) {
586 static AVPicture *fill_pict(
struct fbuf_t *b, AVPicture *p)
589 int l4 = b->
w * b->
h/4;
594 memset(p,
'\0',
sizeof(*p));
605 case PIX_FMT_YUYV422:
612 p->data[0] = b->
data;
613 p->linesize[0] =
len;
617 p->linesize[1] = luv;
618 p->linesize[2] = luv;
622 p->data[0] += len*b->
win_y + b->
win_x*sample_size;
624 p->data[1] += luv*(b->
win_y/2) + (b->
win_x/2) * sample_size;
625 p->data[2] += luv*(b->
win_y/2) + (b->
win_x/2) * sample_size;
634 static void my_scale(
struct fbuf_t *in, AVPicture *p_in,
635 struct fbuf_t *out, AVPicture *p_out)
637 AVPicture my_p_in, my_p_out;
638 int eff_w=out->
w, eff_h=out->
h;
641 p_in = fill_pict(in, &my_p_in);
643 p_out = fill_pict(out, &my_p_out);
654 img_convert(p_out, out->
pix_fmt,
658 struct SwsContext *convert_ctx;
660 convert_ctx = sws_getContext(in->
w, in->
h, in->
pix_fmt,
662 SWS_BICUBIC, NULL, NULL, NULL);
663 if (convert_ctx == NULL) {
664 ast_log(
LOG_ERROR,
"FFMPEG::convert_cmodel : swscale context initialization failed\n");
670 sws_scale(convert_ctx,
671 p_in->data, p_in->linesize,
673 p_out->data, p_out->linesize);
675 sws_freeContext(convert_ctx);
710 #if defined(DROP_PACKETS) && DROP_PACKETS > 0
712 if ((random() % 10000) <= 100*DROP_PACKETS) {
785 static struct ast_frame *get_video_frames(
struct video_desc *env,
struct ast_frame **tail)
787 struct video_out_desc *v = &env->out;
789 struct fbuf_t *loc_src_primary = NULL, *p_read;
792 if (!env->out.device_num)
796 for (i = 0; i < env->out.device_num; i++) {
797 p_read = grabber_read(&env->out.devices[i], env->out.fps);
800 env->out.devices[i].dev_buf = p_read;
803 loc_src_primary = env->out.devices[env->out.device_primary].dev_buf;
806 if (loc_src_primary) {
809 my_scale(loc_src_primary, NULL, &env->enc_in, NULL);
811 if (env->out.picture_in_picture) {
812 struct fbuf_t *loc_src_secondary;
814 loc_src_secondary = env->out.devices[env->out.device_secondary].dev_buf;
815 if (loc_src_secondary) {
816 env->enc_in.win_x = env->out.pip_x;
817 env->enc_in.win_y = env->out.pip_y;
818 env->enc_in.win_w = env->enc_in.w/3;
819 env->enc_in.win_h = env->enc_in.h/3;
822 my_scale(loc_src_secondary, NULL, &env->enc_in, NULL);
824 env->enc_in.win_x = 0;
825 env->enc_in.win_y = 0;
826 env->enc_in.win_w = 0;
827 env->enc_in.win_h = 0;
832 env->out.picture_in_picture = 0;
836 for (i = 0; i < env->out.device_num; i++)
842 if (!env->owner || !loc_src_primary || !v->sendvideo)
844 if (v->enc_out.data == NULL) {
845 static volatile int a = 0;
851 return v->enc->enc_encap(&v->enc_out, v->mtu, tail);
860 static void *video_thread(
void *arg)
862 struct video_desc *env = arg;
864 char save_display[128] =
"";
872 const char *s = getenv(
"DISPLAY");
873 setenv(
"SDL_VIDEODRIVER", env->sdl_videodriver, 1);
874 if (s && !strcasecmp(env->sdl_videodriver,
"aalib-console")) {
881 setenv(
"DISPLAY", save_display, 1);
886 if (grabber_open(&env->out)) {
890 if (env->out.device_num) {
902 for (i = 0; i < env->out.device_num; i++) {
904 src_msgs[env->out.devices[i].status_index]);
909 struct timespec t = { 0, 50000000 };
913 char *caption = NULL, buf[160];
916 if (count++ % 10 == 0) {
917 if (env->out.sendvideo && env->out.devices) {
918 snprintf(buf,
sizeof(buf),
"%s %s %dx%d @@ %dfps %dkbps",
919 env->out.devices[env->out.device_primary].name, env->codec_name,
920 env->enc_in.w, env->enc_in.h,
921 env->out.fps, env->out.bitrate / 1000);
923 sprintf(buf,
"hold");
966 f = get_video_frames(env, &p);
994 int blah = 1, l =
sizeof(blah);
996 if (write(fd, &blah, l) != l)
997 ast_log(
LOG_WARNING,
"Unable to write to alert pipe on %s, frametype/subclass %d/%d: %s!\n",
1006 video_out_uninit(env);
1009 env->gui =
cleanup_sdl(env->gui, env->out.device_num);
1027 static void init_env(
struct video_desc *env)
1029 struct fbuf_t *c = &(env->out.loc_src_geometry);
1030 struct fbuf_t *ei = &(env->enc_in);
1031 struct fbuf_t *ld = &(env->loc_dpy);
1032 struct fbuf_t *rd = &(env->rem_dpy);
1036 ei->
pix_fmt = PIX_FMT_YUV420P;
1037 if (ei->
w == 0 || ei->
h == 0) {
1043 copy_geometry(ei, c);
1044 copy_geometry(ei, rd);
1045 copy_geometry(rd, ld);
1048 for (i = 0; i < env->out.device_num; i++) {
1049 env->src_dpy[i].pix_fmt = PIX_FMT_YUV420P;
1056 env->out.pip_x = ei->
w - ei->
w/3;
1057 env->out.pip_y = ei->
h - ei->
h/3;
1076 env->out.enc = map_config_video_format(env->codec_name);
1079 env->codec_name, env->enc_in.w, env->enc_in.h);
1086 avcodec_register_all();
1087 av_log_set_level(AV_LOG_ERROR);
1089 if (env->out.fps == 0) {
1093 if (env->out.bitrate == 0) {
1094 env->out.bitrate = 65000;
1099 NULL, video_thread, env);
1113 const char *s;
int w;
int h;
1115 {
"16cif", 1408, 1152 },
1116 {
"xga", 1024, 768 },
1117 {
"4cif", 704, 576 },
1120 {
"qvga", 320, 240 },
1121 {
"qcif", 176, 144 },
1122 {
"sqcif", 128, 96 },
1125 if (*s ==
'<' || *s ==
'>')
1126 sscanf(s+1,
"%dx%d", &w, &h);
1127 for (fp = formats; fp->s; fp++) {
1134 }
else if (*s ==
'<') {
1137 }
else if (!strcasecmp(s, fp->s)) {
1141 if (*s ==
'<' && fp->s == NULL)
1146 }
else if (sscanf(s,
"%dx%d", &b->
w, &b->
h) != 2) {
1170 static int device_table_fill(
struct video_device *
devices,
int *device_num_p,
const char *s)
1173 struct video_device *p;
1176 if (*device_num_p >= 9)
1179 for (i = 0; i < *device_num_p; i++) {
1180 if (!strcmp(devices[i].name, s))
1184 p = &devices[*device_num_p];
1190 p->grabber_data = NULL;
1193 p->status_index = 0;
1204 if (!strcasecmp(var,
"videodevice")) {
1205 ast_cli(fd,
"videodevice is [%s]\n", env->out.devices[env->out.device_primary].name);
1206 }
else if (!strcasecmp(var,
"videocodec")) {
1207 ast_cli(fd,
"videocodec is [%s]\n", env->codec_name);
1208 }
else if (!strcasecmp(var,
"sendvideo")) {
1209 ast_cli(fd,
"sendvideo is [%s]\n", env->out.sendvideo ?
"on" :
"off");
1210 }
else if (!strcasecmp(var,
"video_size")) {
1211 int in_w = 0, in_h = 0;
1213 in_w = env->in->dec_out.w;
1214 in_h = env->in->dec_out.h;
1216 ast_cli(fd,
"sizes: video %dx%d camera %dx%d local %dx%d remote %dx%d in %dx%d\n",
1217 env->enc_in.w, env->enc_in.h,
1218 env->out.loc_src_geometry.w, env->out.loc_src_geometry.h,
1219 env->loc_dpy.w, env->loc_dpy.h,
1220 env->rem_dpy.w, env->rem_dpy.h,
1222 }
else if (!strcasecmp(var,
"bitrate")) {
1223 ast_cli(fd,
"bitrate is [%d]\n", env->out.bitrate);
1224 }
else if (!strcasecmp(var,
"qmin")) {
1225 ast_cli(fd,
"qmin is [%d]\n", env->out.qmin);
1226 }
else if (!strcasecmp(var,
"fps")) {
1227 ast_cli(fd,
"fps is [%d]\n", env->out.fps);
1228 }
else if (!strcasecmp(var,
"startgui")) {
1231 }
else if (!strcasecmp(var,
"stopgui") && env->stayopen != 0) {
1233 if (env->gui && env->owner)
1245 const char *var,
const char *
val)
1247 struct video_desc *env;
1256 env = *penv =
ast_calloc(1,
sizeof(
struct video_desc));
1263 env->out.device_primary = 0;
1264 env->out.device_secondary = 0;
1266 env->out.bitrate = 65000;
1267 env->out.sendvideo = 1;
1269 env->out.device_num = 0;
1272 CV_F(
"videodevice", device_table_fill(env->out.devices, &env->out.device_num, val));
1273 CV_BOOL(
"sendvideo", env->out.sendvideo);
1275 CV_F(
"camera_size",
video_geom(&env->out.loc_src_geometry, val));
1278 CV_STR(
"keypad", env->keypad_file);
1280 CV_UINT(
"startgui", env->stayopen);
1281 CV_STR(
"keypad_font", env->keypad_font);
1282 CV_STR(
"sdl_videodriver", env->sdl_videodriver);
1284 CV_UINT(
"bitrate", env->out.bitrate);
1285 CV_UINT(
"qmin", env->out.qmin);
1286 CV_STR(
"videocodec", env->codec_name);
union ast_frame_subclass subclass
static void sdl_setup(struct video_desc *env)
[re]set the main sdl window, useful in case of resize. We can tell the first from subsequent calls fr...
#define ast_channel_lock(chan)
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
static struct gui_info * cleanup_sdl(struct gui_info *gui, int device_num)
free the resources in struct gui_info and the descriptor itself. Return NULL so we can assign the val...
void fbuf_free(struct fbuf_t *)
static struct video_codec_desc * supported_codecs[]
#define CV_STR(__x, __dst)
struct fbuf_t dec_in[N_DEC_IN]
struct ast_frame::@173 frame_list
static void dummy(char *unused,...)
#define CV_UINT(__x, __dst)
struct video_codec_desc * d_callbacks
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
#define AST_FORMAT_H263_PLUS
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.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int console_video_formats
void *(* open)(const char *name, struct fbuf_t *geom, int fps)
void ast_cli(int fd, const char *fmt,...)
struct grab_desc * console_grabbers[]
#define ast_pthread_create_detached_background(a, b, c, d)
int console_write_video(struct ast_channel *chan, struct ast_frame *f)
static void show_frame(struct video_desc *env, int out)
static int video_geom(struct fbuf_t *b, const char *s)
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_END
close a variable parsing block
General Asterisk PBX channel definitions.
#define CV_START(__in_var, __in_val)
the macro to open a block for variable parsing
static force_inline int attribute_pure ast_strlen_zero(const char *s)
#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 ...
int setenv(const char *name, const char *value, int overwrite)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
const ast_string_field name
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
#define ast_channel_unlock(chan)
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)
read a keypad entry line in the format reset token circle xc yc diameter token circle xc yc x1 y1 h #...
int console_video_cli(struct video_desc *env, const char *var, int fd)
static void grabber_move(struct video_device *, int dx, int dy)
#define CV_F(__pattern, __body)
call a generic function if the name matches.
static void eventhandler(struct video_desc *env, const char *caption)
refresh the screen, and also grab a bunch of events.
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.
int print_message(struct board *b, const char *s)
Data structure associated with a single frame of data.
enum ast_frame_type frametype
#define ast_mutex_init(pmutex)
#define ast_mutex_destroy(a)
#define AST_FORMAT_MP4_VIDEO
decoder_decap_f dec_decap
union ast_frame::@172 data
struct ast_channel::@156 readq
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 ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
#define ast_mutex_unlock(a)