OpusDecoderPlugin.cxx 9.14 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2019 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * 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 "OpusDecoderPlugin.h"
21
#include "OggDecoder.hxx"
22
#include "OpusDomain.hxx"
23 24
#include "OpusHead.hxx"
#include "OpusTags.hxx"
25
#include "lib/xiph/OggPacket.hxx"
26
#include "lib/xiph/OggFind.hxx"
27
#include "../DecoderAPI.hxx"
28 29
#include "decoder/Reader.hxx"
#include "input/Reader.hxx"
30
#include "OggCodec.hxx"
31 32
#include "tag/Handler.hxx"
#include "tag/Builder.hxx"
Max Kellermann's avatar
Max Kellermann committed
33
#include "input/InputStream.hxx"
34
#include "util/RuntimeError.hxx"
35
#include "Log.hxx"
36 37 38 39

#include <opus.h>
#include <ogg/ogg.h>

Max Kellermann's avatar
Max Kellermann committed
40
#include <string.h>
41

42 43
namespace {

44
static constexpr opus_int32 opus_sample_rate = 48000;
45

46 47 48 49 50 51
/**
 * Allocate an output buffer for 16 bit PCM samples big enough to hold
 * a quarter second, larger than 120ms required by libopus.
 */
static constexpr unsigned opus_output_buffer_frames = opus_sample_rate / 4;

52 53
gcc_pure
static bool
54
IsOpusHead(const ogg_packet &packet) noexcept
55 56 57 58 59 60
{
	return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0;
}

gcc_pure
static bool
61
IsOpusTags(const ogg_packet &packet) noexcept
62 63 64 65 66
{
	return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0;
}

static bool
67
mpd_opus_init(gcc_unused const ConfigBlock &block)
68
{
69
	LogDebug(opus_domain, opus_get_version_string());
70 71 72 73

	return true;
}

74
class MPDOpusDecoder final : public OggDecoder {
75 76
	OpusDecoder *opus_decoder = nullptr;
	opus_int16 *output_buffer = nullptr;
77

78 79 80 81 82 83
	/**
	 * If non-zero, then a previous Opus stream has been found
	 * already with this number of channels.  If opus_decoder is
	 * nullptr, then its end-of-stream packet has been found
	 * already.
	 */
84
	unsigned previous_channels = 0;
85

86 87 88
	size_t frame_size;

public:
89 90
	explicit MPDOpusDecoder(DecoderReader &reader)
		:OggDecoder(reader) {}
91

92 93
	~MPDOpusDecoder();

94
	/**
95
	 * Has DecoderClient::Ready() been called yet?
96 97 98 99 100
	 */
	bool IsInitialized() const {
		return previous_channels != 0;
	}

101
	bool Seek(uint64_t where_frame);
102 103

private:
104 105 106 107 108 109 110 111
	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;
112 113 114 115
};

MPDOpusDecoder::~MPDOpusDecoder()
{
116
	delete[] output_buffer;
117 118 119

	if (opus_decoder != nullptr)
		opus_decoder_destroy(opus_decoder);
120 121
}

122 123
void
MPDOpusDecoder::OnOggPacket(const ogg_packet &packet)
124
{
125
	if (IsOpusTags(packet))
126 127 128
		HandleTags(packet);
	else
		HandleAudio(packet);
129 130
}

131 132
void
MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet)
133 134 135
{
	assert(packet.b_o_s);

136 137
	if (opus_decoder != nullptr || !IsOpusHead(packet))
		throw std::runtime_error("BOS packet must be OpusHead");
138 139 140

	unsigned channels;
	if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
141 142
	    !audio_valid_channel_count(channels))
		throw std::runtime_error("Malformed BOS packet");
143 144

	assert(opus_decoder == nullptr);
145
	assert(IsInitialized() == (output_buffer != nullptr));
146

147 148 149
	if (IsInitialized() && channels != previous_channels)
		throw FormatRuntimeError("Next stream has different channels (%u -> %u)",
					 previous_channels, channels);
