diff options
author | Max Kellermann <max@duempel.org> | 2014-11-11 07:41:01 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2014-11-11 11:20:18 +0100 |
commit | c98cb1d6f9704e1fefcc28938bcedb8a4d84a7e5 (patch) | |
tree | 3b61e60c56de975644a4739a6e240c5cb441d314 | |
parent | ba6f2b0467d85e5d855c7bc9649a09cfe973e4b9 (diff) |
decoder/opus: support chained streams
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | src/decoder/plugins/OpusDecoderPlugin.cxx | 37 |
2 files changed, 37 insertions, 1 deletions
@@ -10,6 +10,7 @@ ver 0.19.3 (not yet released) - audiofile: fix bit rate calculation - ffmpeg: support opus - opus: fix bogus duration on streams + - opus: support chained streams - opus: improved error logging * fix distorted audio with soxr resampler * fix build failure on Mac OS X with non-Apple compilers 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; } |