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

@@ -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,
};