OpusDecoderPlugin.cxx 9.12 KB
Newer Older
1
/*
2
 * Copyright 2003-2018 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
	if (ScanOpusTags(packet.packet, packet.bytes, &rgi, h) &&
212
	    !tag_builder.empty()) {
213
		client.SubmitReplayGain(&rgi);
214

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

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

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

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

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

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

257
	const ogg_int64_t where_granulepos(where_frame);
258

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

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

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

281
	DecoderReader reader(client, input_stream);
282

283
	MPDOpusDecoder d(reader);
284

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

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

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

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

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

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

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

340
static bool
341
mpd_opus_scan_stream(InputStream &is, TagHandler &handler) noexcept
342 343 344 345 346 347 348 349 350 351 352 353
{
	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) ||
354
	    !ReadAndVisitOpusTags(oy, os, handler))
355 356
		return false;

357 358 359
	handler.OnAudioFormat(AudioFormat(opus_sample_rate,
					  SampleFormat::S16, channels));

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

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

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

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

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

383 384
} /* anonymous namespace */

385
const struct DecoderPlugin opus_decoder_plugin = {
386 387 388 389 390 391 392 393 394 395 396
	"opus",
	mpd_opus_init,
	nullptr,
	mpd_opus_stream_decode,
	nullptr,
	nullptr,
	mpd_opus_scan_stream,
	nullptr,
	opus_suffixes,
	opus_mime_types,
};