Skip to content

Commit

Permalink
Refactor multi-frame encoding to be non-recursive
Browse files Browse the repository at this point in the history
  • Loading branch information
jmvalin committed Dec 16, 2023
1 parent 00b37a6 commit 6e14492
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 112 deletions.
200 changes: 90 additions & 110 deletions src/opus_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -955,105 +955,6 @@ static int decide_dtx_mode(opus_int activity, /* indicates if this fr

#endif

static opus_int32 encode_multiframe_packet(OpusEncoder *st,
const opus_val16 *pcm,
int nb_frames,
int frame_size,
unsigned char *data,
opus_int32 out_data_bytes,
int to_celt,
int lsb_depth,
int float_api)
{
int i;
int ret = 0;
VARDECL(unsigned char, tmp_data);
int bak_mode, bak_bandwidth, bak_channels, bak_to_mono;
VARDECL(OpusRepacketizer, rp);
int max_header_bytes;
opus_int32 bytes_per_frame;
opus_int32 cbr_bytes;
opus_int32 repacketize_len;
int tmp_len;
ALLOC_STACK;

/* Worst cases:
* 2 frames: Code 2 with different compressed sizes
* >2 frames: Code 3 VBR */
max_header_bytes = nb_frames == 2 ? 3 : (2+(nb_frames-1)*2);

if (st->use_vbr || st->user_bitrate_bps==OPUS_BITRATE_MAX)
repacketize_len = out_data_bytes;
else {
cbr_bytes = 3*st->bitrate_bps/(3*8*st->Fs/(frame_size*nb_frames));
repacketize_len = IMIN(cbr_bytes, out_data_bytes);
}
bytes_per_frame = IMIN(1276, 1+(repacketize_len-max_header_bytes)/nb_frames);

ALLOC(tmp_data, nb_frames*bytes_per_frame, unsigned char);
ALLOC(rp, 1, OpusRepacketizer);
opus_repacketizer_init(rp);

bak_mode = st->user_forced_mode;
bak_bandwidth = st->user_bandwidth;
bak_channels = st->force_channels;

st->user_forced_mode = st->mode;
st->user_bandwidth = st->bandwidth;
st->force_channels = st->stream_channels;

bak_to_mono = st->silk_mode.toMono;
if (bak_to_mono)
st->force_channels = 1;
else
st->prev_channels = st->stream_channels;

for (i=0;i<nb_frames;i++)
{
int first_frame = i == 0;
st->silk_mode.toMono = 0;
st->nonfinal_frame = i<(nb_frames-1);

/* When switching from SILK/Hybrid to CELT, only ask for a switch at the last frame */
if (to_celt && i==nb_frames-1)
st->user_forced_mode = MODE_CELT_ONLY;

tmp_len = opus_encode_native(st, pcm+i*(st->channels*frame_size), frame_size,
tmp_data+i*bytes_per_frame, bytes_per_frame, lsb_depth, NULL, 0, 0, 0, 0,
NULL, float_api, first_frame);

if (tmp_len<0)
{
RESTORE_STACK;
return OPUS_INTERNAL_ERROR;
}

ret = opus_repacketizer_cat(rp, tmp_data+i*bytes_per_frame, tmp_len);

if (ret<0)
{
RESTORE_STACK;
return OPUS_INTERNAL_ERROR;
}
}

ret = opus_repacketizer_out_range_impl(rp, 0, nb_frames, data, repacketize_len, 0, !st->use_vbr, NULL, 0);
if (ret<0)
{
RESTORE_STACK;
return OPUS_INTERNAL_ERROR;
}

/* Discard configs that were forced locally for the purpose of repacketization */
st->user_forced_mode = bak_mode;
st->user_bandwidth = bak_bandwidth;
st->force_channels = bak_channels;
st->silk_mode.toMono = bak_to_mono;

RESTORE_STACK;
return ret;
}

