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

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

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

43 44
namespace {

45
static constexpr opus_int32 opus_sample_rate = 48000;
46

47 48 49 50 51 52
/**
 * 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;

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
gcc_pure
static bool
IsOpusHead(const ogg_packet &packet)
{
	return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0;
}

gcc_pure
static bool
IsOpusTags(const ogg_packet &packet)
{
	return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0;
}

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

	return true;
}

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

79 80 81 82 83 84
	/**
	 * 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.
	 */
85
	unsigned previous_channels = 0;
86

87 88 89
	size_t frame_size;

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

93 94
	~MPDOpusDecoder();

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

102
	bool Seek(uint64_t where_frame);
103 104

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

209
	TagBuilder tag_builder;
210

Max Kellermann's avatar
Max Kellermann committed
211
	if (ScanOpusTags(packet.packet, packet.bytes,
212
			 &rgi,
213
			 add_tag_handler, &tag_builder) &&
214
	    !tag_builder.IsEmpty()) {
215
		client.SubmitReplayGain(&rgi);
216

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 248
			client.SubmitTimestamp(double(packet.granulepos)
					       / 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 264 265 266
	try {
		SeekGranulePos(where_granulepos);
		return true;
	} catch (const std::runtime_error &) {
		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 280 281
	try {
		input_stream.LockRewind();
	} catch (const std::runtime_error &) {
	}
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 317 318 319
static bool
ReadAndVisitOpusTags(OggSyncState &sync, OggStreamState &stream,
		     const TagHandler &handler, void *handler_ctx)
{
	ogg_packet packet;
320

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

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

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

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
static bool
mpd_opus_scan_stream(InputStream &is,
		     const TagHandler &handler, void *handler_ctx)
{
	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) ||
	    !ReadAndVisitOpusTags(oy, os, handler, handler_ctx))
		return false;

	VisitOpusDuration(is, oy, os, handler, handler_ctx);
	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,
};