From 8ff0d99092acfe8e80eba9709c2e93727f9e47b9 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 10 Nov 2014 09:00:50 +0100 Subject: decoder/audiofile: fix bit rate calculation --- src/decoder/plugins/AudiofileDecoderPlugin.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/decoder') diff --git a/src/decoder/plugins/AudiofileDecoderPlugin.cxx b/src/decoder/plugins/AudiofileDecoderPlugin.cxx index 4b347817e..a0ef71e49 100644 --- a/src/decoder/plugins/AudiofileDecoderPlugin.cxx +++ b/src/decoder/plugins/AudiofileDecoderPlugin.cxx @@ -209,7 +209,7 @@ audiofile_stream_decode(Decoder &decoder, InputStream &is) const auto total_time = audiofile_get_duration(fh); const uint16_t kbit_rate = (uint16_t) - (is.GetSize() * uint64_t(8000) / total_time.ToMS()); + (is.GetSize() * uint64_t(8) / total_time.ToMS()); const unsigned frame_size = (unsigned) afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, true); -- cgit v1.2.3 From 4a04f73434ed6953ae1403d6f1eb33a3807e01d7 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 07:45:31 +0100 Subject: decoder/opus: add constexpr output_buffer_frames --- src/decoder/plugins/OpusDecoderPlugin.cxx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'src/decoder') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index 97c81984c..9599f7bcf 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -40,6 +40,12 @@ static constexpr opus_int32 opus_sample_rate = 48000; +/** + * Allocate an output buffer for 16 bit PCM samples big enough to hold + * a quarter second, larger than 120ms required by libopus. + */ +static constexpr unsigned opus_output_buffer_frames = opus_sample_rate / 4; + gcc_pure static bool IsOpusHead(const ogg_packet &packet) @@ -70,7 +76,6 @@ class MPDOpusDecoder { OpusDecoder *opus_decoder; opus_int16 *output_buffer; - unsigned output_size; bool os_initialized; bool found_opus; @@ -86,7 +91,7 @@ public: InputStream &_input_stream) :decoder(_decoder), input_stream(_input_stream), opus_decoder(nullptr), - output_buffer(nullptr), output_size(0), + output_buffer(nullptr), os_initialized(false), found_opus(false) {} ~MPDOpusDecoder(); @@ -264,11 +269,8 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) eos_granulepos > 0, duration); frame_size = audio_format.GetFrameSize(); - /* allocate an output buffer for 16 bit PCM samples big enough - to hold a quarter second, larger than 120ms required by - libopus */ - output_size = audio_format.sample_rate / 4; - output_buffer = new opus_int16[output_size * audio_format.channels]; + output_buffer = new opus_int16[opus_output_buffer_frames + * audio_format.channels]; return decoder_get_command(decoder); } @@ -304,7 +306,7 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet) int nframes = opus_decode(opus_decoder, (const unsigned char*)packet.packet, packet.bytes, - output_buffer, output_size, + output_buffer, opus_output_buffer_frames, 0); if (nframes < 0) { LogError(opus_domain, opus_strerror(nframes)); -- cgit v1.2.3 From 466b6a23cdce42143a25f87ce2234e045e963bdf Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 08:30:11 +0100 Subject: decoder/opus: eliminate flag "found_opus" Check opus_decoder!=nullptr instead. --- src/decoder/plugins/OpusDecoderPlugin.cxx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/decoder') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index 9599f7bcf..cf102a9b5 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -78,7 +78,6 @@ class MPDOpusDecoder { opus_int16 *output_buffer; bool os_initialized; - bool found_opus; int opus_serialno; @@ -92,7 +91,7 @@ public: :decoder(_decoder), input_stream(_input_stream), opus_decoder(nullptr), output_buffer(nullptr), - os_initialized(false), found_opus(false) {} + os_initialized(false) {} ~MPDOpusDecoder(); bool ReadFirstPage(OggSyncState &oy); @@ -168,7 +167,7 @@ MPDOpusDecoder::HandlePacket(const ogg_packet &packet) if (packet.b_o_s) return HandleBOS(packet); - else if (!found_opus) + else if (opus_decoder == nullptr) return DecoderCommand::STOP; if (IsOpusTags(packet)) @@ -230,7 +229,7 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) { assert(packet.b_o_s); - if (found_opus || !IsOpusHead(packet)) + if (opus_decoder != nullptr || !IsOpusHead(packet)) return DecoderCommand::STOP; unsigned channels; @@ -242,7 +241,6 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) assert(output_buffer == nullptr); opus_serialno = os.serialno; - found_opus = true; /* TODO: parse attributes from the OpusHead (sample rate, channels, ...) */ -- cgit v1.2.3 From 7886a14b74f58896670bb8c0573646cd1a947d05 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 11:18:51 +0100 Subject: decoder/opus: fix mistyped LoadEOSPacket() return value --- src/decoder/plugins/OpusDecoderPlugin.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/decoder') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index cf102a9b5..a4be8eb60 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -188,7 +188,7 @@ LoadEOSPacket(InputStream &is, Decoder *decoder, int serialno, /* we do this for local files only, because seeking around remote files is expensive and not worth the troubl */ - return -1; + return false; const auto old_offset = is.GetOffset(); -- cgit v1.2.3 From 23465ad9855f4878fd7210bf076fed938a409b7d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 10:56:36 +0100 Subject: decoder/opus: improved error logging --- src/decoder/plugins/OpusDecoderPlugin.cxx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/decoder') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index a4be8eb60..c3ac96687 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -167,8 +167,10 @@ MPDOpusDecoder::HandlePacket(const ogg_packet &packet) if (packet.b_o_s) return HandleBOS(packet); - else if (opus_decoder == nullptr) + else if (opus_decoder == nullptr) { + LogDebug(opus_domain, "BOS packet expected"); return DecoderCommand::STOP; + } if (IsOpusTags(packet)) return HandleTags(packet); @@ -229,13 +231,17 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) { assert(packet.b_o_s); - if (opus_decoder != nullptr || !IsOpusHead(packet)) + if (opus_decoder != nullptr || !IsOpusHead(packet)) { + LogDebug(opus_domain, "BOS packet must be OpusHead"); return DecoderCommand::STOP; + } unsigned channels; if (!ScanOpusHeader(packet.packet, packet.bytes, channels) || - !audio_valid_channel_count(channels)) + !audio_valid_channel_count(channels)) { + LogDebug(opus_domain, "Malformed BOS packet"); return DecoderCommand::STOP; + } assert(opus_decoder == nullptr); assert(output_buffer == nullptr); @@ -307,7 +313,8 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet) output_buffer, opus_output_buffer_frames, 0); if (nframes < 0) { - LogError(opus_domain, opus_strerror(nframes)); + FormatError(opus_domain, "libopus error: %s", + opus_strerror(nframes)); return DecoderCommand::STOP; } -- cgit v1.2.3 From ba6f2b0467d85e5d855c7bc9649a09cfe973e4b9 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 08:36:22 +0100 Subject: decoder/opus: move code to HandleEOS() --- src/decoder/plugins/OpusDecoderPlugin.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/decoder') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index c3ac96687..30ce06708 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -100,6 +100,7 @@ public: DecoderCommand HandlePackets(); DecoderCommand HandlePacket(const ogg_packet &packet); DecoderCommand HandleBOS(const ogg_packet &packet); + DecoderCommand HandleEOS(); DecoderCommand HandleTags(const ogg_packet &packet); DecoderCommand HandleAudio(const ogg_packet &packet); @@ -163,7 +164,7 @@ inline DecoderCommand MPDOpusDecoder::HandlePacket(const ogg_packet &packet) { if (packet.e_o_s) - return DecoderCommand::STOP; + return HandleEOS(); if (packet.b_o_s) return HandleBOS(packet); @@ -279,6 +280,12 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) return decoder_get_command(decoder); } +inline DecoderCommand +MPDOpusDecoder::HandleEOS() +{ + return DecoderCommand::STOP; +} + inline DecoderCommand MPDOpusDecoder::HandleTags(const ogg_packet &packet) { -- cgit v1.2.3 From c98cb1d6f9704e1fefcc28938bcedb8a4d84a7e5 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 11 Nov 2014 07:41:01 +0100 Subject: decoder/opus: support chained streams --- src/decoder/plugins/OpusDecoderPlugin.cxx | 37 ++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'src/decoder') diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index 30ce06708..b6b2e0465 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -77,6 +77,14 @@ class MPDOpusDecoder { OpusDecoder *opus_decoder; opus_int16 *output_buffer; + /** + * If non-zero, then a previous Opus stream has been found + * already with this number of channels. If opus_decoder is + * nullptr, then its end-of-stream packet has been found + * already. + */ + unsigned previous_channels; + bool os_initialized; int opus_serialno; @@ -91,6 +99,7 @@ public: :decoder(_decoder), input_stream(_input_stream), opus_decoder(nullptr), output_buffer(nullptr), + previous_channels(0), os_initialized(false) {} ~MPDOpusDecoder(); @@ -245,7 +254,14 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) } assert(opus_decoder == nullptr); - assert(output_buffer == nullptr); + assert((previous_channels == 0) == (output_buffer == nullptr)); + + if (previous_channels != 0 && channels != previous_channels) { + FormatWarning(opus_domain, + "Next stream has different channels (%u -> %u)", + previous_channels, channels); + return DecoderCommand::STOP; + } opus_serialno = os.serialno; @@ -261,6 +277,13 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) return DecoderCommand::STOP; } + if (previous_channels != 0) { + /* decoder was already initialized by the previous + stream; skip the rest of this method */ + LogDebug(opus_domain, "Found another stream"); + return decoder_get_command(decoder); + } + eos_granulepos = LoadEOSGranulePos(input_stream, &decoder, opus_serialno); const auto duration = eos_granulepos >= 0 @@ -268,6 +291,7 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) opus_sample_rate) : SignedSongTime::Negative(); + previous_channels = channels; const AudioFormat audio_format(opus_sample_rate, SampleFormat::S16, channels); decoder_initialized(decoder, audio_format, @@ -283,6 +307,17 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) inline DecoderCommand MPDOpusDecoder::HandleEOS() { + if (eos_granulepos < 0 && previous_channels != 0) { + /* allow chaining of (unseekable) streams */ + assert(opus_decoder != nullptr); + assert(output_buffer != nullptr); + + opus_decoder_destroy(opus_decoder); + opus_decoder = nullptr; + + return decoder_get_command(decoder); + } + return DecoderCommand::STOP; } -- cgit v1.2.3