aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--src/decoder/plugins/OpusDecoderPlugin.cxx37
2 files changed, 37 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index 8a6f33c1b..342e14eb6 100644
--- a/NEWS
+++ b/NEWS
@@ -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;
}