--- asterisk-11.7.0.a/formats/format_g729.c 2012-04-29 21:50:57.000000000 +0200 +++ asterisk-11.7.0.b/formats/format_g729.c 2014-01-28 13:13:03.743740774 +0200 @@ -42,20 +42,34 @@ /* Portions of the conversion code are by guido@sienanet.it */ -#define BUF_SIZE 20 /* two G729 frames */ -#define G729A_SAMPLES 160 +#define G729A_FRAME_SIZE 10 +#define G729A_FRAME_SAMPLES 80 +#define G729A_CNG_SIZE 2 + +/* The original code assumed 20-byte frames, which isn't always true, consider + * for example a ptime=30, these are set up here to keep backwards compatible, + * and assumes two frames per packet as per previous. */ +#define G729A_BUF_SIZE (G729A_FRAME_SIZE * 2) +#define G729A_BUF_SAMPLES (G729A_FRAME_SAMPLES * 2) + +struct ast_g729_pvt +{ + int detected_vad:1; /* simple flag indicating whether or not we detected and reported on VAD on this stream yet or not */ + int warned_wrong_write_offset:1; /* Safety flag that if g729_write gets called and we're not on a G729A_FRAME_SIZE boundary, we can warn that there is a bug, just once */ +}; static struct ast_frame *g729_read(struct ast_filestream *s, int *whennext) { + /* This assumes ptime=20, which is probably OK */ int res; /* Send a frame from the file to the appropriate channel */ s->fr.frametype = AST_FRAME_VOICE; ast_format_set(&s->fr.subclass.format, AST_FORMAT_G729A, 0); s->fr.mallocd = 0; - s->fr.samples = G729A_SAMPLES; - AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, BUF_SIZE); + s->fr.samples = G729A_BUF_SAMPLES; + AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, G729A_BUF_SIZE); if ((res = fread(s->fr.data.ptr, 1, s->fr.datalen, s->f)) != s->fr.datalen) { - if (res && (res != 10)) /* XXX what for ? */ + if (res && (res != G729A_FRAME_SIZE)) /* XXX what for ? */ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno)); return NULL; } @@ -65,7 +79,15 @@ static int g729_write(struct ast_filestream *fs, struct ast_frame *f) { - int res; + struct ast_g729_pvt* pvt = fs->_private; + int res, writelen = f->datalen; + int frame_has_cng = 0; + off_t position = ftello(fs->f); + + ast_debug(4, "filestream(filename=%s,pos=%ld), frame(datalen=%d,samples=%d,ts=%ld,len=%ld,seqno=%d)\n", + fs->filename, position, + f->datalen, f->samples, f->ts, f->len, f->seqno); + if (f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); return -1; @@ -74,14 +96,30 @@ ast_log(LOG_WARNING, "Asked to write non-G729 frame (%s)!\n", ast_getformatname(&f->subclass.format)); return -1; } - if (f->datalen % 10) { - ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 10\n", f->datalen); + if (f->datalen % G729A_FRAME_SIZE) { + if (f->datalen % G729A_FRAME_SIZE == G729A_CNG_SIZE) { + frame_has_cng = 1; + writelen -= G729A_CNG_SIZE; + if (!pvt->detected_vad) { + ast_log(LOG_WARNING, "Probable CNG parameter update detected, this is a bug in the sender who should not be sending CNG updates - try and switch off Voice Activity Detected (VAD) or Silence Suppression on the sender.\n"); + pvt->detected_vad = 1; + } + } else { + ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of %d\n", f->datalen, G729A_FRAME_SIZE); return -1; } - if ((res = fwrite(f->data.ptr, 1, f->datalen, fs->f)) != f->datalen) { - ast_log(LOG_WARNING, "Bad write (%d/10): %s\n", res, strerror(errno)); + } + if (!pvt->warned_wrong_write_offset && position % G729A_FRAME_SIZE) { + ast_log(LOG_WARNING, "Writing a G729A frame to the wrong offset. This is indicative of a bug, please contact jaco@uls.co.za for assistance.\n"); + pvt->warned_wrong_write_offset = 1; + } + if ((res = fwrite(f->data.ptr, 1, writelen, fs->f)) != writelen) { + ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", res, writelen, strerror(errno)); return -1; } + /* Pre-emptive seek G729A_CNG_SIZE bytes ahead in case of CNG */ + if (frame_has_cng) + fseeko(fs->f, G729A_CNG_SIZE, SEEK_CUR); return 0; } @@ -94,7 +132,8 @@ fseeko(fs->f, 0, SEEK_END); max = ftello(fs->f); - bytes = BUF_SIZE * (sample_offset / G729A_SAMPLES); + /* Sample offset does not need to be a multiple of G729A_FRAME_SAMPLES in case of CNG having being received, the destination seek point, however, does */ + bytes = sample_offset * G729A_FRAME_SIZE / G729A_FRAME_SAMPLES; if (whence == SEEK_SET) offset = bytes; else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) @@ -106,6 +145,13 @@ } /* protect against seeking beyond begining. */ offset = (offset < min)?min:offset; + ast_debug(4, "g729_seek(%s, %lu, %s), cur=%lu, offset=%lu\n", + fs->filename, sample_offset, whence == SEEK_SET ? "SEEK_SET" : + whence == SEEK_CUR ? "SEEK_CUR" : + whence == SEEK_FORCECUR ? "SEEK_FORCECUR" : + whence == SEEK_END ? "SEEK_END" : "SEEK_???", + cur, + offset); if (fseeko(fs->f, offset, SEEK_SET) < 0) return -1; return 0; @@ -131,7 +177,7 @@ static off_t g729_tell(struct ast_filestream *fs) { off_t offset = ftello(fs->f); - return (offset/BUF_SIZE)*G729A_SAMPLES; + return offset * G729A_FRAME_SAMPLES / G729A_FRAME_SIZE; } static struct ast_format_def g729_f = { @@ -142,7 +188,8 @@ .trunc = g729_trunc, .tell = g729_tell, .read = g729_read, - .buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET, + .buf_size = G729A_BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct ast_g729_pvt), }; static int load_module(void)