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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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