VorbisDecoderPlugin.cxx 9.46 KB
Newer Older
1
/*
2
 * Copyright 2003-2016 The Music Player Daemon Project
3
 * http://www.musicpd.org
Warren Dukes's avatar
Warren Dukes committed
4 5 6 7 8 9 10 11 12 13
 *
 * 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.
14 15 16 17
 *
 * 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.
Warren Dukes's avatar
Warren Dukes committed
18 19
 */

20
#include "config.h"
21
#include "VorbisDecoderPlugin.h"
22
#include "OggDecoder.hxx"
23
#include "lib/xiph/VorbisComments.hxx"
24 25
#include "lib/xiph/OggPacket.hxx"
#include "lib/xiph/OggFind.hxx"
26
#include "VorbisDomain.hxx"
27
#include "../DecoderAPI.hxx"
Max Kellermann's avatar
Max Kellermann committed
28
#include "input/InputStream.hxx"
29
#include "input/Reader.hxx"
30
#include "OggCodec.hxx"
31
#include "pcm/Interleave.hxx"
32
#include "util/Error.hxx"
33
#include "util/Macros.hxx"
34
#include "util/ScopeExit.hxx"
35
#include "CheckAudioFormat.hxx"
36
#include "tag/TagHandler.hxx"
37
#include "Log.hxx"
38 39

#ifndef HAVE_TREMOR
40
#include <vorbis/codec.h>
41
#else
42
#include <tremor/ivorbiscodec.h>
Avuton Olrich's avatar
Avuton Olrich committed
43
#endif /* HAVE_TREMOR */
44

45
#include <stdexcept>
46

47 48 49 50 51 52 53 54 55 56
class VorbisDecoder final : public OggDecoder {
#ifdef HAVE_TREMOR
	static constexpr SampleFormat sample_format = SampleFormat::S16;
	typedef ogg_int32_t in_sample_t;
	typedef int16_t out_sample_t;
#else
	static constexpr SampleFormat sample_format = SampleFormat::FLOAT;
	typedef float in_sample_t;
	typedef float out_sample_t;
#endif
57

58
	unsigned remaining_header_packets;
59

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	vorbis_info vi;
	vorbis_comment vc;
	vorbis_dsp_state dsp;
	vorbis_block block;

	/**
	 * If non-zero, then a previous Vorbis stream has been found
	 * already with this number of channels.
	 */
	AudioFormat audio_format = AudioFormat::Undefined();
	size_t frame_size;

	bool dsp_initialized = false;

public:
	explicit VorbisDecoder(DecoderReader &reader)
		:OggDecoder(reader) {
		InitVorbis();
	}
79

80 81 82
	~VorbisDecoder() {
		DeinitVorbis();
	}
Max Kellermann's avatar
Max Kellermann committed
83

84
	bool Seek(uint64_t where_frame);
85

86 87 88 89 90
private:
	void InitVorbis() {
		vorbis_info_init(&vi);
		vorbis_comment_init(&vc);
	}
91

92 93 94
	void DeinitVorbis() {
		if (dsp_initialized) {
			dsp_initialized = false;
95

96 97 98
			vorbis_block_clear(&block);
			vorbis_dsp_clear(&dsp);
		}
99

100 101 102
		vorbis_comment_clear(&vc);
		vorbis_info_clear(&vi);
	}
103

104 105 106 107
	void ReinitVorbis() {
		DeinitVorbis();
		InitVorbis();
	}
108

109 110 111
	void SubmitInit();
	bool SubmitSomePcm();
	void SubmitPcm();
112

113 114 115 116 117 118
protected:
	/* virtual methods from class OggVisitor */
	void OnOggBeginning(const ogg_packet &packet) override;
	void OnOggPacket(const ogg_packet &packet) override;
	void OnOggEnd() override;
};
119

120 121 122 123 124 125 126 127 128 129 130
bool
VorbisDecoder::Seek(uint64_t where_frame)
{
	assert(IsSeekable());
	assert(input_stream.IsSeekable());
	assert(input_stream.KnownSize());

	const ogg_int64_t where_granulepos(where_frame);

	if (!SeekGranulePos(where_granulepos, IgnoreError()))
		return false;
131

132 133
	vorbis_synthesis_restart(&dsp);
	return true;
134 135
}

136 137
void
VorbisDecoder::OnOggBeginning(const ogg_packet &_packet)
Avuton Olrich's avatar
Avuton Olrich committed
138
{
139 140 141 142 143 144 145 146 147
	/* libvorbis wants non-const packets */
	ogg_packet &packet = const_cast<ogg_packet &>(_packet);

	ReinitVorbis();

	if (vorbis_synthesis_headerin(&vi, &vc, &packet) != 0)
		throw std::runtime_error("Unrecognized Vorbis BOS packet");

	remaining_header_packets = 2;
148 149
}

