VorbisDecoderPlugin.cxx 9.48 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 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/Macros.hxx"
33
#include "util/ScopeExit.hxx"
34
#include "CheckAudioFormat.hxx"
35
#include "tag/Handler.hxx"
36
#include "Log.hxx"
37 38

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

44
#include <stdexcept>
45

46 47 48 49 50 51 52 53 54 55
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
56

57
	unsigned remaining_header_packets;
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
	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();
	}
78

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

83
	bool Seek(uint64_t where_frame);
84

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

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

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

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

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

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

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

119 120 121 122 123 124 125 126 127
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);

128 129 130 131
	try {
		SeekGranulePos(where_granulepos);
		vorbis_synthesis_restart(&dsp);
		return true;
132
	} catch (...) {
133
		return false;
134
	}
135 136
}

137 138
void
VorbisDecoder::OnOggBeginning(const ogg_packet &_packet)
Avuton Olrich's avatar
Avuton Olrich committed
139
{
140 141 142 143 144 145 146 147 148
	/* 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;
149 150
}

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

159
	client.SubmitTag(is, std::move(*tag));
160 161
}

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

167
	audio_format = CheckAudioFormat(vi.rate, sample_format, vi.channels);
168

169
	frame_size = audio_format.GetFrameSize();
170

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

177
	client.Ready(audio_format, eos_granulepos > 0, duration);
178 179
}

180 181 182 183 184 185 186 187 188 189 190 191 192 193
#ifdef HAVE_TREMOR
static inline int16_t tremor_clip_sample(int32_t x)
{
	x >>= 9;

	if (x < INT16_MIN)
		return INT16_MIN;
	if (x > INT16_MAX)
		return INT16_MAX;

	return x;
}
#endif

194 195
bool
VorbisDecoder::SubmitSomePcm()
196
{
197 198 199
	in_sample_t **pcm;
	int result = vorbis_synthesis_pcmout(&dsp, &pcm);
	if (result <= 0)
200
		return false;
201 202 203 204 205 206 207 208 209 210 211 212

	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) {
213
			*dest = tremor_clip_sample(*src++);
214 215
			dest += channels;
		}
216
	}
217 218 219 220 221 222 223 224 225 226
#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;
227 228 229
	auto cmd = client.SubmitData(input_stream,
				     buffer, nbytes,
				     0);
230 231
	if (cmd != DecoderCommand::NONE)
		throw cmd;
232 233 234 235

	return true;
}

236 237
void
VorbisDecoder::SubmitPcm()
238
{
239
	while (SubmitSomePcm()) {}
240 241
}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
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();

264
		vorbis_send_comments(client, input_stream, vc.user_comments);
265 266 267

		ReplayGainInfo rgi;
		if (vorbis_comments_to_replay_gain(rgi, vc.user_comments))
268
			client.SubmitReplayGain(&rgi);
269 270 271 272 273 274 275 276 277 278 279
	} 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 */
280
			auto cmd = client.GetCommand();
281 282 283 284 285 286 287 288 289 290
			if (cmd != DecoderCommand::NONE)
				throw cmd;
			return;
		}

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

		SubmitPcm();

291
#ifndef HAVE_TREMOR
292
		if (packet.granulepos > 0)
293
			client.SubmitTimestamp(vorbis_granule_time(&dsp, packet.granulepos));
294 295 296 297 298 299
#endif
	}
}

void
VorbisDecoder::OnOggEnd()
300 301 302
{
}

303
/* public */
304 305

static bool
306
vorbis_init(gcc_unused const ConfigBlock &block)
307 308 309 310 311 312 313
{
#ifndef HAVE_TREMOR
	LogDebug(vorbis_domain, vorbis_version_string());
#endif
	return true;
}

314
static void
315
vorbis_stream_decode(DecoderClient &client,
316
		     InputStream &input_stream)
Warren Dukes's avatar
Warren Dukes committed
317
{
318
	if (ogg_codec_detect(&client, input_stream) != OGG_CODEC_VORBIS)
319
		return;
320

321
	/* rewind the stream, because ogg_codec_detect() has
322
	   moved it */
323 324
	try {
		input_stream.LockRewind();
325
	} catch (...) {
326
	}
327

328
	DecoderReader reader(client, input_stream);
329
	VorbisDecoder d(reader);
330

331 332 333
	while (true) {
		try {
			d.Visit();
334
			break;
335 336
		} catch (DecoderCommand cmd) {
			if (cmd == DecoderCommand::SEEK) {
337 338
				if (d.Seek(client.GetSeekFrame()))
					client.CommandFinished();
339
				else
340
					client.SeekError();
341
			} else if (cmd != DecoderCommand::NONE)
342 343
				break;
		}
344
	}
Warren Dukes's avatar
Warren Dukes committed
345 346
}

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
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);
}

364
static bool
365
vorbis_scan_stream(InputStream &is,
366
		   const TagHandler &handler, void *handler_ctx)
Avuton Olrich's avatar
Avuton Olrich committed
367
{
368
	/* initialize libogg */
Warren Dukes's avatar
Warren Dukes committed
369

370 371 372 373 374
	InputStreamReader reader(is);
	OggSyncState sync(reader);

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

377
	OggStreamState stream(first_page);
Warren Dukes's avatar
Warren Dukes committed
378

379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
	/* 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,
401
			     handler, handler_ctx);
Warren Dukes's avatar
Warren Dukes committed
402

403 404 405 406
	/* check the song duration by locating the e_o_s packet */

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

407
	return true;
Warren Dukes's avatar
Warren Dukes committed
408 409
}

Max Kellermann's avatar
Max Kellermann committed
410
static const char *const vorbis_suffixes[] = {
411
	"ogg", "oga", nullptr
Max Kellermann's avatar
Max Kellermann committed
412 413 414
};

static const char *const vorbis_mime_types[] = {
415 416
	"application/ogg",
	"application/x-ogg",
417 418 419 420 421 422
	"audio/ogg",
	"audio/vorbis",
	"audio/vorbis+ogg",
	"audio/x-ogg",
	"audio/x-vorbis",
	"audio/x-vorbis+ogg",
423
	nullptr
424
};
Warren Dukes's avatar
Warren Dukes committed
425

426
const struct DecoderPlugin vorbis_decoder_plugin = {
427
	"vorbis",
428
	vorbis_init,
429 430 431 432 433 434 435 436
	nullptr,
	vorbis_stream_decode,
	nullptr,
	nullptr,
	vorbis_scan_stream,
	nullptr,
	vorbis_suffixes,
	vorbis_mime_types
Warren Dukes's avatar
Warren Dukes committed
437
};