160 lines
4.6 KiB
C++
160 lines
4.6 KiB
C++
#include "OpusConverter.h"
|
|
#include "../../logger.h"
|
|
|
|
using namespace std;
|
|
using namespace tc::audio::codec;
|
|
|
|
OpusConverter::OpusConverter(size_t c, size_t s, size_t f) : Converter(c, s, f) { }
|
|
OpusConverter::~OpusConverter() = default;
|
|
|
|
bool OpusConverter::valid() {
|
|
return this->encoder && this->decoder;
|
|
}
|
|
|
|
bool OpusConverter::initialize(std::string &error, int application_type) {
|
|
lock_guard lock(this->coder_lock);
|
|
this->_application_type = application_type;
|
|
|
|
if(!this->_initialize_encoder(error))
|
|
return false;
|
|
|
|
if(!this->_initialize_decoder(error)) {
|
|
this->reset_encoder();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void OpusConverter::reset_encoder() {
|
|
lock_guard lock(this->coder_lock);
|
|
|
|
log_info(category::audio, tr("Resetting encoder"));
|
|
auto result = opus_encoder_ctl(this->encoder, OPUS_RESET_STATE);
|
|
if(result != OPUS_OK)
|
|
log_warn(category::audio, tr("Failed to reset opus encoder. Opus result: {}"), result);
|
|
}
|
|
|
|
void OpusConverter::reset_decoder() {
|
|
lock_guard lock(this->coder_lock);
|
|
|
|
log_info(category::audio, tr("Resetting decoder"));
|
|
auto result = opus_decoder_ctl(this->decoder, OPUS_RESET_STATE);
|
|
if(result != OPUS_OK)
|
|
log_warn(category::audio, tr("Failed to reset opus decoder. Opus result: {}"), result);
|
|
this->fec_decoder_ = true;
|
|
}
|
|
|
|
|
|
void OpusConverter::finalize() {
|
|
lock_guard lock(this->coder_lock);
|
|
|
|
if(this->encoder) opus_encoder_destroy(this->encoder);
|
|
this->encoder = nullptr;
|
|
|
|
if(this->decoder) opus_decoder_destroy(this->decoder);
|
|
this->decoder = nullptr;
|
|
}
|
|
|
|
ssize_t OpusConverter::encode(std::string &error, const void *source, void *target, size_t target_length, bool head_package) {
|
|
lock_guard lock(this->coder_lock);
|
|
|
|
opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(head_package ? 100 : 15));
|
|
auto result = opus_encode_float(this->encoder, (float*) source, (int) this->_frame_size, (uint8_t*) target, (opus_int32) target_length);
|
|
if(result < OPUS_OK) {
|
|
error = to_string(result) + "|" + opus_strerror(result);
|
|
return -1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t OpusConverter::decode(std::string &error, const void *source, size_t source_length, void *target, bool use_fec) {
|
|
lock_guard lock(this->coder_lock);
|
|
|
|
auto result = opus_decode_float(this->decoder, (uint8_t*) source, (opus_int32) source_length, (float*) target, (int) this->_frame_size, use_fec ? 1 : 0);
|
|
if(result < OPUS_OK) {
|
|
error = to_string(result) + "|" + opus_strerror(result);
|
|
return -1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ssize_t OpusConverter::decode_lost(std::string &error, size_t packets) {
|
|
lock_guard lock(this->coder_lock);
|
|
|
|
auto buffer = (float*) malloc(this->_frame_size * this->_channels * sizeof(float));
|
|
while (packets-- > 0) {
|
|
auto result = opus_decode_float(this->decoder, nullptr, 0, buffer, (int) this->_frame_size, false);
|
|
if(result < OPUS_OK)
|
|
log_warn(category::audio, tr("Opus decode lost resulted in error: {}"), result);
|
|
}
|
|
this->fec_decoder_ = true;
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|
|
size_t OpusConverter::expected_encoded_length(size_t sample_count) {
|
|
//TODO calculate stuff
|
|
return 512;
|
|
}
|
|
|
|
bool OpusConverter::_initialize_decoder(std::string &error) {
|
|
if(!this->_finalize_decoder(error))
|
|
return false;
|
|
|
|
int error_id = 0;
|
|
this->decoder = opus_decoder_create((opus_int32) this->_sample_rate, (int) this->_channels, &error_id);
|
|
if(!this->encoder || error_id) {
|
|
error = "failed to create decoder (" + to_string(error_id) + ")";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OpusConverter::_initialize_encoder(std::string &error) {
|
|
if(!this->_finalize_encoder(error))
|
|
return false;
|
|
|
|
int error_id = 0;
|
|
this->encoder = opus_encoder_create((opus_int32) this->_sample_rate, (int) this->_channels, this->_application_type, &error_id);
|
|
if(!this->encoder || error_id) {
|
|
error = "failed to create encoder (" + to_string(error_id) + ")";
|
|
return false;
|
|
}
|
|
|
|
error_id = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(64000));
|
|
if(error_id) {
|
|
error = "failed to set bitrate (" + to_string(error_id) + ")";
|
|
return false;
|
|
}
|
|
|
|
error_id = opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1));
|
|
if(error_id) {
|
|
error = "failed to enable fec (" + to_string(error_id) + ")";
|
|
return false;
|
|
}
|
|
|
|
error_id = opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(15));
|
|
if(error_id) {
|
|
error = "failed to assume a 15% packet loss (" + to_string(error_id) + ")";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OpusConverter::_finalize_decoder(std::string &) {
|
|
if(this->decoder) {
|
|
opus_decoder_destroy(this->decoder);
|
|
this->decoder = nullptr;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OpusConverter::_finalize_encoder(std::string &) {
|
|
if(this->encoder) {
|
|
opus_encoder_destroy(this->encoder);
|
|
this->encoder = nullptr;
|
|
}
|
|
return true;
|
|
} |