translate: Fix transcoding while different in frame size.
When Asterisk translates between codecs, each with a different frame size (for example between iLBC 30 and Speex-WB), too large frames were created by ast_trans_frameout. Now, ast_trans_frameout is called with the correct frame length, creating several frames when necessary. Affects all transcoding modules which used ast_trans_frameout: GSM, iLBC, LPC10, and Speex. ASTERISK-25353 #close Change-Id: I2e229569d73191d66a4e43fef35432db24000212
This commit is contained in:
parent
229b95d253
commit
077adf48b8
|
@ -39,6 +39,7 @@ ASTERISK_REGISTER_FILE()
|
|||
#include "asterisk/config.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
|
||||
#ifdef HAVE_GSM_HEADER
|
||||
#include "gsm.h"
|
||||
|
@ -139,25 +140,35 @@ static int lintogsm_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
|
|||
static struct ast_frame *lintogsm_frameout(struct ast_trans_pvt *pvt)
|
||||
{
|
||||
struct gsm_translator_pvt *tmp = pvt->pvt;
|
||||
int datalen = 0;
|
||||
int samples = 0;
|
||||
struct ast_frame *result = NULL;
|
||||
struct ast_frame *last = NULL;
|
||||
int samples = 0; /* output samples */
|
||||
|
||||
/* We can't work on anything less than a frame in size */
|
||||
if (pvt->samples < GSM_SAMPLES)
|
||||
return NULL;
|
||||
while (pvt->samples >= GSM_SAMPLES) {
|
||||
struct ast_frame *current;
|
||||
|
||||
/* Encode a frame of data */
|
||||
gsm_encode(tmp->gsm, tmp->buf + samples, (gsm_byte *) pvt->outbuf.c + datalen);
|
||||
datalen += GSM_FRAME_LEN;
|
||||
gsm_encode(tmp->gsm, tmp->buf + samples, (gsm_byte *) pvt->outbuf.c);
|
||||
samples += GSM_SAMPLES;
|
||||
pvt->samples -= GSM_SAMPLES;
|
||||
|
||||
current = ast_trans_frameout(pvt, GSM_FRAME_LEN, GSM_SAMPLES);
|
||||
if (!current) {
|
||||
continue;
|
||||
} else if (last) {
|
||||
AST_LIST_NEXT(last, frame_list) = current;
|
||||
} else {
|
||||
result = current;
|
||||
}
|
||||
last = current;
|
||||
}
|
||||
|
||||
/* Move the data at the end of the buffer to the front */
|
||||
if (pvt->samples)
|
||||
if (samples) {
|
||||
memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
|
||||
}
|
||||
|
||||
return ast_trans_frameout(pvt, datalen, samples);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void gsm_destroy_stuff(struct ast_trans_pvt *pvt)
|
||||
|
|
|
@ -37,6 +37,7 @@ ASTERISK_REGISTER_FILE()
|
|||
#include "asterisk/translate.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
|
||||
#ifdef ILBC_WEBRTC
|
||||
#include <ilbc.h>
|
||||
|
@ -150,31 +151,40 @@ static int lintoilbc_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
|
|||
static struct ast_frame *lintoilbc_frameout(struct ast_trans_pvt *pvt)
|
||||
{
|
||||
struct ilbc_coder_pvt *tmp = pvt->pvt;
|
||||
int datalen = 0;
|
||||
int samples = 0;
|
||||
struct ast_frame *result = NULL;
|
||||
struct ast_frame *last = NULL;
|
||||
int samples = 0; /* output samples */
|
||||
|
||||
/* We can't work on anything less than a frame in size */
|
||||
if (pvt->samples < ILBC_SAMPLES)
|
||||
return NULL;
|
||||
while (pvt->samples >= ILBC_SAMPLES) {
|
||||
struct ast_frame *current;
|
||||
ilbc_block tmpf[ILBC_SAMPLES];
|
||||
int i;
|
||||
|
||||
/* Encode a frame of data */
|
||||
for (i = 0 ; i < ILBC_SAMPLES ; i++)
|
||||
tmpf[i] = tmp->buf[samples + i];
|
||||
iLBC_encode( (ilbc_bytes*)pvt->outbuf.BUF_TYPE + datalen, tmpf, &tmp->enc);
|
||||
iLBC_encode((ilbc_bytes *) pvt->outbuf.BUF_TYPE, tmpf, &tmp->enc);
|
||||
|
||||
datalen += ILBC_FRAME_LEN;
|
||||
samples += ILBC_SAMPLES;
|
||||
pvt->samples -= ILBC_SAMPLES;
|
||||
|
||||
current = ast_trans_frameout(pvt, ILBC_FRAME_LEN, ILBC_SAMPLES);
|
||||
if (!current) {
|
||||
continue;
|
||||
} else if (last) {
|
||||
AST_LIST_NEXT(last, frame_list) = current;
|
||||
} else {
|
||||
result = current;
|
||||
}
|
||||
last = current;
|
||||
}
|
||||
|
||||
/* Move the data at the end of the buffer to the front */
|
||||
if (pvt->samples)
|
||||
if (samples) {
|
||||
memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
|
||||
}
|
||||
|
||||
return ast_trans_frameout(pvt, datalen, samples);
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct ast_translator ilbctolin = {
|
||||
|
|
|
@ -39,6 +39,7 @@ ASTERISK_REGISTER_FILE()
|
|||
#include "asterisk/config.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
|
||||
#include "lpc10/lpc10.h"
|
||||
|
||||
|
@ -160,31 +161,45 @@ static int lintolpc10_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
|
|||
static struct ast_frame *lintolpc10_frameout(struct ast_trans_pvt *pvt)
|
||||
{
|
||||
struct lpc10_coder_pvt *tmp = pvt->pvt;
|
||||
int x;
|
||||
int datalen = 0; /* output frame */
|
||||
int samples = 0; /* output samples */
|
||||
float tmpbuf[LPC10_SAMPLES_PER_FRAME];
|
||||
INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME]; /* XXX what ??? */
|
||||
/* We can't work on anything less than a frame in size */
|
||||
if (pvt->samples < LPC10_SAMPLES_PER_FRAME)
|
||||
return NULL;
|
||||
while (pvt->samples >= LPC10_SAMPLES_PER_FRAME) {
|
||||
struct ast_frame *result = NULL;
|
||||
struct ast_frame *last = NULL;
|
||||
int samples = 0; /* output samples */
|
||||
|
||||
while (pvt->samples >= LPC10_SAMPLES_PER_FRAME) {
|
||||
struct ast_frame *current;
|
||||
float tmpbuf[LPC10_SAMPLES_PER_FRAME];
|
||||
INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME]; /* XXX what ??? */
|
||||
int x;
|
||||
|
||||
/* Encode a frame of data */
|
||||
for (x=0;x<LPC10_SAMPLES_PER_FRAME;x++)
|
||||
tmpbuf[x] = (float)tmp->buf[x + samples] / 32768.0;
|
||||
lpc10_encode(tmpbuf, bits, tmp->lpc10.enc);
|
||||
build_bits(pvt->outbuf.uc + datalen, bits);
|
||||
datalen += LPC10_BYTES_IN_COMPRESSED_FRAME;
|
||||
build_bits(pvt->outbuf.uc, bits);
|
||||
|
||||
samples += LPC10_SAMPLES_PER_FRAME;
|
||||
pvt->samples -= LPC10_SAMPLES_PER_FRAME;
|
||||
/* Use one of the two left over bits to record if this is a 22 or 23 ms frame...
|
||||
important for IAX use */
|
||||
tmp->longer = 1 - tmp->longer;
|
||||
|
||||
current = ast_trans_frameout(pvt, LPC10_BYTES_IN_COMPRESSED_FRAME, LPC10_SAMPLES_PER_FRAME);
|
||||
if (!current) {
|
||||
continue;
|
||||
} else if (last) {
|
||||
AST_LIST_NEXT(last, frame_list) = current;
|
||||
} else {
|
||||
result = current;
|
||||
}
|
||||
last = current;
|
||||
}
|
||||
|
||||
/* Move the data at the end of the buffer to the front */
|
||||
if (pvt->samples)
|
||||
if (samples) {
|
||||
memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
|
||||
return ast_trans_frameout(pvt, datalen, samples);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ ASTERISK_REGISTER_FILE()
|
|||
#include "asterisk/module.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
|
||||
/* codec variables */
|
||||
static int quality = 3;
|
||||
|
@ -259,15 +261,16 @@ static int lintospeex_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
|
|||
static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
|
||||
{
|
||||
struct speex_coder_pvt *tmp = pvt->pvt;
|
||||
int is_speech=1;
|
||||
int datalen = 0; /* output bytes */
|
||||
int samples = 0; /* output samples */
|
||||
struct ast_frame *result = NULL;
|
||||
struct ast_frame *last = NULL;
|
||||
int samples = 0; /* output samples */
|
||||
|
||||
/* We can't work on anything less than a frame in size */
|
||||
if (pvt->samples < tmp->framesize)
|
||||
return NULL;
|
||||
speex_bits_reset(&tmp->bits);
|
||||
while (pvt->samples >= tmp->framesize) {
|
||||
struct ast_frame *current;
|
||||
int is_speech = 1;
|
||||
|
||||
speex_bits_reset(&tmp->bits);
|
||||
|
||||
#ifdef _SPEEX_TYPES_H
|
||||
/* Preprocess audio */
|
||||
if (preproc)
|
||||
|
@ -293,18 +296,18 @@ static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
|
|||
#endif
|
||||
samples += tmp->framesize;
|
||||
pvt->samples -= tmp->framesize;
|
||||
}
|
||||
|
||||
/* Move the data at the end of the buffer to the front */
|
||||
if (pvt->samples)
|
||||
memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
|
||||
/* Use AST_FRAME_CNG to signify the start of any silence period */
|
||||
if (is_speech) {
|
||||
int datalen = 0; /* output bytes */
|
||||
|
||||
/* Use AST_FRAME_CNG to signify the start of any silence period */
|
||||
if (is_speech) {
|
||||
tmp->silent_state = 0;
|
||||
} else {
|
||||
if (tmp->silent_state) {
|
||||
return NULL;
|
||||
tmp->silent_state = 0;
|
||||
/* Terminate bit stream */
|
||||
speex_bits_pack(&tmp->bits, 15, 5);
|
||||
datalen = speex_bits_write(&tmp->bits, pvt->outbuf.c, pvt->t->buf_size);
|
||||
current = ast_trans_frameout(pvt, datalen, tmp->framesize);
|
||||
} else if (tmp->silent_state) {
|
||||
current = NULL;
|
||||
} else {
|
||||
struct ast_frame frm = {
|
||||
.frametype = AST_FRAME_CNG,
|
||||
|
@ -320,14 +323,25 @@ static struct ast_frame *lintospeex_frameout(struct ast_trans_pvt *pvt)
|
|||
tmp->silent_state = 1;
|
||||
|
||||
/* XXX what now ? format etc... */
|
||||
return ast_frisolate(&frm);
|
||||
current = ast_frisolate(&frm);
|
||||
}
|
||||
|
||||
if (!current) {
|
||||
continue;
|
||||
} else if (last) {
|
||||
AST_LIST_NEXT(last, frame_list) = current;
|
||||
} else {
|
||||
result = current;
|
||||
}
|
||||
last = current;
|
||||
}
|
||||
|
||||
/* Terminate bit stream */
|
||||
speex_bits_pack(&tmp->bits, 15, 5);
|
||||
datalen = speex_bits_write(&tmp->bits, pvt->outbuf.c, pvt->t->buf_size);
|
||||
return ast_trans_frameout(pvt, datalen, samples);
|
||||
/* Move the data at the end of the buffer to the front */
|
||||
if (samples) {
|
||||
memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void speextolin_destroy(struct ast_trans_pvt *arg)
|
||||
|
|
|
@ -44,6 +44,7 @@ ASTERISK_REGISTER_FILE()
|
|||
#include "asterisk/cli.h"
|
||||
#include "asterisk/term.h"
|
||||
#include "asterisk/format.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
|
||||
/*! \todo
|
||||
* TODO: sample frames for each supported input format.
|
||||
|
@ -547,7 +548,12 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
|
|||
}
|
||||
delivery = f->delivery;
|
||||
for (out = f; out && p ; p = p->next) {
|
||||
framein(p, out);
|
||||
struct ast_frame *current = out;
|
||||
|
||||
do {
|
||||
framein(p, current);
|
||||
current = AST_LIST_NEXT(current, frame_list);
|
||||
} while (current);
|
||||
if (out != f) {
|
||||
ast_frfree(out);
|
||||
}
|
||||
|
@ -556,22 +562,33 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
|
|||
if (out) {
|
||||
/* we have a frame, play with times */
|
||||
if (!ast_tvzero(delivery)) {
|
||||
/* Regenerate prediction after a discontinuity */
|
||||
if (ast_tvzero(path->nextout)) {
|
||||
path->nextout = ast_tvnow();
|
||||
}
|
||||
struct ast_frame *current = out;
|
||||
|
||||
/* Use next predicted outgoing timestamp */
|
||||
out->delivery = path->nextout;
|
||||
do {
|
||||
/* Regenerate prediction after a discontinuity */
|
||||
if (ast_tvzero(path->nextout)) {
|
||||
path->nextout = ast_tvnow();
|
||||
}
|
||||
|
||||
/* Predict next outgoing timestamp from samples in this
|
||||
frame. */
|
||||
path->nextout = ast_tvadd(path->nextout, ast_samp2tv(
|
||||
out->samples, ast_format_get_sample_rate(out->subclass.format)));
|
||||
if (f->samples != out->samples && ast_test_flag(out, AST_FRFLAG_HAS_TIMING_INFO)) {
|
||||
ast_debug(4, "Sample size different %d vs %d\n", f->samples, out->samples);
|
||||
ast_clear_flag(out, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
}
|
||||
/* Use next predicted outgoing timestamp */
|
||||
current->delivery = path->nextout;
|
||||
|
||||
/* Invalidate prediction if we're entering a silence period */
|
||||
if (current->frametype == AST_FRAME_CNG) {
|
||||
path->nextout = ast_tv(0, 0);
|
||||
/* Predict next outgoing timestamp from samples in this
|
||||
frame. */
|
||||
} else {
|
||||
path->nextout = ast_tvadd(path->nextout, ast_samp2tv(
|
||||
current->samples, ast_format_get_sample_rate(current->subclass.format)));
|
||||
}
|
||||
|
||||
if (f->samples != current->samples && ast_test_flag(current, AST_FRFLAG_HAS_TIMING_INFO)) {
|
||||
ast_debug(4, "Sample size different %d vs %d\n", f->samples, current->samples);
|
||||
ast_clear_flag(current, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
}
|
||||
current = AST_LIST_NEXT(current, frame_list);
|
||||
} while (current);
|
||||
} else {
|
||||
out->delivery = ast_tv(0, 0);
|
||||
ast_set2_flag(out, has_timing_info, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
|
@ -580,10 +597,10 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
|
|||
out->len = len;
|
||||
out->seqno = seqno;
|
||||
}
|
||||
}
|
||||
/* Invalidate prediction if we're entering a silence period */
|
||||
if (out->frametype == AST_FRAME_CNG) {
|
||||
path->nextout = ast_tv(0, 0);
|
||||
/* Invalidate prediction if we're entering a silence period */
|
||||
if (out->frametype == AST_FRAME_CNG) {
|
||||
path->nextout = ast_tv(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (consume) {
|
||||
|
|
Loading…
Reference in New Issue