summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Regan <john@jrjrtech.com>2021-05-15 10:16:49 -0400
committerJohn Regan <john@jrjrtech.com>2021-05-15 11:31:01 -0400
commit87fa6bca544722550424404b19594f465a391374 (patch)
treef2f06b8b42ad0e80d0005ec59c245d6b6a655b93 /src
parentc3226a319510a7ac57d6e88363a32780eb099888 (diff)
flac encoder: enable Ogg FLAC and Ogg chaining
refactors GenerateOggSerial into a generic GenerateSerial utility, under the util lib. libFLAC may be encoded without Ogg support. If Ogg support is disabled, libFLAC will still export Ogg-related methods (like setting a serial number), and throw a runtime error when initializing an Ogg stream. GenerateOggSerial does not depend on libogg. Refactoring it into a generic GenerateSerial prevents having to add build-time checks for libogg within the FLAC encoder plugin.
Diffstat (limited to 'src')
-rw-r--r--src/encoder/plugins/FlacEncoderPlugin.cxx97
-rw-r--r--src/encoder/plugins/OggEncoder.hxx4
-rw-r--r--src/encoder/plugins/OpusEncoderPlugin.cxx2
-rw-r--r--src/encoder/plugins/VorbisEncoderPlugin.cxx2
-rw-r--r--src/lib/xiph/meson.build1
-rw-r--r--src/util/Serial.cxx (renamed from src/lib/xiph/OggSerial.cxx)12
-rw-r--r--src/util/Serial.hxx (renamed from src/lib/xiph/OggSerial.hxx)8
-rw-r--r--src/util/meson.build1
8 files changed, 95 insertions, 32 deletions
diff --git a/src/encoder/plugins/FlacEncoderPlugin.cxx b/src/encoder/plugins/FlacEncoderPlugin.cxx
index f2c514178..352434f41 100644
--- a/src/encoder/plugins/FlacEncoderPlugin.cxx
+++ b/src/encoder/plugins/FlacEncoderPlugin.cxx
@@ -23,8 +23,11 @@
#include "pcm/Buffer.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include "util/RuntimeError.hxx"
+#include "util/Serial.hxx"
+#include "util/StringUtil.hxx"
#include <FLAC/stream_encoder.h>
+#include <FLAC/metadata.h>
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
#error libFLAC is too old
@@ -34,6 +37,7 @@ class FlacEncoder final : public Encoder {
const AudioFormat audio_format;
FLAC__StreamEncoder *const fse;
+ const unsigned compression;
PcmBuffer expand_buffer;
@@ -44,7 +48,7 @@ class FlacEncoder final : public Encoder {
DynamicFifoBuffer<uint8_t> output_buffer;
public:
- FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse);
+ FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse, unsigned _compression, bool _oggflac, bool _oggchaining);
~FlacEncoder() noexcept override {
FLAC__stream_encoder_delete(fse);
@@ -56,9 +60,14 @@ public:
}
void Flush() override {
+ }
+
+ void PreTag() override {
(void) FLAC__stream_encoder_finish(fse);
}
+ void SendTag(const Tag &tag) override;
+
void Write(const void *data, size_t length) override;
size_t Read(void *dest, size_t length) noexcept override {
@@ -80,6 +89,8 @@ private:
class PreparedFlacEncoder final : public PreparedEncoder {
const unsigned compression;
+ const bool oggchaining;
+ const bool oggflac;
public:
explicit PreparedFlacEncoder(const ConfigBlock &block);
@@ -88,12 +99,16 @@ public:
Encoder *Open(AudioFormat &audio_format) override;
[[nodiscard]] const char *GetMimeType() const noexcept override {
- return "audio/flac";
+ if(oggflac)
+ return "audio/ogg";
+ return "audio/flac";
}
};
PreparedFlacEncoder::PreparedFlacEncoder(const ConfigBlock &block)
- :compression(block.GetBlockValue("compression", 5U))
+ :compression(block.GetBlockValue("compression", 5U)),
+ oggchaining(block.GetBlockValue("oggchaining",false)),
+ oggflac(block.GetBlockValue("oggflac",false) || oggchaining)
{
}
@@ -105,8 +120,23 @@ flac_encoder_init(const ConfigBlock &block)
static void
flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
- const AudioFormat &audio_format, unsigned bits_per_sample)
+ const AudioFormat &audio_format)
{
+ unsigned bits_per_sample;
+
+ switch (audio_format.format) {
+ case SampleFormat::S8:
+ bits_per_sample = 8;
+ break;
+
+ case SampleFormat::S16:
+ bits_per_sample = 16;
+ break;
+
+ default:
+ bits_per_sample = 24;
+ }
+
if (!FLAC__stream_encoder_set_compression_level(fse, compression))
throw FormatRuntimeError("error setting flac compression to %d",
compression);
@@ -123,16 +153,26 @@ flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression,
audio_format.sample_rate))
throw FormatRuntimeError("error setting flac sample rate to %d",
audio_format.sample_rate);
+
+ if (!FLAC__stream_encoder_set_ogg_serial_number(fse,
+ GenerateSerial()))
+ throw FormatRuntimeError("error setting ogg serial number");
}
-FlacEncoder::FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse)
- :Encoder(false),
+FlacEncoder::FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse, unsigned _compression, bool _oggflac, bool _oggchaining)
+ :Encoder(_oggchaining),
audio_format(_audio_format), fse(_fse),
+ compression(_compression),
output_buffer(8192)
{
/* this immediately outputs data through callback */
- auto init_status =
+ auto init_status = _oggflac ?
+ FLAC__stream_encoder_init_ogg_stream(fse,
+ nullptr, WriteCallback,
+ nullptr, nullptr, nullptr,
+ this)
+ :
FLAC__stream_encoder_init_stream(fse,
WriteCallback,
nullptr, nullptr, nullptr,
@@ -146,24 +186,17 @@ FlacEncoder::FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse)
Encoder *
PreparedFlacEncoder::Open(AudioFormat &audio_format)
{
- unsigned bits_per_sample;
-
- /* FIXME: flac should support 32bit as well */
switch (audio_format.format) {
case SampleFormat::S8:
- bits_per_sample = 8;
break;
case SampleFormat::S16:
- bits_per_sample = 16;
break;
case SampleFormat::S24_P32:
- bits_per_sample = 24;
break;
default:
- bits_per_sample = 24;
audio_format.format = SampleFormat::S24_P32;
}
@@ -173,16 +206,46 @@ PreparedFlacEncoder::Open(AudioFormat &audio_format)
throw std::runtime_error("FLAC__stream_encoder_new() failed");
try {
- flac_encoder_setup(fse, compression,
- audio_format, bits_per_sample);
+ flac_encoder_setup(fse, compression, audio_format);
} catch (...) {
FLAC__stream_encoder_delete(fse);
throw;
}
- return new FlacEncoder(audio_format, fse);
+ return new FlacEncoder(audio_format, fse, compression, oggflac, oggchaining);
+}
+
+void
+FlacEncoder::SendTag(const Tag &tag)
+{
+ /* re-initialize encoder since flac_encoder_finish resets everything */
+ flac_encoder_setup(fse, compression, audio_format);
+
+ FLAC__StreamMetadata *metadata = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
+ FLAC__StreamMetadata_VorbisComment_Entry entry;
+
+ for (const auto &item : tag) {
+ char name[64];
+ ToUpperASCII(name, tag_item_names[item.type], sizeof(name));
+ FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, name, item.value);
+ FLAC__metadata_object_vorbiscomment_append_comment(metadata, entry, false);
+ }
+
+ FLAC__stream_encoder_set_metadata(fse,&metadata,1);
+
+ auto init_status = FLAC__stream_encoder_init_ogg_stream(fse,
+ nullptr, WriteCallback,
+ nullptr, nullptr, nullptr,
+ this);
+
+ FLAC__metadata_object_delete(metadata);
+
+ if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
+ throw FormatRuntimeError("failed to initialize encoder: %s\n",
+ FLAC__StreamEncoderInitStatusString[init_status]);
}
+
static inline void
pcm8_to_flac(int32_t *out, const int8_t *in, unsigned num_samples) noexcept
{
diff --git a/src/encoder/plugins/OggEncoder.hxx b/src/encoder/plugins/OggEncoder.hxx
index aa668bcb9..39b62d822 100644
--- a/src/encoder/plugins/OggEncoder.hxx
+++ b/src/encoder/plugins/OggEncoder.hxx
@@ -23,7 +23,7 @@
#include "../EncoderAPI.hxx"
#include "lib/xiph/OggStreamState.hxx"
#include "lib/xiph/OggPage.hxx"
-#include "lib/xiph/OggSerial.hxx"
+#include "util/Serial.hxx"
#include <ogg/ogg.h>
@@ -42,7 +42,7 @@ protected:
public:
OggEncoder(bool _implements_tag)
:Encoder(_implements_tag),
- stream(GenerateOggSerial()) {
+ stream(GenerateSerial()) {
}
/* virtual methods from class Encoder */
diff --git a/src/encoder/plugins/OpusEncoderPlugin.cxx b/src/encoder/plugins/OpusEncoderPlugin.cxx
index d579d1e50..717e5f998 100644
--- a/src/encoder/plugins/OpusEncoderPlugin.cxx
+++ b/src/encoder/plugins/OpusEncoderPlugin.cxx
@@ -405,7 +405,7 @@ OpusEncoder::PreTag()
void
OpusEncoder::SendTag(const Tag &tag)
{
- stream.Reinitialize(GenerateOggSerial());
+ stream.Reinitialize(GenerateSerial());
opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
GenerateHeaders(&tag);
}
diff --git a/src/encoder/plugins/VorbisEncoderPlugin.cxx b/src/encoder/plugins/VorbisEncoderPlugin.cxx
index 4acf1ec05..c460ab94e 100644
--- a/src/encoder/plugins/VorbisEncoderPlugin.cxx
+++ b/src/encoder/plugins/VorbisEncoderPlugin.cxx
@@ -225,7 +225,7 @@ VorbisEncoder::SendTag(const Tag &tag)
/* reset ogg_stream_state and begin a new stream */
- stream.Reinitialize(GenerateOggSerial());
+ stream.Reinitialize(GenerateSerial());
/* send that vorbis_comment to the ogg_stream_state */
diff --git a/src/lib/xiph/meson.build b/src/lib/xiph/meson.build
index d57d68d8f..4666d28fc 100644
--- a/src/lib/xiph/meson.build
+++ b/src/lib/xiph/meson.build
@@ -72,7 +72,6 @@ if libogg_dep.found()
ogg = static_library(
'ogg',
'OggVisitor.cxx',
- 'OggSerial.cxx',
'OggSyncState.cxx',
'OggFind.cxx',
'OggPacket.cxx',
diff --git a/src/lib/xiph/OggSerial.cxx b/src/util/Serial.cxx
index 6f5ba3f84..c91fe62da 100644
--- a/src/lib/xiph/OggSerial.cxx
+++ b/src/util/Serial.cxx
@@ -17,18 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "OggSerial.hxx"
-#include "util/Compiler.h"
+#include "Serial.hxx"
+#include "Compiler.h"
#include <atomic>
#include <chrono>
-static std::atomic_uint next_ogg_serial;
+static std::atomic_uint next_serial;
int
-GenerateOggSerial() noexcept
+GenerateSerial() noexcept
{
- unsigned serial = ++next_ogg_serial;
+ unsigned serial = ++next_serial;
if (gcc_unlikely(serial < 16)) {
/* first-time initialization: seed with a clock value,
which is random enough for our use */
@@ -38,7 +38,7 @@ GenerateOggSerial() noexcept
const auto now = steady_clock::now().time_since_epoch();
const auto now_ms = duration_cast<milliseconds>(now);
const unsigned seed = now_ms.count();
- next_ogg_serial = serial = seed;
+ next_serial = serial = seed;
}
return serial;
diff --git a/src/lib/xiph/OggSerial.hxx b/src/util/Serial.hxx
index bc6298139..2e8723e7f 100644
--- a/src/lib/xiph/OggSerial.hxx
+++ b/src/util/Serial.hxx
@@ -17,13 +17,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OGG_SERIAL_HXX
-#define MPD_OGG_SERIAL_HXX
+#ifndef MPD_SERIAL_HXX
+#define MPD_SERIAL_HXX
/**
- * Generate the next pseudo-random Ogg serial.
+ * Generate the next pseudo-random serial.
*/
int
-GenerateOggSerial() noexcept;
+GenerateSerial() noexcept;
#endif
diff --git a/src/util/meson.build b/src/util/meson.build
index dc37588cb..953296e5c 100644
--- a/src/util/meson.build
+++ b/src/util/meson.build
@@ -29,6 +29,7 @@ util = static_library(
'ByteReverse.cxx',
'format.c',
'BitReverse.cxx',
+ 'Serial.cxx',
include_directories: inc,
)