150 151 152
static void
vorbis_send_comments(Decoder &decoder, InputStream &is,
		     char **comments)
Avuton Olrich's avatar
Avuton Olrich committed
153
{
154 155 156
	Tag *tag = vorbis_comments_to_tag(comments);
	if (!tag)
		return;
157

158 159
	decoder_tag(decoder, is, std::move(*tag));
	delete tag;
160 161
}

162 163
void
VorbisDecoder::SubmitInit()
164
{
165
	assert(!dsp_initialized);
166

167 168 169 170
	Error error;
	if (!audio_format_init_checked(audio_format, vi.rate, sample_format,
				       vi.channels, error))
		throw std::runtime_error(error.GetMessage());
171

172
	frame_size = audio_format.GetFrameSize();
173

174 175 176 177 178
	const auto eos_granulepos = UpdateEndGranulePos();
	const auto duration = eos_granulepos >= 0
		? SignedSongTime::FromScale<uint64_t>(eos_granulepos,
						      audio_format.sample_rate)
		: SignedSongTime::Negative();
179

180 181
	decoder_initialized(decoder, audio_format,
			    eos_granulepos > 0, duration);
182 183
}

184 185
bool
VorbisDecoder::SubmitSomePcm()
186
{
187 188 189
	in_sample_t **pcm;
	int result = vorbis_synthesis_pcmout(&dsp, &pcm);
	if (result <= 0)
190
		return false;
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205

	out_sample_t buffer[4096];
	const unsigned channels = audio_format.channels;
	size_t max_frames = ARRAY_SIZE(buffer) / channels;
	size_t n_frames = std::min(size_t(result), max_frames);

#ifdef HAVE_TREMOR
	for (unsigned c = 0; c < channels; ++c) {
		const auto *src = pcm[c];
		auto *dest = &buffer[c];

		for (size_t i = 0; i < n_frames; ++i) {
			*dest = *src++;
			dest += channels;
		}
206
	}
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
#else
	PcmInterleaveFloat(buffer,
			   ConstBuffer<const in_sample_t *>(pcm,
							    channels),
			   n_frames);
#endif

	vorbis_synthesis_read(&dsp, n_frames);

	const size_t nbytes = n_frames * frame_size;
	auto cmd = decoder_data(decoder, input_stream,
				buffer, nbytes,
				0);
	if (cmd != DecoderCommand::NONE)
		throw cmd;
222 223 224 225

	return true;
}

226 227
void
VorbisDecoder::SubmitPcm()
228
{
229
	while (SubmitSomePcm()) {}
230 231
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
void
VorbisDecoder::OnOggPacket(const ogg_packet &_packet)
{
	/* libvorbis wants non-const packets */
	ogg_packet &packet = const_cast<ogg_packet &>(_packet);

	if (remaining_header_packets > 0) {
		if (vorbis_synthesis_headerin(&vi, &vc, &packet) != 0)
			throw std::runtime_error("Unrecognized Vorbis header packet");

		if (--remaining_header_packets > 0)
			return;

		if (audio_format.IsDefined()) {
			/* TODO: change the MPD decoder plugin API to
			   allow mid-song AudioFormat changes */
			if ((unsigned)vi.rate != audio_format.sample_rate ||
			    (unsigned)vi.channels != audio_format.channels)
				throw std::runtime_error("Next stream has different audio format");
		} else
			SubmitInit();

		vorbis_send_comments(decoder, input_stream, vc.user_comments);

		ReplayGainInfo rgi;
		if (vorbis_comments_to_replay_gain(rgi, vc.user_comments))
			decoder_replay_gain(decoder, &rgi);
	} else {
		if (!dsp_initialized) {
			dsp_initialized = true;

			vorbis_synthesis_init(&dsp, &vi);
			vorbis_block_init(&dsp, &block);
		}

		if (vorbis_synthesis(&block, &packet) != 0) {
			/* ignore bad packets, but give the MPD core a
			   chance to stop us */
			auto cmd = decoder_get_command(decoder);
			if (cmd != DecoderCommand::NONE)
				throw cmd;
			return;
		}

		if (vorbis_synthesis_blockin(&dsp, &block) != 0)
			throw std::runtime_error("vorbis_synthesis_blockin() failed");

		SubmitPcm();

281
#ifndef HAVE_TREMOR
282 283 284 285 286 287 288 289 290
		if (packet.granulepos > 0)
			decoder_timestamp(decoder,
					  vorbis_granule_time(&dsp, packet.granulepos));
#endif
	}
}

void
VorbisDecoder::OnOggEnd()
291 292 293
{
}

294
/* public */
295 296

static bool
297
vorbis_init(gcc_unused const ConfigBlock &block)
298 299 300 301 302 303 304
{
#ifndef HAVE_TREMOR
	LogDebug(vorbis_domain, vorbis_version_string());
#endif
	return true;
}

305
static void
306
vorbis_stream_decode(Decoder &decoder,
307
		     InputStream &input_stream)
Warren Dukes's avatar
Warren Dukes committed
308
{
309
	if (ogg_codec_detect(&decoder, input_stream) != OGG_CODEC_VORBIS)
310
		return;
311

312
	/* rewind the stream, because ogg_codec_detect() has
313
	   moved it */
314
	input_stream.LockRewind(IgnoreError());
315

316 317
	DecoderReader reader(decoder, input_stream);
	VorbisDecoder d(reader);
318

319 320 321
	while (true) {
		try {
			d.Visit();
322
			break;
323 324 325 326 327 328 329
		} 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)
330 331
				break;
		}
332
	}
Warren Dukes's avatar
Warren Dukes committed
333 334
}

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
static void
VisitVorbisDuration(InputStream &is,
		    OggSyncState &sync, OggStreamState &stream,
		    unsigned sample_rate,
		    const TagHandler &handler, void *handler_ctx)
{
	ogg_packet packet;

	if (!OggSeekFindEOS(sync, stream, packet, is))
		return;

	const auto duration =
		SongTime::FromScale<uint64_t>(packet.granulepos,
					      sample_rate);
	tag_handler_invoke_duration(handler, handler_ctx, duration);
}

352
static bool
353
vorbis_scan_stream(InputStream &is,
354
		   const TagHandler &handler, void *handler_ctx)
Avuton Olrich's avatar
Avuton Olrich committed
355
{
356
	/* initialize libogg */
Warren Dukes's avatar
Warren Dukes committed
357

358 359 360 361 362
	InputStreamReader reader(is);
	OggSyncState sync(reader);

	ogg_page first_page;
	if (!sync.ExpectPage(first_page))
363
		return false;
Warren Dukes's avatar
Warren Dukes committed
364

365
	OggStreamState stream(first_page);
Warren Dukes's avatar
Warren Dukes committed
366

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
	/* initialize libvorbis */

	vorbis_info vi;
	vorbis_info_init(&vi);
	AtScopeExit(&) { vorbis_info_clear(&vi); };

	vorbis_comment vc;
	vorbis_comment_init(&vc);
	AtScopeExit(&) { vorbis_comment_clear(&vc); };

	/* feed the first 3 packets to libvorbis */

	for (unsigned i = 0; i < 3; ++i) {
		ogg_packet packet;
		if (!OggReadPacket(sync, stream, packet) ||
		    vorbis_synthesis_headerin(&vi, &vc, &packet) != 0)
			return false;
	}

	/* visit the Vorbis comments we just read */

	vorbis_comments_scan(vc.user_comments,
389
			     handler, handler_ctx);
Warren Dukes's avatar
Warren Dukes committed
390

391 392 393 394
	/* check the song duration by locating the e_o_s packet */

	VisitVorbisDuration(is, sync, stream, vi.rate, handler, handler_ctx);

395
	return true;
Warren Dukes's avatar
Warren Dukes committed
396 397
}

Max Kellermann's avatar
Max Kellermann committed
398
static const char *const vorbis_suffixes[] = {
399
	"ogg", "oga", nullptr
Max Kellermann's avatar
Max Kellermann committed
400 401 402
};

static const char *const vorbis_mime_types[] = {
403 404
	"application/ogg",
	"application/x-ogg",
405 406 407 408 409 410
	"audio/ogg",
	"audio/vorbis",
	"audio/vorbis+ogg",
	"audio/x-ogg",
	"audio/x-vorbis",
	"audio/x-vorbis+ogg",
411
	nullptr
412
};
Warren Dukes's avatar
Warren Dukes committed
413

414
const struct DecoderPlugin vorbis_decoder_plugin = {
415
	"vorbis",
416
	vorbis_init,
417 418 419 420 421 422 423 424
	nullptr,
	vorbis_stream_decode,
	nullptr,
	nullptr,
	vorbis_scan_stream,
	nullptr,
	vorbis_suffixes,
	vorbis_mime_types
Warren Dukes's avatar
Warren Dukes committed
425
};