Opus improve error handling

This commit is contained in:
badaix 2019-11-03 20:23:54 +01:00
parent 6cd3b7df67
commit 7dffa6ed65
2 changed files with 31 additions and 21 deletions

View file

@ -23,6 +23,8 @@
#include "common/utils/string_utils.hpp"
#define ID_OPUS 0x4F505553
static constexpr opus_int32 const_min_bitrate = 6000;
static constexpr opus_int32 const_max_bitrate = 512000;
namespace
{
@ -37,7 +39,6 @@ void assign(void* pointer, T val)
// TODO:
// - handle variable chunk durations (now it's fixed to 10ms)
// const msg::SampleFormat& format);
OpusEncoder::OpusEncoder(const std::string& codecOptions) : Encoder(codecOptions), enc_(nullptr)
{
headerChunk_.reset(new msg::CodecHeader("opus"));
@ -53,15 +54,16 @@ OpusEncoder::~OpusEncoder()
std::string OpusEncoder::getAvailableOptions() const
{
return "BR:[6 - 512|MAX|AUTO],COMPLEXITY:[1-10]";
return "BITRATE:[" + cpt::to_string(const_min_bitrate) + " - " + cpt::to_string(const_max_bitrate) + "|MAX|AUTO],COMPLEXITY:[1-10]";
}
std::string OpusEncoder::getDefaultOptions() const
{
return "BR:192,COMPLEXITY:10";
return "BITRATE:192000,COMPLEXITY:10";
}
std::string OpusEncoder::name() const
{
return "opus";
@ -70,15 +72,22 @@ std::string OpusEncoder::name() const
void OpusEncoder::initEncoder()
{
// Opus is quite restrictive in sample rate and bit depth
// It can handle mono signals, but we will check for stereo
if ((sampleFormat_.rate != 48000) || (sampleFormat_.bits != 16) || (sampleFormat_.channels != 2))
throw SnapException("Opus sampleformat must be 48000:16:2");
opus_int32 bitrate = 192000;
opus_int32 complexity = 10;
// parse options: bitrate and complexity
auto options = utils::string::split(codecOptions_, ',');
for (const auto& option : options)
{
auto kv = utils::string::split(option, ':');
if (kv.size() == 2)
{
if (kv.front() == "BR")
if (kv.front() == "BITRATE")
{
if (kv.back() == "MAX")
bitrate = OPUS_BITRATE_MAX;
@ -89,13 +98,14 @@ void OpusEncoder::initEncoder()
try
{
bitrate = cpt::stoi(kv.back());
if ((bitrate < 6) || (bitrate > 512))
throw SnapException("Opus bitrate must be between 6 and 512");
bitrate *= 1000;
if ((bitrate < const_min_bitrate) || (bitrate > const_max_bitrate))
throw SnapException("Opus bitrate must be between " + cpt::to_string(const_min_bitrate) + " and " +
cpt::to_string(const_max_bitrate));
}
catch (const std::invalid_argument&)
{
throw SnapException("Opus error parsing bitrate (must be between 6 and 512): " + kv.back());
throw SnapException("Opus error parsing bitrate (must be between " + cpt::to_string(const_min_bitrate) + " and " +
cpt::to_string(const_max_bitrate) + "): " + kv.back());
}
}
}
@ -125,14 +135,11 @@ void OpusEncoder::initEncoder()
enc_ = opus_encoder_create(sampleFormat_.rate, sampleFormat_.channels, OPUS_APPLICATION_RESTRICTED_LOWDELAY, &error);
if (error != 0)
{
throw SnapException("Failed to initialize Opus encoder: " + cpt::to_string(error));
throw SnapException("Failed to initialize Opus encoder: " + std::string(opus_strerror(error)));
}
opus_encoder_ctl(enc_, OPUS_SET_BITRATE(bitrate)); // max 512000, OPUS_BITRATE_MAX, OPUS_AUTO
opus_encoder_ctl(enc_, OPUS_SET_COMPLEXITY(complexity)); // max 10
if ((sampleFormat_.rate != 48000) || (sampleFormat_.bits != 16) || (sampleFormat_.channels != 2))
throw SnapException("Opus sampleformat must be 48000:16:2");
opus_encoder_ctl(enc_, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(enc_, OPUS_SET_COMPLEXITY(complexity));
// create some opus pseudo header to let the decoder know about the sample format
headerChunk_->payloadSize = 12;
@ -147,12 +154,14 @@ void OpusEncoder::initEncoder()
void OpusEncoder::encode(const msg::PcmChunk* chunk)
{
int samples = chunk->payloadSize / 4;
int samples_per_channel = chunk->getFrameCount();
if (encoded_.size() < chunk->payloadSize)
encoded_.resize(chunk->payloadSize);
LOG(ERROR) << "samples / channel: " << samples_per_channel << ", bytes: " << chunk->payloadSize << '\n';
// encode
opus_int32 len = opus_encode(enc_, (opus_int16*)chunk->payload, samples, encoded_.data(), chunk->payloadSize);
opus_int32 len = opus_encode(enc_, (opus_int16*)chunk->payload, samples_per_channel, encoded_.data(), chunk->payloadSize);
LOG(DEBUG) << "Encoded: size " << chunk->payloadSize << " bytes, encoded: " << len << " bytes" << '\n';
if (len > 0)
@ -162,10 +171,11 @@ void OpusEncoder::encode(const msg::PcmChunk* chunk)
opusChunk->payloadSize = len;
opusChunk->payload = (char*)realloc(opusChunk->payload, opusChunk->payloadSize);
memcpy(opusChunk->payload, encoded_.data(), len);
listener_->onChunkEncoded(this, opusChunk, (double)samples / ((double)sampleFormat_.rate / 1000.));
listener_->onChunkEncoded(this, opusChunk, (double)samples_per_channel / ((double)sampleFormat_.rate / 1000.));
}
else
{
LOG(ERROR) << "Failed to encode chunk: " << len << '\n' << " Frame size: " << samples / 2 << '\n' << " Max bytes: " << chunk->payloadSize << '\n';
LOG(ERROR) << "Failed to encode chunk: " << opus_strerror(len) << ", samples / channel: " << samples_per_channel << ", bytes: " << chunk->payloadSize
<< '\n';
}
}