encoder/opus: move functions into the struct

This commit is contained in:
Max Kellermann 2016-05-03 23:56:47 +02:00
parent 48d3af7d9a
commit 33c5da700d

View File

@ -67,42 +67,59 @@ struct OpusEncoder {
ogg_int64_t granulepos; ogg_int64_t granulepos;
OpusEncoder():encoder(opus_encoder_plugin), granulepos(0) {} OpusEncoder():encoder(opus_encoder_plugin), granulepos(0) {}
bool Configure(const ConfigBlock &block, Error &error);
bool Open(AudioFormat &audio_format, Error &error);
void Close();
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);
}; };
static constexpr Domain opus_encoder_domain("opus_encoder"); static constexpr Domain opus_encoder_domain("opus_encoder");
static bool bool
opus_encoder_configure(OpusEncoder *encoder, OpusEncoder::Configure(const ConfigBlock &block, Error &error)
const ConfigBlock &block, Error &error)
{ {
const char *value = block.GetBlockValue("bitrate", "auto"); const char *value = block.GetBlockValue("bitrate", "auto");
if (strcmp(value, "auto") == 0) if (strcmp(value, "auto") == 0)
encoder->bitrate = OPUS_AUTO; bitrate = OPUS_AUTO;
else if (strcmp(value, "max") == 0) else if (strcmp(value, "max") == 0)
encoder->bitrate = OPUS_BITRATE_MAX; bitrate = OPUS_BITRATE_MAX;
else { else {
char *endptr; char *endptr;
encoder->bitrate = strtoul(value, &endptr, 10); bitrate = strtoul(value, &endptr, 10);
if (endptr == value || *endptr != 0 || if (endptr == value || *endptr != 0 ||
encoder->bitrate < 500 || encoder->bitrate > 512000) { bitrate < 500 || bitrate > 512000) {
error.Set(config_domain, "Invalid bit rate"); error.Set(config_domain, "Invalid bit rate");
return false; return false;
} }
} }
encoder->complexity = block.GetBlockValue("complexity", 10u); complexity = block.GetBlockValue("complexity", 10u);
if (encoder->complexity > 10) { if (complexity > 10) {
error.Format(config_domain, "Invalid complexity"); error.Format(config_domain, "Invalid complexity");
return false; return false;
} }
value = block.GetBlockValue("signal", "auto"); value = block.GetBlockValue("signal", "auto");
if (strcmp(value, "auto") == 0) if (strcmp(value, "auto") == 0)
encoder->signal = OPUS_AUTO; signal = OPUS_AUTO;
else if (strcmp(value, "voice") == 0) else if (strcmp(value, "voice") == 0)
encoder->signal = OPUS_SIGNAL_VOICE; signal = OPUS_SIGNAL_VOICE;
else if (strcmp(value, "music") == 0) else if (strcmp(value, "music") == 0)
encoder->signal = OPUS_SIGNAL_MUSIC; signal = OPUS_SIGNAL_MUSIC;
else { else {
error.Format(config_domain, "Invalid signal"); error.Format(config_domain, "Invalid signal");
return false; return false;
@ -117,7 +134,7 @@ opus_encoder_init(const ConfigBlock &block, Error &error)
auto *encoder = new OpusEncoder(); auto *encoder = new OpusEncoder();
/* load configuration from "block" */ /* load configuration from "block" */
if (!opus_encoder_configure(encoder, block, error)) { if (!encoder->Configure(block, error)) {
/* configuration has failed, roll back and return error */ /* configuration has failed, roll back and return error */
delete encoder; delete encoder;
return nullptr; return nullptr;
@ -136,156 +153,207 @@ opus_encoder_finish(Encoder *_encoder)
delete encoder; delete encoder;
} }
static bool bool
opus_encoder_open(Encoder *_encoder, OpusEncoder::Open(AudioFormat &_audio_format, Error &error)
AudioFormat &audio_format,
Error &error)
{ {
auto *encoder = (OpusEncoder *)_encoder;
/* libopus supports only 48 kHz */ /* libopus supports only 48 kHz */
audio_format.sample_rate = 48000; _audio_format.sample_rate = 48000;
if (audio_format.channels > 2) if (_audio_format.channels > 2)
audio_format.channels = 1; _audio_format.channels = 1;
switch (audio_format.format) { switch (_audio_format.format) {
case SampleFormat::S16: case SampleFormat::S16:
case SampleFormat::FLOAT: case SampleFormat::FLOAT:
break; break;
case SampleFormat::S8: case SampleFormat::S8:
audio_format.format = SampleFormat::S16; _audio_format.format = SampleFormat::S16;
break; break;
default: default:
audio_format.format = SampleFormat::FLOAT; _audio_format.format = SampleFormat::FLOAT;
break; break;
} }
encoder->audio_format = audio_format; audio_format = _audio_format;
encoder->frame_size = audio_format.GetFrameSize(); frame_size = _audio_format.GetFrameSize();
int error_code; int error_code;
encoder->enc = opus_encoder_create(audio_format.sample_rate, enc = opus_encoder_create(_audio_format.sample_rate,
audio_format.channels, _audio_format.channels,
OPUS_APPLICATION_AUDIO, OPUS_APPLICATION_AUDIO,
&error_code); &error_code);
if (encoder->enc == nullptr) { if (enc == nullptr) {
error.Set(opus_encoder_domain, error_code, error.Set(opus_encoder_domain, error_code,
opus_strerror(error_code)); opus_strerror(error_code));
return false; return false;
} }
opus_encoder_ctl(encoder->enc, OPUS_SET_BITRATE(encoder->bitrate)); opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(encoder->enc, opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
OPUS_SET_COMPLEXITY(encoder->complexity)); opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal));
opus_encoder_ctl(encoder->enc, OPUS_SET_SIGNAL(encoder->signal));
opus_encoder_ctl(encoder->enc, OPUS_GET_LOOKAHEAD(&encoder->lookahead)); opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
encoder->buffer_frames = audio_format.sample_rate / 50; buffer_frames = _audio_format.sample_rate / 50;
encoder->buffer_size = encoder->frame_size * encoder->buffer_frames; buffer_size = frame_size * buffer_frames;
encoder->buffer_position = 0; buffer_position = 0;
encoder->buffer = (unsigned char *)xalloc(encoder->buffer_size); buffer = (unsigned char *)xalloc(buffer_size);
encoder->stream.Initialize(GenerateOggSerial()); stream.Initialize(GenerateOggSerial());
encoder->packetno = 0; packetno = 0;
return true; return true;
} }
static bool
opus_encoder_open(Encoder *_encoder,
AudioFormat &audio_format,
Error &error)
{
auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Open(audio_format, error);
}
void
OpusEncoder::Close()
{
stream.Deinitialize();
free(buffer);
opus_encoder_destroy(enc);
}
static void static void
opus_encoder_close(Encoder *_encoder) opus_encoder_close(Encoder *_encoder)
{ {
auto *encoder = (OpusEncoder *)_encoder; auto &encoder = *(OpusEncoder *)_encoder;
encoder.Close();
encoder->stream.Deinitialize();
free(encoder->buffer);
opus_encoder_destroy(encoder->enc);
} }
static bool bool
opus_encoder_do_encode(OpusEncoder *encoder, bool eos, OpusEncoder::DoEncode(bool eos, Error &error)
Error &error)
{ {
assert(encoder->buffer_position == encoder->buffer_size); assert(buffer_position == buffer_size);
opus_int32 result = opus_int32 result =
encoder->audio_format.format == SampleFormat::S16 audio_format.format == SampleFormat::S16
? opus_encode(encoder->enc, ? opus_encode(enc,
(const opus_int16 *)encoder->buffer, (const opus_int16 *)buffer,
encoder->buffer_frames, buffer_frames,
encoder->buffer2, buffer2,
sizeof(encoder->buffer2)) sizeof(buffer2))
: opus_encode_float(encoder->enc, : opus_encode_float(enc,
(const float *)encoder->buffer, (const float *)buffer,
encoder->buffer_frames, buffer_frames,
encoder->buffer2, buffer2,
sizeof(encoder->buffer2)); sizeof(buffer2));
if (result < 0) { if (result < 0) {
error.Set(opus_encoder_domain, "Opus encoder error"); error.Set(opus_encoder_domain, "Opus encoder error");
return false; return false;
} }
encoder->granulepos += encoder->buffer_frames; granulepos += buffer_frames;
ogg_packet packet; ogg_packet packet;
packet.packet = encoder->buffer2; packet.packet = buffer2;
packet.bytes = result; packet.bytes = result;
packet.b_o_s = false; packet.b_o_s = false;
packet.e_o_s = eos; packet.e_o_s = eos;
packet.granulepos = encoder->granulepos; packet.granulepos = granulepos;
packet.packetno = encoder->packetno++; packet.packetno = packetno++;
encoder->stream.PacketIn(packet); stream.PacketIn(packet);
encoder->buffer_position = 0; buffer_position = 0;
return true; return true;
} }
bool
OpusEncoder::End(Error &error)
{
stream.Flush();
memset(buffer + buffer_position, 0,
buffer_size - buffer_position);
buffer_position = buffer_size;
return DoEncode(true, error);
}
static bool static bool
opus_encoder_end(Encoder *_encoder, Error &error) opus_encoder_end(Encoder *_encoder, Error &error)
{ {
auto *encoder = (OpusEncoder *)_encoder; auto &encoder = *(OpusEncoder *)_encoder;
return encoder.End(error);
encoder->stream.Flush();
memset(encoder->buffer + encoder->buffer_position, 0,
encoder->buffer_size - encoder->buffer_position);
encoder->buffer_position = encoder->buffer_size;
return opus_encoder_do_encode(encoder, true, error);
} }
static bool bool
opus_encoder_flush(Encoder *_encoder, gcc_unused Error &error) OpusEncoder::Flush(gcc_unused Error &error)
{ {
auto *encoder = (OpusEncoder *)_encoder; stream.Flush();
encoder->stream.Flush();
return true; return true;
} }
static bool static bool
opus_encoder_write_silence(OpusEncoder *encoder, unsigned fill_frames, opus_encoder_flush(Encoder *_encoder, Error &error)
Error &error)
{ {
size_t fill_bytes = fill_frames * encoder->frame_size; auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Flush(error);
}
bool
OpusEncoder::WriteSilence(unsigned fill_frames, Error &error)
{
size_t fill_bytes = fill_frames * frame_size;
while (fill_bytes > 0) { while (fill_bytes > 0) {
size_t nbytes = size_t nbytes = buffer_size - buffer_position;
encoder->buffer_size - encoder->buffer_position;
if (nbytes > fill_bytes) if (nbytes > fill_bytes)
nbytes = fill_bytes; nbytes = fill_bytes;
memset(encoder->buffer + encoder->buffer_position, memset(buffer + buffer_position, 0, nbytes);
0, nbytes); buffer_position += nbytes;
encoder->buffer_position += nbytes;
fill_bytes -= nbytes; fill_bytes -= nbytes;
if (encoder->buffer_position == encoder->buffer_size && if (buffer_position == buffer_size &&
!opus_encoder_do_encode(encoder, false, error)) !DoEncode(false, error))
return false;
}
return true;
}
bool
OpusEncoder::Write(const void *_data, size_t length, Error &error)
{
const uint8_t *data = (const uint8_t *)_data;
if (lookahead > 0) {
/* generate some silence at the beginning of the
stream */
assert(buffer_position == 0);
if (!WriteSilence(lookahead, error))
return false;
lookahead = 0;
}
while (length > 0) {
size_t nbytes = buffer_size - buffer_position;
if (nbytes > length)
nbytes = length;
memcpy(buffer + buffer_position, data, nbytes);
data += nbytes;
length -= nbytes;
buffer_position += nbytes;
if (buffer_position == buffer_size &&
!DoEncode(false, error))
return false; return false;
} }
@ -294,55 +362,22 @@ opus_encoder_write_silence(OpusEncoder *encoder, unsigned fill_frames,
static bool static bool
opus_encoder_write(Encoder *_encoder, opus_encoder_write(Encoder *_encoder,
const void *_data, size_t length, const void *data, size_t length,
Error &error) Error &error)
{ {
auto *encoder = (OpusEncoder *)_encoder; auto &encoder = *(OpusEncoder *)_encoder;
const uint8_t *data = (const uint8_t *)_data; return encoder.Write(data, length, error);
if (encoder->lookahead > 0) {
/* generate some silence at the beginning of the
stream */
assert(encoder->buffer_position == 0);
if (!opus_encoder_write_silence(encoder, encoder->lookahead,
error))
return false;
encoder->lookahead = 0;
}
while (length > 0) {
size_t nbytes =
encoder->buffer_size - encoder->buffer_position;
if (nbytes > length)
nbytes = length;
memcpy(encoder->buffer + encoder->buffer_position,
data, nbytes);
data += nbytes;
length -= nbytes;
encoder->buffer_position += nbytes;
if (encoder->buffer_position == encoder->buffer_size &&
!opus_encoder_do_encode(encoder, false, error))
return false;
}
return true;
} }
static void void
opus_encoder_generate_head(OpusEncoder *encoder) OpusEncoder::GenerateHead()
{ {
unsigned char header[19]; unsigned char header[19];
memcpy(header, "OpusHead", 8); memcpy(header, "OpusHead", 8);
header[8] = 1; header[8] = 1;
header[9] = encoder->audio_format.channels; header[9] = audio_format.channels;
*(uint16_t *)(header + 10) = ToLE16(encoder->lookahead); *(uint16_t *)(header + 10) = ToLE16(lookahead);
*(uint32_t *)(header + 12) = *(uint32_t *)(header + 12) = ToLE32(audio_format.sample_rate);
ToLE32(encoder->audio_format.sample_rate);
header[16] = 0; header[16] = 0;
header[17] = 0; header[17] = 0;
header[18] = 0; header[18] = 0;
@ -353,13 +388,13 @@ opus_encoder_generate_head(OpusEncoder *encoder)
packet.b_o_s = true; packet.b_o_s = true;
packet.e_o_s = false; packet.e_o_s = false;
packet.granulepos = 0; packet.granulepos = 0;
packet.packetno = encoder->packetno++; packet.packetno = packetno++;
encoder->stream.PacketIn(packet); stream.PacketIn(packet);
encoder->stream.Flush(); stream.Flush();
} }
static void void
opus_encoder_generate_tags(OpusEncoder *encoder) OpusEncoder::GenerateTags()
{ {
const char *version = opus_get_version_string(); const char *version = opus_get_version_string();
size_t version_length = strlen(version); size_t version_length = strlen(version);
@ -377,24 +412,29 @@ opus_encoder_generate_tags(OpusEncoder *encoder)
packet.b_o_s = false; packet.b_o_s = false;
packet.e_o_s = false; packet.e_o_s = false;
packet.granulepos = 0; packet.granulepos = 0;
packet.packetno = encoder->packetno++; packet.packetno = packetno++;
encoder->stream.PacketIn(packet); stream.PacketIn(packet);
encoder->stream.Flush(); stream.Flush();
free(comments); free(comments);
} }
size_t
OpusEncoder::Read(void *dest, size_t length)
{
if (packetno == 0)
GenerateHead();
else if (packetno == 1)
GenerateTags();
return stream.PageOut(dest, length);
}
static size_t static size_t
opus_encoder_read(Encoder *_encoder, void *dest, size_t length) opus_encoder_read(Encoder *_encoder, void *dest, size_t length)
{ {
auto *encoder = (OpusEncoder *)_encoder; auto &encoder = *(OpusEncoder *)_encoder;
return encoder.Read(dest, length);
if (encoder->packetno == 0)
opus_encoder_generate_head(encoder);
else if (encoder->packetno == 1)
opus_encoder_generate_tags(encoder);
return encoder->stream.PageOut(dest, length);
} }
static const char * static const char *