[asterisk-commits] rizzo: branch rizzo/video_v2 r85091 - /team/rizzo/video_v2/channels/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Oct 9 08:32:56 CDT 2007
Author: rizzo
Date: Tue Oct 9 08:32:55 2007
New Revision: 85091
URL: http://svn.digium.com/view/asterisk?view=rev&rev=85091
Log:
some restructuring from Sergio Fadda to deal with different codecs.
Modified:
team/rizzo/video_v2/channels/chan_alsa.c
team/rizzo/video_v2/channels/console_video.c
Modified: team/rizzo/video_v2/channels/chan_alsa.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/video_v2/channels/chan_alsa.c?view=diff&rev=85091&r1=85090&r2=85091
==============================================================================
--- team/rizzo/video_v2/channels/chan_alsa.c (original)
+++ team/rizzo/video_v2/channels/chan_alsa.c Tue Oct 9 08:32:55 2007
@@ -1083,11 +1083,54 @@
return res;
}
+/*! generic console command handler. Basically a wrapper for a subset
+ * * of config file options.
+ * */
+static char *console_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chan_alsa_pvt *o = &alsa;
+ const char *var, *value;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "console [sendvideo|device|videodevice|fps|bitrate|videowidth|videoheight]";
+ e->usage =
+ "Usage: console ...\n"
+ " Generic handler for console commands.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc < e->args)
+ return CLI_SHOWUSAGE;
+ var = a->argv[e->args-1];
+ value = a->argc > e->args ? a->argv[e->args] : NULL;
+ //if (value) /* handle setting */
+ // store_config_core(o, var, value);
+ /* XXX these should be moved to console_video.c */
+ if (!strcasecmp(var, "videodevice")) {
+ ast_cli(a->fd, "videodevice is [%s]\n", o->env->videodevice);
+ } else if (!strcasecmp(var, "videowidth")) {
+ ast_cli(a->fd, "videowidth is [%d]\n", o->env->w);
+ } else if (!strcasecmp(var, "videoheight")) {
+ ast_cli(a->fd, "videoheight is [%d]\n", o->env->h);
+ } else if (!strcasecmp(var, "bitrate")) {
+ ast_cli(a->fd, "bitrate is [%d]\n", o->env->bitrate);
+ } else if (!strcasecmp(var, "fps")) {
+ ast_cli(a->fd, "fps is [%d]\n", o->env->fps);
+ } else if (!strcasecmp(var, "device")) {
+ ast_cli(a->fd, "device is [in: %s - out: %s]\n", indevname, outdevname);
+ }
+ return CLI_SUCCESS;
+}
+
static const char dial_usage[] =
"Usage: console dial [extension[@context]]\n"
" Dials a given extension (and context if specified)\n";
static struct ast_cli_entry cli_alsa[] = {
+ NEW_CLI(console_cmd, "Generic console command"),
{ { "console", "answer", NULL },
console_answer, "Answer an incoming console call",
answer_usage },
Modified: team/rizzo/video_v2/channels/console_video.c
URL: http://svn.digium.com/view/asterisk/team/rizzo/video_v2/channels/console_video.c?view=diff&rev=85091&r1=85090&r2=85091
==============================================================================
--- team/rizzo/video_v2/channels/console_video.c (original)
+++ team/rizzo/video_v2/channels/console_video.c Tue Oct 9 08:32:55 2007
@@ -240,17 +240,17 @@
/*! \brief encapsulate the bistream in RTP frames */
/* XXX len is redundant */
-typedef struct ast_frame (*encoder_encap_f)(struct video_out_desc *out,
- int len, struct ast_frame **tail);
+typedef struct ast_frame *(*encoder_encap_f)(struct video_out_desc *out,
+ struct ast_frame **tail);
/*! \brief inizialize the decoder */
typedef int (*decoder_init_f)(struct video_in_desc *v);
/*! \brief extract the bitstream from RTP frames */
-typedef int (*decoder_decap_f)(struct video_in_desc *v);
+typedef uint8_t *(*decoder_decap_f)(uint8_t *data, int *len);
/*! \brief actually call the decoder */
-typedef int (*decoder_decode_f)(struct video_in_desc *v);
+typedef int (*decoder_decode_f)(struct video_in_desc *v, struct fbuf_t *b);
struct video_codec_desc {
int format; /* AST_FORMAT_* */
@@ -272,8 +272,7 @@
int fps; /* framerate */
int bitrate;
char videodevice[64];
-
- int rtp_fmt; /* compressed format, AST_FORMAT_* */
+ char codec_name[64];
pthread_t vthread; /* video thread */
int shutdown; /* set to shutdown vthread */
@@ -281,6 +280,8 @@
struct video_in_desc in; /* remote video descriptor */
struct video_out_desc out; /* local video descriptor */
+
+ struct video_codec_desc *current_codec;
SDL_Surface *screen;
int sdl_ok;
@@ -293,7 +294,7 @@
* The list of video formats we support. You can OR the formats, and
* provide corresponding ffmpeg mappings in the table below.
*/
-#define CONSOLE_FORMAT_VIDEO AST_FORMAT_H263_PLUS
+#define CONSOLE_FORMAT_VIDEO (AST_FORMAT_H263_PLUS | AST_FORMAT_H263 | AST_FORMAT_H261)
static AVPicture *fill_pict(struct fbuf_t *b, AVPicture *p);
@@ -310,12 +311,59 @@
b->pix_fmt = x.pix_fmt;
}
+/*!
+ * Build an ast_frame for a given chunk of data, and link it into
+ * the queue, with possibly 'head' bytes at the beginning to
+ * fill in some fields later.
+ */
+static struct ast_frame *create_video_frame(uint8_t *start, uint8_t *end,
+ int format, int head, struct ast_frame *prev)
+{
+ int len = end-start;
+ uint8_t *data;
+ struct ast_frame *f;
+
+ data = ast_calloc(1, len+head);
+ f = ast_calloc(1, sizeof(*f));
+ if (f == NULL || data == NULL) {
+ ast_log(LOG_WARNING, "--- frame error f %p data %p len %d format %d\n",
+ f, data, len, format);
+ if (f)
+ ast_free(f);
+ if (data)
+ ast_free(data);
+ return NULL;
+ }
+ memcpy(data+head, start, len);
+ f->data = data;
+ f->mallocd = AST_MALLOCD_DATA | AST_MALLOCD_HDR;
+ //f->has_timing_info = 1;
+ //f->ts = ast_tvdiff_ms(ast_tvnow(), out->ts);
+ f->datalen = len+head;
+ f->frametype = AST_FRAME_VIDEO;
+ f->subclass = format;
+ f->samples = 0;
+ f->offset = 0;
+ f->src = "Console";
+ f->delivery.tv_sec = 0;
+ f->delivery.tv_usec = 0;
+ f->seqno = 0;
+ AST_LIST_NEXT(f, frame_list) = NULL;
+
+ if (prev)
+ AST_LIST_NEXT(prev, frame_list) = f;
+
+ return f;
+}
+
+
/*----- codec specific code --------*/
/*
* For each codec, we define the various callback in use
*/
+/*! \brief initialization of h263p */
static int h263p_enc_init(struct video_out_desc *v)
{
/* modes supported are
@@ -340,20 +388,21 @@
/*
- * Create RTP/H.263 fragmets to avoid IP fragmentation
+ * Create RTP/H.263 fragments to avoid IP fragmentation
*/
static struct ast_frame *h263p_encap(struct video_out_desc *out,
- int len, struct ast_frame **tail)
+ struct ast_frame **tail)
{
struct ast_frame *cur = NULL, *first = NULL;
uint8_t *d = out->enc_out.data;
+ int len = out->enc_out.used;
int l = len; /* size of the current fragment. If 0, must look for a psc */
int frags = 0;
for (;len > 0; len -= l, d += l) {
uint8_t *data;
struct ast_frame *f;
- int i;
+ int i, h;
if (len >= 3 && d[0] == 0 && d[1] == 0 && d[2] >= 0x80) {
/* we are starting a new block, so look for a PSC. */
@@ -371,46 +420,27 @@
ast_log(LOG_WARNING, "--- frame error l %d\n", l);
break;
}
- f = ast_calloc(1, sizeof(*f));
- data = ast_calloc(1, l+2); /* 2 extra bytes for header */
- if (f == NULL || data == NULL) {
- ast_log(LOG_WARNING, "--- frame error f %p d %p l %d\n",
- f, d, l);
- if (f)
- ast_free(f);
- if (data)
- ast_free(data);
+
+ if (d[0] == 0 && d[1] == 0) { /* we start with a psc */
+ h = 0;
+ } else { /* no psc, create a header */
+ h = 2;
+ }
+
+ f = create_video_frame(d, d+l, AST_FORMAT_H263_PLUS, h, cur);
+ if (!f)
break;
+
+ data = f->data;
+ if (h == 0) { /* we start with a psc */
+ data[0] |= 0x04; // set P == 1, and we are done
+ } else { /* no psc, create a header */
+ data[0] = data[1] = 0; // P == 0
}
- f->mallocd = AST_MALLOCD_DATA | AST_MALLOCD_HDR;
- f->data = data;
-
- if (d[0] == 0 && d[1] == 0) { /* we start with a psc */
- memcpy(data, d, l);
- data[0] |= 0x04; // set P == 1, and we are done
- f->datalen = l;
- } else { /* no psc, create a header */
- data[0] = data[1] = 0; // P == 0
- memcpy(data + 2, d, l);
- f->datalen = l + 2;
- }
-
- //f->has_timing_info = 1;
- //f->ts = ast_tvdiff_ms(ast_tvnow(), out->ts);
- f->frametype = AST_FRAME_VIDEO;
- f->subclass = CONSOLE_FORMAT_VIDEO;
- f->samples = 0;
- f->offset = 0;
- f->src = "Console";
- f->delivery.tv_sec = 0;
- f->delivery.tv_usec = 0;
- f->seqno = ++(out->lasttxframe);
- AST_LIST_NEXT(f, frame_list) = NULL;
-
- if (cur)
- AST_LIST_NEXT(cur, frame_list) = f;
- else
+
+ if (!cur)
first = f;
+
cur = f;
frags++;
// ast_log(LOG_WARNING, "-- frag %d size %d left %d\n", frags, f->datalen, len - l);
@@ -495,12 +525,13 @@
}
/*
- * Decode a valid H.263 frame.
+ * Generic decoder, which is used by h263p, h263 and h261 as it simply
+ * invokes ffmpeg's decoder.
* av_parser_parse should merge a randomly chopped up stream into
* proper frames. After that, if we have a valid frame, we decode it
* until the entire frame is processed.
*/
-static int h263p_decode(struct video_in_desc *v, struct fbuf_t *b)
+static int ffmpeg_decode(struct video_in_desc *v, struct fbuf_t *b)
{
uint8_t *src = b->data;
int srclen = b->used;
@@ -523,6 +554,228 @@
}
return 1;
}
+
+struct video_codec_desc h263p_codec = {
+ .format = AST_FORMAT_H263_PLUS,
+ .enc_init = h263p_enc_init,
+ .enc_encap = h263p_encap,
+ .enc_run = NULL,
+ .dec_init = NULL,
+ .dec_decap = h263p_decap,
+ .dec_run = ffmpeg_decode
+};
+
+/*--- plain H263 support --------*/
+
+static int h263_enc_init(struct video_out_desc *v)
+{
+ /* XXX check whether these are supported */
+ v->enc_ctx->flags |= CODEC_FLAG_H263P_UMV;
+ v->enc_ctx->flags |= CODEC_FLAG_H263P_AIC;
+ v->enc_ctx->flags |= CODEC_FLAG_H263P_SLICE_STRUCT;
+ v->enc_ctx->flags |= CODEC_FLAG_AC_PRED;
+
+ v->enc_ctx->bit_rate = v->bitrate;
+ v->enc_ctx->gop_size = v->fps*5;
+ v->enc_ctx->qmin = 3;
+
+ return 0;
+}
+
+/*
+ * h263 encapsulation is specified in RFC2190. There are three modes
+ * defined (A, B, C), with 4, 8 and 12 bytes of header, respectively.
+ * The header is made as follows
+ * 0.....................|.......................|.............|....31
+ * F:1 P:1 SBIT:3 EBIT:3 SRC:3 I:1 U:1 S:1 A:1 R:4 DBQ:2 TRB:3 TR:8
+ * FP = 0- mode A, (only one word of header)
+ * FP = 10 mode B, and also means this is an I or P frame
+ * FP = 11 mode C, and also means this is a PB frame.
+ * SBIT, EBIT nuber of bits to ignore at beginning (msbits) and end (lsbits)
+ * SRC bits 6,7,8 from the h263 PTYPE field
+ * I = 0 intra-coded, 1 = inter-coded (bit 9 from PTYPE)
+ * U = 1 for Unrestricted Motion Vector (bit 10 from PTYPE)
+ * S = 1 for Syntax Based Arith coding (bit 11 from PTYPE)
+ * A = 1 for Advanced Prediction (bit 12 from PTYPE)
+ * R = reserved, must be 0
+ * DBQ = differential quantization, DBQUANT from h263, 0 unless we are using
+ * PB frames
+ * TRB = temporal reference for bframes, also 0 unless this is a PB frame
+ * TR = temporal reference for P frames, also 0 unless PB frame.
+ *
+ * Mode B and mode C description omitted.
+ *
+ * Note - Group Of Blocks (GOB) are byte-aligned, they start with
+ * PSC:22 0000 0000 0000 0000 1000 00 picture start code
+ * TR:8 .... .... temporal reference
+ * PTYPE:13 or more ptype...
+ * If we don't fragment a GOB SBIT and EBIT = 0.
+ * reference, 8 bit)
+ *
+ */
+static struct ast_frame *h263_encap(struct video_out_desc *out,
+ struct ast_frame **tail)
+{
+ uint8_t *nextgob = NULL;
+ uint8_t *d = out->enc_out.data;
+ uint8_t *end = d + out->enc_out.used;
+ struct ast_frame *f, *cur = NULL, *first = NULL;
+ const int pheader_len = 4; /* Use RFC-2190 Mode A */
+
+ for (; d != end; d = nextgob) {
+ uint8_t *data = NULL;
+ int l;
+ uint16_t test = 0xffff;
+
+ /* XXX double check this.
+ * look for two consecutive 0s and at least 5 bytes
+ * of data. On exit, nextgob points two bytes before
+ * the 00 marker, or right at the end of data.
+ */
+ for (nextgob = d; nextgob < end; nextgob++) {
+ test = (test << 8) | (*nextgob);
+ if (test == 0 && nextgob - d > 4) {
+ nextgob -= 3;
+ break;
+ }
+ }
+
+ /* We cannot split the block even if larger than MTU */
+ l = nextgob - d;
+
+ f = create_video_frame(d, d+l, AST_FORMAT_H263,
+ pheader_len, cur);
+
+ if (!f)
+ break;
+ data = f->data;
+
+ /* Now set the header bytes */
+ data[0] = 0; /* this is easy, F:1 P:1 SBIT:3 EBIT:3 */
+ /* ptype starts 30 bits in the GOB, so the first useful
+ * bit for us is bit 36 i.e. within d[4] (0 is the msbit).
+ * SRC = d[4] & 0x1c goes into data[1] & 0xe0
+ * I = d[4] & 0x02 goes into data[1] & 0x10
+ * U = d[4] & 0x01 goes into data[1] & 0x08
+ * S = d[5] & 0x80 goes into data[1] & 0x04
+ * A = d[5] & 0x40 goes into data[1] & 0x02
+ * R = 0 goes into data[1] & 0x01
+ * Optimizing it, we have
+ */
+ data[1] = /* SRC:3 I:1 U:1 S:1 A:1 R:1 */
+ ( (d[4] & 0x1f) << 3 ) | /* SRC, I, U */
+ ( (d[5] & 0xc0) >> 5 ); /* S, A, R */
+ /* in mode A, all other fields are 0 */
+ data[2] = 0;
+ data[3] = 0;
+ if (!cur)
+ first = f;
+ cur = f;
+ }
+
+ if (cur)
+ cur->subclass |= 1; // RTP Marker
+
+ *tail = cur;
+ return first;
+}
+
+/* XXX We only drop the header here, but maybe we need more. */
+static uint8_t *h263_decap(uint8_t *data, int *len)
+{
+ if (*len < 4) {
+ ast_log(LOG_WARNING, "invalid framesize %d\n", *len);
+ *len = 0;
+ return data;
+ }
+
+ if ( (data[0] & 0x80) == 0) {
+ *len -= 4;
+ return data+4;
+ } else {
+ ast_log(LOG_WARNING, "unsupported mode 0x%x\n",
+ data[0]);
+ }
+ return data;
+}
+
+struct video_codec_desc h263_codec = {
+ .format = AST_FORMAT_H263,
+ .enc_init = h263_enc_init,
+ .enc_encap = h263_encap,
+ .enc_run = NULL,
+ .dec_init = NULL,
+ .dec_decap = h263_decap,
+ .dec_run = ffmpeg_decode
+
+};
+
+/*---- plain H261 support -----*/
+static int h261_enc_init(struct video_out_desc *v)
+{
+ return 0;
+}
+
+static struct ast_frame *h261_encap(struct video_out_desc *out,
+ struct ast_frame **tail)
+{
+ struct ast_frame *f, *cur = NULL, *first = NULL;
+ uint8_t *d = out->enc_out.data;
+ int len = out->enc_out.used;
+ uint8_t *end = d+len;
+ int l;
+
+ for (; len > 0; len -= l, d +=l) {
+ uint8_t *iter;
+ uint8_t *data;
+ for (iter = d+2; iter < end-2; iter++)
+ if (iter[0] == 0x00 && iter[1] == 0x01)
+ break;
+
+ if (iter == end-2)
+ iter = end;
+ l = iter-d;
+ f = create_video_frame(d, iter, AST_FORMAT_H261, 4, cur);
+ if (!f)
+ break;
+
+ data = f->data;
+ data[0] = 0x03;
+
+ if (!cur)
+ first = f;
+
+ cur = f;
+ }
+
+ if (cur)
+ cur->subclass |= 1; // RTP Marker
+
+ *tail = cur;
+ return first;
+}
+
+static uint8_t *h261_decap(uint8_t *data, int *len)
+{
+ if (*len < 4) {
+ ast_log(LOG_WARNING, "invalid framesize %d\n", *len);
+ *len = 0;
+ return data;
+ }
+
+ *len -= 4;
+ return data+4;
+}
+
+struct video_codec_desc h261_codec = {
+ .format = AST_FORMAT_H261,
+ .enc_init = h261_enc_init,
+ .enc_encap = h261_encap,
+ .enc_run = NULL,
+ .dec_init = NULL,
+ .dec_decap = h261_decap,
+ .dec_run = ffmpeg_decode
+};
/*------ end codec specific code -----*/
@@ -739,6 +992,7 @@
{ AST_FORMAT_H263_PLUS, CODEC_ID_H263P, CM_WR },
{ AST_FORMAT_H263, CODEC_ID_H263, CM_RD },
{ AST_FORMAT_H263, CODEC_ID_H263, CM_WR },
+ { AST_FORMAT_H261, CODEC_ID_H261, CM_RDWD },
{ 0, 0, 0 },
};
@@ -752,6 +1006,32 @@
if (ast_format & i->ast_format && rw & i->rw && rw & i->rw)
return i->codec;
return CODEC_ID_NONE;
+}
+
+struct _config_map { /* map config format to asterisk format */
+ char *name;
+ struct video_codec_desc *codec;
+} str_format_map[] = {
+ { "h263+", &h263p_codec },
+ { "h263", &h263_codec },
+ { "h261", &h261_codec },
+ { NULL, NULL }
+};
+
+/*
+ * Map the codec name to the library. If not recognised, use a default.
+ */
+static struct video_codec_desc *map_config_video_format(char *name)
+{
+ int i;
+
+ for (i = 0; str_format_map[i].name != NULL; i++)
+ if(strcasecmp(name, str_format_map[i].name) == 0)
+ return str_format_map[i].codec;
+ ast_log(LOG_WARNING, "Cannot find codec for '%s', using '%s'\n",
+ name, str_format_map[0].name);
+ strcpy(name, str_format_map[0].name);
+ return str_format_map[i].codec;
}
/*! \brief uninitialize the descriptor for remote video stream */
@@ -848,11 +1128,12 @@
* - encbuf is used to store the encoded frame (to be sent)
* - mtu is used to determine the max size of video fragment
*/
-static int video_out_init(struct video_out_desc *v, uint32_t format)
+static int video_out_init(struct video_desc *env)
{
int codec;
int size;
struct fbuf_t *enc_in;
+ struct video_out_desc *v = &env->out;
v->enc_ctx = NULL;
v->codec = NULL;
@@ -864,7 +1145,7 @@
ast_log(LOG_WARNING, "No local source active\n");
return video_out_uninit(v);
}
- codec = map_video_format(format, CM_WR);
+ codec = map_video_format(env->current_codec->format, CM_WR);
v->codec = avcodec_find_encoder(codec);
if (!v->codec) {
ast_log(LOG_WARNING, "Cannot find the encoder for format %d\n",
@@ -912,7 +1193,7 @@
v->enc_ctx->rtp_payload_size = v->mtu / 2; // mtu/2
v->enc_ctx->bit_rate_tolerance = v->enc_ctx->bit_rate/2;
- h263p_enc_init(v);
+ env->current_codec->enc_init(v);
ast_log(LOG_WARNING, "w: %d h: %d fps: %d\n", v->w, v->h, v->fps);
v->enc_ctx->time_base = (AVRational){1, v->fps};
@@ -1081,6 +1362,7 @@
AVCodecContext *c = env->in.dec_ctx;
b_in = &env->in.dec_out;
b_in->pix_fmt = c->pix_fmt;
+ ast_log(LOG_WARNING, "dec_in pix_fmt %d %dx%d\n", c->pix_fmt, c->width, c->height);
b_in->w = c->width;
b_in->h = c->height;
@@ -1176,7 +1458,7 @@
return 0;
}
len = f->datalen;
- data = h263p_decap(f->data, &len);
+ data = env->current_codec->dec_decap(f->data, &len);
if (len < 1 || len > 128*1024) {
ast_log(LOG_WARNING, "--- huge frame %d\n", len);
v->discard = 1;
@@ -1206,7 +1488,7 @@
v->dec_in.used += len;
v->dec_in.data[v->dec_in.used] = '\0';
if (f->subclass & 0x01) { // RTP Marker
- if (h263p_decode(v, &v->dec_in)) {
+ if (env->current_codec->dec_run(v, &v->dec_in)) {
show_frame(env, 0);
v->completed = 0;
v->dec_in.used = 0;
@@ -1252,7 +1534,7 @@
if (!v->sendvideo)
return NULL;
b->used = avcodec_encode_video(v->enc_ctx, b->data, b->size, v->frame);
- return h263p_encap(v, b->used, tail);
+ return env->current_codec->enc_encap(v, tail);
}
/*
@@ -1362,8 +1644,10 @@
*/
avcodec_init();
avcodec_register_all();
+ env->current_codec = map_config_video_format(env->codec_name);
+
av_log_set_level(AV_LOG_ERROR); /* only report errors */
- if (video_in_init(&env->in, CONSOLE_FORMAT_VIDEO)) {
+ if (video_in_init(&env->in, env->current_codec->format)) {
/* This is not fatal, but we won't have incoming video */
ast_log(LOG_WARNING, "Cannot initialize input decoder - %s\n",
SDL_GetError());
@@ -1436,7 +1720,7 @@
*/
if (env->out.fd >= 0)
ast_channel_set_fd(owner, 1, env->out.fd);
- video_out_init(&env->out, CONSOLE_FORMAT_VIDEO);
+ video_out_init(env);
}
ast_pthread_create_background(&env->vthread, NULL, video_thread, env);
}
@@ -1463,6 +1747,7 @@
const char *var, const char *val)
{
struct video_desc *env;
+ char name[10];
M_START(var, val);
if (penv == NULL) {
@@ -1486,6 +1771,7 @@
M_UINT("videoheight", env->h)
M_UINT("fps", env->fps)
M_UINT("bitrate", env->bitrate)
+ M_STR("videoformat", name)
M_END(return 1;) /* the 'nothing found' case */
return 0; /* found something */
}
More information about the asterisk-commits
mailing list