encoder/Interface: move instance methods to abstract class

Rename struct Encoder to PreparedEncoder, and add a new (abstract)
class Encoder which represents one encoder instance.
This commit is contained in:
Max Kellermann 2016-05-04 09:31:21 +02:00
parent 69bf835059
commit e7edc02647
18 changed files with 777 additions and 996 deletions

View File

@ -21,30 +21,108 @@
#define MPD_ENCODER_INTERFACE_HXX
#include "EncoderPlugin.hxx"
#include "Compiler.h"
#include <assert.h>
#include <stddef.h>
struct Encoder {
struct Tag;
class Encoder {
const bool implements_tag;
public:
explicit Encoder(bool _implements_tag)
:implements_tag(_implements_tag) {}
virtual ~Encoder() {}
bool ImplementsTag() const {
return implements_tag;
}
/**
* Ends the stream: flushes the encoder object, generate an
* end-of-stream marker (if applicable), make everything which
* might currently be buffered available by encoder_read().
*
* After this function has been called, the encoder may not be
* usable for more data, and only Read() and Close() can be
* called.
*
* @return true on success
*/
virtual bool End(gcc_unused Error &error) {
return true;
}
/**
* Flushes an encoder object, make everything which might
* currently be buffered available by Read().
*
* @return true on success
*/
virtual bool Flush(gcc_unused Error &error) {
return true;
}
/**
* Prepare for sending a tag to the encoder. This is used by
* some encoders to flush the previous sub-stream, in
* preparation to begin a new one.
*
* @return true on success
*/
virtual bool PreTag(gcc_unused Error &error) {
return true;
}
/**
* Sends a tag to the encoder.
*
* Instructions: call PreTag(); then obtain flushed data with
* Read(); finally call Tag().
*
* @param tag the tag object
* @return true on success
*/
virtual bool SendTag(gcc_unused const Tag &tag,
gcc_unused Error &error) {
return true;
}
/**
* Writes raw PCM data to the encoder.
*
* @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;
/**
* Reads encoded data from the encoder.
*
* Call this repeatedly until no more data is returned.
*
* @param dest the destination buffer to copy to
* @param length the maximum length of the destination buffer
* @return the number of bytes written to #dest
*/
virtual size_t Read(void *dest, size_t length) = 0;
};
struct PreparedEncoder {
const EncoderPlugin &plugin;
#ifndef NDEBUG
bool open, pre_tag, tag, end;
#endif
explicit Encoder(const EncoderPlugin &_plugin)
:plugin(_plugin)
#ifndef NDEBUG
, open(false)
#endif
{}
explicit PreparedEncoder(const EncoderPlugin &_plugin)
:plugin(_plugin) {}
/**
* Frees an #Encoder object.
*/
void Dispose() {
assert(!open);
plugin.finish(this);
}
@ -61,191 +139,19 @@ struct Encoder {
* may modify the struct to adapt it to its abilities
* @return true on success
*/
bool Open(AudioFormat &audio_format, Error &error) {
assert(!open);
bool success = plugin.open(this, audio_format, error);
#ifndef NDEBUG
open = success;
pre_tag = tag = end = false;
#endif
return success;
Encoder *Open(AudioFormat &audio_format, Error &error) {
return plugin.open(this, audio_format, error);
}
/**
* Closes the object. This disables the encoder, and readies
* it for reusal by calling Open() again.
*/
void Close() {
assert(open);
if (plugin.close != nullptr)
plugin.close(this);
#ifndef NDEBUG
open = false;
#endif
}
};
/**
* Ends the stream: flushes the encoder object, generate an
* end-of-stream marker (if applicable), make everything which might
* currently be buffered available by encoder_read().
*
* After this function has been called, the encoder may not be usable
* for more data, and only encoder_read() and Encoder::Close() can be
* called.
*
* @param encoder the encoder
* @return true on success
*/
static inline bool
encoder_end(Encoder *encoder, Error &error)
{
assert(encoder->open);
assert(!encoder->end);
#ifndef NDEBUG
encoder->end = true;
#endif
/* this method is optional */
return encoder->plugin.end != nullptr
? encoder->plugin.end(encoder, error)
: true;
}
/**
* Flushes an encoder object, make everything which might currently be
* buffered available by encoder_read().
*
* @param encoder the encoder
* @return true on success
*/
static inline bool
encoder_flush(Encoder *encoder, Error &error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
/* this method is optional */
return encoder->plugin.flush != nullptr
? encoder->plugin.flush(encoder, error)
: true;
}
/**
* Prepare for sending a tag to the encoder. This is used by some
* encoders to flush the previous sub-stream, in preparation to begin
* a new one.
*
* @param encoder the encoder
* @return true on success
*/
static inline bool
encoder_pre_tag(Encoder *encoder, Error &error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
/* this method is optional */
bool success = encoder->plugin.pre_tag != nullptr
? encoder->plugin.pre_tag(encoder, error)
: true;
#ifndef NDEBUG
encoder->pre_tag = success;
#endif
return success;
}
/**
* Sends a tag to the encoder.
*
* Instructions: call encoder_pre_tag(); then obtain flushed data with
* encoder_read(); finally call encoder_tag().
*
* @param encoder the encoder
* @param tag the tag object
* @return true on success
*/
static inline bool
encoder_tag(Encoder *encoder, const Tag &tag, Error &error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(encoder->tag);
assert(!encoder->end);
#ifndef NDEBUG
encoder->tag = false;
#endif
/* this method is optional */
return encoder->plugin.tag != nullptr
? encoder->plugin.tag(encoder, tag, error)
: true;
}
/**
* Writes raw PCM data to the encoder.
*
* @param encoder the encoder
* @param data the buffer containing PCM samples
* @param length the length of the buffer in bytes
* @return true on success
*/
static inline bool
encoder_write(Encoder *encoder, const void *data, size_t length,
Error &error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
return encoder->plugin.write(encoder, data, length, error);
}
/**
* Reads encoded data from the encoder.
*
* Call this repeatedly until no more data is returned.
*
* @param encoder the encoder
* @param dest the destination buffer to copy to
* @param length the maximum length of the destination buffer
* @return the number of bytes written to #dest
*/
static inline size_t
encoder_read(Encoder *encoder, void *dest, size_t length)
{
assert(encoder->open);
assert(!encoder->pre_tag || !encoder->tag);
#ifndef NDEBUG
if (encoder->pre_tag) {
encoder->pre_tag = false;
encoder->tag = true;
}
#endif
return encoder->plugin.read(encoder, dest, length);
}
/**
* Get mime type of encoded content.
*
* @return an constant string, nullptr on failure
*/
static inline const char *
encoder_get_mime_type(Encoder *encoder)
encoder_get_mime_type(PreparedEncoder *encoder)
{
/* this method is optional */
return encoder->plugin.get_mime_type != nullptr

View File

@ -20,44 +20,25 @@
#ifndef MPD_ENCODER_PLUGIN_HXX
#define MPD_ENCODER_PLUGIN_HXX
#include <stddef.h>
struct Encoder;
struct PreparedEncoder;
class Encoder;
struct AudioFormat;
struct ConfigBlock;
struct Tag;
class Error;
struct EncoderPlugin {
const char *name;
Encoder *(*init)(const ConfigBlock &block,
PreparedEncoder *(*init)(const ConfigBlock &block,
Error &error);
void (*finish)(PreparedEncoder *encoder);
Encoder *(*open)(PreparedEncoder *encoder,
AudioFormat &audio_format,
Error &error);
void (*finish)(Encoder *encoder);
bool (*open)(Encoder *encoder,
AudioFormat &audio_format,
Error &error);
void (*close)(Encoder *encoder);
bool (*end)(Encoder *encoder, Error &error);
bool (*flush)(Encoder *encoder, Error &error);
bool (*pre_tag)(Encoder *encoder, Error &error);
bool (*tag)(Encoder *encoder, const Tag &tag,
Error &error);
bool (*write)(Encoder *encoder,
const void *data, size_t length,
Error &error);
size_t (*read)(Encoder *encoder, void *dest, size_t length);
const char *(*get_mime_type)(Encoder *encoder);
const char *(*get_mime_type)(PreparedEncoder *encoder);
};
/**
@ -67,7 +48,7 @@ struct EncoderPlugin {
* @param error location to store the error occurring, or nullptr to ignore errors.
* @return an encoder object on success, nullptr on failure
*/
static inline Encoder *
static inline PreparedEncoder *
encoder_init(const EncoderPlugin &plugin, const ConfigBlock &block,
Error &error)
{

View File

@ -29,7 +29,7 @@ EncoderToOutputStream(OutputStream &os, Encoder &encoder)
/* read from the encoder */
char buffer[32768];
size_t nbytes = encoder_read(&encoder, buffer, sizeof(buffer));
size_t nbytes = encoder.Read(buffer, sizeof(buffer));
if (nbytes == 0)
return;

View File

@ -22,8 +22,8 @@
#include "check.h"
struct Encoder;
class OutputStream;
class Encoder;
void
EncoderToOutputStream(OutputStream &os, Encoder &encoder);

View File

@ -23,7 +23,6 @@
#include "AudioFormat.hxx"
#include "pcm/PcmBuffer.hxx"
#include "config/ConfigError.hxx"
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
@ -34,13 +33,10 @@
#error libFLAC is too old
#endif
struct flac_encoder {
Encoder encoder;
class FlacEncoder final : public Encoder {
const AudioFormat audio_format;
AudioFormat audio_format;
unsigned compression;
FLAC__StreamEncoder *fse;
FLAC__StreamEncoder *const fse;
PcmBuffer expand_buffer;
@ -48,29 +44,76 @@ struct flac_encoder {
* This buffer will hold encoded data from libFLAC until it is
* picked up with flac_encoder_read().
*/
Manual<DynamicFifoBuffer<uint8_t>> output_buffer;
DynamicFifoBuffer<uint8_t> output_buffer;
flac_encoder():encoder(flac_encoder_plugin) {}
public:
FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse)
:Encoder(false),
audio_format(_audio_format), fse(_fse),
output_buffer(8192) {}
~FlacEncoder() override {
FLAC__stream_encoder_delete(fse);
}
bool Init(Error &error);
/* virtual methods from class Encoder */
bool End(Error &) override {
(void) FLAC__stream_encoder_finish(fse);
return true;
}
bool Flush(Error &) override {
(void) FLAC__stream_encoder_finish(fse);
return true;
}
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override {
return output_buffer.Read((uint8_t *)dest, length);
}
private:
static FLAC__StreamEncoderWriteStatus WriteCallback(const FLAC__StreamEncoder *,
const FLAC__byte data[],
size_t bytes,
gcc_unused unsigned samples,
gcc_unused unsigned current_frame,
void *client_data) {
auto &encoder = *(FlacEncoder *)client_data;
encoder.output_buffer.Append((const uint8_t *)data, bytes);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
};
struct PreparedFlacEncoder {
PreparedEncoder encoder;
unsigned compression;
PreparedFlacEncoder():encoder(flac_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
};
static constexpr Domain flac_encoder_domain("vorbis_encoder");
static bool
flac_encoder_configure(struct flac_encoder *encoder, const ConfigBlock &block,
gcc_unused Error &error)
bool
PreparedFlacEncoder::Configure(const ConfigBlock &block, Error &)
{
encoder->compression = block.GetBlockValue("compression", 5u);
compression = block.GetBlockValue("compression", 5u);
return true;
}
static Encoder *
static PreparedEncoder *
flac_encoder_init(const ConfigBlock &block, Error &error)
{
flac_encoder *encoder = new flac_encoder();
auto *encoder = new PreparedFlacEncoder();
/* load configuration from "block" */
if (!flac_encoder_configure(encoder, block, error)) {
if (!encoder->Configure(block, error)) {
/* configuration has failed, roll back and return error */
delete encoder;
return nullptr;
@ -80,9 +123,9 @@ flac_encoder_init(const ConfigBlock &block, Error &error)
}
static void
flac_encoder_finish(Encoder *_encoder)
flac_encoder_finish(PreparedEncoder *_encoder)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
auto *encoder = (PreparedFlacEncoder *)_encoder;
/* the real libFLAC cleanup was already performed by
flac_encoder_close(), so no real work here */
@ -90,71 +133,67 @@ flac_encoder_finish(Encoder *_encoder)
}
static bool
flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
const AudioFormat &audio_format, unsigned bits_per_sample,
Error &error)
{
if ( !FLAC__stream_encoder_set_compression_level(encoder->fse,
encoder->compression)) {
if (!FLAC__stream_encoder_set_compression_level(fse, compression)) {
error.Format(config_domain,
"error setting flac compression to %d",
encoder->compression);
compression);
return false;
}
if ( !FLAC__stream_encoder_set_channels(encoder->fse,
encoder->audio_format.channels)) {
if (!FLAC__stream_encoder_set_channels(fse, audio_format.channels)) {
error.Format(config_domain,
"error setting flac channels num to %d",
encoder->audio_format.channels);
audio_format.channels);
return false;
}
if ( !FLAC__stream_encoder_set_bits_per_sample(encoder->fse,
bits_per_sample)) {
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_sample_rate(encoder->fse,
encoder->audio_format.sample_rate)) {
if (!FLAC__stream_encoder_set_sample_rate(fse,
audio_format.sample_rate)) {
error.Format(config_domain,
"error setting flac sample rate to %d",
encoder->audio_format.sample_rate);
audio_format.sample_rate);
return false;
}
return true;
}
static FLAC__StreamEncoderWriteStatus
flac_write_callback(gcc_unused const FLAC__StreamEncoder *fse,
const FLAC__byte data[],
size_t bytes,
gcc_unused unsigned samples,
gcc_unused unsigned current_frame, void *client_data)
bool
FlacEncoder::Init(Error &error)
{
struct flac_encoder *encoder = (struct flac_encoder *) client_data;
/* this immediately outputs data through callback */
//transfer data to buffer
encoder->output_buffer->Append((const uint8_t *)data, bytes);
auto init_status =
FLAC__stream_encoder_init_stream(fse,
WriteCallback,
nullptr, nullptr, nullptr,
this);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
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;
}
static void
flac_encoder_close(Encoder *_encoder)
static Encoder *
flac_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format, Error &error)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
FLAC__stream_encoder_delete(encoder->fse);
encoder->expand_buffer.Clear();
encoder->output_buffer.Destruct();
}
static bool
flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
auto *encoder = (PreparedFlacEncoder *)_encoder;
unsigned bits_per_sample;
/* FIXME: flac should support 32bit as well */
@ -176,51 +215,26 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
audio_format.format = SampleFormat::S24_P32;
}
encoder->audio_format = audio_format;
/* allocate the encoder */
encoder->fse = FLAC__stream_encoder_new();
if (encoder->fse == nullptr) {
error.Set(flac_encoder_domain, "flac_new() failed");
return false;
auto fse = FLAC__stream_encoder_new();
if (fse == nullptr) {
error.Set(flac_encoder_domain, "FLAC__stream_encoder_new() failed");
return nullptr;
}
if (!flac_encoder_setup(encoder, bits_per_sample, error)) {
FLAC__stream_encoder_delete(encoder->fse);
return false;
if (!flac_encoder_setup(fse, encoder->compression,
audio_format, bits_per_sample, error)) {
FLAC__stream_encoder_delete(fse);
return nullptr;
}
encoder->output_buffer.Construct(8192);
/* this immediately outputs data through callback */
{
FLAC__StreamEncoderInitStatus init_status;
init_status = FLAC__stream_encoder_init_stream(encoder->fse,
flac_write_callback,
nullptr, nullptr, nullptr, encoder);
if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
error.Format(flac_encoder_domain,
"failed to initialize encoder: %s\n",
FLAC__StreamEncoderInitStatusString[init_status]);
flac_encoder_close(_encoder);
return false;
}
auto *e = new FlacEncoder(audio_format, fse);
if (!e->Init(error)) {
delete e;
return nullptr;
}
return true;
}
static bool
flac_encoder_flush(Encoder *_encoder, gcc_unused Error &error)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
(void) FLAC__stream_encoder_finish(encoder->fse);
return true;
return e;
}
static inline void
@ -241,31 +255,27 @@ pcm16_to_flac(int32_t *out, const int16_t *in, unsigned num_samples)
}
}
static bool
flac_encoder_write(Encoder *_encoder,
const void *data, size_t length,
gcc_unused Error &error)
bool
FlacEncoder::Write(const void *data, size_t length, Error &error)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
unsigned num_frames, num_samples;
void *exbuffer;
const void *buffer = nullptr;
/* format conversion */
num_frames = length / encoder->audio_format.GetFrameSize();
num_samples = num_frames * encoder->audio_format.channels;
const unsigned num_frames = length / audio_format.GetFrameSize();
const unsigned num_samples = num_frames * audio_format.channels;
switch (encoder->audio_format.format) {
switch (audio_format.format) {
case SampleFormat::S8:
exbuffer = encoder->expand_buffer.Get(length * 4);
exbuffer = expand_buffer.Get(length * 4);
pcm8_to_flac((int32_t *)exbuffer, (const int8_t *)data,
num_samples);
buffer = exbuffer;
break;
case SampleFormat::S16:
exbuffer = encoder->expand_buffer.Get(length * 2);
exbuffer = expand_buffer.Get(length * 2);
pcm16_to_flac((int32_t *)exbuffer, (const int16_t *)data,
num_samples);
buffer = exbuffer;
@ -284,7 +294,7 @@ flac_encoder_write(Encoder *_encoder,
/* feed samples to encoder */
if (!FLAC__stream_encoder_process_interleaved(encoder->fse,
if (!FLAC__stream_encoder_process_interleaved(fse,
(const FLAC__int32 *)buffer,
num_frames)) {
error.Set(flac_encoder_domain, "flac encoder process failed");
@ -294,16 +304,8 @@ flac_encoder_write(Encoder *_encoder,
return true;
}
static size_t
flac_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
return encoder->output_buffer->Read((uint8_t *)dest, length);
}
static const char *
flac_encoder_get_mime_type(gcc_unused Encoder *_encoder)
flac_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{
return "audio/flac";
}
@ -313,13 +315,6 @@ const EncoderPlugin flac_encoder_plugin = {
flac_encoder_init,
flac_encoder_finish,
flac_encoder_open,
flac_encoder_close,
flac_encoder_flush,
flac_encoder_flush,
nullptr,
nullptr,
flac_encoder_write,
flac_encoder_read,
flac_encoder_get_mime_type,
};

View File

@ -24,7 +24,6 @@
#include "config/ConfigError.hxx"
#include "util/NumberParser.hxx"
#include "util/ReusableArray.hxx"
#include "util/Manual.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
@ -33,19 +32,34 @@
#include <assert.h>
#include <string.h>
struct LameEncoder final {
Encoder encoder;
class LameEncoder final : public Encoder {
const AudioFormat audio_format;
lame_global_flags *const gfp;
ReusableArray<unsigned char, 32768> output_buffer;
unsigned char *output_begin = nullptr, *output_end = nullptr;
public:
LameEncoder(const AudioFormat _audio_format,
lame_global_flags *_gfp)
:Encoder(false),
audio_format(_audio_format), gfp(_gfp) {}
~LameEncoder() override;
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override;
};
struct PreparedLameEncoder final {
PreparedEncoder encoder;
AudioFormat audio_format;
float quality;
int bitrate;
lame_global_flags *gfp;
Manual<ReusableArray<unsigned char, 32768>> output_buffer;
unsigned char *output_begin, *output_end;
LameEncoder():encoder(lame_encoder_plugin) {}
PreparedLameEncoder():encoder(lame_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
};
@ -53,7 +67,7 @@ struct LameEncoder final {
static constexpr Domain lame_encoder_domain("lame_encoder");
bool
LameEncoder::Configure(const ConfigBlock &block, Error &error)
PreparedLameEncoder::Configure(const ConfigBlock &block, Error &error)
{
const char *value;
char *endptr;
@ -100,10 +114,10 @@ LameEncoder::Configure(const ConfigBlock &block, Error &error)
return true;
}
static Encoder *
static PreparedEncoder *
lame_encoder_init(const ConfigBlock &block, Error &error)
{
LameEncoder *encoder = new LameEncoder();
auto *encoder = new PreparedLameEncoder();
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
@ -116,9 +130,9 @@ lame_encoder_init(const ConfigBlock &block, Error &error)
}
static void
lame_encoder_finish(Encoder *_encoder)
lame_encoder_finish(PreparedEncoder *_encoder)
{
LameEncoder *encoder = (LameEncoder *)_encoder;
auto *encoder = (PreparedLameEncoder *)_encoder;
/* the real liblame cleanup was already performed by
lame_encoder_close(), so no real work here */
@ -126,17 +140,18 @@ lame_encoder_finish(Encoder *_encoder)
}
static bool
lame_encoder_setup(LameEncoder *encoder, Error &error)
lame_encoder_setup(lame_global_flags *gfp, float quality, int bitrate,
const AudioFormat &audio_format, Error &error)
{
if (encoder->quality >= -1.0) {
if (quality >= -1.0) {
/* a quality was configured (VBR) */
if (0 != lame_set_VBR(encoder->gfp, vbr_rh)) {
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(encoder->gfp, encoder->quality)) {
if (0 != lame_set_VBR_q(gfp, quality)) {
error.Set(lame_encoder_domain,
"error setting lame VBR quality");
return false;
@ -144,35 +159,32 @@ lame_encoder_setup(LameEncoder *encoder, Error &error)
} else {
/* a bit rate was configured */
if (0 != lame_set_brate(encoder->gfp, encoder->bitrate)) {
if (0 != lame_set_brate(gfp, bitrate)) {
error.Set(lame_encoder_domain,
"error setting lame bitrate");
return false;
}
}
if (0 != lame_set_num_channels(encoder->gfp,
encoder->audio_format.channels)) {
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_in_samplerate(encoder->gfp,
encoder->audio_format.sample_rate)) {
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_out_samplerate(encoder->gfp,
encoder->audio_format.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_init_params(encoder->gfp)) {
if (0 > lame_init_params(gfp)) {
error.Set(lame_encoder_domain,
"error initializing lame params");
return false;
@ -181,98 +193,84 @@ lame_encoder_setup(LameEncoder *encoder, Error &error)
return true;
}
static bool
lame_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
static Encoder *
lame_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
Error &error)
{
LameEncoder *encoder = (LameEncoder *)_encoder;
auto *encoder = (PreparedLameEncoder *)_encoder;
audio_format.format = SampleFormat::S16;
audio_format.channels = 2;
encoder->audio_format = audio_format;
encoder->gfp = lame_init();
if (encoder->gfp == nullptr) {
auto gfp = lame_init();
if (gfp == nullptr) {
error.Set(lame_encoder_domain, "lame_init() failed");
return false;
return nullptr;
}
if (!lame_encoder_setup(encoder, error)) {
lame_close(encoder->gfp);
return false;
if (!lame_encoder_setup(gfp, encoder->quality, encoder->bitrate,
audio_format, error)) {
lame_close(gfp);
return nullptr;
}
encoder->output_buffer.Construct();
encoder->output_begin = encoder->output_end = nullptr;
return true;
return new LameEncoder(audio_format, gfp);
}
static void
lame_encoder_close(Encoder *_encoder)
LameEncoder::~LameEncoder()
{
LameEncoder *encoder = (LameEncoder *)_encoder;
lame_close(encoder->gfp);
encoder->output_buffer.Destruct();
lame_close(gfp);
}
static bool
lame_encoder_write(Encoder *_encoder,
const void *data, size_t length,
bool
LameEncoder::Write(const void *data, size_t length,
gcc_unused Error &error)
{
LameEncoder *encoder = (LameEncoder *)_encoder;
const int16_t *src = (const int16_t*)data;
assert(encoder->output_begin == encoder->output_end);
assert(output_begin == output_end);
const unsigned num_frames =
length / encoder->audio_format.GetFrameSize();
const unsigned num_samples =
length / encoder->audio_format.GetSampleSize();
const unsigned num_frames = length / audio_format.GetFrameSize();
const unsigned num_samples = length / audio_format.GetSampleSize();
/* worst-case formula according to LAME documentation */
const size_t output_buffer_size = 5 * num_samples / 4 + 7200;
const auto output_buffer = encoder->output_buffer->Get(output_buffer_size);
const auto dest = output_buffer.Get(output_buffer_size);
/* this is for only 16-bit audio */
int bytes_out = lame_encode_buffer_interleaved(encoder->gfp,
int bytes_out = lame_encode_buffer_interleaved(gfp,
const_cast<short *>(src),
num_frames,
output_buffer,
output_buffer_size);
dest, output_buffer_size);
if (bytes_out < 0) {
error.Set(lame_encoder_domain, "lame encoder failed");
return false;
}
encoder->output_begin = output_buffer;
encoder->output_end = output_buffer + bytes_out;
output_begin = dest;
output_end = dest + bytes_out;
return true;
}
static size_t
lame_encoder_read(Encoder *_encoder, void *dest, size_t length)
size_t
LameEncoder::Read(void *dest, size_t length)
{
LameEncoder *encoder = (LameEncoder *)_encoder;
const auto begin = encoder->output_begin;
assert(begin <= encoder->output_end);
const size_t remainning = encoder->output_end - begin;
const auto begin = output_begin;
assert(begin <= output_end);
const size_t remainning = output_end - begin;
if (length > remainning)
length = remainning;
memcpy(dest, begin, length);
encoder->output_begin = begin + length;
output_begin = begin + length;
return length;
}
static const char *
lame_encoder_get_mime_type(gcc_unused Encoder *_encoder)
lame_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{
return "audio/mpeg";
}
@ -282,12 +280,5 @@ const EncoderPlugin lame_encoder_plugin = {
lame_encoder_init,
lame_encoder_finish,
lame_encoder_open,
lame_encoder_close,
nullptr,
nullptr,
nullptr,
nullptr,
lame_encoder_write,
lame_encoder_read,
lame_encoder_get_mime_type,
};

View File

@ -20,73 +20,58 @@
#include "config.h"
#include "NullEncoderPlugin.hxx"
#include "../EncoderAPI.hxx"
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include "Compiler.h"
#include <assert.h>
struct NullEncoder final {
Encoder encoder;
Manual<DynamicFifoBuffer<uint8_t>> buffer;
class NullEncoder final : public Encoder {
DynamicFifoBuffer<uint8_t> buffer;
public:
NullEncoder()
:Encoder(false),
buffer(8192) {}
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override {
buffer.Append((const uint8_t *)data, length);
return true;
}
size_t Read(void *dest, size_t length) override {
return buffer.Read((uint8_t *)dest, length);
}
};
struct PreparedNullEncoder final {
PreparedEncoder encoder;
PreparedNullEncoder()
:encoder(null_encoder_plugin) {}
};
static Encoder *
static PreparedEncoder *
null_encoder_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error)
{
NullEncoder *encoder = new NullEncoder();
auto *encoder = new PreparedNullEncoder();
return &encoder->encoder;
}
static void
null_encoder_finish(Encoder *_encoder)
null_encoder_finish(PreparedEncoder *_encoder)
{
NullEncoder *encoder = (NullEncoder *)_encoder;
auto *encoder = (PreparedNullEncoder *)_encoder;
delete encoder;
}
static void
null_encoder_close(Encoder *_encoder)
{
NullEncoder *encoder = (NullEncoder *)_encoder;
encoder->buffer.Destruct();
}
static bool
null_encoder_open(Encoder *_encoder,
static Encoder *
null_encoder_open(gcc_unused PreparedEncoder *encoder,
gcc_unused AudioFormat &audio_format,
gcc_unused Error &error)
{
NullEncoder *encoder = (NullEncoder *)_encoder;
encoder->buffer.Construct(8192);
return true;
}
static bool
null_encoder_write(Encoder *_encoder,
const void *data, size_t length,
gcc_unused Error &error)
{
NullEncoder *encoder = (NullEncoder *)_encoder;
encoder->buffer->Append((const uint8_t *)data, length);
return length;
}
static size_t
null_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
NullEncoder *encoder = (NullEncoder *)_encoder;
return encoder->buffer->Read((uint8_t *)dest, length);
return new NullEncoder();
}
const EncoderPlugin null_encoder_plugin = {
@ -94,12 +79,5 @@ const EncoderPlugin null_encoder_plugin = {
null_encoder_init,
null_encoder_finish,
null_encoder_open,
null_encoder_close,
nullptr,
nullptr,
nullptr,
nullptr,
null_encoder_write,
null_encoder_read,
nullptr,
};

View File

@ -35,26 +35,18 @@
#include <assert.h>
#include <stdlib.h>
struct OpusEncoder {
/** the base class */
Encoder encoder;
namespace {
/* configuration */
class OpusEncoder final : public Encoder {
const AudioFormat audio_format;
opus_int32 bitrate;
int complexity;
int signal;
const size_t frame_size;
/* runtime information */
const size_t buffer_frames, buffer_size;
size_t buffer_position = 0;
uint8_t *const buffer;
AudioFormat audio_format;
size_t frame_size;
size_t buffer_frames, buffer_size, buffer_position;
uint8_t *buffer;
OpusEncoder *enc;
::OpusEncoder *const enc;
unsigned char buffer2[1275 * 3 + 7];
@ -62,35 +54,49 @@ struct OpusEncoder {
int lookahead;
ogg_int64_t packetno;
ogg_int64_t packetno = 0;
ogg_int64_t granulepos;
OpusEncoder():encoder(opus_encoder_plugin), granulepos(0) {}
public:
OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc);
~OpusEncoder() override;
bool Configure(const ConfigBlock &block, Error &error);
bool Open(AudioFormat &audio_format, Error &error);
void Close();
/* virtual methods from class Encoder */
bool End(Error &) override;
bool Flush(Error &) override;
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override;
private:
bool DoEncode(bool eos, Error &error);
bool End(Error &error);
bool Flush(Error &error);
bool WriteSilence(unsigned fill_frames, Error &error);
bool Write(const void *_data, size_t length, Error &error);
void GenerateHead();
void GenerateTags();
};
size_t Read(void *dest, size_t length);
struct PreparedOpusEncoder {
/** the base class */
PreparedEncoder encoder;
/* configuration */
opus_int32 bitrate;
int complexity;
int signal;
PreparedOpusEncoder():encoder(opus_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
Encoder *Open(AudioFormat &audio_format, Error &error);
};
static constexpr Domain opus_encoder_domain("opus_encoder");
bool
OpusEncoder::Configure(const ConfigBlock &block, Error &error)
PreparedOpusEncoder::Configure(const ConfigBlock &block, Error &error)
{
const char *value = block.GetBlockValue("bitrate", "auto");
if (strcmp(value, "auto") == 0)
@ -128,10 +134,10 @@ OpusEncoder::Configure(const ConfigBlock &block, Error &error)
return true;
}
static Encoder *
static PreparedEncoder *
opus_encoder_init(const ConfigBlock &block, Error &error)
{
auto *encoder = new OpusEncoder();
auto *encoder = new PreparedOpusEncoder();
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
@ -144,93 +150,86 @@ opus_encoder_init(const ConfigBlock &block, Error &error)
}
static void
opus_encoder_finish(Encoder *_encoder)
opus_encoder_finish(PreparedEncoder *_encoder)
{
auto *encoder = (OpusEncoder *)_encoder;
auto *encoder = (PreparedOpusEncoder *)_encoder;
/* the real libopus cleanup was already performed by
opus_encoder_close(), so no real work here */
delete encoder;
}
bool
OpusEncoder::Open(AudioFormat &_audio_format, Error &error)
OpusEncoder::OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc)
:Encoder(false),
audio_format(_audio_format),
frame_size(_audio_format.GetFrameSize()),
buffer_frames(_audio_format.sample_rate / 50),
buffer_size(frame_size * buffer_frames),
buffer((unsigned char *)xalloc(buffer_size)),
enc(_enc)
{
opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
stream.Initialize(GenerateOggSerial());
}
Encoder *
PreparedOpusEncoder::Open(AudioFormat &audio_format, Error &error)
{
/* libopus supports only 48 kHz */
_audio_format.sample_rate = 48000;
audio_format.sample_rate = 48000;
if (_audio_format.channels > 2)
_audio_format.channels = 1;
if (audio_format.channels > 2)
audio_format.channels = 1;
switch (_audio_format.format) {
switch (audio_format.format) {
case SampleFormat::S16:
case SampleFormat::FLOAT:
break;
case SampleFormat::S8:
_audio_format.format = SampleFormat::S16;
audio_format.format = SampleFormat::S16;
break;
default:
_audio_format.format = SampleFormat::FLOAT;
audio_format.format = SampleFormat::FLOAT;
break;
}
audio_format = _audio_format;
frame_size = _audio_format.GetFrameSize();
int error_code;
enc = opus_encoder_create(_audio_format.sample_rate,
_audio_format.channels,
OPUS_APPLICATION_AUDIO,
&error_code);
auto *enc = opus_encoder_create(audio_format.sample_rate,
audio_format.channels,
OPUS_APPLICATION_AUDIO,
&error_code);
if (enc == nullptr) {
error.Set(opus_encoder_domain, error_code,
opus_strerror(error_code));
return false;
return nullptr;
}
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal));
opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
buffer_frames = _audio_format.sample_rate / 50;
buffer_size = frame_size * buffer_frames;
buffer_position = 0;
buffer = (unsigned char *)xalloc(buffer_size);
stream.Initialize(GenerateOggSerial());
packetno = 0;
return true;
return new OpusEncoder(audio_format, enc);
}
static bool
opus_encoder_open(Encoder *_encoder,
static Encoder *
opus_encoder_open(PreparedEncoder *_encoder,
AudioFormat &audio_format,
Error &error)
{
auto &encoder = *(OpusEncoder *)_encoder;
auto &encoder = *(PreparedOpusEncoder *)_encoder;
return encoder.Open(audio_format, error);
}
void
OpusEncoder::Close()
OpusEncoder::~OpusEncoder()
{
stream.Deinitialize();
free(buffer);
opus_encoder_destroy(enc);
}
static void
opus_encoder_close(Encoder *_encoder)
{
auto &encoder = *(OpusEncoder *)_encoder;
encoder.Close();
}
bool
OpusEncoder::DoEncode(bool eos, Error &error)
{
@ -281,13 +280,6 @@ OpusEncoder::End(Error &error)
return DoEncode(true, error);
}
static bool
opus_encoder_end(Encoder *_encoder, Error &error)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.End(error);
}
bool
OpusEncoder::Flush(gcc_unused Error &error)
{
@ -295,14 +287,6 @@ OpusEncoder::Flush(gcc_unused Error &error)
return true;
}
static bool
opus_encoder_flush(Encoder *_encoder, Error &error)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Flush(error);
}
bool
OpusEncoder::WriteSilence(unsigned fill_frames, Error &error)
{
@ -360,15 +344,6 @@ OpusEncoder::Write(const void *_data, size_t length, Error &error)
return true;
}
static bool
opus_encoder_write(Encoder *_encoder,
const void *data, size_t length,
Error &error)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Write(data, length, error);
}
void
OpusEncoder::GenerateHead()
{
@ -430,30 +405,18 @@ OpusEncoder::Read(void *dest, size_t length)
return stream.PageOut(dest, length);
}
static size_t
opus_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Read(dest, length);
}
static const char *
opus_encoder_get_mime_type(gcc_unused Encoder *_encoder)
opus_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{
return "audio/ogg";
}
}
const EncoderPlugin opus_encoder_plugin = {
"opus",
opus_encoder_init,
opus_encoder_finish,
opus_encoder_open,
opus_encoder_close,
opus_encoder_end,
opus_encoder_flush,
nullptr,
nullptr,
opus_encoder_write,
opus_encoder_read,
opus_encoder_get_mime_type,
};

View File

@ -22,7 +22,6 @@
#include "../EncoderAPI.hxx"
#include "AudioFormat.hxx"
#include "config/ConfigError.hxx"
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include "util/Error.hxx"
@ -34,32 +33,71 @@ extern "C"
static constexpr size_t BUFFER_INIT_SIZE = 8192;
static constexpr unsigned CHANNELS = 2;
struct ShineEncoder {
Encoder encoder;
class ShineEncoder final : public Encoder {
const AudioFormat audio_format;
AudioFormat audio_format;
const shine_t shine;
shine_t shine;
const size_t frame_size;
/* workaround for bug:
https://github.com/savonet/shine/issues/11 */
size_t input_pos = SHINE_MAX_SAMPLES + 1;
int16_t *stereo[CHANNELS];
DynamicFifoBuffer<uint8_t> output_buffer;
public:
ShineEncoder(AudioFormat _audio_format, shine_t _shine)
:Encoder(false),
audio_format(_audio_format), shine(_shine),
frame_size(shine_samples_per_pass(shine)),
stereo{new int16_t[frame_size], new int16_t[frame_size]},
output_buffer(BUFFER_INIT_SIZE) {}
~ShineEncoder() override {
if (input_pos > SHINE_MAX_SAMPLES) {
/* write zero chunk */
input_pos = 0;
WriteChunk(true);
}
shine_close(shine);
delete[] stereo[0];
delete[] stereo[1];
}
bool WriteChunk(bool flush);
/* virtual methods from class Encoder */
bool End(Error &error) override {
return Flush(error);
}
bool Flush(Error &) override;
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override {
return output_buffer.Read((uint8_t *)dest, length);
}
};
struct PreparedShineEncoder {
PreparedEncoder encoder;
shine_config_t config;
size_t frame_size;
size_t input_pos;
int16_t *stereo[CHANNELS];
Manual<DynamicFifoBuffer<uint8_t>> output_buffer;
ShineEncoder():encoder(shine_encoder_plugin){}
PreparedShineEncoder():encoder(shine_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
bool Setup(Error &error);
bool WriteChunk(bool flush);
};
inline bool
ShineEncoder::Configure(const ConfigBlock &block, gcc_unused Error &error)
PreparedShineEncoder::Configure(const ConfigBlock &block, Error &)
{
shine_set_config_mpeg_defaults(&config.mpeg);
config.mpeg.bitr = block.GetBlockValue("bitrate", 128);
@ -67,10 +105,10 @@ ShineEncoder::Configure(const ConfigBlock &block, gcc_unused Error &error)
return true;
}
static Encoder *
static PreparedEncoder *
shine_encoder_init(const ConfigBlock &block, Error &error)
{
ShineEncoder *encoder = new ShineEncoder();
auto *encoder = new PreparedShineEncoder();
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
@ -83,16 +121,20 @@ shine_encoder_init(const ConfigBlock &block, Error &error)
}
static void
shine_encoder_finish(Encoder *_encoder)
shine_encoder_finish(PreparedEncoder *_encoder)
{
ShineEncoder *encoder = (ShineEncoder *)_encoder;
auto *encoder = (PreparedShineEncoder *)_encoder;
delete encoder;
}
inline bool
ShineEncoder::Setup(Error &error)
static shine_t
SetupShine(shine_config_t config, AudioFormat &audio_format,
Error &error)
{
audio_format.format = SampleFormat::S16;
audio_format.channels = CHANNELS;
config.mpeg.mode = audio_format.channels == 2 ? STEREO : MONO;
config.wave.samplerate = audio_format.sample_rate;
config.wave.channels =
@ -106,61 +148,28 @@ ShineEncoder::Setup(Error &error)
config.wave.samplerate,
config.mpeg.bitr);
return false;
return nullptr;
}
shine = shine_initialise(&config);
if (!shine) {
auto shine = shine_initialise(&config);
if (!shine)
error.Format(config_domain,
"error initializing shine.");
return false;
}
frame_size = shine_samples_per_pass(shine);
return true;
return shine;
}
static bool
shine_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
static Encoder *
shine_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
Error &error)
{
ShineEncoder *encoder = (ShineEncoder *)_encoder;
auto *encoder = (PreparedShineEncoder *)_encoder;
audio_format.format = SampleFormat::S16;
audio_format.channels = CHANNELS;
encoder->audio_format = audio_format;
auto shine = SetupShine(encoder->config, audio_format, error);
if (!shine)
return nullptr;
if (!encoder->Setup(error))
return false;
encoder->stereo[0] = new int16_t[encoder->frame_size];
encoder->stereo[1] = new int16_t[encoder->frame_size];
/* workaround for bug:
https://github.com/savonet/shine/issues/11 */
encoder->input_pos = SHINE_MAX_SAMPLES + 1;
encoder->output_buffer.Construct(BUFFER_INIT_SIZE);
return true;
}
static void
shine_encoder_close(Encoder *_encoder)
{
ShineEncoder *encoder = (ShineEncoder *)_encoder;
if (encoder->input_pos > SHINE_MAX_SAMPLES) {
/* write zero chunk */
encoder->input_pos = 0;
encoder->WriteChunk(true);
}
shine_close(encoder->shine);
delete[] encoder->stereo[0];
delete[] encoder->stereo[1];
encoder->output_buffer.Destruct();
return new ShineEncoder(audio_format, shine);
}
bool
@ -179,7 +188,7 @@ ShineEncoder::WriteChunk(bool flush)
shine_encode_buffer(shine, stereo, &written);
if (written > 0)
output_buffer->Append(out, written);
output_buffer.Append(out, written);
input_pos = 0;
}
@ -187,65 +196,50 @@ ShineEncoder::WriteChunk(bool flush)
return true;
}
static bool
shine_encoder_write(Encoder *_encoder,
const void *_data, size_t length,
gcc_unused Error &error)
bool
ShineEncoder::Write(const void *_data, size_t length, gcc_unused Error &error)
{
ShineEncoder *encoder = (ShineEncoder *)_encoder;
const int16_t *data = (const int16_t*)_data;
length /= sizeof(*data) * encoder->audio_format.channels;
length /= sizeof(*data) * audio_format.channels;
size_t written = 0;
if (encoder->input_pos > SHINE_MAX_SAMPLES) {
encoder->input_pos = 0;
}
if (input_pos > SHINE_MAX_SAMPLES)
input_pos = 0;
/* write all data to de-interleaved buffers */
while (written < length) {
for (;
written < length
&& encoder->input_pos < encoder->frame_size;
written++, encoder->input_pos++) {
written < length && input_pos < frame_size;
written++, input_pos++) {
const size_t base =
written * encoder->audio_format.channels;
encoder->stereo[0][encoder->input_pos] = data[base];
encoder->stereo[1][encoder->input_pos] = data[base + 1];
written * audio_format.channels;
stereo[0][input_pos] = data[base];
stereo[1][input_pos] = data[base + 1];
}
/* write if chunk is filled */
encoder->WriteChunk(false);
WriteChunk(false);
}
return true;
}
static bool
shine_encoder_flush(Encoder *_encoder, gcc_unused Error &error)
bool
ShineEncoder::Flush(gcc_unused Error &error)
{
ShineEncoder *encoder = (ShineEncoder *)_encoder;
/* flush buffers and flush shine */
encoder->WriteChunk(true);
WriteChunk(true);
int written;
const uint8_t *data = shine_flush(encoder->shine, &written);
const uint8_t *data = shine_flush(shine, &written);
if (written > 0)
encoder->output_buffer->Append(data, written);
output_buffer.Append(data, written);
return true;
}
static size_t
shine_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
ShineEncoder *encoder = (ShineEncoder *)_encoder;
return encoder->output_buffer->Read((uint8_t *)dest, length);
}
static const char *
shine_encoder_get_mime_type(gcc_unused Encoder *_encoder)
shine_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{
return "audio/mpeg";
}
@ -255,12 +249,5 @@ const EncoderPlugin shine_encoder_plugin = {
shine_encoder_init,
shine_encoder_finish,
shine_encoder_open,
shine_encoder_close,
shine_encoder_flush,
shine_encoder_flush,
nullptr,
nullptr,
shine_encoder_write,
shine_encoder_read,
shine_encoder_get_mime_type,
};

View File

@ -32,26 +32,53 @@
#include <assert.h>
#include <string.h>
struct TwolameEncoder final {
Encoder encoder;
AudioFormat audio_format;
float quality;
int bitrate;
class TwolameEncoder final : public Encoder {
const AudioFormat audio_format;
twolame_options *options;
unsigned char output_buffer[32768];
size_t output_buffer_length;
size_t output_buffer_position;
size_t output_buffer_length = 0;
size_t output_buffer_position = 0;
/**
* Call libtwolame's flush function when the output_buffer is
* empty?
*/
bool flush;
bool flush = false;
TwolameEncoder():encoder(twolame_encoder_plugin) {}
public:
TwolameEncoder(const AudioFormat _audio_format,
twolame_options *_options)
:Encoder(false),
audio_format(_audio_format), options(_options) {}
~TwolameEncoder() override;
bool Configure(const ConfigBlock &block, Error &error);
/* virtual methods from class Encoder */
bool End(Error &) override {
flush = true;
return true;
}
bool Flush(Error &) override {
flush = true;
return true;
}
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override;
};
struct PreparedTwolameEncoder final {
PreparedEncoder encoder;
float quality;
int bitrate;
PreparedTwolameEncoder():encoder(twolame_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
};
@ -59,7 +86,7 @@ struct TwolameEncoder final {
static constexpr Domain twolame_encoder_domain("twolame_encoder");
bool
TwolameEncoder::Configure(const ConfigBlock &block, Error &error)
PreparedTwolameEncoder::Configure(const ConfigBlock &block, Error &error)
{
const char *value;
char *endptr;
@ -106,13 +133,13 @@ TwolameEncoder::Configure(const ConfigBlock &block, Error &error)
return true;
}
static Encoder *
static PreparedEncoder *
twolame_encoder_init(const ConfigBlock &block, Error &error_r)
{
FormatDebug(twolame_encoder_domain,
"libtwolame version %s", get_twolame_version());
TwolameEncoder *encoder = new TwolameEncoder();
auto *encoder = new PreparedTwolameEncoder();
/* load configuration from "block" */
if (!encoder->Configure(block, error_r)) {
@ -125,9 +152,9 @@ twolame_encoder_init(const ConfigBlock &block, Error &error_r)
}
static void
twolame_encoder_finish(Encoder *_encoder)
twolame_encoder_finish(PreparedEncoder *_encoder)
{
TwolameEncoder *encoder = (TwolameEncoder *)_encoder;
auto *encoder = (PreparedTwolameEncoder *)_encoder;
/* the real libtwolame cleanup was already performed by
twolame_encoder_close(), so no real work here */
@ -135,17 +162,18 @@ twolame_encoder_finish(Encoder *_encoder)
}
static bool
twolame_encoder_setup(TwolameEncoder *encoder, Error &error)
twolame_encoder_setup(twolame_options *options, float quality, int bitrate,
const AudioFormat &audio_format, Error &error)
{
if (encoder->quality >= -1.0) {
if (quality >= -1.0) {
/* a quality was configured (VBR) */
if (0 != twolame_set_VBR(encoder->options, true)) {
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(encoder->options, encoder->quality)) {
if (0 != twolame_set_VBR_q(options, quality)) {
error.Set(twolame_encoder_domain,
"error setting twolame VBR quality");
return false;
@ -153,28 +181,27 @@ twolame_encoder_setup(TwolameEncoder *encoder, Error &error)
} else {
/* a bit rate was configured */
if (0 != twolame_set_brate(encoder->options, encoder->bitrate)) {
if (0 != twolame_set_brate(options, bitrate)) {
error.Set(twolame_encoder_domain,
"error setting twolame bitrate");
return false;
}
}
if (0 != twolame_set_num_channels(encoder->options,
encoder->audio_format.channels)) {
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_in_samplerate(encoder->options,
encoder->audio_format.sample_rate)) {
if (0 != twolame_set_in_samplerate(options,
audio_format.sample_rate)) {
error.Set(twolame_encoder_domain,
"error setting twolame sample rate");
return false;
}
if (0 > twolame_init_params(encoder->options)) {
if (0 > twolame_init_params(options)) {
error.Set(twolame_encoder_domain,
"error initializing twolame params");
return false;
@ -183,117 +210,89 @@ twolame_encoder_setup(TwolameEncoder *encoder, Error &error)
return true;
}
static bool
twolame_encoder_open(Encoder *_encoder, AudioFormat &audio_format,
static Encoder *
twolame_encoder_open(PreparedEncoder *_encoder, AudioFormat &audio_format,
Error &error)
{
TwolameEncoder *encoder = (TwolameEncoder *)_encoder;
auto *encoder = (PreparedTwolameEncoder *)_encoder;
audio_format.format = SampleFormat::S16;
audio_format.channels = 2;
encoder->audio_format = audio_format;
encoder->options = twolame_init();
if (encoder->options == nullptr) {
auto options = twolame_init();
if (options == nullptr) {
error.Set(twolame_encoder_domain, "twolame_init() failed");
return false;
return nullptr;
}
if (!twolame_encoder_setup(encoder, error)) {
twolame_close(&encoder->options);
return false;
if (!twolame_encoder_setup(options, encoder->quality, encoder->bitrate,
audio_format, error)) {
twolame_close(&options);
return nullptr;
}
encoder->output_buffer_length = 0;
encoder->output_buffer_position = 0;
encoder->flush = false;
return true;
return new TwolameEncoder(audio_format, options);
}
static void
twolame_encoder_close(Encoder *_encoder)
TwolameEncoder::~TwolameEncoder()
{
TwolameEncoder *encoder = (TwolameEncoder *)_encoder;
twolame_close(&encoder->options);
twolame_close(&options);
}
static bool
twolame_encoder_flush(Encoder *_encoder, gcc_unused Error &error)
{
TwolameEncoder *encoder = (TwolameEncoder *)_encoder;
encoder->flush = true;
return true;
}
static bool
twolame_encoder_write(Encoder *_encoder,
const void *data, size_t length,
bool
TwolameEncoder::Write(const void *data, size_t length,
gcc_unused Error &error)
{
TwolameEncoder *encoder = (TwolameEncoder *)_encoder;
const int16_t *src = (const int16_t*)data;
assert(encoder->output_buffer_position ==
encoder->output_buffer_length);
assert(output_buffer_position == output_buffer_length);
const unsigned num_frames =
length / encoder->audio_format.GetFrameSize();
const unsigned num_frames = length / audio_format.GetFrameSize();
int bytes_out = twolame_encode_buffer_interleaved(encoder->options,
int bytes_out = twolame_encode_buffer_interleaved(options,
src, num_frames,
encoder->output_buffer,
sizeof(encoder->output_buffer));
output_buffer,
sizeof(output_buffer));
if (bytes_out < 0) {
error.Set(twolame_encoder_domain, "twolame encoder failed");
return false;
}
encoder->output_buffer_length = (size_t)bytes_out;
encoder->output_buffer_position = 0;
output_buffer_length = (size_t)bytes_out;
output_buffer_position = 0;
return true;
}
static size_t
twolame_encoder_read(Encoder *_encoder, void *dest, size_t length)
size_t
TwolameEncoder::Read(void *dest, size_t length)
{
TwolameEncoder *encoder = (TwolameEncoder *)_encoder;
assert(output_buffer_position <= output_buffer_length);
assert(encoder->output_buffer_position <=
encoder->output_buffer_length);
if (encoder->output_buffer_position == encoder->output_buffer_length &&
encoder->flush) {
int ret = twolame_encode_flush(encoder->options,
encoder->output_buffer,
sizeof(encoder->output_buffer));
if (output_buffer_position == output_buffer_length && flush) {
int ret = twolame_encode_flush(options, output_buffer,
sizeof(output_buffer));
if (ret > 0) {
encoder->output_buffer_length = (size_t)ret;
encoder->output_buffer_position = 0;
output_buffer_length = (size_t)ret;
output_buffer_position = 0;
}
encoder->flush = false;
flush = false;
}
const size_t remainning = encoder->output_buffer_length
- encoder->output_buffer_position;
const size_t remainning = output_buffer_length - output_buffer_position;
if (length > remainning)
length = remainning;
memcpy(dest, encoder->output_buffer + encoder->output_buffer_position,
length);
memcpy(dest, output_buffer + output_buffer_position, length);
encoder->output_buffer_position += length;
output_buffer_position += length;
return length;
}
static const char *
twolame_encoder_get_mime_type(gcc_unused Encoder *_encoder)
twolame_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{
return "audio/mpeg";
}
@ -303,12 +302,5 @@ const EncoderPlugin twolame_encoder_plugin = {
twolame_encoder_init,
twolame_encoder_finish,
twolame_encoder_open,
twolame_encoder_close,
twolame_encoder_flush,
twolame_encoder_flush,
nullptr,
nullptr,
twolame_encoder_write,
twolame_encoder_read,
twolame_encoder_get_mime_type,
};

View File

@ -31,17 +31,7 @@
#include <vorbis/vorbisenc.h>
struct VorbisEncoder {
/** the base class */
Encoder encoder;
/* configuration */
float quality;
int bitrate;
/* runtime information */
class VorbisEncoder final : public Encoder {
AudioFormat audio_format;
vorbis_dsp_state vd;
@ -50,22 +40,57 @@ struct VorbisEncoder {
OggStream stream;
VorbisEncoder():encoder(vorbis_encoder_plugin) {}
public:
VorbisEncoder()
:Encoder(true) {}
bool Configure(const ConfigBlock &block, Error &error);
virtual ~VorbisEncoder() {
Clear();
}
bool Reinit(Error &error);
bool Open(float quality, int bitrate, AudioFormat &audio_format,
Error &error);
/* virtual methods from class Encoder */
bool End(Error &error) override {
return PreTag(error);
}
bool Flush(Error &error) override;
bool PreTag(Error &error) override;
bool SendTag(const Tag &tag, Error &error) override;
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override {
return stream.PageOut(dest, length);
}
private:
void HeaderOut(vorbis_comment &vc);
void SendHeader();
void BlockOut();
void Clear();
};
struct PreparedVorbisEncoder {
/** the base class */
PreparedEncoder encoder;
/* configuration */
float quality;
int bitrate;
PreparedVorbisEncoder():encoder(vorbis_encoder_plugin) {}
bool Configure(const ConfigBlock &block, Error &error);
};
static constexpr Domain vorbis_encoder_domain("vorbis_encoder");
bool
VorbisEncoder::Configure(const ConfigBlock &block, Error &error)
PreparedVorbisEncoder::Configure(const ConfigBlock &block, Error &error)
{
const char *value = block.GetBlockValue("quality");
if (value != nullptr) {
@ -111,10 +136,10 @@ VorbisEncoder::Configure(const ConfigBlock &block, Error &error)
return true;
}
static Encoder *
static PreparedEncoder *
vorbis_encoder_init(const ConfigBlock &block, Error &error)
{
auto *encoder = new VorbisEncoder();
auto *encoder = new PreparedVorbisEncoder();
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
@ -127,9 +152,9 @@ vorbis_encoder_init(const ConfigBlock &block, Error &error)
}
static void
vorbis_encoder_finish(Encoder *_encoder)
vorbis_encoder_finish(PreparedEncoder *_encoder)
{
VorbisEncoder *encoder = (VorbisEncoder *)_encoder;
auto *encoder = (PreparedVorbisEncoder *)_encoder;
/* the real libvorbis/libogg cleanup was already performed by
vorbis_encoder_close(), so no real work here */
@ -137,8 +162,12 @@ vorbis_encoder_finish(Encoder *_encoder)
}
bool
VorbisEncoder::Reinit(Error &error)
VorbisEncoder::Open(float quality, int bitrate, AudioFormat &_audio_format,
Error &error)
{
_audio_format.format = SampleFormat::FLOAT;
audio_format = _audio_format;
vorbis_info_init(&vi);
if (quality >= -1.0) {
@ -171,6 +200,8 @@ VorbisEncoder::Reinit(Error &error)
vorbis_block_init(&vd, &vb);
stream.Initialize(GenerateOggSerial());
SendHeader();
return true;
}
@ -197,23 +228,20 @@ VorbisEncoder::SendHeader()
vorbis_comment_clear(&vc);
}
static bool
vorbis_encoder_open(Encoder *_encoder,
static Encoder *
vorbis_encoder_open(PreparedEncoder *_encoder,
AudioFormat &audio_format,
Error &error)
{
auto &encoder = *(VorbisEncoder *)_encoder;
auto &encoder = *(PreparedVorbisEncoder *)_encoder;
audio_format.format = SampleFormat::FLOAT;
auto *e = new VorbisEncoder();
if (!e->Open(encoder.quality, encoder.bitrate, audio_format, error)) {
delete e;
return nullptr;
}
encoder.audio_format = audio_format;
if (!encoder.Reinit(error))
return false;
encoder.SendHeader();
return true;
return e;
}
void
@ -225,14 +253,6 @@ VorbisEncoder::Clear()
vorbis_info_clear(&vi);
}
static void
vorbis_encoder_close(Encoder *_encoder)
{
auto &encoder = *(VorbisEncoder *)_encoder;
encoder.Clear();
}
void
VorbisEncoder::BlockOut()
{
@ -246,31 +266,27 @@ VorbisEncoder::BlockOut()
}
}
static bool
vorbis_encoder_flush(Encoder *_encoder, gcc_unused Error &error)
bool
VorbisEncoder::Flush(gcc_unused Error &error)
{
auto &encoder = *(VorbisEncoder *)_encoder;
encoder.stream.Flush();
stream.Flush();
return true;
}
static bool
vorbis_encoder_pre_tag(Encoder *_encoder, gcc_unused Error &error)
bool
VorbisEncoder::PreTag(gcc_unused Error &error)
{
auto &encoder = *(VorbisEncoder *)_encoder;
vorbis_analysis_wrote(&encoder.vd, 0);
encoder.BlockOut();
vorbis_analysis_wrote(&vd, 0);
BlockOut();
/* reinitialize vorbis_dsp_state and vorbis_block to reset the
end-of-stream marker */
vorbis_block_clear(&encoder.vb);
vorbis_dsp_clear(&encoder.vd);
vorbis_analysis_init(&encoder.vd, &encoder.vi);
vorbis_block_init(&encoder.vd, &encoder.vb);
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
vorbis_analysis_init(&vd, &vi);
vorbis_block_init(&vd, &vb);
encoder.stream.Flush();
stream.Flush();
return true;
}
@ -284,11 +300,9 @@ copy_tag_to_vorbis_comment(vorbis_comment *vc, const Tag &tag)
}
}
static bool
vorbis_encoder_tag(Encoder *_encoder, const Tag &tag,
gcc_unused Error &error)
bool
VorbisEncoder::SendTag(const Tag &tag, gcc_unused Error &error)
{
auto &encoder = *(VorbisEncoder *)_encoder;
vorbis_comment comment;
/* write the vorbis_comment object */
@ -298,11 +312,11 @@ vorbis_encoder_tag(Encoder *_encoder, const Tag &tag,
/* reset ogg_stream_state and begin a new stream */
encoder.stream.Reinitialize(GenerateOggSerial());
stream.Reinitialize(GenerateOggSerial());
/* send that vorbis_comment to the ogg_stream_state */
encoder.HeaderOut(comment);
HeaderOut(comment);
vorbis_comment_clear(&comment);
return true;
@ -317,38 +331,25 @@ interleaved_to_vorbis_buffer(float **dest, const float *src,
dest[j][i] = *src++;
}
static bool
vorbis_encoder_write(Encoder *_encoder,
const void *data, size_t length,
gcc_unused Error &error)
bool
VorbisEncoder::Write(const void *data, size_t length, gcc_unused Error &error)
{
auto &encoder = *(VorbisEncoder *)_encoder;
unsigned num_frames = length / encoder.audio_format.GetFrameSize();
unsigned num_frames = length / audio_format.GetFrameSize();
/* this is for only 16-bit audio */
interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&encoder.vd,
num_frames),
interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&vd, num_frames),
(const float *)data,
num_frames,
encoder.audio_format.channels);
audio_format.channels);
vorbis_analysis_wrote(&encoder.vd, num_frames);
encoder.BlockOut();
vorbis_analysis_wrote(&vd, num_frames);
BlockOut();
return true;
}
static size_t
vorbis_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
auto &encoder = *(VorbisEncoder *)_encoder;
return encoder.stream.PageOut(dest, length);
}
static const char *
vorbis_encoder_get_mime_type(gcc_unused Encoder *_encoder)
vorbis_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{
return "audio/ogg";
}
@ -358,12 +359,5 @@ const EncoderPlugin vorbis_encoder_plugin = {
vorbis_encoder_init,
vorbis_encoder_finish,
vorbis_encoder_open,
vorbis_encoder_close,
vorbis_encoder_pre_tag,
vorbis_encoder_flush,
vorbis_encoder_pre_tag,
vorbis_encoder_tag,
vorbis_encoder_write,
vorbis_encoder_read,
vorbis_encoder_get_mime_type,
};

View File

@ -21,7 +21,6 @@
#include "WaveEncoderPlugin.hxx"
#include "../EncoderAPI.hxx"
#include "system/ByteOrder.hxx"
#include "util/Manual.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include <assert.h>
@ -29,13 +28,26 @@
static constexpr uint16_t WAVE_FORMAT_PCM = 1;
struct WaveEncoder {
Encoder encoder;
class WaveEncoder final : public Encoder {
unsigned bits;
Manual<DynamicFifoBuffer<uint8_t>> buffer;
DynamicFifoBuffer<uint8_t> buffer;
WaveEncoder():encoder(wave_encoder_plugin) {}
public:
WaveEncoder(AudioFormat &audio_format);
/* virtual methods from class Encoder */
bool Write(const void *data, size_t length, Error &) override;
size_t Read(void *dest, size_t length) override {
return buffer.Read((uint8_t *)dest, length);
}
};
struct PreparedWaveEncoder {
PreparedEncoder encoder;
PreparedWaveEncoder():encoder(wave_encoder_plugin) {}
};
struct WaveHeader {
@ -80,78 +92,71 @@ fill_wave_header(WaveHeader *header, int channels, int bits,
header->riff_size = ToLE32(4 + (8 + 16) + (8 + data_size));
}
static Encoder *
static PreparedEncoder *
wave_encoder_init(gcc_unused const ConfigBlock &block,
gcc_unused Error &error)
{
WaveEncoder *encoder = new WaveEncoder();
auto *encoder = new PreparedWaveEncoder();
return &encoder->encoder;
}
static void
wave_encoder_finish(Encoder *_encoder)
wave_encoder_finish(PreparedEncoder *_encoder)
{
WaveEncoder *encoder = (WaveEncoder *)_encoder;
auto *encoder = (PreparedWaveEncoder *)_encoder;
delete encoder;
}
static bool
wave_encoder_open(Encoder *_encoder,
AudioFormat &audio_format,
gcc_unused Error &error)
WaveEncoder::WaveEncoder(AudioFormat &audio_format)
:Encoder(false),
buffer(8192)
{
WaveEncoder *encoder = (WaveEncoder *)_encoder;
assert(audio_format.IsValid());
switch (audio_format.format) {
case SampleFormat::S8:
encoder->bits = 8;
bits = 8;
break;
case SampleFormat::S16:
encoder->bits = 16;
bits = 16;
break;
case SampleFormat::S24_P32:
encoder->bits = 24;
bits = 24;
break;
case SampleFormat::S32:
encoder->bits = 32;
bits = 32;
break;
default:
audio_format.format = SampleFormat::S16;
encoder->bits = 16;
bits = 16;
break;
}
encoder->buffer.Construct(8192);
auto range = encoder->buffer->Write();
auto range = buffer.Write();
assert(range.size >= sizeof(WaveHeader));
auto *header = (WaveHeader *)range.data;
/* create PCM wave header in initial buffer */
fill_wave_header(header,
audio_format.channels,
encoder->bits,
bits,
audio_format.sample_rate,
(encoder->bits / 8) * audio_format.channels);
(bits / 8) * audio_format.channels);
encoder->buffer->Append(sizeof(*header));
return true;
buffer.Append(sizeof(*header));
}
static void
wave_encoder_close(Encoder *_encoder)
static Encoder *
wave_encoder_open(gcc_unused PreparedEncoder *_encoder,
AudioFormat &audio_format,
gcc_unused Error &error)
{
WaveEncoder *encoder = (WaveEncoder *)_encoder;
encoder->buffer.Destruct();
return new WaveEncoder(audio_format);
}
static size_t
@ -194,17 +199,14 @@ pcm24_to_wave(uint8_t *dst8, const uint32_t *src32, size_t length)
return (dst8 - dst_old);
}
static bool
wave_encoder_write(Encoder *_encoder,
const void *src, size_t length,
bool
WaveEncoder::Write(const void *src, size_t length,
gcc_unused Error &error)
{
WaveEncoder *encoder = (WaveEncoder *)_encoder;
uint8_t *dst = encoder->buffer->Write(length);
uint8_t *dst = buffer.Write(length);
if (IsLittleEndian()) {
switch (encoder->bits) {
switch (bits) {
case 8:
case 16:
case 32:// optimized cases
@ -215,7 +217,7 @@ wave_encoder_write(Encoder *_encoder,
break;
}
} else {
switch (encoder->bits) {
switch (bits) {
case 8:
memcpy(dst, src, length);
break;
@ -233,20 +235,12 @@ wave_encoder_write(Encoder *_encoder,
}
}
encoder->buffer->Append(length);
buffer.Append(length);
return true;
}
static size_t
wave_encoder_read(Encoder *_encoder, void *dest, size_t length)
{
WaveEncoder *encoder = (WaveEncoder *)_encoder;
return encoder->buffer->Read((uint8_t *)dest, length);
}
static const char *
wave_encoder_get_mime_type(gcc_unused Encoder *_encoder)
wave_encoder_get_mime_type(gcc_unused PreparedEncoder *_encoder)
{
return "audio/wav";
}
@ -256,12 +250,5 @@ const EncoderPlugin wave_encoder_plugin = {
wave_encoder_init,
wave_encoder_finish,
wave_encoder_open,
wave_encoder_close,
nullptr,
nullptr,
nullptr,
nullptr,
wave_encoder_write,
wave_encoder_read,
wave_encoder_get_mime_type,
};

View File

@ -47,7 +47,8 @@ class RecorderOutput {
/**
* The configured encoder plugin.
*/
Encoder *encoder = nullptr;
PreparedEncoder *prepared_encoder = nullptr;
Encoder *encoder;
/**
* The destination file name.
@ -75,8 +76,8 @@ class RecorderOutput {
:base(recorder_output_plugin) {}
~RecorderOutput() {
if (encoder != nullptr)
encoder->Dispose();
if (prepared_encoder != nullptr)
prepared_encoder->Dispose();
}
bool Initialize(const ConfigBlock &block, Error &error_r) {
@ -148,8 +149,8 @@ RecorderOutput::Configure(const ConfigBlock &block, Error &error)
/* initialize encoder */
encoder = encoder_init(*encoder_plugin, block, error);
if (encoder == nullptr)
prepared_encoder = encoder_init(*encoder_plugin, block, error);
if (prepared_encoder == nullptr)
return false;
return true;
@ -205,7 +206,8 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
/* open the encoder */
if (!encoder->Open(audio_format, error)) {
encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr) {
delete file;
return false;
}
@ -214,7 +216,7 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
try {
EncoderToFile();
} catch (const std::exception &e) {
encoder->Close();
delete encoder;
error.Set(recorder_domain, e.what());
return false;
}
@ -224,7 +226,7 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
/* close the encoder for now; it will be opened as
soon as we have received a tag */
encoder->Close();
delete encoder;
}
return true;
@ -237,19 +239,19 @@ RecorderOutput::Commit(Error &error)
/* flush the encoder and write the rest to the file */
bool success = encoder_end(encoder, error);
bool success = encoder->End(error);
if (success) {
try {
EncoderToFile();
} catch (...) {
encoder->Close();
delete encoder;
throw;
}
}
/* now really close everything */
encoder->Close();
delete encoder;
if (success) {
try {
@ -326,7 +328,8 @@ RecorderOutput::ReopenFormat(AllocatedPath &&new_path, Error &error)
}
AudioFormat new_audio_format = effective_audio_format;
if (!encoder->Open(new_audio_format, error)) {
encoder = prepared_encoder->Open(new_audio_format, error);
if (encoder == nullptr) {
delete new_file;
return false;
}
@ -338,7 +341,7 @@ RecorderOutput::ReopenFormat(AllocatedPath &&new_path, Error &error)
try {
EncoderToOutputStream(*new_file, *encoder);
} catch (const std::exception &e) {
encoder->Close();
delete encoder;
delete new_file;
error.Set(recorder_domain, e.what());
return false;
@ -386,7 +389,7 @@ RecorderOutput::SendTag(const Tag &tag)
}
Error error;
if (!encoder_pre_tag(encoder, error)) {
if (!encoder->PreTag(error)) {
LogError(error);
return;
}
@ -398,7 +401,7 @@ RecorderOutput::SendTag(const Tag &tag)
return;
}
if (!encoder_tag(encoder, tag, error))
if (!encoder->SendTag(tag, error))
LogError(error);
}
@ -413,7 +416,7 @@ RecorderOutput::Play(const void *chunk, size_t size, Error &error)
return size;
}
if (!encoder_write(encoder, chunk, size, error))
if (!encoder->Write(chunk, size, error))
return 0;
try {

View File

@ -45,7 +45,8 @@ struct ShoutOutput final {
shout_t *shout_conn;
shout_metadata_t *shout_meta;
Encoder *encoder = nullptr;
PreparedEncoder *prepared_encoder = nullptr;
Encoder *encoder;
float quality = -2.0;
int bitrate = -1;
@ -93,8 +94,7 @@ ShoutOutput::~ShoutOutput()
if (shout_init_count == 0)
shout_shutdown();
if (encoder != nullptr)
encoder->Dispose();
delete prepared_encoder;
}
static const EncoderPlugin *
@ -192,8 +192,8 @@ ShoutOutput::Configure(const ConfigBlock &block, Error &error)
return false;
}
encoder = encoder_init(*encoder_plugin, block, error);
if (encoder == nullptr)
prepared_encoder = encoder_init(*encoder_plugin, block, error);
if (prepared_encoder == nullptr)
return false;
unsigned shout_format;
@ -345,8 +345,8 @@ write_page(ShoutOutput *sd, Error &error)
assert(sd->encoder != nullptr);
while (true) {
size_t nbytes = encoder_read(sd->encoder,
sd->buffer, sizeof(sd->buffer));
size_t nbytes = sd->encoder->Read(sd->buffer,
sizeof(sd->buffer));
if (nbytes == 0)
return true;
@ -362,10 +362,10 @@ void
ShoutOutput::Close()
{
if (encoder != nullptr) {
if (encoder_end(encoder, IgnoreError()))
if (encoder->End(IgnoreError()))
write_page(this, IgnoreError());
encoder->Close();
delete encoder;
}
if (shout_get_connected(shout_conn) != SHOUTERR_UNCONNECTED &&
@ -406,13 +406,14 @@ ShoutOutput::Open(AudioFormat &audio_format, Error &error)
if (!shout_connect(this, error))
return false;
if (!encoder->Open(audio_format, error)) {
encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr) {
shout_close(shout_conn);
return false;
}
if (!write_page(this, error)) {
encoder->Close();
delete encoder;
shout_close(shout_conn);
return false;
}
@ -433,7 +434,7 @@ ShoutOutput::Delay() const
size_t
ShoutOutput::Play(const void *chunk, size_t size, Error &error)
{
return encoder_write(encoder, chunk, size, error) &&
return encoder->Write(chunk, size, error) &&
write_page(this, error)
? size
: 0;
@ -476,13 +477,13 @@ shout_tag_to_metadata(const Tag &tag, char *dest, size_t size)
void
ShoutOutput::SendTag(const Tag &tag)
{
if (encoder->plugin.tag != nullptr) {
if (encoder->ImplementsTag()) {
/* encoder plugin supports stream tags */
Error error;
if (!encoder_pre_tag(encoder, error) ||
if (!encoder->PreTag(error) ||
!write_page(this, error) ||
!encoder_tag(encoder, tag, error)) {
!encoder->SendTag(tag, error)) {
LogError(error);
return;
}

View File

@ -45,7 +45,8 @@ class EventLoop;
class ServerSocket;
class HttpdClient;
class Page;
struct Encoder;
struct PreparedEncoder;
class Encoder;
struct Tag;
class HttpdOutput final : ServerSocket, DeferredMonitor {
@ -60,6 +61,7 @@ class HttpdOutput final : ServerSocket, DeferredMonitor {
/**
* The configured encoder plugin.
*/
PreparedEncoder *prepared_encoder = nullptr;
Encoder *encoder;
/**

View File

@ -63,8 +63,8 @@ HttpdOutput::~HttpdOutput()
if (metadata != nullptr)
metadata->Unref();
if (encoder != nullptr)
encoder->Dispose();
if (prepared_encoder != nullptr)
prepared_encoder->Dispose();
}
@ -123,12 +123,12 @@ HttpdOutput::Configure(const ConfigBlock &block, Error &error)
/* initialize encoder */
encoder = encoder_init(*encoder_plugin, block, error);
if (encoder == nullptr)
prepared_encoder = encoder_init(*encoder_plugin, block, error);
if (prepared_encoder == nullptr)
return false;
/* determine content type */
content_type = encoder_get_mime_type(encoder);
content_type = encoder_get_mime_type(prepared_encoder);
if (content_type == nullptr)
content_type = "application/octet-stream";
@ -169,7 +169,7 @@ inline void
HttpdOutput::AddClient(int fd)
{
auto *client = new HttpdClient(*this, fd, GetEventLoop(),
encoder->plugin.tag == nullptr);
!encoder->ImplementsTag());
clients.push_front(*client);
/* pass metadata to client */
@ -250,15 +250,14 @@ 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(encoder, IgnoreError());
encoder->Flush(IgnoreError());
unflushed_input = 0;
}
size_t size = 0;
do {
size_t nbytes = encoder_read(encoder,
buffer + size,
sizeof(buffer) - size);
size_t nbytes = encoder->Read(buffer + size,
sizeof(buffer) - size);
if (nbytes == 0)
break;
@ -292,7 +291,8 @@ httpd_output_disable(AudioOutput *ao)
inline bool
HttpdOutput::OpenEncoder(AudioFormat &audio_format, Error &error)
{
if (!encoder->Open(audio_format, error))
encoder = prepared_encoder->Open(audio_format, error);
if (encoder == nullptr)
return false;
/* we have to remember the encoder header, i.e. the first
@ -351,7 +351,7 @@ HttpdOutput::Close()
if (header != nullptr)
header->Unref();
encoder->Close();
delete encoder;
}
static void
@ -441,7 +441,7 @@ HttpdOutput::BroadcastFromEncoder()
inline bool
HttpdOutput::EncodeAndPlay(const void *chunk, size_t size, Error &error)
{
if (!encoder_write(encoder, chunk, size, error))
if (!encoder->Write(chunk, size, error))
return false;
unflushed_input += size;
@ -491,18 +491,18 @@ httpd_output_pause(AudioOutput *ao)
inline void
HttpdOutput::SendTag(const Tag &tag)
{
if (encoder->plugin.tag != nullptr) {
if (encoder->ImplementsTag()) {
/* embed encoder tags */
/* flush the current stream, and end it */
encoder_pre_tag(encoder, IgnoreError());
encoder->PreTag(IgnoreError());
BroadcastFromEncoder();
/* send the tag to the encoder - which starts a new
stream now */
encoder_tag(encoder, tag, IgnoreError());
encoder->SendTag(tag, IgnoreError());
/* the first page generated by the encoder will now be
used as the new "header" page, which is sent to all

View File

@ -65,8 +65,8 @@ int main(int argc, char **argv)
try {
Error error;
const auto encoder = encoder_init(*plugin, block, error);
if (encoder == NULL) {
const auto p_encoder = encoder_init(*plugin, block, error);
if (p_encoder == nullptr) {
LogError(error, "Failed to initialize encoder");
return EXIT_FAILURE;
}
@ -81,7 +81,8 @@ int main(int argc, char **argv)
}
}
if (!encoder->Open(audio_format, error)) {
auto *encoder = p_encoder->Open(audio_format, error);
if (encoder == nullptr) {
LogError(error, "Failed to open encoder");
return EXIT_FAILURE;
}
@ -94,7 +95,7 @@ int main(int argc, char **argv)
ssize_t nbytes;
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
if (!encoder_write(encoder, buffer, nbytes, error)) {
if (!encoder->Write(buffer, nbytes, error)) {
LogError(error, "encoder_write() failed");
return EXIT_FAILURE;
}
@ -102,15 +103,15 @@ int main(int argc, char **argv)
EncoderToOutputStream(os, *encoder);
}
if (!encoder_end(encoder, error)) {
if (!encoder->End(error)) {
LogError(error, "encoder_flush() failed");
return EXIT_FAILURE;
}
EncoderToOutputStream(os, *encoder);
encoder->Close();
encoder->Dispose();
delete encoder;
p_encoder->Dispose();
return EXIT_SUCCESS;
} catch (const std::exception &e) {

View File

@ -48,15 +48,15 @@ main(gcc_unused int argc, gcc_unused char **argv)
ConfigBlock block;
block.AddBlockParam("quality", "5.0", -1);
const auto encoder = encoder_init(*plugin, block, IgnoreError());
assert(encoder != NULL);
const auto p_encoder = encoder_init(*plugin, block, IgnoreError());
assert(p_encoder != nullptr);
try {
/* open the encoder */
AudioFormat audio_format(44100, SampleFormat::S16, 2);
success = encoder->Open(audio_format, IgnoreError());
assert(success);
auto encoder = p_encoder->Open(audio_format, IgnoreError());
assert(encoder != nullptr);
StdioOutputStream os(stdout);
@ -64,14 +64,14 @@ main(gcc_unused int argc, gcc_unused char **argv)
/* write a block of data */
success = encoder_write(encoder, zero, sizeof(zero), IgnoreError());
success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success);
EncoderToOutputStream(os, *encoder);
/* write a tag */
success = encoder_pre_tag(encoder, IgnoreError());
success = encoder->PreTag(IgnoreError());
assert(success);
EncoderToOutputStream(os, *encoder);
@ -85,25 +85,25 @@ main(gcc_unused int argc, gcc_unused char **argv)
tag_builder.Commit(tag);
}
success = encoder_tag(encoder, tag, IgnoreError());
success = encoder->SendTag(tag, IgnoreError());
assert(success);
EncoderToOutputStream(os, *encoder);
/* write another block of data */
success = encoder_write(encoder, zero, sizeof(zero), IgnoreError());
success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success);
/* finish */
success = encoder_end(encoder, IgnoreError());
success = encoder->End(IgnoreError());
assert(success);
EncoderToOutputStream(os, *encoder);
encoder->Close();
encoder->Dispose();
delete encoder;
p_encoder->Dispose();
return EXIT_SUCCESS;
} catch (const std::exception &e) {