Commit 7f354594 authored by jehan's avatar jehan

add better supports of ptime for opus:

Benefits: 
- support for ptime 10
- avoid wasting bandwidth sending LBRR and original frames in the same packets
- more efficient FEC, for example a 120ms packet contains 60ms useful LBRR versus 20ms before

Thanks to Julien C
parent 40fb0cfc
......@@ -35,9 +35,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define SIGNAL_SAMPLE_SIZE 2 // 2 bytes per sample
/* Define codec specific settings */
#define FRAME_LENGTH 20 // ptime may be 20, 40, 60, 80, 100 or 120, packets composed of multiples 20ms frames
#define MAX_BYTES_PER_FRAME 500 // Equals peak bitrate of 200 kbps
#define MAX_INPUT_FRAMES 6
#define MAX_BYTES_PER_MS 25 // Equals peak bitrate of 200 kbps
#define MAX_INPUT_FRAMES 5 // The maximum amount of Opus frames in a packet we are using
/**
......@@ -47,6 +46,8 @@ typedef struct _OpusEncData {
OpusEncoder *state;
MSBufferizer *bufferizer;
uint32_t ts;
uint8_t *pcmbuffer;
int pcmbufsize;
int samplerate;
int channels;
int application;
......@@ -172,77 +173,141 @@ static void ms_opus_enc_preprocess(MSFilter *f) {
static void ms_opus_enc_process(MSFilter *f) {
OpusEncData *d = (OpusEncData *)f->data;
mblk_t *im;
OpusRepacketizer *repacketizer = NULL;
mblk_t *om = NULL;
int packet_size, pcm_buffer_size;
int max_frame_byte_size, ptime = 20;
int frame_count = 0, frame_size = 0;
opus_int32 total_length = 0;
uint8_t *repacketizer_frame_buffer[MAX_INPUT_FRAMES];
int i;
int frameNumber, packet_size;
uint8_t *signalFrameBuffer = NULL;
uint8_t *codedFrameBuffer[MAX_INPUT_FRAMES];
OpusRepacketizer *rp = opus_repacketizer_create();
opus_int32 ret = 0;
opus_int32 totalLength = 0;
int frame_size = d->samplerate * FRAME_LENGTH / 1000; /* in samples */
// lock the access while getting ptime
ms_filter_lock(f);
frameNumber = d->ptime/FRAME_LENGTH; /* encode 20ms frames, ptime is a multiple of 20ms */
packet_size = d->samplerate * d->ptime / 1000; /* in samples */
ptime = d->ptime;
packet_size = d->samplerate * ptime / 1000; /* in samples */
ms_filter_unlock(f);
switch (ptime) {
case 10:
frame_size = d->samplerate * 10 / 1000;
frame_count = 1;
break;
case 20:
frame_size = d->samplerate * 20 / 1000;
frame_count = 1;
break;
case 40:
frame_size = d->samplerate * 40 / 1000;
frame_count = 1;
break;
case 60:
frame_size = d->samplerate * 60 / 1000;
frame_count = 1;
break;
case 80:
frame_size = d->samplerate * 40 / 1000;
frame_count = 2;
break;
case 100:
frame_size = d->samplerate * 20 / 1000;
frame_count = 5;
break;
case 120:
frame_size = d->samplerate * 60 / 1000;
frame_count = 2;
break;
default:
frame_size = d->samplerate * 20 / 1000;
frame_count = 1;
}
max_frame_byte_size = MAX_BYTES_PER_MS * ptime/frame_count;
while ((im = ms_queue_get(f->inputs[0])) != NULL) {
ms_bufferizer_put(d->bufferizer, im);
pcm_buffer_size = d->channels * frame_size * SIGNAL_SAMPLE_SIZE;
if (pcm_buffer_size > d->pcmbufsize){
if (d->pcmbuffer) ms_free(d->pcmbuffer);
d->pcmbuffer = ms_malloc(pcm_buffer_size);
d->pcmbufsize = pcm_buffer_size;
}
for (i=0; i<MAX_INPUT_FRAMES; i++) {
codedFrameBuffer[i]=NULL;
repacketizer_frame_buffer[i]=NULL;
}
ms_bufferizer_put_from_queue(d->bufferizer, f->inputs[0]);
while (ms_bufferizer_get_avail(d->bufferizer) >= (d->channels * packet_size * SIGNAL_SAMPLE_SIZE)) {
totalLength = 0;
opus_repacketizer_init(rp);
for (i=0; i<frameNumber; i++) { /* encode 20ms by 20ms and repacketize all of them together */
if (!codedFrameBuffer[i]) codedFrameBuffer[i] = ms_malloc(MAX_BYTES_PER_FRAME); /* the repacketizer need the pointer to packet to remain valid, so we shall have a buffer for each coded frame */
if (!signalFrameBuffer) signalFrameBuffer = ms_malloc(frame_size * SIGNAL_SAMPLE_SIZE * d->channels);
ms_bufferizer_read(d->bufferizer, signalFrameBuffer, frame_size * SIGNAL_SAMPLE_SIZE * d->channels);
ret = opus_encode(d->state, (opus_int16 *)signalFrameBuffer, frame_size, codedFrameBuffer[i], MAX_BYTES_PER_FRAME);
opus_int32 ret = 0;
if (frame_count == 1) { /* One Opus frame, not using the repacketizer */
om = allocb(max_frame_byte_size, 0);
ms_bufferizer_read(d->bufferizer, d->pcmbuffer, frame_size * SIGNAL_SAMPLE_SIZE * d->channels);
ret = opus_encode(d->state, (opus_int16 *)d->pcmbuffer, frame_size, om->b_wptr, max_frame_byte_size);
if (ret < 0) {
freemsg(om);
om=NULL;
ms_error("Opus encoder error: %s", opus_strerror(ret));
break;
} else {
total_length = ret;
om->b_wptr += total_length;
}
} else if(frame_count > 1) { /* We have multiple Opus frames we will use the opus repacketizer */
repacketizer = opus_repacketizer_create();
opus_repacketizer_init(repacketizer);
/* Do not include FEC/LBRR in any frame after the first one since it will be sent with the previous one */
ret = opus_encoder_ctl(d->state, OPUS_SET_INBAND_FEC(0));
if (ret != OPUS_OK) {
ms_error("could not set inband FEC to opus encoder: %s", opus_strerror(ret));
}
if (ret > 0) {
int err = opus_repacketizer_cat(rp, codedFrameBuffer[i], ret); /* add the encoded frame into the current packet */
if (err != OPUS_OK) {
ms_error("Opus repacketizer error: %s", opus_strerror(err));
for (i=0; i<frame_count; i++) {
if(frame_count == i+1){ /* if configured, reactivate FEC on the last frame to tell the encoder he should restart saving LBRR frames */
ret = opus_encoder_ctl(d->state, OPUS_SET_INBAND_FEC(d->useinbandfec));
if (ret != OPUS_OK) {
ms_error("could not set inband FEC to opus encoder: %s", opus_strerror(ret));
}
}
if (!repacketizer_frame_buffer[i]) repacketizer_frame_buffer[i] = ms_malloc(max_frame_byte_size); /* the repacketizer need the pointer to packet to remain valid, so we shall have a buffer for each coded frame */
ms_bufferizer_read(d->bufferizer, d->pcmbuffer, frame_size * SIGNAL_SAMPLE_SIZE * d->channels);
ret = opus_encode(d->state, (opus_int16 *)d->pcmbuffer, frame_size, repacketizer_frame_buffer[i], max_frame_byte_size);
if (ret < 0) {
ms_error("Opus encoder error: %s", opus_strerror(ret));
break;
} else if (ret > 0) {
int err = opus_repacketizer_cat(repacketizer, repacketizer_frame_buffer[i], ret); /* add the encoded frame into the current packet */
if (err != OPUS_OK) {
ms_error("Opus repacketizer error: %s", opus_strerror(err));
break;
}
total_length += ret;
}
totalLength += ret;
}
}
if (ret > 0) {
om = allocb(totalLength+frameNumber + 1, 0); /* opus repacktizer API: allocate at leat number of frame + size of all data added before */
ret = opus_repacketizer_out(rp, om->b_wptr, totalLength+frameNumber);
om = allocb(total_length + frame_count + 1, 0); /* opus repacketizer API: allocate at least number of frame + size of all data added before */
ret = opus_repacketizer_out(repacketizer, om->b_wptr, total_length+frame_count);
if(ret < 0){
freemsg(om);
om=NULL;
ms_error("Opus repacketizer out error: %s", opus_strerror(ret));
} else {
om->b_wptr += ret;
}
opus_repacketizer_destroy(repacketizer);
for (i=0; i<frame_count; i++) {
if (repacketizer_frame_buffer[i] != NULL) {
ms_free(repacketizer_frame_buffer[i]);
}
}
}
om->b_wptr += ret;
if(om) { /* we have an encoded output message */
mblk_set_timestamp_info(om, d->ts);
ms_bufferizer_fill_current_metas(d->bufferizer, om);
ms_queue_put(f->outputs[0], om);
d->ts += packet_size*48000/d->samplerate; /* RFC payload RTP opus 03 - section 4: RTP timestamp multiplier : WARNING works only with sr at 48000 */
ret = 0;
total_length = 0;
}
}
opus_repacketizer_destroy(rp);
if (signalFrameBuffer != NULL) {
ms_free(signalFrameBuffer);
}
for (i=0; i<frameNumber; i++) {
if (codedFrameBuffer[i] != NULL) {
ms_free(codedFrameBuffer[i]);
}
}
}
static void ms_opus_enc_postprocess(MSFilter *f) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment