diff options
author | Max Kellermann <max@duempel.org> | 2013-10-24 20:23:26 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2013-10-24 23:26:46 +0200 |
commit | 982ab9e4965736ba725be3c2a167a9c55fede961 (patch) | |
tree | 47c27c9c8c600337537a2c8cece084ce6f81ca15 | |
parent | b74bcf2274ca27cd2db00ffe94f2154ba889d281 (diff) | |
download | mpd-982ab9e4965736ba725be3c2a167a9c55fede961.tar.gz mpd-982ab9e4965736ba725be3c2a167a9c55fede961.tar.xz mpd-982ab9e4965736ba725be3c2a167a9c55fede961.zip |
decoder/opus: show song duration during playback
This requires seeking to the end-of-stream, checking its granulepos,
and then seeking back to the previous file position. We do this only
for local files.
-rw-r--r-- | src/decoder/OpusDecoderPlugin.cxx | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/OpusDecoderPlugin.cxx index c90e31232..b010de49e 100644 --- a/src/decoder/OpusDecoderPlugin.cxx +++ b/src/decoder/OpusDecoderPlugin.cxx @@ -171,6 +171,56 @@ MPDOpusDecoder::HandlePacket(const ogg_packet &packet) return HandleAudio(packet); } +/** + * Load the end-of-stream packet and restore the previous file + * position. + */ +static bool +LoadEOSPacket(InputStream &is, Decoder *decoder, int serialno, + ogg_packet &packet) +{ + if (!is.CheapSeeking()) + /* we do this for local files only, because seeking + around remote files is expensive and not worth the + troubl */ + return -1; + + const auto old_offset = is.offset; + if (old_offset < 0) + return -1; + + /* create temporary Ogg objects for seeking and parsing the + EOS packet */ + OggSyncState oy(is, decoder); + ogg_stream_state os; + ogg_stream_init(&os, serialno); + + bool result = OggSeekFindEOS(oy, os, packet, is); + ogg_stream_clear(&os); + + /* restore the previous file position */ + is.Seek(old_offset, SEEK_SET, IgnoreError()); + + return result; +} + +/** + * Load the end-of-stream granulepos and restore the previous file + * position. + * + * @return -1 on error + */ +gcc_pure +static ogg_int64_t +LoadEOSGranulePos(InputStream &is, Decoder *decoder, int serialno) +{ + ogg_packet packet; + if (!LoadEOSPacket(is, decoder, serialno, packet)) + return -1; + + return packet.granulepos; +} + inline DecoderCommand MPDOpusDecoder::HandleBOS(const ogg_packet &packet) { @@ -202,9 +252,15 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) return DecoderCommand::STOP; } + const ogg_int64_t eos_granulepos = + LoadEOSGranulePos(input_stream, &decoder, opus_serialno); + const double duration = eos_granulepos >= 0 + ? double(eos_granulepos) / opus_sample_rate + : -1.0; + const AudioFormat audio_format(opus_sample_rate, SampleFormat::S16, channels); - decoder_initialized(decoder, audio_format, false, -1); + decoder_initialized(decoder, audio_format, false, duration); frame_size = audio_format.GetFrameSize(); /* allocate an output buffer for 16 bit PCM samples big enough |