Generalize the C-language Opus interface.

Switch to explicit channel mappings (RFC 7845) when creating
multi-stream Opus en/de-coders. The responsibility of setting up the
channel mappings will shift from WebRTC to the WebRTC user.

See https://webrtc-review.googlesource.com/c/src/+/121764 for the
current vision. See also the first child CL
https://webrtc-review.googlesource.com/c/src/+/129768
that sets up the Decoder to use this code.

Bug: webrtc:8649
Change-Id: I55959a293d54bb4c982eff68ec107c5ef8666c5c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/129767
Commit-Queue: Alex Loiko <aleloi@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27452}
This commit is contained in:
Alex Loiko
2019-04-03 15:12:01 +02:00
committed by Commit Bot
parent 21f6fd79ad
commit 50b8c399c9
5 changed files with 311 additions and 165 deletions

View File

@ -37,40 +37,6 @@ enum {
kWebRtcOpusDefaultFrameSize = 960,
};
int16_t GetSurroundParameters(int channels,
int *streams,
int *coupled_streams,
unsigned char *mapping) {
int opus_error;
int ret = 0;
// Use 'surround encoder create' to get values for 'coupled_streams',
// 'streams' and 'mapping'.
OpusMSEncoder* ms_encoder_ptr = opus_multistream_surround_encoder_create(
48000,
channels,
/* mapping family */ channels <= 2 ? 0 : 1,
streams,
coupled_streams,
mapping,
OPUS_APPLICATION_VOIP, // Application type shouldn't affect
// streams/mapping values.
&opus_error);
// This shouldn't fail; if it fails,
// signal an error and return invalid values.
if (opus_error != OPUS_OK || ms_encoder_ptr == NULL) {
ret = -1;
*streams = -1;
*coupled_streams = -1;
}
// We don't need the encoder.
if (ms_encoder_ptr != NULL) {
opus_multistream_encoder_destroy(ms_encoder_ptr);
}
return ret;
}
int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst,
size_t channels,
int32_t application) {
@ -93,30 +59,60 @@ int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst,
RTC_DCHECK(state);
int error;
if (channels <= 2) {
state->encoder.encoder = opus_encoder_create(48000, (int)channels, opus_app,
&error);
state->encoder = opus_encoder_create(48000, (int)channels, opus_app,
&error);
} else {
unsigned char mapping[255];
memset(mapping, 0, 255);
int streams = -1;
int coupled_streams = -1;
state->encoder.multistream_encoder =
opus_multistream_surround_encoder_create(
48000,
channels,
/* mapping family */ 1,
&streams,
&coupled_streams,
mapping,
opus_app,
&error);
if (error != OPUS_OK || (!state->encoder &&
!state->multistream_encoder)) {
WebRtcOpus_EncoderFree(state);
return -1;
}
if (error != OPUS_OK || (!state->encoder.encoder &&
!state->encoder.multistream_encoder)) {
state->in_dtx_mode = 0;
state->channels = channels;
*inst = state;
return 0;
}
int16_t WebRtcOpus_MultistreamEncoderCreate(
OpusEncInst** inst,
size_t channels,
int32_t application,
size_t coupled_streams,
const unsigned char *channel_mapping) {
int opus_app;
if (!inst)
return -1;
switch (application) {
case 0:
opus_app = OPUS_APPLICATION_VOIP;
break;
case 1:
opus_app = OPUS_APPLICATION_AUDIO;
break;
default:
return -1;
}
OpusEncInst* state = (OpusEncInst*)calloc(1, sizeof(OpusEncInst));
RTC_DCHECK(state);
int streams = channels - coupled_streams;
int error;
state->multistream_encoder =
opus_multistream_encoder_create(
48000,
channels,
streams,
coupled_streams,
channel_mapping,
opus_app,
&error);
if (error != OPUS_OK || (!state->encoder &&
!state->multistream_encoder)) {
WebRtcOpus_EncoderFree(state);
return -1;
}
@ -130,10 +126,10 @@ int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst,
int16_t WebRtcOpus_EncoderFree(OpusEncInst* inst) {
if (inst) {
if (inst->channels <= 2) {
opus_encoder_destroy(inst->encoder.encoder);
if (inst->encoder) {
opus_encoder_destroy(inst->encoder);
} else {
opus_multistream_encoder_destroy(inst->encoder.multistream_encoder);
opus_multistream_encoder_destroy(inst->multistream_encoder);
}
free(inst);
return 0;
@ -153,14 +149,14 @@ int WebRtcOpus_Encode(OpusEncInst* inst,
return -1;
}
if (inst->channels <= 2) {
res = opus_encode(inst->encoder.encoder,
if (inst->encoder) {
res = opus_encode(inst->encoder,
(const opus_int16*)audio_in,
(int)samples,
encoded,
(opus_int32)length_encoded_buffer);
} else {
res = opus_multistream_encode(inst->encoder.multistream_encoder,
res = opus_multistream_encode(inst->multistream_encoder,
(const opus_int16*)audio_in,
(int)samples,
encoded,
@ -187,10 +183,10 @@ int WebRtcOpus_Encode(OpusEncInst* inst,
return res;
}
#define ENCODER_CTL(inst, vargs) ( \
inst->channels <= 2 ? \
opus_encoder_ctl(inst->encoder.encoder, vargs) \
: opus_multistream_encoder_ctl(inst->encoder.multistream_encoder, vargs))
#define ENCODER_CTL(inst, vargs) ( \
inst->encoder ? \
opus_encoder_ctl(inst->encoder, vargs) \
: opus_multistream_encoder_ctl(inst->multistream_encoder, vargs))
int16_t WebRtcOpus_SetBitRate(OpusEncInst* inst, int32_t rate) {
@ -231,9 +227,9 @@ int16_t WebRtcOpus_SetMaxPlaybackRate(OpusEncInst* inst, int32_t frequency_hz) {
int16_t WebRtcOpus_GetMaxPlaybackRate(OpusEncInst* const inst,
int32_t* result_hz) {
if (inst->channels <= 2) {
if (inst->encoder) {
if (opus_encoder_ctl(
inst->encoder.encoder,
inst->encoder,
OPUS_GET_MAX_BANDWIDTH(result_hz)) == OPUS_OK) {
return 0;
}
@ -388,30 +384,10 @@ int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, size_t channels) {
return -1;
}
if (channels <= 2) {
state->decoder.decoder = opus_decoder_create(48000,
(int)channels, &error);
} else {
unsigned char mapping[255];
memset(mapping, 0, 255);
int streams = -1;
int coupled_streams = -1;
if (GetSurroundParameters(channels, &streams,
&coupled_streams, mapping) != 0) {
free(state);
return -1;
}
// Create new memory, always at 48000 Hz.
state->decoder.multistream_decoder = opus_multistream_decoder_create(
48000, (int)channels,
/* streams = */ streams,
/* coupled streams = */ coupled_streams,
mapping,
&error);
}
if (error == OPUS_OK && (state->decoder.decoder ||
state->decoder.multistream_decoder)) {
// Create new memory, always at 48000 Hz.
state->decoder = opus_decoder_create(48000,
(int)channels, &error);
if (error == OPUS_OK && state->decoder) {
// Creation of memory all ok.
state->channels = channels;
state->prev_decoded_samples = kWebRtcOpusDefaultFrameSize;
@ -421,23 +397,60 @@ int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, size_t channels) {
}
// If memory allocation was unsuccessful, free the entire state.
if (state->decoder.decoder) {
opus_decoder_destroy(state->decoder.decoder);
} else if (state->decoder.multistream_decoder) {
opus_multistream_decoder_destroy(state->decoder.multistream_decoder);
if (state->decoder) {
opus_decoder_destroy(state->decoder);
}
free(state);
}
return -1;
}
int16_t WebRtcOpus_MultistreamDecoderCreate(
OpusDecInst** inst, size_t channels,
size_t coupled_streams,
const unsigned char* channel_mapping) {
int error;
OpusDecInst* state;
if (inst != NULL) {
// Create Opus decoder state.
state = (OpusDecInst*) calloc(1, sizeof(OpusDecInst));
if (state == NULL) {
return -1;
}
int streams = channels - coupled_streams;
// Create new memory, always at 48000 Hz.
state->multistream_decoder = opus_multistream_decoder_create(
48000, (int)channels,
streams,
coupled_streams,
channel_mapping,
&error);
if (error == OPUS_OK && state->multistream_decoder) {
// Creation of memory all ok.
state->channels = channels;
state->prev_decoded_samples = kWebRtcOpusDefaultFrameSize;
state->in_dtx_mode = 0;
*inst = state;
return 0;
}
// If memory allocation was unsuccessful, free the entire state.
opus_multistream_decoder_destroy(state->multistream_decoder);
free(state);
}
return -1;
}
int16_t WebRtcOpus_DecoderFree(OpusDecInst* inst) {
if (inst) {
if (inst->channels <= 2) {
opus_decoder_destroy(inst->decoder.decoder);
} else if (inst->channels > 2) {
opus_multistream_decoder_destroy(inst->decoder.multistream_decoder);
if (inst->decoder) {
opus_decoder_destroy(inst->decoder);
} else if (inst->multistream_decoder) {
opus_multistream_decoder_destroy(inst->multistream_decoder);
}
free(inst);
return 0;
@ -451,10 +464,10 @@ size_t WebRtcOpus_DecoderChannels(OpusDecInst* inst) {
}
void WebRtcOpus_DecoderInit(OpusDecInst* inst) {
if (inst->channels <= 2) {
opus_decoder_ctl(inst->decoder.decoder, OPUS_RESET_STATE);
if (inst->decoder) {
opus_decoder_ctl(inst->decoder, OPUS_RESET_STATE);
} else {
opus_multistream_decoder_ctl(inst->decoder.multistream_decoder,
opus_multistream_decoder_ctl(inst->multistream_decoder,
OPUS_RESET_STATE);
}
inst->in_dtx_mode = 0;
@ -490,12 +503,12 @@ static int DecodeNative(OpusDecInst* inst, const uint8_t* encoded,
size_t encoded_bytes, int frame_size,
int16_t* decoded, int16_t* audio_type, int decode_fec) {
int res = -1;
if (inst->channels <= 2) {
res = opus_decode(inst->decoder.decoder, encoded, (opus_int32)encoded_bytes,
if (inst->decoder) {
res = opus_decode(inst->decoder, encoded, (opus_int32)encoded_bytes,
(opus_int16*)decoded, frame_size, decode_fec);
} else {
res = opus_multistream_decode(
inst->decoder.multistream_decoder, encoded, (opus_int32)encoded_bytes,
inst->multistream_decoder, encoded, (opus_int32)encoded_bytes,
(opus_int16*)decoded, frame_size, decode_fec);
}