static int compute_redundancy_bytes(opus_int32 max_data_bytes, opus_int32 bitrate_bps, int frame_rate, int channels)
{
int redundancy_bytes_cap;
Expand Down Expand Up @@ -1097,7 +998,7 @@ static opus_int32 opus_encode_native_process(OpusEncoder *st, const opus_val16 *
opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
unsigned char *data, opus_int32 out_data_bytes, int lsb_depth,
const void *analysis_pcm, opus_int32 analysis_size, int c1, int c2,
int analysis_channels, downmix_func downmix, int float_api, int first_frame)
int analysis_channels, downmix_func downmix, int float_api)
{
void *silk_enc;
CELTEncoder *celt_enc;
Expand All @@ -1106,7 +1007,6 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_
int prefill=0;
int redundancy = 0;
int celt_to_silk = 0;
VARDECL(opus_val16, pcm_buf);
int to_celt = 0;
int voice_est; /* Probability of voice in Q7 */
opus_int32 equiv_rate;
Expand All @@ -1129,8 +1029,6 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_
#endif
opus_int activity = VAD_NO_DECISION;

VARDECL(opus_val16, tmp_prefill);

ALLOC_STACK;

max_data_bytes = IMIN(1276, out_data_bytes);
Expand Down Expand Up @@ -1619,6 +1517,14 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_
{
int enc_frame_size;
int nb_frames;
VARDECL(unsigned char, tmp_data);
VARDECL(OpusRepacketizer, rp);
int max_header_bytes;
opus_int32 bytes_per_frame;
opus_int32 cbr_bytes;
opus_int32 repacketize_len;
int tmp_len;
ALLOC_STACK;

if (st->mode == MODE_SILK_ONLY)
{
Expand All @@ -1637,18 +1543,90 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_
#ifndef DISABLE_FLOAT_API
if (analysis_read_pos_bak!= -1)
{
/* Reset analysis position to the beginning of the first frame so we
can use it one frame at a time. */
st->analysis.read_pos = analysis_read_pos_bak;
st->analysis.read_subframe = analysis_read_subframe_bak;
}
#endif

ret = encode_multiframe_packet(st, pcm, nb_frames, enc_frame_size, data,
out_data_bytes, to_celt, lsb_depth, float_api);
/* Worst cases:
* 2 frames: Code 2 with different compressed sizes
* >2 frames: Code 3 VBR */
max_header_bytes = nb_frames == 2 ? 3 : (2+(nb_frames-1)*2);

if (st->use_vbr || st->user_bitrate_bps==OPUS_BITRATE_MAX)
repacketize_len = out_data_bytes;
else {
cbr_bytes = 3*st->bitrate_bps/(3*8*st->Fs/(enc_frame_size*nb_frames));
repacketize_len = IMIN(cbr_bytes, out_data_bytes);
}
bytes_per_frame = IMIN(1276, 1+(repacketize_len-max_header_bytes)/nb_frames);

ALLOC(tmp_data, nb_frames*bytes_per_frame, unsigned char);
ALLOC(rp, 1, OpusRepacketizer);
opus_repacketizer_init(rp);


int bak_to_mono = st->silk_mode.toMono;
if (bak_to_mono)
st->force_channels = 1;
else
st->prev_channels = st->stream_channels;

for (i=0;i<nb_frames;i++)
{
int first_frame = i == 0;
int frame_to_celt;
int frame_redundancy;
st->silk_mode.toMono = 0;
st->nonfinal_frame = i<(nb_frames-1);

/* When switching from SILK/Hybrid to CELT, only ask for a switch at the last frame */
frame_to_celt = to_celt && i==nb_frames-1;
frame_redundancy = redundancy && (frame_to_celt || (!to_celt && i==0));

#ifndef DISABLE_FLOAT_API
/* Get analysis for current frame. */
tonality_get_info(&st->analysis, &analysis_info, enc_frame_size);
#endif

tmp_len = opus_encode_native_process(st, pcm+i*(st->channels*enc_frame_size), enc_frame_size, tmp_data+i*bytes_per_frame, float_api, first_frame,
#ifdef ENABLE_DRED
dred_bitrate_bps,
#endif
#ifndef DISABLE_FLOAT_API
&analysis_info,
is_silence,
#endif
frame_redundancy, celt_to_silk, prefill, bytes_per_frame,
total_buffer, activity, equiv_rate, frame_to_celt
);

if (tmp_len<0)
{
RESTORE_STACK;
return OPUS_INTERNAL_ERROR;
}

ret = opus_repacketizer_cat(rp, tmp_data+i*bytes_per_frame, tmp_len);

if (ret<0)
{
RESTORE_STACK;
return OPUS_INTERNAL_ERROR;
}
}
ret = opus_repacketizer_out_range_impl(rp, 0, nb_frames, data, repacketize_len, 0, !st->use_vbr, NULL, 0);
if (ret<0)
{
ret = OPUS_INTERNAL_ERROR;
}
st->silk_mode.toMono = bak_to_mono;
RESTORE_STACK;
return ret;
} else {
ret = opus_encode_native_process(st, pcm, frame_size, data, float_api, first_frame,
ret = opus_encode_native_process(st, pcm, frame_size, data, float_api, 1,
#ifdef ENABLE_DRED
dred_bitrate_bps,
#endif
Expand Down Expand Up @@ -1694,6 +1672,8 @@ static opus_int32 opus_encode_native_process(OpusEncoder *st, const opus_val16 *
int apply_padding;
int frame_rate;
int curr_bandwidth;
VARDECL(opus_val16, pcm_buf);
VARDECL(opus_val16, tmp_prefill);

silk_enc = (char*)st+st->silk_enc_offset;
celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset);
Expand Down Expand Up @@ -2388,7 +2368,7 @@ opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int analysis_fra
for (i=0;i<frame_size*st->channels;i++)
in[i] = FLOAT2INT16(pcm[i]);
ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, 16,
pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1, 1);
pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1);
RESTORE_STACK;
return ret;
}
Expand All @@ -2400,7 +2380,7 @@ opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_fram
int frame_size;
frame_size = frame_size_select(analysis_frame_size, st->variable_duration, st->Fs);
return opus_encode_native(st, pcm, frame_size, data, out_data_bytes, 16,
pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 0, 1);
pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 0);
}

