encoder: migrate from class Error to C++ exceptions

This commit is contained in:
Max Kellermann 2016-11-07 09:20:12 +01:00
parent b8aac3f8fc
commit d8b6aff23a
16 changed files with 299 additions and 477 deletions

View File

@ -28,7 +28,6 @@
struct AudioFormat;
struct Tag;
class Error;
class Encoder {
const bool implements_tag;
@ -51,20 +50,18 @@ public:
* usable for more data, and only Read() and Close() can be
* called.
*
* @return true on success
* Throws #std::runtime_error on error.
*/
virtual bool End(gcc_unused Error &error) {
return true;
virtual void End() {
}
/**
* Flushes an encoder object, make everything which might
* currently be buffered available by Read().
*
* @return true on success
* Throws #std::runtime_error on error.
*/
virtual bool Flush(gcc_unused Error &error) {
return true;
virtual void Flush() {
}
/**
@ -72,10 +69,9 @@ public:
* some encoders to flush the previous sub-stream, in
* preparation to begin a new one.
*
* @return true on success
* Throws #std::runtime_error on error.
*/
virtual bool PreTag(gcc_unused Error &error) {
return true;
virtual void PreTag() {
}
/**
@ -84,23 +80,22 @@ public:
* Instructions: call PreTag(); then obtain flushed data with
* Read(); finally call Tag().
*
* Throws #std::runtime_error on error.
*
* @param tag the tag object
* @return true on success
*/
virtual bool SendTag(gcc_unused const Tag &tag,
gcc_unused Error &error) {
return true;
virtual void SendTag(gcc_unused const Tag &tag) {
}
/**
* Writes raw PCM data to the encoder.
*
* Throws #std::runtime_error on error.
*
* @param data the buffer containing PCM samples
* @param length the length of the buffer in bytes
* @return true on success
*/
virtual bool Write(const void *data, size_t length,
Error &error) = 0;
virtual void Write(const void *data, size_t length) = 0;
/**
* Reads encoded data from the encoder.
@ -127,11 +122,12 @@ public:
* first encoder_write() call, you should invoke
* encoder_read() to obtain the file header.
*
* Throws #std::runtime_error on error.
*
* @param audio_format the encoder's input audio format; the plugin
* may modify the struct to adapt it to its abilities
* @return true on success
*/
virtual Encoder *Open(AudioFormat &audio_format, Error &error) = 0;
virtual Encoder *Open(AudioFormat &audio_format) = 0;
/**
* Get mime type of encoded content.

View File

@ -24,8 +24,7 @@
#include "pcm/PcmBuffer.hxx"
#include "config/ConfigError.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "util/RuntimeError.hxx"
#include <FLAC/stream_encoder.h>
@ -47,29 +46,22 @@ class FlacEncoder final : public Encoder {
DynamicFifoBuffer<uint8_t> output_buffer;
public:
FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse)
:Encoder(false),
audio_format(_audio_format), fse(_fse),
output_buffer(8192) {}
FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse);
~FlacEncoder() override {
FLAC__stream_encoder_delete(fse);
}
bool Init(Error &error);
/* virtual methods from class Encoder */
bool End(Error &) override {
void End() override {
(void) FLAC__stream_encoder_finish(fse);
return true;
}
bool Flush(Error &) override {
void Flush() override {
(void) FLAC__stream_encoder_finish(fse);
return true;
}
bool Write(const void *data, size_t length, Error &) override;
void Write(const void *data, size_t length) override;
size_t Read(void *dest, size_t length) override {
return output_buffer.Read((uint8_t *)dest, length);
@ -95,15 +87,13 @@ public:
PreparedFlacEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override;
Encoder *Open(AudioFormat &audio_format) override;
const char *GetMimeType() const override {
return "audio/flac";
}
};
static constexpr Domain flac_encoder_domain("vorbis_encoder");
PreparedFlacEncoder::PreparedFlacEncoder(const ConfigBlock &block)
:compression(block.GetBlockValue("compression", 5u))
{
@ -115,45 +105,32 @@ flac_encoder_init(const ConfigBlock &block)
return new PreparedFlacEncoder(block);
}
static bool
static void
flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
const AudioFormat &audio_format, unsigned bits_per_sample,
Error &error)
const AudioFormat &audio_format, unsigned bits_per_sample)
{
if (!FLAC__stream_encoder_set_compression_level(fse, compression)) {
error.Format(config_domain,
"error setting flac compression to %d",
compression);
return false;
}
if (!FLAC__stream_encoder_set_compression_level(fse, compression))
throw FormatRuntimeError("error setting flac compression to %d",
compression);
if (!FLAC__stream_encoder_set_channels(fse, audio_format.channels)) {
error.Format(config_domain,
"error setting flac channels num to %d",
audio_format.channels);
return false;
}
if (!FLAC__stream_encoder_set_channels(fse, audio_format.channels))
throw FormatRuntimeError("error setting flac channels num to %d",
audio_format.channels);
if (!FLAC__stream_encoder_set_bits_per_sample(fse, bits_per_sample)) {
error.Format(config_domain,
"error setting flac bit format to %d",
bits_per_sample);
return false;
}
if (!FLAC__stream_encoder_set_bits_per_sample(fse, bits_per_sample))
throw FormatRuntimeError("error setting flac bit format to %d",
bits_per_sample);
if (!FLAC__stream_encoder_set_sample_rate(fse,
audio_format.sample_rate)) {
error.Format(config_domain,
"error setting flac sample rate to %d",
audio_format.sample_rate);
return false;
}
return true;
audio_format.sample_rate))
throw FormatRuntimeError("error setting flac sample rate to %d",
audio_format.sample_rate);
}
bool
FlacEncoder::Init(Error &error)
FlacEncoder::FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse)
:Encoder(false),
audio_format(_audio_format), fse(_fse),
output_buffer(8192)
{
/* this immediately outputs data through callback */
@ -163,18 +140,13 @@ FlacEncoder::Init(Error &error)
nullptr, nullptr, nullptr,
this);
if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
error.Format(flac_encoder_domain,
"failed to initialize encoder: %s\n",
FLAC__StreamEncoderInitStatusString[init_status]);
return false;
}
return true;
if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
throw FormatRuntimeError("failed to initialize encoder: %s\n",
FLAC__StreamEncoderInitStatusString[init_status]);
}
Encoder *
PreparedFlacEncoder::Open(AudioFormat &audio_format, Error &error)
PreparedFlacEncoder::Open(AudioFormat &audio_format)
{
unsigned bits_per_sample;
@ -199,24 +171,18 @@ PreparedFlacEncoder::Open(AudioFormat &audio_format, Error &error)
/* allocate the encoder */
auto fse = FLAC__stream_encoder_new();
if (fse == nullptr) {
error.Set(flac_encoder_domain, "FLAC__stream_encoder_new() failed");
return nullptr;
}
if (fse == nullptr)
throw std::runtime_error("FLAC__stream_encoder_new() failed");
if (!flac_encoder_setup(fse, compression,
audio_format, bits_per_sample, error)) {
try {
flac_encoder_setup(fse, compression,
audio_format, bits_per_sample);
} catch (...) {
FLAC__stream_encoder_delete(fse);
return nullptr;
throw;
}
auto *e = new FlacEncoder(audio_format, fse);
if (!e->Init(error)) {
delete e;
return nullptr;
}
return e;
return new FlacEncoder(audio_format, fse);
}
static inline void
@ -237,8 +203,8 @@ pcm16_to_flac(int32_t *out, const int16_t *in, unsigned num_samples)
}
}
bool
FlacEncoder::Write(const void *data, size_t length, Error &error)
void
FlacEncoder::Write(const void *data, size_t length)
{
void *exbuffer;
const void *buffer = nullptr;
@ -278,12 +244,8 @@ FlacEncoder::Write(const void *data, size_t length, Error &error)
if (!FLAC__stream_encoder_process_interleaved(fse,
(const FLAC__int32 *)buffer,
num_frames)) {
error.Set(flac_encoder_domain, "flac encoder process failed");
return false;
}
return true;
num_frames))
throw std::runtime_error("flac encoder process failed");
}
const EncoderPlugin flac_encoder_plugin = {

View File

@ -25,11 +25,11 @@
#include "util/NumberParser.hxx"
#include "util/ReusableArray.hxx"
#include "util/RuntimeError.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include <lame/lame.h>
#include <stdexcept>
#include <assert.h>
#include <string.h>
@ -50,7 +50,7 @@ public:
~LameEncoder() override;
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override;
void Write(const void *data, size_t length) override;
size_t Read(void *dest, size_t length) override;
};
@ -62,15 +62,13 @@ public:
PreparedLameEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override;
Encoder *Open(AudioFormat &audio_format) override;
const char *GetMimeType() const override {
return "audio/mpeg";
}
};
static constexpr Domain lame_encoder_domain("lame_encoder");
PreparedLameEncoder::PreparedLameEncoder(const ConfigBlock &block)
{
const char *value;
@ -110,76 +108,53 @@ lame_encoder_init(const ConfigBlock &block)
return new PreparedLameEncoder(block);
}
static bool
static void
lame_encoder_setup(lame_global_flags *gfp, float quality, int bitrate,
const AudioFormat &audio_format, Error &error)
const AudioFormat &audio_format)
{
if (quality >= -1.0) {
/* a quality was configured (VBR) */
if (0 != lame_set_VBR(gfp, vbr_rh)) {
error.Set(lame_encoder_domain,
"error setting lame VBR mode");
return false;
}
if (0 != lame_set_VBR_q(gfp, quality)) {
error.Set(lame_encoder_domain,
"error setting lame VBR quality");
return false;
}
if (0 != lame_set_VBR(gfp, vbr_rh))
throw std::runtime_error("error setting lame VBR mode");
if (0 != lame_set_VBR_q(gfp, quality))
throw std::runtime_error("error setting lame VBR quality");
} else {
/* a bit rate was configured */
if (0 != lame_set_brate(gfp, bitrate)) {
error.Set(lame_encoder_domain,
"error setting lame bitrate");
return false;
}
if (0 != lame_set_brate(gfp, bitrate))
throw std::runtime_error("error setting lame bitrate");
}
if (0 != lame_set_num_channels(gfp, audio_format.channels)) {
error.Set(lame_encoder_domain,
"error setting lame num channels");
return false;
}
if (0 != lame_set_num_channels(gfp, audio_format.channels))
throw std::runtime_error("error setting lame num channels");
if (0 != lame_set_in_samplerate(gfp, audio_format.sample_rate)) {
error.Set(lame_encoder_domain,
"error setting lame sample rate");
return false;
}
if (0 != lame_set_in_samplerate(gfp, audio_format.sample_rate))
throw std::runtime_error("error setting lame sample rate");
if (0 != lame_set_out_samplerate(gfp, audio_format.sample_rate)) {
error.Set(lame_encoder_domain,
"error setting lame out sample rate");
return false;
}
if (0 != lame_set_out_samplerate(gfp, audio_format.sample_rate))
throw std::runtime_error("error setting lame out sample rate");
if (0 > lame_init_params(gfp)) {
error.Set(lame_encoder_domain,
"error initializing lame params");
return false;
}
return true;
if (0 > lame_init_params(gfp))
throw std::runtime_error("error initializing lame params");
}
Encoder *
PreparedLameEncoder::Open(AudioFormat &audio_format, Error &error)
PreparedLameEncoder::Open(AudioFormat &audio_format)
{
audio_format.format = SampleFormat::S16;
audio_format.channels = 2;
auto gfp = lame_init();
if (gfp == nullptr) {
error.Set(lame_encoder_domain, "lame_init() failed");
return nullptr;
}
if (gfp == nullptr)
throw std::runtime_error("lame_init() failed");
if (!lame_encoder_setup(gfp, quality, bitrate,
audio_format, error)) {
try {
lame_encoder_setup(gfp, quality, bitrate, audio_format);
} catch (...) {
lame_close(gfp);
return nullptr;
throw;
}
return new LameEncoder(audio_format, gfp);
@ -190,9 +165,8 @@ LameEncoder::~LameEncoder()
lame_close(gfp);
}
bool
LameEncoder::Write(const void *data, size_t length,
gcc_unused Error &error)
void
LameEncoder::Write(const void *data, size_t length)
{
const int16_t *src = (const int16_t*)data;
@ -212,14 +186,11 @@ LameEncoder::Write(const void *data, size_t length,
num_frames,
dest, output_buffer_size);
if (bytes_out < 0) {
error.Set(lame_encoder_domain, "lame encoder failed");
return false;
}
if (bytes_out < 0)
throw std::runtime_error("lame encoder failed");
output_begin = dest;
output_end = dest + bytes_out;
return true;
}
size_t

View File

@ -32,9 +32,8 @@ public:
buffer(8192) {}
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override {
void Write(const void *data, size_t length) override {
buffer.Append((const uint8_t *)data, length);
return true;
}
size_t Read(void *dest, size_t length) override {
@ -45,7 +44,7 @@ public:
class PreparedNullEncoder final : public PreparedEncoder {
public:
/* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &, Error &) override {
Encoder *Open(AudioFormat &) override {
return new NullEncoder();
}
};

View File

@ -47,9 +47,8 @@ public:
}
/* virtual methods from class Encoder */
bool Flush(Error &) override {
Flush();
return true;
void Flush() final {
flush = true;
}
size_t Read(void *dest, size_t length) override {
@ -67,11 +66,6 @@ public:
return ReadPage(page, dest, length);
}
protected:
void Flush() {
flush = true;
}
};
#endif

View File

@ -23,8 +23,6 @@
#include "AudioFormat.hxx"
#include "config/ConfigError.hxx"
#include "util/Alloc.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "system/ByteOrder.hxx"
#include <opus.h>
@ -61,14 +59,14 @@ public:
~OpusEncoder() override;
/* virtual methods from class Encoder */
bool End(Error &) override;
bool Write(const void *data, size_t length, Error &) override;
void End() override;
void Write(const void *data, size_t length) override;
size_t Read(void *dest, size_t length) override;
private:
bool DoEncode(bool eos, Error &error);
bool WriteSilence(unsigned fill_frames, Error &error);
void DoEncode(bool eos);
void WriteSilence(unsigned fill_frames);
void GenerateHead();
void GenerateTags();
@ -83,15 +81,13 @@ public:
PreparedOpusEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override;
Encoder *Open(AudioFormat &audio_format) override;
const char *GetMimeType() const override {
return "audio/ogg";
}
};
static constexpr Domain opus_encoder_domain("opus_encoder");
PreparedOpusEncoder::PreparedOpusEncoder(const ConfigBlock &block)
{
const char *value = block.GetBlockValue("bitrate", "auto");
@ -141,7 +137,7 @@ OpusEncoder::OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc)
}
Encoder *
PreparedOpusEncoder::Open(AudioFormat &audio_format, Error &error)
PreparedOpusEncoder::Open(AudioFormat &audio_format)
{
/* libopus supports only 48 kHz */
audio_format.sample_rate = 48000;
@ -168,11 +164,8 @@ PreparedOpusEncoder::Open(AudioFormat &audio_format, Error &error)
audio_format.channels,
OPUS_APPLICATION_AUDIO,
&error_code);
if (enc == nullptr) {
error.Set(opus_encoder_domain, error_code,
opus_strerror(error_code));
return nullptr;
}
if (enc == nullptr)
throw std::runtime_error(opus_strerror(error_code));
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
@ -187,8 +180,8 @@ OpusEncoder::~OpusEncoder()
opus_encoder_destroy(enc);
}
bool
OpusEncoder::DoEncode(bool eos, Error &error)
void
OpusEncoder::DoEncode(bool eos)
{
assert(buffer_position == buffer_size);
@ -204,10 +197,8 @@ OpusEncoder::DoEncode(bool eos, Error &error)
buffer_frames,
buffer2,
sizeof(buffer2));
if (result < 0) {
error.Set(opus_encoder_domain, "Opus encoder error");
return false;
}
if (result < 0)
throw std::runtime_error("Opus encoder error");
granulepos += buffer_frames;
@ -221,12 +212,10 @@ OpusEncoder::DoEncode(bool eos, Error &error)
stream.PacketIn(packet);
buffer_position = 0;
return true;
}
bool
OpusEncoder::End(Error &error)
void
OpusEncoder::End()
{
Flush();
@ -234,11 +223,11 @@ OpusEncoder::End(Error &error)
buffer_size - buffer_position);
buffer_position = buffer_size;
return DoEncode(true, error);
DoEncode(true);
}
bool
OpusEncoder::WriteSilence(unsigned fill_frames, Error &error)
void
OpusEncoder::WriteSilence(unsigned fill_frames)
{
size_t fill_bytes = fill_frames * frame_size;
@ -251,16 +240,13 @@ OpusEncoder::WriteSilence(unsigned fill_frames, Error &error)
buffer_position += nbytes;
fill_bytes -= nbytes;
if (buffer_position == buffer_size &&
!DoEncode(false, error))
return false;
if (buffer_position == buffer_size)
DoEncode(false);
}
return true;
}
bool
OpusEncoder::Write(const void *_data, size_t length, Error &error)
void
OpusEncoder::Write(const void *_data, size_t length)
{
const uint8_t *data = (const uint8_t *)_data;
@ -270,9 +256,7 @@ OpusEncoder::Write(const void *_data, size_t length, Error &error)
assert(buffer_position == 0);
if (!WriteSilence(lookahead, error))
return false;
WriteSilence(lookahead);
lookahead = 0;
}
@ -286,12 +270,9 @@ OpusEncoder::Write(const void *_data, size_t length, Error &error)
length -= nbytes;
buffer_position += nbytes;
if (buffer_position == buffer_size &&
!DoEncode(false, error))
return false;
if (buffer_position == buffer_size)
DoEncode(false);
}
return true;
}
void