150 151 152 153 154 155 156

	/* TODO: parse attributes from the OpusHead (sample rate,
	   channels, ...) */

	int opus_error;
	opus_decoder = opus_decoder_create(opus_sample_rate, channels,
					   &opus_error);
157 158 159
	if (opus_decoder == nullptr)
		throw FormatRuntimeError("libopus error: %s",
					 opus_strerror(opus_error));
160

161
	if (IsInitialized()) {
162 163 164
		/* decoder was already initialized by the previous
		   stream; skip the rest of this method */
		LogDebug(opus_domain, "Found another stream");
165
		return;
166 167
	}

168
	const auto eos_granulepos = UpdateEndGranulePos();
169 170 171 172
	const auto duration = eos_granulepos >= 0
		? SignedSongTime::FromScale<uint64_t>(eos_granulepos,
						      opus_sample_rate)
		: SignedSongTime::Negative();
173

174
	previous_channels = channels;
175 176
	const AudioFormat audio_format(opus_sample_rate,
				       SampleFormat::S16, channels);
177
	client.Ready(audio_format, eos_granulepos > 0, duration);
178
	frame_size = audio_format.GetFrameSize();
179

180 181
	output_buffer = new opus_int16[opus_output_buffer_frames
				       * audio_format.channels];
182

183
	auto cmd = client.GetCommand();
184 185
	if (cmd != DecoderCommand::NONE)
		throw cmd;
186 187
}

188 189
void
MPDOpusDecoder::OnOggEnd()
190
{
191
	if (!IsSeekable() && IsInitialized()) {
192 193 194 195 196 197
		/* allow chaining of (unseekable) streams */
		assert(opus_decoder != nullptr);
		assert(output_buffer != nullptr);

		opus_decoder_destroy(opus_decoder);
		opus_decoder = nullptr;
198 199
	} else
		throw StopDecoder();
200 201
}

202
inline void
203 204
MPDOpusDecoder::HandleTags(const ogg_packet &packet)
{
205
	ReplayGainInfo rgi;
206
	rgi.Clear();
207

208
	TagBuilder tag_builder;
209
	AddTagHandler h(tag_builder);
210

211 212 213 214
	if (!ScanOpusTags(packet.packet, packet.bytes, &rgi, h))
		return;

	client.SubmitReplayGain(&rgi);
215

216
	if (!tag_builder.empty()) {
217
		Tag tag = tag_builder.Commit();
218
		auto cmd = client.SubmitTag(input_stream, std::move(tag));
219 220 221
		if (cmd != DecoderCommand::NONE)
			throw cmd;
	}
222 223
}

224
inline void
225 226 227 228 229 230 231
MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
{
	assert(opus_decoder != nullptr);

	int nframes = opus_decode(opus_decoder,
				  (const unsigned char*)packet.packet,
				  packet.bytes,
232
				  output_buffer, opus_output_buffer_frames,
233
				  0);
234 235 236
	if (nframes < 0)
		throw FormatRuntimeError("libopus error: %s",
					 opus_strerror(nframes));
237 238 239

	if (nframes > 0) {
		const size_t nbytes = nframes * frame_size;
240 241 242
		auto cmd = client.SubmitData(input_stream,
					     output_buffer, nbytes,
					     0);
243
		if (cmd != DecoderCommand::NONE)
244
			throw cmd;
245 246

		if (packet.granulepos > 0)
247
			client.SubmitTimestamp(FloatDuration(packet.granulepos)
248
					       / opus_sample_rate);
249 250 251
	}
}

252
bool
253
MPDOpusDecoder::Seek(uint64_t where_frame)
254
{
255
	assert(IsSeekable());
256 257
	assert(input_stream.IsSeekable());
	assert(input_stream.KnownSize());
258

259
	const ogg_int64_t where_granulepos(where_frame);
260

261 262 263
	try {
		SeekGranulePos(where_granulepos);
		return true;
264
	} catch (...) {
265 266
		return false;
	}
267 268
}