#else
Expand All @@ -2423,7 +2403,7 @@ opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_fram
for (i=0;i<frame_size*st->channels;i++)
in[i] = (1.0f/32768)*pcm[i];
ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, 16,
pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 0, 1);
pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 0);
RESTORE_STACK;
return ret;
}
Expand All @@ -2433,7 +2413,7 @@ opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int analysis_fra
int frame_size;
frame_size = frame_size_select(analysis_frame_size, st->variable_duration, st->Fs);
return opus_encode_native(st, pcm, frame_size, data, out_data_bytes, 24,
pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1, 1);
pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1);
}
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/opus_multistream_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@ int opus_multistream_encode_native
if (!vbr && s == st->layout.nb_streams-1)
opus_encoder_ctl(enc, OPUS_SET_BITRATE(curr_max*(8*Fs/frame_size)));
len = opus_encode_native(enc, buf, frame_size, tmp_data, curr_max, lsb_depth,
pcm, analysis_frame_size, c1, c2, st->layout.nb_channels, downmix, float_api, 1);
pcm, analysis_frame_size, c1, c2, st->layout.nb_channels, downmix, float_api);
if (len<0)
{
RESTORE_STACK;
Expand Down
2 changes: 1 addition & 1 deletion src/opus_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ opus_int32 frame_size_select(opus_int32 frame_size, int variable_duration, opus_
opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_size,
unsigned char *data, opus_int32 out_data_bytes, int lsb_depth,
const void *analysis_pcm, opus_int32 analysis_size, int c1, int c2,
int analysis_channels, downmix_func downmix, int float_api, int first_frame);
int analysis_channels, downmix_func downmix, int float_api);

int opus_decode_native(OpusDecoder *st, const unsigned char *data, opus_int32 len,
opus_val16 *pcm, int frame_size, int decode_fec, int self_delimited,
Expand Down

0 comments on commit 6e14492

Please sign in to comment.