decoder/opus: move code to class OggVisitor
This commit is contained in:
		| @@ -585,6 +585,7 @@ if HAVE_XIPH | ||||
| noinst_LIBRARIES += libxiph.a | ||||
|  | ||||
| libxiph_a_SOURCES = \ | ||||
| 	src/lib/xiph/OggVisitor.cxx src/lib/xiph/OggVisitor.hxx \ | ||||
| 	src/lib/xiph/VorbisComment.hxx \ | ||||
| 	src/lib/xiph/VorbisComments.cxx src/lib/xiph/VorbisComments.hxx \ | ||||
| 	src/lib/xiph/XiphTags.cxx src/lib/xiph/XiphTags.hxx | ||||
|   | ||||
| @@ -23,8 +23,7 @@ | ||||
| #include "OpusHead.hxx" | ||||
| #include "OpusTags.hxx" | ||||
| #include "OggFind.hxx" | ||||
| #include "lib/xiph/OggStreamState.hxx" | ||||
| #include "lib/xiph/OggSyncState.hxx" | ||||
| #include "lib/xiph/OggVisitor.hxx" | ||||
| #include "../DecoderAPI.hxx" | ||||
| #include "decoder/Reader.hxx" | ||||
| #include "input/Reader.hxx" | ||||
| @@ -73,13 +72,10 @@ mpd_opus_init(gcc_unused const ConfigBlock &block) | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| class MPDOpusDecoder { | ||||
| class MPDOpusDecoder final : public OggVisitor { | ||||
| 	Decoder &decoder; | ||||
| 	InputStream &input_stream; | ||||
|  | ||||
| 	OggSyncState oy; | ||||
| 	OggStreamState os; | ||||
|  | ||||
| 	OpusDecoder *opus_decoder = nullptr; | ||||
| 	opus_int16 *output_buffer = nullptr; | ||||
|  | ||||
| @@ -97,21 +93,12 @@ class MPDOpusDecoder { | ||||
|  | ||||
| public: | ||||
| 	MPDOpusDecoder(DecoderReader &reader) | ||||
| 		:decoder(reader.GetDecoder()), | ||||
| 		 input_stream(reader.GetInputStream()), | ||||
| 		 oy(reader), | ||||
| 		 os(0) {} | ||||
| 		:OggVisitor(reader), | ||||
| 		 decoder(reader.GetDecoder()), | ||||
| 		 input_stream(reader.GetInputStream()) {} | ||||
|  | ||||
| 	~MPDOpusDecoder(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Has the OggStreamState been initialized with the first | ||||
| 	 * serial? | ||||
| 	 */ | ||||
| 	bool HasSerial() const { | ||||
| 		return os.GetSerialNo() != 0; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Has decoder_initialized() been called yet? | ||||
| 	 */ | ||||
| @@ -119,18 +106,19 @@ public: | ||||
| 		return previous_channels != 0; | ||||
| 	} | ||||
|  | ||||
| 	bool ReadNextPage(); | ||||
|  | ||||
| 	DecoderCommand HandlePackets(); | ||||
|  | ||||
| 	bool Seek(uint64_t where_frame); | ||||
|  | ||||
| private: | ||||
| 	DecoderCommand HandlePacket(const ogg_packet &packet); | ||||
| 	void HandleBOS(const ogg_packet &packet); | ||||
| 	void HandleEOS(); | ||||
| 	DecoderCommand HandleTags(const ogg_packet &packet); | ||||
| 	DecoderCommand HandleAudio(const ogg_packet &packet); | ||||
| 	void HandleTags(const ogg_packet &packet); | ||||
| 	void HandleAudio(const ogg_packet &packet); | ||||
|  | ||||
| protected: | ||||
| 	/* virtual methods from class OggVisitor */ | ||||
| 	void OnOggBeginning(const ogg_packet &packet) override; | ||||
| 	void OnOggPacket(const ogg_packet &packet) override; | ||||
| 	void OnOggEnd() override; | ||||
| }; | ||||
|  | ||||
| MPDOpusDecoder::~MPDOpusDecoder() | ||||
| @@ -141,52 +129,13 @@ MPDOpusDecoder::~MPDOpusDecoder() | ||||
| 		opus_decoder_destroy(opus_decoder); | ||||
| } | ||||
|  | ||||
| inline bool | ||||
| MPDOpusDecoder::ReadNextPage() | ||||
| void | ||||
| MPDOpusDecoder::OnOggPacket(const ogg_packet &packet) | ||||
| { | ||||
| 	ogg_page page; | ||||
| 	if (!oy.ExpectPage(page)) | ||||
| 		return false; | ||||
|  | ||||
| 	const auto page_serialno = ogg_page_serialno(&page); | ||||
| 	if (page_serialno != os.GetSerialNo()) | ||||
| 		os.Reinitialize(page_serialno); | ||||
|  | ||||
| 	os.PageIn(page); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| inline DecoderCommand | ||||
| MPDOpusDecoder::HandlePackets() | ||||
| { | ||||
| 	ogg_packet packet; | ||||
| 	while (os.PacketOut(packet) == 1) { | ||||
| 		auto cmd = HandlePacket(packet); | ||||
| 		if (cmd != DecoderCommand::NONE) | ||||
| 			return cmd; | ||||
| 	} | ||||
|  | ||||
| 	return DecoderCommand::NONE; | ||||
| } | ||||
|  | ||||
| inline DecoderCommand | ||||
| MPDOpusDecoder::HandlePacket(const ogg_packet &packet) | ||||
| { | ||||
| 	if (packet.e_o_s) { | ||||
| 		HandleEOS(); | ||||
| 		return decoder_get_command(decoder); | ||||
| 	} | ||||
|  | ||||
| 	if (packet.b_o_s) { | ||||
| 		HandleBOS(packet); | ||||
| 		return decoder_get_command(decoder); | ||||
| 	} else if (opus_decoder == nullptr) | ||||
| 		throw std::runtime_error("BOS packet expected"); | ||||
|  | ||||
| 	if (IsOpusTags(packet)) | ||||
| 		return HandleTags(packet); | ||||
|  | ||||
| 	return HandleAudio(packet); | ||||
| 		HandleTags(packet); | ||||
| 	else | ||||
| 		HandleAudio(packet); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -240,8 +189,8 @@ LoadEOSGranulePos(InputStream &is, Decoder &decoder, int serialno) | ||||
| 	return packet.granulepos; | ||||
| } | ||||
|  | ||||
| inline void | ||||
| MPDOpusDecoder::HandleBOS(const ogg_packet &packet) | ||||
| void | ||||
| MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet) | ||||
| { | ||||
| 	assert(packet.b_o_s); | ||||
|  | ||||
| @@ -260,7 +209,7 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) | ||||
| 		throw FormatRuntimeError("Next stream has different channels (%u -> %u)", | ||||
| 					 previous_channels, channels); | ||||
|  | ||||
| 	const auto opus_serialno = os.GetSerialNo(); | ||||
| 	const auto opus_serialno = GetSerialNo(); | ||||
|  | ||||
| 	/* TODO: parse attributes from the OpusHead (sample rate, | ||||
| 	   channels, ...) */ | ||||
| @@ -295,10 +244,14 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) | ||||
|  | ||||
| 	output_buffer = new opus_int16[opus_output_buffer_frames | ||||
| 				       * audio_format.channels]; | ||||
|  | ||||
| 	auto cmd = decoder_get_command(decoder); | ||||
| 	if (cmd != DecoderCommand::NONE) | ||||
| 		throw cmd; | ||||
| } | ||||
|  | ||||
| inline void | ||||
| MPDOpusDecoder::HandleEOS() | ||||
| void | ||||
| MPDOpusDecoder::OnOggEnd() | ||||
| { | ||||
| 	if (eos_granulepos < 0 && IsInitialized()) { | ||||
| 		/* allow chaining of (unseekable) streams */ | ||||
| @@ -311,7 +264,7 @@ MPDOpusDecoder::HandleEOS() | ||||
| 		throw StopDecoder(); | ||||
| } | ||||
|  | ||||
| inline DecoderCommand | ||||
| inline void | ||||
| MPDOpusDecoder::HandleTags(const ogg_packet &packet) | ||||
| { | ||||
| 	ReplayGainInfo rgi; | ||||
| @@ -319,7 +272,6 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet) | ||||
|  | ||||
| 	TagBuilder tag_builder; | ||||
|  | ||||
| 	DecoderCommand cmd; | ||||
| 	if (ScanOpusTags(packet.packet, packet.bytes, | ||||
| 			 &rgi, | ||||
| 			 add_tag_handler, &tag_builder) && | ||||
| @@ -327,14 +279,13 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet) | ||||
| 		decoder_replay_gain(decoder, &rgi); | ||||
|  | ||||
| 		Tag tag = tag_builder.Commit(); | ||||
| 		cmd = decoder_tag(decoder, input_stream, std::move(tag)); | ||||
| 	} else | ||||
| 		cmd = decoder_get_command(decoder); | ||||
|  | ||||
| 	return cmd; | ||||
| 		auto cmd = decoder_tag(decoder, input_stream, std::move(tag)); | ||||
| 		if (cmd != DecoderCommand::NONE) | ||||
| 			throw cmd; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| inline DecoderCommand | ||||
| inline void | ||||
| MPDOpusDecoder::HandleAudio(const ogg_packet &packet) | ||||
| { | ||||
| 	assert(opus_decoder != nullptr); | ||||
| @@ -354,15 +305,13 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet) | ||||
| 					output_buffer, nbytes, | ||||
| 					0); | ||||
| 		if (cmd != DecoderCommand::NONE) | ||||
| 			return cmd; | ||||
| 			throw cmd; | ||||
|  | ||||
| 		if (packet.granulepos > 0) | ||||
| 			decoder_timestamp(decoder, | ||||
| 					  double(packet.granulepos) | ||||
| 					  / opus_sample_rate); | ||||
| 	} | ||||
|  | ||||
| 	return DecoderCommand::NONE; | ||||
| } | ||||
|  | ||||
| bool | ||||
| @@ -380,7 +329,11 @@ MPDOpusDecoder::Seek(uint64_t where_frame) | ||||
| 	offset_type offset(where_granulepos * input_stream.GetSize() | ||||
| 			   / eos_granulepos); | ||||
|  | ||||
| 	return OggSeekPageAtOffset(oy, os, input_stream, offset); | ||||
| 	if (!input_stream.LockSeek(offset, IgnoreError())) | ||||
| 		return false; | ||||
|  | ||||
| 	PostSeek(); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -399,21 +352,18 @@ mpd_opus_stream_decode(Decoder &decoder, | ||||
| 	MPDOpusDecoder d(reader); | ||||
|  | ||||
| 	while (true) { | ||||
| 		auto cmd = d.HandlePackets(); | ||||
| 		if (cmd == DecoderCommand::SEEK) { | ||||
| 			if (d.Seek(decoder_seek_where_frame(decoder))) | ||||
| 				decoder_command_finished(decoder); | ||||
| 			else | ||||
| 				decoder_seek_error(decoder); | ||||
|  | ||||
| 			continue; | ||||
| 		try { | ||||
| 			d.Visit(); | ||||
| 			break; | ||||
| 		} catch (DecoderCommand cmd) { | ||||
| 			if (cmd == DecoderCommand::SEEK) { | ||||
| 				if (d.Seek(decoder_seek_where_frame(decoder))) | ||||
| 					decoder_command_finished(decoder); | ||||
| 				else | ||||
| 					decoder_seek_error(decoder); | ||||
| 			} else if (cmd != DecoderCommand::NONE) | ||||
| 				break; | ||||
| 		} | ||||
|  | ||||
| 		if (cmd != DecoderCommand::NONE) | ||||
| 			break; | ||||
|  | ||||
| 		if (!d.ReadNextPage()) | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										101
									
								
								src/lib/xiph/OggVisitor.cxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/lib/xiph/OggVisitor.cxx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| /* | ||||
|  * 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" | ||||
| #include "OggVisitor.hxx" | ||||
|  | ||||
| #include <stdexcept> | ||||
|  | ||||
| void | ||||
| OggVisitor::EndStream() | ||||
| { | ||||
| 	if (!has_stream) | ||||
| 		return; | ||||
|  | ||||
| 	has_stream = false; | ||||
| 	OnOggEnd(); | ||||
| } | ||||
|  | ||||
| inline bool | ||||
| OggVisitor::ReadNextPage() | ||||
| { | ||||
| 	ogg_page page; | ||||
| 	if (!sync.ExpectPage(page)) | ||||
| 		return false; | ||||
|  | ||||
| 	const auto page_serialno = ogg_page_serialno(&page); | ||||
| 	if (page_serialno != stream.GetSerialNo()) { | ||||
| 		EndStream(); | ||||
| 		stream.Reinitialize(page_serialno); | ||||
| 	} | ||||
|  | ||||
| 	stream.PageIn(page); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| inline void | ||||
| OggVisitor::HandlePacket(const ogg_packet &packet) | ||||
| { | ||||
| 	if (packet.b_o_s) { | ||||
| 		EndStream(); | ||||
| 		has_stream = true; | ||||
| 		OnOggBeginning(packet); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (!has_stream) | ||||
| 		/* fail if BOS is missing */ | ||||
| 		throw std::runtime_error("BOS packet expected"); | ||||
|  | ||||
| 	if (packet.e_o_s) { | ||||
| 		EndStream(); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	OnOggPacket(packet); | ||||
| } | ||||
|  | ||||
| inline void | ||||
| OggVisitor::HandlePackets() | ||||
| { | ||||
| 	ogg_packet packet; | ||||
| 	while (stream.PacketOut(packet) == 1) | ||||
| 		HandlePacket(packet); | ||||
| } | ||||
|  | ||||
| void | ||||
| OggVisitor::Visit() | ||||
| { | ||||
| 	do { | ||||
| 		HandlePackets(); | ||||
| 	} while (ReadNextPage()); | ||||
| } | ||||
|  | ||||
| void | ||||
| OggVisitor::PostSeek() | ||||
| { | ||||
| 	sync.Reset(); | ||||
|  | ||||
| 	/* reset the stream to clear any previous partial packet | ||||
| 	   data */ | ||||
| 	stream.Reset(); | ||||
|  | ||||
| 	/* find the next Ogg page and feed it into the stream */ | ||||
| 	sync.ExpectPageSeekIn(stream); | ||||
| } | ||||
							
								
								
									
										70
									
								
								src/lib/xiph/OggVisitor.hxx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/lib/xiph/OggVisitor.hxx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* | ||||
|  * 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_VISITOR_HXX | ||||
| #define MPD_OGG_VISITOR_HXX | ||||
|  | ||||
| #include "check.h" | ||||
| #include "OggSyncState.hxx" | ||||
| #include "OggStreamState.hxx" | ||||
|  | ||||
| #include <ogg/ogg.h> | ||||
|  | ||||
| #include <stddef.h> | ||||
|  | ||||
| class Reader; | ||||
|  | ||||
| /** | ||||
|  * Abstract class which iterates over Ogg packets in a #Reader. | ||||
|  * Subclass it and implement the virtual methods. | ||||
|  */ | ||||
| class OggVisitor { | ||||
| 	OggSyncState sync; | ||||
| 	OggStreamState stream; | ||||
|  | ||||
| 	bool has_stream = false; | ||||
|  | ||||
| public: | ||||
| 	explicit OggVisitor(Reader &reader) | ||||
| 		:sync(reader), stream(0) {} | ||||
|  | ||||
| 	long GetSerialNo() const { | ||||
| 		return stream.GetSerialNo(); | ||||
| 	} | ||||
|  | ||||
| 	void Visit(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Call this method after seeking the #Reader. | ||||
| 	 */ | ||||
| 	void PostSeek(); | ||||
|  | ||||
| private: | ||||
| 	void EndStream(); | ||||
| 	bool ReadNextPage(); | ||||
| 	void HandlePacket(const ogg_packet &packet); | ||||
| 	void HandlePackets(); | ||||
|  | ||||
| protected: | ||||
| 	virtual void OnOggBeginning(const ogg_packet &packet) = 0; | ||||
| 	virtual void OnOggPacket(const ogg_packet &packet) = 0; | ||||
| 	virtual void OnOggEnd() = 0; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann