diff options
author | Max Kellermann <max@duempel.org> | 2016-05-09 16:46:08 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2016-05-14 11:31:31 +0200 |
commit | bbbda7f8127136e766ce07db597874eacf9175b6 (patch) | |
tree | aa69eb112770f6a1582c86d4b7fcd50ece6a00e3 /src/decoder | |
parent | e95890038068f552d92ceb6b62afbfc9e81d0c7a (diff) |
decoder/opus: move code to new class OggDecoder
Prepare to reuse the same code for a new Vorbis decoder plugin based
on libvorbis instead of libvorbisfile.
Diffstat (limited to 'src/decoder')
-rw-r--r-- | src/decoder/plugins/OggDecoder.cxx | 87 | ||||
-rw-r--r-- | src/decoder/plugins/OggDecoder.hxx | 64 | ||||
-rw-r--r-- | src/decoder/plugins/OpusDecoderPlugin.cxx | 89 |
3 files changed, 159 insertions, 81 deletions
diff --git a/src/decoder/plugins/OggDecoder.cxx b/src/decoder/plugins/OggDecoder.cxx new file mode 100644 index 000000000..b987c45b0 --- /dev/null +++ b/src/decoder/plugins/OggDecoder.cxx @@ -0,0 +1,87 @@ +/* + * Copyright 2003-2016 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" /* must be first for large file support */ +#include "OggDecoder.hxx" +#include "lib/xiph/OggFind.hxx" +#include "input/InputStream.hxx" +#include "util/Error.hxx" + +/** + * Load the end-of-stream packet and restore the previous file + * position. + */ +bool +OggDecoder::LoadEndPacket(ogg_packet &packet) const +{ + if (!input_stream.CheapSeeking()) + /* we do this for local files only, because seeking + around remote files is expensive and not worth the + trouble */ + return false; + + const auto old_offset = input_stream.GetOffset(); + + /* create temporary Ogg objects for seeking and parsing the + EOS packet */ + + bool result; + + { + DecoderReader reader(decoder, input_stream); + OggSyncState sync2(reader); + OggStreamState stream2(GetSerialNo()); + result = OggSeekFindEOS(sync2, stream2, packet, + input_stream); + } + + /* restore the previous file position */ + input_stream.LockSeek(old_offset, IgnoreError()); + + return result; +} + +ogg_int64_t +OggDecoder::LoadEndGranulePos() const +{ + ogg_packet packet; + if (!LoadEndPacket(packet)) + return -1; + + return packet.granulepos; +} + +bool +OggDecoder::SeekGranulePos(ogg_int64_t where_granulepos, Error &error) +{ + assert(IsSeekable()); + + /* interpolate the file offset where we expect to find the + given granule position */ + /* TODO: implement binary search */ + offset_type offset(where_granulepos * input_stream.GetSize() + / end_granulepos); + + if (!input_stream.LockSeek(offset, error)) + return false; + + PostSeek(); + return true; +} + diff --git a/src/decoder/plugins/OggDecoder.hxx b/src/decoder/plugins/OggDecoder.hxx new file mode 100644 index 000000000..bb33f69cc --- /dev/null +++ b/src/decoder/plugins/OggDecoder.hxx @@ -0,0 +1,64 @@ +/* + * Copyright 2003-2016 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_OGG_DECODER_HXX +#define MPD_OGG_DECODER_HXX + +#include "config.h" /* must be first for large file support */ +#include "lib/xiph/OggVisitor.hxx" +#include "decoder/Reader.hxx" + +class Error; + +class OggDecoder : public OggVisitor { + ogg_int64_t end_granulepos; + +protected: + Decoder &decoder; + InputStream &input_stream; + +public: + explicit OggDecoder(DecoderReader &reader) + :OggVisitor(reader), + decoder(reader.GetDecoder()), + input_stream(reader.GetInputStream()) {} + + bool Seek(OggSyncState &oy, uint64_t where_frame); + +private: + /** + * Load the end-of-stream packet and restore the previous file + * position. + */ + bool LoadEndPacket(ogg_packet &packet) const; + ogg_int64_t LoadEndGranulePos() const; + +protected: + ogg_int64_t UpdateEndGranulePos() { + return end_granulepos = LoadEndGranulePos(); + } + + bool IsSeekable() const { + return end_granulepos > 0; + } + + bool SeekGranulePos(ogg_int64_t where_granulepos, Error &error); +}; + +#endif diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index 6fe2d7006..25e08f8e1 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -19,12 +19,12 @@ #include "config.h" /* must be first for large file support */ #include "OpusDecoderPlugin.h" +#include "OggDecoder.hxx" #include "OpusDomain.hxx" #include "OpusHead.hxx" #include "OpusTags.hxx" #include "lib/xiph/OggPacket.hxx" #include "lib/xiph/OggFind.hxx" -#include "lib/xiph/OggVisitor.hxx" #include "../DecoderAPI.hxx" #include "decoder/Reader.hxx" #include "input/Reader.hxx" @@ -73,10 +73,7 @@ mpd_opus_init(gcc_unused const ConfigBlock &block) return true; } -class MPDOpusDecoder final : public OggVisitor { - Decoder &decoder; - InputStream &input_stream; - +class MPDOpusDecoder final : public OggDecoder { OpusDecoder *opus_decoder = nullptr; opus_int16 *output_buffer = nullptr; @@ -88,15 +85,11 @@ class MPDOpusDecoder final : public OggVisitor { */ unsigned previous_channels = 0; - ogg_int64_t eos_granulepos; - size_t frame_size; public: - MPDOpusDecoder(DecoderReader &reader) - :OggVisitor(reader), - decoder(reader.GetDecoder()), - input_stream(reader.GetInputStream()) {} + explicit MPDOpusDecoder(DecoderReader &reader) + :OggDecoder(reader) {} ~MPDOpusDecoder(); @@ -107,8 +100,6 @@ public: return previous_channels != 0; } - DecoderCommand HandlePackets(); - bool Seek(uint64_t where_frame); private: @@ -139,57 +130,6 @@ MPDOpusDecoder::OnOggPacket(const ogg_packet &packet) 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 - trouble */ - return false; - - const auto old_offset = is.GetOffset(); - - /* create temporary Ogg objects for seeking and parsing the - EOS packet */ - - bool result; - - { - DecoderReader reader(decoder, is); - OggSyncState oy(reader); - OggStreamState os(serialno); - result = OggSeekFindEOS(oy, os, packet, is); - } - - /* restore the previous file position */ - is.LockSeek(old_offset, 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; -} - void MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet) { @@ -210,8 +150,6 @@ MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet) throw FormatRuntimeError("Next stream has different channels (%u -> %u)", previous_channels, channels); - const auto opus_serialno = GetSerialNo(); - /* TODO: parse attributes from the OpusHead (sample rate, channels, ...) */ @@ -229,8 +167,7 @@ MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet) return; } - eos_granulepos = LoadEOSGranulePos(input_stream, decoder, - opus_serialno); + const auto eos_granulepos = UpdateEndGranulePos(); const auto duration = eos_granulepos >= 0 ? SignedSongTime::FromScale<uint64_t>(eos_granulepos, opus_sample_rate) @@ -254,7 +191,7 @@ MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet) void MPDOpusDecoder::OnOggEnd() { - if (eos_granulepos < 0 && IsInitialized()) { + if (!IsSeekable() && IsInitialized()) { /* allow chaining of (unseekable) streams */ assert(opus_decoder != nullptr); assert(output_buffer != nullptr); @@ -318,23 +255,13 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet) bool MPDOpusDecoder::Seek(uint64_t where_frame) { - assert(eos_granulepos > 0); + assert(IsSeekable()); assert(input_stream.IsSeekable()); assert(input_stream.KnownSize()); const ogg_int64_t where_granulepos(where_frame); - /* interpolate the file offset where we expect to find the - given granule position */ - /* TODO: implement binary search */ - offset_type offset(where_granulepos * input_stream.GetSize() - / eos_granulepos); - - if (!input_stream.LockSeek(offset, IgnoreError())) - return false; - - PostSeek(); - return true; + return SeekGranulePos(where_granulepos, IgnoreError()); } static void |