diff options
author | John Regan <john@jrjrtech.com> | 2021-05-15 10:16:49 -0400 |
---|---|---|
committer | John Regan <john@jrjrtech.com> | 2021-05-15 11:31:01 -0400 |
commit | 87fa6bca544722550424404b19594f465a391374 (patch) | |
tree | f2f06b8b42ad0e80d0005ec59c245d6b6a655b93 /src | |
parent | c3226a319510a7ac57d6e88363a32780eb099888 (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.cxx | 97 | ||||
-rw-r--r-- | src/encoder/plugins/OggEncoder.hxx | 4 | ||||
-rw-r--r-- | src/encoder/plugins/OpusEncoderPlugin.cxx | 2 | ||||
-rw-r--r-- | src/encoder/plugins/VorbisEncoderPlugin.cxx | 2 | ||||
-rw-r--r-- | src/lib/xiph/meson.build | 1 | ||||
-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.build | 1 |
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, ) |