You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
5.5 KiB
161 lines
5.5 KiB
#include <stdlib.h> |
|
#include <string.h> |
|
#include "opus_codec.h" |
|
#include "opus.h" |
|
#include "debug_config.h" |
|
|
|
#define OPUS_DEFAULT_SAMPLE_RATE 48000 |
|
#define OPUS_DEFAULT_BITRATE 32000 |
|
#define OPUS_DEFAULT_COMPLEXITY 5 |
|
#define OPUS_DEFAULT_FRAME_MS 20 |
|
|
|
struct opus_codec_encoder { |
|
OpusEncoder* opus_enc; |
|
int sample_rate; |
|
int channels; |
|
int frame_samples; |
|
}; |
|
|
|
struct opus_codec_decoder { |
|
OpusDecoder* opus_dec; |
|
int sample_rate; |
|
int channels; |
|
}; |
|
|
|
static int frame_samples_for_rate(int sample_rate) { |
|
return sample_rate * OPUS_DEFAULT_FRAME_MS / 1000; |
|
} |
|
|
|
opus_codec_encoder_t* opus_codec_encoder_create(int sample_rate, int channels) { |
|
int err; |
|
opus_codec_encoder_t* enc; |
|
|
|
if (sample_rate <= 0) sample_rate = OPUS_DEFAULT_SAMPLE_RATE; |
|
if (channels <= 0 || channels > 2) channels = 1; |
|
|
|
enc = (opus_codec_encoder_t*)calloc(1, sizeof(*enc)); |
|
if (!enc) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "opus_codec_encoder_create: OOM"); |
|
return NULL; |
|
} |
|
|
|
enc->sample_rate = sample_rate; |
|
enc->channels = channels; |
|
enc->frame_samples = frame_samples_for_rate(sample_rate); |
|
|
|
enc->opus_enc = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_VOIP, &err); |
|
if (err != OPUS_OK) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "opus_codec_encoder_create: opus_encoder_create failed: %s (%d)", |
|
opus_codec_strerror(err), err); |
|
free(enc); |
|
return NULL; |
|
} |
|
|
|
opus_encoder_ctl(enc->opus_enc, OPUS_SET_BITRATE(OPUS_DEFAULT_BITRATE)); |
|
opus_encoder_ctl(enc->opus_enc, OPUS_SET_COMPLEXITY(OPUS_DEFAULT_COMPLEXITY)); |
|
opus_encoder_ctl(enc->opus_enc, OPUS_SET_SIGNAL(OPUS_AUTO)); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "opus_codec_encoder_create: ok rate=%d ch=%d frame_samples=%d bitrate=%d", |
|
sample_rate, channels, enc->frame_samples, OPUS_DEFAULT_BITRATE); |
|
return enc; |
|
} |
|
|
|
void opus_codec_encoder_destroy(opus_codec_encoder_t* enc) { |
|
if (!enc) return; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "opus_codec_encoder_destroy"); |
|
if (enc->opus_enc) opus_encoder_destroy(enc->opus_enc); |
|
free(enc); |
|
} |
|
|
|
int opus_codec_encode(opus_codec_encoder_t* enc, const int16_t* pcm, int frame_samples, |
|
uint8_t* out, int out_cap) { |
|
if (!enc || !pcm || !out || out_cap <= 0 || frame_samples <= 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "opus_codec_encode: invalid args"); |
|
return -1; |
|
} |
|
int result = opus_encode(enc->opus_enc, pcm, frame_samples, out, out_cap); |
|
if (result < 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "opus_codec_encode: encode failed: %s (%d), pcm_samples=%d cap=%d", |
|
opus_codec_strerror(result), result, frame_samples, out_cap); |
|
} |
|
return result; |
|
} |
|
|
|
opus_codec_decoder_t* opus_codec_decoder_create(int sample_rate, int channels) { |
|
int err; |
|
opus_codec_decoder_t* dec; |
|
|
|
if (sample_rate <= 0) sample_rate = OPUS_DEFAULT_SAMPLE_RATE; |
|
if (channels <= 0 || channels > 2) channels = 1; |
|
|
|
dec = (opus_codec_decoder_t*)calloc(1, sizeof(*dec)); |
|
if (!dec) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "opus_codec_decoder_create: OOM"); |
|
return NULL; |
|
} |
|
|
|
dec->sample_rate = sample_rate; |
|
dec->channels = channels; |
|
|
|
dec->opus_dec = opus_decoder_create(sample_rate, channels, &err); |
|
if (err != OPUS_OK) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "opus_codec_decoder_create: opus_decoder_create failed: %s (%d)", |
|
opus_codec_strerror(err), err); |
|
free(dec); |
|
return NULL; |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "opus_codec_decoder_create: ok rate=%d ch=%d", |
|
sample_rate, channels); |
|
return dec; |
|
} |
|
|
|
void opus_codec_decoder_destroy(opus_codec_decoder_t* dec) { |
|
if (!dec) return; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "opus_codec_decoder_destroy"); |
|
if (dec->opus_dec) opus_decoder_destroy(dec->opus_dec); |
|
free(dec); |
|
} |
|
|
|
int opus_codec_decode(opus_codec_decoder_t* dec, const uint8_t* data, int len, |
|
int16_t* pcm, int frame_samples) { |
|
if (!dec || !pcm || frame_samples <= 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "opus_codec_decode: invalid args"); |
|
return -1; |
|
} |
|
int result = opus_decode(dec->opus_dec, data, len, pcm, frame_samples, 0); |
|
if (result < 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "opus_codec_decode: decode failed: %s (%d), len=%d", |
|
opus_codec_strerror(result), result, len); |
|
} |
|
return result; |
|
} |
|
|
|
int opus_codec_encoder_bitrate_set(opus_codec_encoder_t* enc, int bitrate_bps) { |
|
if (!enc || !enc->opus_enc) return -1; |
|
return opus_encoder_ctl(enc->opus_enc, OPUS_SET_BITRATE(bitrate_bps)); |
|
} |
|
|
|
int opus_codec_encoder_complexity_set(opus_codec_encoder_t* enc, int complexity) { |
|
if (!enc || !enc->opus_enc) return -1; |
|
return opus_encoder_ctl(enc->opus_enc, OPUS_SET_COMPLEXITY(complexity)); |
|
} |
|
|
|
int opus_codec_encoder_signal_set(opus_codec_encoder_t* enc, int signal_type) { |
|
if (!enc || !enc->opus_enc) return -1; |
|
return opus_encoder_ctl(enc->opus_enc, OPUS_SET_SIGNAL(signal_type)); |
|
} |
|
|
|
const char* opus_codec_strerror(int err) { |
|
switch (err) { |
|
case OPUS_OK: return "OK"; |
|
case OPUS_BAD_ARG: return "BAD_ARG"; |
|
case OPUS_BUFFER_TOO_SMALL: return "BUFFER_TOO_SMALL"; |
|
case OPUS_INTERNAL_ERROR: return "INTERNAL_ERROR"; |
|
case OPUS_INVALID_PACKET: return "INVALID_PACKET"; |
|
case OPUS_UNIMPLEMENTED: return "UNIMPLEMENTED"; |
|
case OPUS_INVALID_STATE: return "INVALID_STATE"; |
|
case OPUS_ALLOC_FAIL: return "ALLOC_FAIL"; |
|
default: return "UNKNOWN"; |
|
} |
|
}
|
|
|