mirror of
https://github.com/badaix/snapcast.git
synced 2025-07-07 03:37:42 +02:00
Opus improve error handling
This commit is contained in:
parent
6cd3b7df67
commit
7dffa6ed65
2 changed files with 31 additions and 21 deletions
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue