FlacDecoderPlugin.cxx 9.75 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" /* must be first for large file support */
21
#include "FlacDecoderPlugin.h"
22
#include "FlacStreamDecoder.hxx"
23
#include "FlacDomain.hxx"
24
#include "FlacCommon.hxx"
25
#include "lib/xiph/FlacMetadataChain.hxx"
26
#include "OggCodec.hxx"
27
#include "input/InputStream.hxx"
28
#include "fs/Path.hxx"
29
#include "fs/NarrowPath.hxx"
30
#include "Log.hxx"
31

32 33 34 35
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
#error libFLAC is too old
#endif

36
static void flacPrintErroredState(FLAC__StreamDecoderState state)
37
{
Avuton Olrich's avatar
Avuton Olrich committed
38
	switch (state) {
39 40 41 42 43 44
	case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
	case FLAC__STREAM_DECODER_READ_METADATA:
	case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
	case FLAC__STREAM_DECODER_READ_FRAME:
	case FLAC__STREAM_DECODER_END_OF_STREAM:
		return;
45

46 47 48 49 50
	case FLAC__STREAM_DECODER_OGG_ERROR:
	case FLAC__STREAM_DECODER_SEEK_ERROR:
	case FLAC__STREAM_DECODER_ABORTED:
	case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
	case FLAC__STREAM_DECODER_UNINITIALIZED:
51
		break;
Warren Dukes's avatar
Warren Dukes committed
52
	}
53

54
	LogError(flac_domain, FLAC__StreamDecoderStateString[state]);
Warren Dukes's avatar
Warren Dukes committed
55 56
}

57
static void flacMetadata(gcc_unused const FLAC__StreamDecoder * dec,
Avuton Olrich's avatar
Avuton Olrich committed
58
			 const FLAC__StreamMetadata * block, void *vdata)
59
{
60 61
	auto &fd = *(FlacDecoder *)vdata;
	fd.OnMetadata(*block);
Warren Dukes's avatar
Warren Dukes committed
62 63
}

Max Kellermann's avatar
Max Kellermann committed
64
static FLAC__StreamDecoderWriteStatus
65
flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame,
Max Kellermann's avatar
Max Kellermann committed
66
	      const FLAC__int32 *const buf[], void *vdata)
67
{
68 69
	auto &fd = *(FlacDecoder *)vdata;
	return fd.OnWrite(*frame, buf, fd.GetDeltaPosition(*dec));
Warren Dukes's avatar
Warren Dukes committed
70 71
}

72
static bool
73
flac_scan_file(Path path_fs, TagHandler &handler) noexcept
Avuton Olrich's avatar
Avuton Olrich committed
74
{
75
	FlacMetadataChain chain;
76
	if (!chain.Read(NarrowPath(path_fs))) {
77 78 79
		FormatDebug(flac_domain,
			    "Failed to read FLAC tags: %s",
			    chain.GetStatusString());
80 81 82
		return false;
	}

83
	chain.Scan(handler);
84
	return true;
Warren Dukes's avatar
Warren Dukes committed
85 86
}

87
static bool
88
flac_scan_stream(InputStream &is, TagHandler &handler) noexcept
89
{
90
	FlacMetadataChain chain;
91
	if (!chain.Read(is)) {
92 93 94
		FormatDebug(flac_domain,
			    "Failed to read FLAC tags: %s",
			    chain.GetStatusString());
95 96 97
		return false;
	}

98
	chain.Scan(handler);
99 100 101
	return true;
}

102 103 104
/**
 * Some glue code around FLAC__stream_decoder_new().
 */
105
static FlacStreamDecoder
106 107
flac_decoder_new(void)
{
108 109
	FlacStreamDecoder sd;
	if(!FLAC__stream_decoder_set_metadata_respond(sd.get(), FLAC__METADATA_TYPE_VORBIS_COMMENT))
110 111
		LogDebug(flac_domain,
			 "FLAC__stream_decoder_set_metadata_respond() has failed");
112 113 114 115

	return sd;
}

116
static bool
117
flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd)
118 119
{
	if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) {
120 121
		if (FLAC__stream_decoder_get_state(sd) != FLAC__STREAM_DECODER_END_OF_STREAM)
			LogWarning(flac_domain, "problem reading metadata");
122 123 124
		return false;
	}

125
	if (data->initialized) {
126 127
		/* done */
		return true;
128
	}
129

130
	if (data->GetInputStream().IsSeekable())
131 132 133 134 135 136 137
		/* allow the workaround below only for nonseekable
		   streams*/
		return false;

	/* no stream_info packet found; try to initialize the decoder
	   from the first frame header */
	FLAC__stream_decoder_process_single(sd);
138
	return data->initialized;
139 140
}

141 142 143
static DecoderCommand
FlacSubmitToClient(DecoderClient &client, FlacDecoder &d) noexcept
{
144
	if (d.tag.IsEmpty() && d.chunk.empty())
145 146
		return client.GetCommand();

147 148 149 150
	if (!d.tag.IsEmpty()) {
		auto cmd = client.SubmitTag(d.GetInputStream(),
					    std::move(d.tag));
		d.tag.Clear();
151 152 153 154
		if (cmd != DecoderCommand::NONE)
			return cmd;
	}

155
	if (!d.chunk.empty()) {
156 157 158 159 160 161 162 163 164 165
		auto cmd = client.SubmitData(d.GetInputStream(),
					     d.chunk.data,
					     d.chunk.size,
					     d.kbit_rate);
		d.chunk = nullptr;
		if (cmd != DecoderCommand::NONE)
			return cmd;
	}

	return DecoderCommand::NONE;
166 167
}

168
static void
169
flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
170
{
171
	DecoderClient &client = *data->GetClient();
172 173

	while (true) {
174
		DecoderCommand cmd = FlacSubmitToClient(client, *data);
175

176
		if (cmd == DecoderCommand::SEEK) {
177
			FLAC__uint64 seek_sample = client.GetSeekFrame();
178
			if (FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) {
179
				data->position = 0;
180
				client.CommandFinished();
181
			} else
182
				client.SeekError();
183 184 185 186 187

			/* FLAC__stream_decoder_seek_absolute()
			   decodes one frame and may have provided
			   data to be submitted to the client */
			continue;
188
		} else if (cmd == DecoderCommand::STOP)
189 190
			break;

191 192 193 194 195 196 197 198 199 200 201 202 203
		switch (FLAC__stream_decoder_get_state(flac_dec)) {
		case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA:
		case FLAC__STREAM_DECODER_READ_METADATA:
		case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
		case FLAC__STREAM_DECODER_READ_FRAME:
			/* continue decoding */
			break;

		case FLAC__STREAM_DECODER_END_OF_STREAM:
			/* regular end of stream */
			return;

		case FLAC__STREAM_DECODER_SEEK_ERROR:
204 205 206 207 208 209
			/* try to recover from seek error */
			if (!FLAC__stream_decoder_flush(flac_dec)) {
				LogError(flac_domain, "FLAC__stream_decoder_flush() failed");
				return;
			}

210 211
			break;

212
		case FLAC__STREAM_DECODER_OGG_ERROR:
213 214 215 216 217 218 219 220 221 222 223
		case FLAC__STREAM_DECODER_ABORTED:
		case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
			/* an error, fatal enough for us to abort the
			   decoder */
			return;

		case FLAC__STREAM_DECODER_UNINITIALIZED:
			/* we shouldn't see this, ever - bail out */
			return;
		}

224
		if (!FLAC__stream_decoder_process_single(flac_dec) &&
225
		    client.GetCommand() == DecoderCommand::NONE) {
226 227 228 229
			/* a failure that was not triggered by a
			   decoder command */
			flacPrintErroredState(FLAC__stream_decoder_get_state(flac_dec));
			break;
230 231 232 233
		}
	}
}

234
static FLAC__StreamDecoderInitStatus
235
stream_init_oggflac(FLAC__StreamDecoder *flac_dec, FlacDecoder *data)
236 237
{
	return FLAC__stream_decoder_init_ogg_stream(flac_dec,
238 239 240 241 242
						    FlacInput::Read,
						    FlacInput::Seek,
						    FlacInput::Tell,
						    FlacInput::Length,
						    FlacInput::Eof,
243 244
						    flac_write_cb,
						    flacMetadata,
245
						    FlacInput::Error,
246 247 248 249
						    data);
}

static FLAC__StreamDecoderInitStatus
250
stream_init_flac(FLAC__StreamDecoder *flac_dec, FlacDecoder *data)
251 252
{
	return FLAC__stream_decoder_init_stream(flac_dec,
253 254 255 256 257
						FlacInput::Read,
						FlacInput::Seek,
						FlacInput::Tell,
						FlacInput::Length,
						FlacInput::Eof,
258
						flac_write_cb,
259
						flacMetadata,
260
						FlacInput::Error,
261 262 263 264
						data);
}

static FLAC__StreamDecoderInitStatus
265
stream_init(FLAC__StreamDecoder *flac_dec, FlacDecoder *data, bool is_ogg)
266 267 268 269 270 271
{
	return is_ogg
		? stream_init_oggflac(flac_dec, data)
		: stream_init_flac(flac_dec, data);
}

272
static bool
273
FlacInitAndDecode(FlacDecoder &data, FLAC__StreamDecoder *sd, bool is_ogg)
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
{
	auto init_status = stream_init(sd, &data, is_ogg);
	if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
		LogWarning(flac_domain,
			   FLAC__StreamDecoderInitStatusString[init_status]);
		return false;
	}

	bool result = flac_decoder_initialize(&data, sd);
	if (result)
		flac_decoder_loop(&data, sd);

	FLAC__stream_decoder_finish(sd);
	return result;
}

290
static void
291
flac_decode_internal(DecoderClient &client,
292
		     InputStream &input_stream,
293
		     bool is_ogg)
294
{
295 296
	auto flac_dec = flac_decoder_new();
	if (!flac_dec)
297
		return;
298

299
	FlacDecoder data(client, input_stream);
300

301
	FlacInitAndDecode(data, flac_dec.get(), is_ogg);
302 303
}

304
static void
305
flac_decode(DecoderClient &client, InputStream &input_stream)
306
{
307
	flac_decode_internal(client, input_stream, false);
308 309
}

310
static bool
311
oggflac_init(gcc_unused const ConfigBlock &block)
312 313 314 315
{
	return !!FLAC_API_SUPPORTS_OGG_FLAC;
}

316
static bool
317
oggflac_scan_file(Path path_fs, TagHandler &handler) noexcept
318
{
319
	FlacMetadataChain chain;
320
	if (!chain.ReadOgg(NarrowPath(path_fs))) {
321 322 323
		FormatDebug(flac_domain,
			    "Failed to read OggFLAC tags: %s",
			    chain.GetStatusString());
324
		return false;
325 326
	}

327
	chain.Scan(handler);
328
	return true;
329 330
}

331
static bool
332
oggflac_scan_stream(InputStream &is, TagHandler &handler) noexcept
333
{
334
	FlacMetadataChain chain;
335
	if (!chain.ReadOgg(is)) {
336 337 338
		FormatDebug(flac_domain,
			    "Failed to read OggFLAC tags: %s",
			    chain.GetStatusString());
339 340 341
		return false;
	}

342
	chain.Scan(handler);
343 344 345
	return true;
}

346
static void
347
oggflac_decode(DecoderClient &client, InputStream &input_stream)
348
{
349
	if (ogg_codec_detect(&client, input_stream) != OGG_CODEC_FLAC)
350
		return;
351

352
	/* rewind the stream, because ogg_codec_detect() has
353
	   moved it */
354 355
	try {
		input_stream.LockRewind();
356
	} catch (...) {
357
	}
358

359
	flac_decode_internal(client, input_stream, true);
360 361
}

362
static const char *const oggflac_suffixes[] = { "ogg", "oga", nullptr };
363 364 365
static const char *const oggflac_mime_types[] = {
	"application/ogg",
	"application/x-ogg",
366 367 368
	"audio/ogg",
	"audio/x-flac+ogg",
	"audio/x-ogg",
369
	nullptr
370
};
371

372
const struct DecoderPlugin oggflac_decoder_plugin = {
373 374 375 376 377 378
	"oggflac",
	oggflac_init,
	nullptr,
	oggflac_decode,
	nullptr,
	oggflac_scan_file,
379
	oggflac_scan_stream,
380 381 382
	nullptr,
	oggflac_suffixes,
	oggflac_mime_types,
383
};
384

385
static const char *const flac_suffixes[] = { "flac", nullptr };
386
static const char *const flac_mime_types[] = {
387 388 389 390
	"application/flac",
	"application/x-flac",
	"audio/flac",
	"audio/x-flac",
391
	nullptr
392
};
Warren Dukes's avatar
Warren Dukes committed
393

394
const struct DecoderPlugin flac_decoder_plugin = {
395 396 397 398 399 400
	"flac",
	nullptr,
	nullptr,
	flac_decode,
	nullptr,
	flac_scan_file,
401
	flac_scan_stream,
402 403 404
	nullptr,
	flac_suffixes,
	flac_mime_types,
Warren Dukes's avatar
Warren Dukes committed
405
};