View File

@ -23,7 +23,7 @@
#include "AudioFormat.hxx"
#include "config/ConfigError.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include "util/Error.hxx"
#include "util/RuntimeError.hxx"
extern "C"
{
@ -71,13 +71,13 @@ public:
bool WriteChunk(bool flush);
/* virtual methods from class Encoder */
bool End(Error &error) override {
return Flush(error);
void End() override {
return Flush();
}
bool Flush(Error &) override;
void Flush() override;
bool Write(const void *data, size_t length, Error &) override;
void Write(const void *data, size_t length) override;
size_t Read(void *dest, size_t length) override {
return output_buffer.Read((uint8_t *)dest, length);
@ -91,7 +91,7 @@ public:
PreparedShineEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override;
Encoder *Open(AudioFormat &audio_format) override;
const char *GetMimeType() const override {
return "audio/mpeg";
@ -111,8 +111,7 @@ shine_encoder_init(const ConfigBlock &block)
}
static shine_t
SetupShine(shine_config_t config, AudioFormat &audio_format,
Error &error)
SetupShine(shine_config_t config, AudioFormat &audio_format)
{
audio_format.format = SampleFormat::S16;
audio_format.channels = CHANNELS;
@ -122,32 +121,24 @@ SetupShine(shine_config_t config, AudioFormat &audio_format,
config.wave.channels =
audio_format.channels == 2 ? PCM_STEREO : PCM_MONO;
if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) {
error.Format(config_domain,
"error configuring shine. "
"samplerate %d and bitrate %d configuration"
" not supported.",
config.wave.samplerate,
config.mpeg.bitr);
return nullptr;
}
if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0)
throw FormatRuntimeError("error configuring shine. "
"samplerate %d and bitrate %d configuration"
" not supported.",
config.wave.samplerate,
config.mpeg.bitr);
auto shine = shine_initialise(&config);
if (!shine)
error.Format(config_domain,
"error initializing shine.");
throw std::runtime_error("error initializing shine");
return shine;
}
Encoder *
PreparedShineEncoder::Open(AudioFormat &audio_format, Error &error)
PreparedShineEncoder::Open(AudioFormat &audio_format)
{
auto shine = SetupShine(config, audio_format, error);
if (!shine)
return nullptr;
auto shine = SetupShine(config, audio_format);
return new ShineEncoder(audio_format, shine);
}
@ -175,8 +166,8 @@ ShineEncoder::WriteChunk(bool flush)
return true;
}
bool
ShineEncoder::Write(const void *_data, size_t length, gcc_unused Error &error)
void
ShineEncoder::Write(const void *_data, size_t length)
{
const int16_t *data = (const int16_t*)_data;
length /= sizeof(*data) * audio_format.channels;
@ -198,12 +189,10 @@ ShineEncoder::Write(const void *_data, size_t length, gcc_unused Error &error)
/* write if chunk is filled */
WriteChunk(false);
}
return true;
}
bool
ShineEncoder::Flush(gcc_unused Error &error)
void
ShineEncoder::Flush()
{
/* flush buffers and flush shine */
WriteChunk(true);
@ -213,8 +202,6 @@ ShineEncoder::Flush(gcc_unused Error &error)
if (written > 0)
output_buffer.Append(data, written);
return true;
}
const EncoderPlugin shine_encoder_plugin = {

View File

@ -24,12 +24,13 @@
#include "config/ConfigError.hxx"
#include "util/NumberParser.hxx"
#include "util/RuntimeError.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
#include <twolame.h>
#include <stdexcept>
#include <assert.h>
#include <string.h>
@ -59,17 +60,15 @@ public:
/* virtual methods from class Encoder */
bool End(Error &) override {
void End() override {
flush = true;
return true;
}
bool Flush(Error &) override {
void Flush() override {
flush = true;
return true;
}
bool Write(const void *data, size_t length, Error &) override;
void Write(const void *data, size_t length) override;
size_t Read(void *dest, size_t length) override;
};
@ -81,7 +80,7 @@ public:
PreparedTwolameEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override;
Encoder *Open(AudioFormat &audio_format) override;
const char *GetMimeType() const override {
return "audio/mpeg";
@ -132,71 +131,52 @@ twolame_encoder_init(const ConfigBlock &block)
return new PreparedTwolameEncoder(block);
}
static bool
static void
twolame_encoder_setup(twolame_options *options, float quality, int bitrate,
const AudioFormat &audio_format, Error &error)
const AudioFormat &audio_format)
{
if (quality >= -1.0) {
/* a quality was configured (VBR) */
if (0 != twolame_set_VBR(options, true)) {
error.Set(twolame_encoder_domain,
"error setting twolame VBR mode");
return false;
}
if (0 != twolame_set_VBR_q(options, quality)) {
error.Set(twolame_encoder_domain,
"error setting twolame VBR quality");
return false;
}
if (0 != twolame_set_VBR(options, true))
throw std::runtime_error("error setting twolame VBR mode");
if (0 != twolame_set_VBR_q(options, quality))
throw std::runtime_error("error setting twolame VBR quality");
} else {
/* a bit rate was configured */
if (0 != twolame_set_brate(options, bitrate)) {
error.Set(twolame_encoder_domain,
"error setting twolame bitrate");
return false;
}
if (0 != twolame_set_brate(options, bitrate))
throw std::runtime_error("error setting twolame bitrate");
}
if (0 != twolame_set_num_channels(options, audio_format.channels)) {
error.Set(twolame_encoder_domain,
"error setting twolame num channels");
return false;
}
if (0 != twolame_set_num_channels(options, audio_format.channels))
throw std::runtime_error("error setting twolame num channels");
if (0 != twolame_set_in_samplerate(options,
audio_format.sample_rate)) {
error.Set(twolame_encoder_domain,
"error setting twolame sample rate");
return false;
}
audio_format.sample_rate))
throw std::runtime_error("error setting twolame sample rate");
if (0 > twolame_init_params(options)) {
error.Set(twolame_encoder_domain,
"error initializing twolame params");
return false;
}
return true;
if (0 > twolame_init_params(options))
throw std::runtime_error("error initializing twolame params");
}
Encoder *
PreparedTwolameEncoder::Open(AudioFormat &audio_format, Error &error)
PreparedTwolameEncoder::Open(AudioFormat &audio_format)
{
audio_format.format = SampleFormat::S16;
audio_format.channels = 2;
auto options = twolame_init();
if (options == nullptr) {
error.Set(twolame_encoder_domain, "twolame_init() failed");
return nullptr;
}
if (options == nullptr)
throw std::runtime_error("twolame_init() failed");
if (!twolame_encoder_setup(options, quality, bitrate,
audio_format, error)) {
try {
twolame_encoder_setup(options, quality, bitrate,
audio_format);
} catch (...) {
twolame_close(&options);
return nullptr;
throw;
}
return new TwolameEncoder(audio_format, options);
@ -207,9 +187,8 @@ TwolameEncoder::~TwolameEncoder()
twolame_close(&options);
}
bool
TwolameEncoder::Write(const void *data, size_t length,
gcc_unused Error &error)
void
TwolameEncoder::Write(const void *data, size_t length)
{
const int16_t *src = (const int16_t*)data;
@ -221,14 +200,11 @@ TwolameEncoder::Write(const void *data, size_t length,
src, num_frames,
output_buffer,
sizeof(output_buffer));
if (bytes_out < 0) {
error.Set(twolame_encoder_domain, "twolame encoder failed");
return false;
}
if (bytes_out < 0)
throw std::runtime_error("twolame encoder failed");
output_buffer_length = (size_t)bytes_out;
output_buffer_position = 0;
return true;
}
size_t

View File

@ -26,8 +26,6 @@
#include "util/StringUtil.hxx"
#include "util/NumberParser.hxx"
#include "util/RuntimeError.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include <vorbis/vorbisenc.h>
@ -39,10 +37,7 @@ class VorbisEncoder final : public OggEncoder {
vorbis_info vi;
public:
VorbisEncoder()
:OggEncoder(true) {
vorbis_info_init(&vi);
}
VorbisEncoder(float quality, int bitrate, AudioFormat &_audio_format);
virtual ~VorbisEncoder() {
vorbis_block_clear(&vb);
@ -50,18 +45,15 @@ public:
vorbis_info_clear(&vi);
}
bool Open(float quality, int bitrate, AudioFormat &audio_format,
Error &error);
/* virtual methods from class Encoder */
bool End(Error &error) override {
return PreTag(error);
void End() override {
PreTag();
}
bool PreTag(Error &error) override;
bool SendTag(const Tag &tag, Error &error) override;
void PreTag() override;
void SendTag(const Tag &tag) override;
bool Write(const void *data, size_t length, Error &) override;
void Write(const void *data, size_t length) override;
private:
void HeaderOut(vorbis_comment &vc);
@ -77,15 +69,13 @@ public:
PreparedVorbisEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override;
Encoder *Open(AudioFormat &audio_format) override;
const char *GetMimeType() const override {
return "audio/ogg";
}
};
static constexpr Domain vorbis_encoder_domain("vorbis_encoder");
PreparedVorbisEncoder::PreparedVorbisEncoder(const ConfigBlock &block)
{
const char *value = block.GetBlockValue("quality");
@ -124,10 +114,12 @@ vorbis_encoder_init(const ConfigBlock &block)
return new PreparedVorbisEncoder(block);
}
bool
VorbisEncoder::Open(float quality, int bitrate, AudioFormat &_audio_format,
Error &error)
VorbisEncoder::VorbisEncoder(float quality, int bitrate,
AudioFormat &_audio_format)
:OggEncoder(true)
{
vorbis_info_init(&vi);
_audio_format.format = SampleFormat::FLOAT;
audio_format = _audio_format;
@ -138,9 +130,8 @@ VorbisEncoder::Open(float quality, int bitrate, AudioFormat &_audio_format,
audio_format.channels,
audio_format.sample_rate,
quality * 0.1)) {
error.Set(vorbis_encoder_domain,
"error initializing vorbis vbr");
return false;
vorbis_info_clear(&vi);
throw std::runtime_error("error initializing vorbis vbr");
}
} else {
/* a bit rate was configured */
@ -149,9 +140,8 @@ VorbisEncoder::Open(float quality, int bitrate, AudioFormat &_audio_format,
audio_format.channels,
audio_format.sample_rate, -1.0,
bitrate * 1000, -1.0)) {
error.Set(vorbis_encoder_domain,
"error initializing vorbis encoder");
return false;
vorbis_info_clear(&vi);
throw std::runtime_error("error initializing vorbis encoder");
}
}
@ -159,8 +149,6 @@ VorbisEncoder::Open(float quality, int bitrate, AudioFormat &_audio_format,
vorbis_block_init(&vd, &vb);
SendHeader();
return true;
}
void
@ -184,15 +172,9 @@ VorbisEncoder::SendHeader()
}
Encoder *
PreparedVorbisEncoder::Open(AudioFormat &audio_format, Error &error)
PreparedVorbisEncoder::Open(AudioFormat &audio_format)
{
auto *e = new VorbisEncoder();
if (!e->Open(quality, bitrate, audio_format, error)) {
delete e;
return nullptr;
}
return e;
return new VorbisEncoder(quality, bitrate, audio_format);
}
void
@ -208,8 +190,8 @@ VorbisEncoder::BlockOut()
}
}
bool
VorbisEncoder::PreTag(gcc_unused Error &error)
void
VorbisEncoder::PreTag()
{
vorbis_analysis_wrote(&vd, 0);
BlockOut();
@ -222,7 +204,6 @@ VorbisEncoder::PreTag(gcc_unused Error &error)
vorbis_block_init(&vd, &vb);
Flush();
return true;
}
static void
@ -235,8 +216,8 @@ copy_tag_to_vorbis_comment(VorbisComment &vc, const Tag &tag)
}
}
bool
VorbisEncoder::SendTag(const Tag &tag, gcc_unused Error &error)
void
VorbisEncoder::SendTag(const Tag &tag)
{
/* write the vorbis_comment object */
@ -250,8 +231,6 @@ VorbisEncoder::SendTag(const Tag &tag, gcc_unused Error &error)
/* send that vorbis_comment to the ogg_stream_state */
HeaderOut(comment);
return true;
}
static void
@ -263,8 +242,8 @@ interleaved_to_vorbis_buffer(float **dest, const float *src,
dest[j][i] = *src++;
}
bool
VorbisEncoder::Write(const void *data, size_t length, gcc_unused Error &error)
void
VorbisEncoder::Write(const void *data, size_t length)
{
unsigned num_frames = length / audio_format.GetFrameSize();
@ -277,7 +256,6 @@ VorbisEncoder::Write(const void *data, size_t length, gcc_unused Error &error)
vorbis_analysis_wrote(&vd, num_frames);
BlockOut();
return true;
}
const EncoderPlugin vorbis_encoder_plugin = {

View File

@ -37,7 +37,7 @@ public:
WaveEncoder(AudioFormat &audio_format);
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override;
void Write(const void *data, size_t length) override;
size_t Read(void *dest, size_t length) override {
return buffer.Read((uint8_t *)dest, length);
@ -46,7 +46,7 @@ public:
class PreparedWaveEncoder final : public PreparedEncoder {
/* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override {
Encoder *Open(AudioFormat &audio_format) override {
return new WaveEncoder(audio_format);
}
@ -186,9 +186,8 @@ pcm24_to_wave(uint8_t *dst8, const uint32_t *src32, size_t length)
return (dst8 - dst_old);
}
bool
WaveEncoder::Write(const void *src, size_t length,
gcc_unused Error &error)
void
WaveEncoder::Write(const void *src, size_t length)
{
uint8_t *dst = buffer.Write(length);
@ -223,7 +222,6 @@ WaveEncoder::Write(const void *src, size_t length,
}
buffer.Append(length);
return true;
}
const EncoderPlugin wave_encoder_plugin = {

View File

@ -110,8 +110,10 @@ private:
/**
* Finish the encoder and commit the file.
*
* Throws #std::runtime_error on error.
*/
bool Commit(Error &error);
void Commit();
void FinishFormat();
bool ReopenFormat(AllocatedPath &&new_path, Error &error);
@ -209,10 +211,11 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
/* open the encoder */
encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr) {
try {
encoder = prepared_encoder->Open(audio_format);
} catch (const std::runtime_error &) {
delete file;
return false;
throw;
}
if (!HasDynamicPath()) {
@ -235,39 +238,33 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
return true;
}
inline bool
RecorderOutput::Commit(Error &error)
inline void
RecorderOutput::Commit()
{
assert(!path.IsNull());
/* flush the encoder and write the rest to the file */
bool success = encoder->End(error);
if (success) {
try {
EncoderToFile();
} catch (...) {
delete encoder;
throw;
}
try {
encoder->End();
EncoderToFile();
} catch (...) {
delete encoder;
throw;
}
/* now really close everything */
delete encoder;
if (success) {
try {
file->Commit();
} catch (...) {
delete file;
throw;
}
try {
file->Commit();
} catch (...) {
delete file;
throw;
}
delete file;
return success;
}
inline void
@ -282,9 +279,7 @@ RecorderOutput::Close()
}
try {
Error error;
if (!Commit(error))
LogError(error);
Commit();
} catch (const std::exception &e) {
LogError(e);
}
@ -304,9 +299,7 @@ RecorderOutput::FinishFormat()
return;
try {
Error error;
if (!Commit(error))
LogError(error);
Commit();
} catch (const std::exception &e) {
LogError(e);
}
@ -331,10 +324,12 @@ RecorderOutput::ReopenFormat(AllocatedPath &&new_path, Error &error)
}
AudioFormat new_audio_format = effective_audio_format;
encoder = prepared_encoder->Open(new_audio_format, error);
if (encoder == nullptr) {
try {
encoder = prepared_encoder->Open(new_audio_format);
} catch (...) {
delete new_file;
return false;
throw;
}
/* reopening the encoder must always result in the same
@ -395,21 +390,9 @@ RecorderOutput::SendTag(const Tag &tag)
}
}
Error error;
if (!encoder->PreTag(error)) {
LogError(error);
return;
}
try {
EncoderToFile();
} catch (const std::exception &e) {
LogError(e);
return;
}
if (!encoder->SendTag(tag, error))
LogError(error);
encoder->PreTag();
EncoderToFile();
encoder->SendTag(tag);
}
inline size_t
@ -423,8 +406,7 @@ RecorderOutput::Play(const void *chunk, size_t size, Error &error)
return size;
}
if (!encoder->Write(chunk, size, error))
return 0;
encoder->Write(chunk, size);
try {
EncoderToFile();

View File

@ -32,6 +32,8 @@
#include <shout/shout.h>
#include <stdexcept>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -366,8 +368,12 @@ void
ShoutOutput::Close()
{
if (encoder != nullptr) {
if (encoder->End(IgnoreError()))
try {
encoder->End();
write_page(this, IgnoreError());
} catch (const std::runtime_error &) {
/* ignore */
}
delete encoder;
}
@ -410,16 +416,22 @@ ShoutOutput::Open(AudioFormat &audio_format, Error &error)
if (!shout_connect(this, error))
return false;
encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr) {
shout_close(shout_conn);
return false;
}
try {
encoder = prepared_encoder->Open(audio_format);
if (!write_page(this, error)) {
delete encoder;
try {
if (!write_page(this, error)) {
delete encoder;
shout_close(shout_conn);
return false;
}
} catch (const std::runtime_error &) {
delete encoder;
throw;
}
} catch (const std::runtime_error &) {
shout_close(shout_conn);
return false;
throw;
}
return true;
@ -438,8 +450,8 @@ ShoutOutput::Delay() const
size_t
ShoutOutput::Play(const void *chunk, size_t size, Error &error)
{
return encoder->Write(chunk, size, error) &&
write_page(this, error)
encoder->Write(chunk, size);
return write_page(this, error)
? size
: 0;
}
@ -484,13 +496,15 @@ ShoutOutput::SendTag(const Tag &tag)
if (encoder->ImplementsTag()) {
/* encoder plugin supports stream tags */
encoder->PreTag();
Error error;
if (!encoder->PreTag(error) ||
!write_page(this, error) ||
!encoder->SendTag(tag, error)) {
if (!write_page(this, error)) {
LogError(error);
return;
}
encoder->SendTag(tag);
} else {
/* no stream tag support: fall back to icy-metadata */
char song[1024];

View File

@ -171,8 +171,10 @@ public:
/**
* Caller must lock the mutex.
*
* Throws #std::runtime_error on error.
*/
bool OpenEncoder(AudioFormat &audio_format, Error &error);
void OpenEncoder(AudioFormat &audio_format);
/**
* Caller must lock the mutex.
@ -237,7 +239,10 @@ public:
*/
void BroadcastFromEncoder();
bool EncodeAndPlay(const void *chunk, size_t size, Error &error);
/**
* Throws #std::runtime_error on error.
*/
void EncodeAndPlay(const void *chunk, size_t size);
void SendTag(const Tag &tag);

View File

@ -221,7 +221,12 @@ HttpdOutput::ReadPage()
/* we have fed a lot of input into the encoder, but it
didn't give anything back yet - flush now to avoid
buffer underruns */
encoder->Flush(IgnoreError());
try {
encoder->Flush();
} catch (const std::runtime_error &) {
/* ignore */
}
unflushed_input = 0;
}
@ -260,12 +265,10 @@ httpd_output_disable(AudioOutput *ao)
httpd->Unbind();
}
inline bool
HttpdOutput::OpenEncoder(AudioFormat &audio_format, Error &error)
inline void
HttpdOutput::OpenEncoder(AudioFormat &audio_format)
{
encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr)
return false;
encoder = prepared_encoder->Open(audio_format);
/* we have to remember the encoder header, i.e. the first
bytes of encoder output after opening it, because it has to
@ -273,20 +276,15 @@ HttpdOutput::OpenEncoder(AudioFormat &audio_format, Error &error)
header = ReadPage();
unflushed_input = 0;
return true;
}
inline bool
HttpdOutput::Open(AudioFormat &audio_format, Error &error)
HttpdOutput::Open(AudioFormat &audio_format, Error &)
{
assert(!open);
assert(clients.empty());
/* open the encoder */
if (!OpenEncoder(audio_format, error))
return false;
OpenEncoder(audio_format);
/* initialize other attributes */
@ -410,25 +408,21 @@ HttpdOutput::BroadcastFromEncoder()
DeferredMonitor::Schedule();
}
inline bool
HttpdOutput::EncodeAndPlay(const void *chunk, size_t size, Error &error)
inline void
HttpdOutput::EncodeAndPlay(const void *chunk, size_t size)
{
if (!encoder->Write(chunk, size, error))
return false;
encoder->Write(chunk, size);
unflushed_input += size;
BroadcastFromEncoder();
return true;
}
inline size_t
HttpdOutput::Play(const void *chunk, size_t size, Error &error)
HttpdOutput::Play(const void *chunk, size_t size, Error &)
{
if (LockHasClients()) {
if (!EncodeAndPlay(chunk, size, error))
return 0;
}
if (LockHasClients())
EncodeAndPlay(chunk, size);
if (!timer->IsStarted())
timer->Start();
@ -468,13 +462,22 @@ HttpdOutput::SendTag(const Tag &tag)
/* flush the current stream, and end it */
encoder->PreTag(IgnoreError());
try {
encoder->PreTag();
} catch (const std::runtime_error &) {
/* ignore */
}
BroadcastFromEncoder();
/* send the tag to the encoder - which starts a new
stream now */
encoder->SendTag(tag, IgnoreError());
try {
encoder->SendTag(tag);
} catch (const std::runtime_error &) {
/* ignore */
}
/* the first page generated by the encoder will now be
used as the new "header" page, which is sent to all

View File

@ -26,7 +26,6 @@
#include "AudioParser.hxx"
#include "config/Block.hxx"
#include "fs/io/StdioOutputStream.hxx"
#include "util/Error.hxx"
#include "Log.hxx"
#include <memory>
@ -66,7 +65,6 @@ int main(int argc, char **argv)
block.AddBlockParam("quality", "5.0", -1);
try {
Error error;
std::unique_ptr<PreparedEncoder> p_encoder(encoder_init(*plugin, block));
/* open the encoder */
@ -75,11 +73,7 @@ int main(int argc, char **argv)
if (argc > 2)
audio_format = ParseAudioFormat(argv[2], false);
std::unique_ptr<Encoder> encoder(p_encoder->Open(audio_format, error));
if (encoder == nullptr) {
LogError(error, "Failed to open encoder");
return EXIT_FAILURE;
}
std::unique_ptr<Encoder> encoder(p_encoder->Open(audio_format));
StdioOutputStream os(stdout);
@ -89,19 +83,11 @@ int main(int argc, char **argv)
ssize_t nbytes;
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
if (!encoder->Write(buffer, nbytes, error)) {
LogError(error, "encoder_write() failed");
return EXIT_FAILURE;
}
encoder->Write(buffer, nbytes);
EncoderToOutputStream(os, *encoder);
}
if (!encoder->End(error)) {
LogError(error, "encoder_flush() failed");
return EXIT_FAILURE;
}
encoder->End();
EncoderToOutputStream(os, *encoder);
return EXIT_SUCCESS;

View File

@ -27,7 +27,6 @@
#include "fs/io/StdioOutputStream.hxx"
#include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx"
#include "util/Error.hxx"
#include "Log.hxx"
#include <memory>
@ -40,8 +39,6 @@ static uint8_t zero[256];
int
main(gcc_unused int argc, gcc_unused char **argv)
try {
gcc_unused bool success;
/* create the encoder */
const auto plugin = encoder_plugin_get("vorbis");
@ -56,8 +53,7 @@ try {
/* open the encoder */
AudioFormat audio_format(44100, SampleFormat::S16, 2);
std::unique_ptr<Encoder> encoder(p_encoder->Open(audio_format,
IgnoreError()));
std::unique_ptr<Encoder> encoder(p_encoder->Open(audio_format));
assert(encoder != nullptr);
StdioOutputStream os(stdout);
@ -66,15 +62,13 @@ try {
/* write a block of data */
success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success);
encoder->Write(zero, sizeof(zero));
EncoderToOutputStream(os, *encoder);
/* write a tag */
success = encoder->PreTag(IgnoreError());
assert(success);
encoder->PreTag();
EncoderToOutputStream(os, *encoder);
@ -87,21 +81,17 @@ try {
tag_builder.Commit(tag);
}
success = encoder->SendTag(tag, IgnoreError());
assert(success);
encoder->SendTag(tag);
EncoderToOutputStream(os, *encoder);
/* write another block of data */
success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success);
encoder->Write(zero, sizeof(zero));
/* finish */
success = encoder->End(IgnoreError());
assert(success);
encoder->End();
EncoderToOutputStream(os, *encoder);
return EXIT_SUCCESS;