VorbisDecoderPlugin.cxx 9.34 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/Macros.hxx"
33
#include "util/ScopeExit.hxx"
34
#include "CheckAudioFormat.hxx"
35
#include "tag/TagHandler.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 132
	try {
		SeekGranulePos(where_granulepos);
		vorbis_synthesis_restart(&dsp);
		return true;
	} catch (const std::runtime_error &) {
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 156 157
	Tag *tag = vorbis_comments_to_tag(comments);
	if (!tag)
		return;
158

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

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

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

170
	frame_size = audio_format.GetFrameSize();
171

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

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

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

	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;
		}
203
	}
204 205 206 207 208 209 210 211 212 213
#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;
214 215 216
	auto cmd = client.SubmitData(input_stream,
				     buffer, nbytes,
				     0);
217 218
	if (cmd != DecoderCommand::NONE)
		throw cmd;
219 220 221 222

	return true;
}

223 224
void
VorbisDecoder::SubmitPcm()
225
{
226
	while (SubmitSomePcm()) {}
227 228
}

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
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();

251
		vorbis_send_comments(client, input_stream, vc.user_comments);
252 253 254

		ReplayGainInfo rgi;
		if (vorbis_comments_to_replay_gain(rgi, vc.user_comments))
255
			client.SubmitReplayGain(&rgi);
256 257 258 259 260 261 262 263 264 265 266
	} 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 */
267
			auto cmd = client.GetCommand();
268 269 270 271 272 273 274 275 276 277
			if (cmd != DecoderCommand::NONE)
				throw cmd;
			return;
		}

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

		SubmitPcm();

278
#ifndef HAVE_TREMOR
279
		if (packet.granulepos > 0)
280
			client.SubmitTimestamp(vorbis_granule_time(&dsp, packet.granulepos));
281 282 283 284 285 286
#endif
	}
}

void
VorbisDecoder::OnOggEnd()
287 288 289
{
}

290
/* public */
291 292

static bool
293
vorbis_init(gcc_unused const ConfigBlock &block)
294 295 296 297 298 299 300
{
#ifndef HAVE_TREMOR
	LogDebug(vorbis_domain, vorbis_version_string());
#endif
	return true;
}

301
static void
302
vorbis_stream_decode(DecoderClient &client,
303
		     InputStream &input_stream)
Warren Dukes's avatar
Warren Dukes committed
304
{
305
	if (ogg_codec_detect(&client, input_stream) != OGG_CODEC_VORBIS)
306
		return;
307

308
	/* rewind the stream, because ogg_codec_detect() has
309
	   moved it */
310 311 312 313
	try {
		input_stream.LockRewind();
	} catch (const std::runtime_error &) {
	}
314

315
	DecoderReader reader(client, input_stream);
316
	VorbisDecoder d(reader);
317

318 319 320
	while (true) {
		try {
			d.Visit();
321
			break;
322 323
		} catch (DecoderCommand cmd) {
			if (cmd == DecoderCommand::SEEK) {
324 325
				if (d.Seek(client.GetSeekFrame()))
					client.CommandFinished();
326
				else
327
					client.SeekError();
328
			} else if (cmd != DecoderCommand::NONE)
329 330
				break;
		}
331
	}
Warren Dukes's avatar
Warren Dukes committed
332 333
}

334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
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);
}

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

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

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

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

366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
	/* 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,
388
			     handler, handler_ctx);
Warren Dukes's avatar
Warren Dukes committed
389

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

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

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

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

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

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