269
static void
270
mpd_opus_stream_decode(DecoderClient &client,
271
		       InputStream &input_stream)
272
{
273
	if (ogg_codec_detect(&client, input_stream) != OGG_CODEC_OPUS)
274 275 276 277
		return;

	/* rewind the stream, because ogg_codec_detect() has
	   moved it */
278 279
	try {
		input_stream.LockRewind();
280
	} catch (...) {
281
	}
282

283
	DecoderReader reader(client, input_stream);
284

285
	MPDOpusDecoder d(reader);
286

287
	while (true) {
288 289
		try {
			d.Visit();
290
			break;
291 292
		} catch (DecoderCommand cmd) {
			if (cmd == DecoderCommand::SEEK) {
293 294
				if (d.Seek(client.GetSeekFrame()))
					client.CommandFinished();
295
				else
296
					client.SeekError();
297 298 299
			} else if (cmd != DecoderCommand::NONE)
				break;
		}
300 301 302 303
	}
}

static bool
304 305
ReadAndParseOpusHead(OggSyncState &sync, OggStreamState &stream,
		     unsigned &channels)
306 307 308
{
	ogg_packet packet;

309 310 311 312 313
	return OggReadPacket(sync, stream, packet) && packet.b_o_s &&
		IsOpusHead(packet) &&
		ScanOpusHeader(packet.packet, packet.bytes, channels) &&
		audio_valid_channel_count(channels);
}
314

315 316
static bool
ReadAndVisitOpusTags(OggSyncState &sync, OggStreamState &stream,
317
		     TagHandler &handler)
318 319
{
	ogg_packet packet;
320

321 322 323 324
	return OggReadPacket(sync, stream, packet) &&
		IsOpusTags(packet) &&
		ScanOpusTags(packet.packet, packet.bytes,
			     nullptr,
325
			     handler);
326
}
327

328 329
static void
VisitOpusDuration(InputStream &is, OggSyncState &sync, OggStreamState &stream,
330
		  TagHandler &handler)
331 332
{
	ogg_packet packet;
333

334
	if (OggSeekFindEOS(sync, stream, packet, is)) {
335 336 337
		const auto duration =
			SongTime::FromScale<uint64_t>(packet.granulepos,
						      opus_sample_rate);
338
		handler.OnDuration(duration);
339
	}
340
}
341

342
static bool
343
mpd_opus_scan_stream(InputStream &is, TagHandler &handler) noexcept
344 345 346 347 348 349 350 351 352 353 354 355
{
	InputStreamReader reader(is);
	OggSyncState oy(reader);

	ogg_page first_page;
	if (!oy.ExpectPage(first_page))
		return false;

	OggStreamState os(first_page);

	unsigned channels;
	if (!ReadAndParseOpusHead(oy, os, channels) ||
356
	    !ReadAndVisitOpusTags(oy, os, handler))
357 358
		return false;

359 360 361
	handler.OnAudioFormat(AudioFormat(opus_sample_rate,
					  SampleFormat::S16, channels));

362
	VisitOpusDuration(is, oy, os, handler);
363
	return true;
364 365 366 367 368 369 370 371 372 373
}

static const char *const opus_suffixes[] = {
	"opus",
	"ogg",
	"oga",
	nullptr
};

static const char *const opus_mime_types[] = {
374 375 376 377 378 379 380
	/* the official MIME type (RFC 5334) */
	"audio/ogg",

	/* deprecated (RFC 5334) */
	"application/ogg",

	/* deprecated; from an early draft */
381 382 383 384
	"audio/opus",
	nullptr
};

385 386
} /* anonymous namespace */

387 388 389 390 391
constexpr DecoderPlugin opus_decoder_plugin =
	DecoderPlugin("opus", mpd_opus_stream_decode, mpd_opus_scan_stream)
	.WithInit(mpd_opus_init)
	.WithSuffixes(opus_suffixes)
	.WithMimeTypes(opus_mime_types);