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:
@@ -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,
|
||||
};
|
||||
|
Reference in New Issue
Block a user