encoder: migrate from class Error to C++ exceptions
This commit is contained in:
parent
b8aac3f8fc
commit
d8b6aff23a
|
@ -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.
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue