VorbisDecoderPlugin.cxx 8.65 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2015 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 22
#include "VorbisDecoderPlugin.h"
#include "VorbisComments.hxx"
23
#include "VorbisDomain.hxx"
24
#include "../DecoderAPI.hxx"
Max Kellermann's avatar
Max Kellermann committed
25
#include "input/InputStream.hxx"
26
#include "OggCodec.hxx"
27
#include "pcm/Interleave.hxx"
28
#include "util/Error.hxx"
29
#include "util/Macros.hxx"
30
#include "CheckAudioFormat.hxx"
31
#include "tag/TagHandler.hxx"
32
#include "Log.hxx"
33 34

#ifndef HAVE_TREMOR
35
#define OV_EXCLUDE_STATIC_CALLBACKS
Warren Dukes's avatar
Warren Dukes committed
36
#include <vorbis/vorbisfile.h>
37 38 39 40
#else
#include <tremor/ivorbisfile.h>
/* Macros to make Tremor's API look like libogg. Tremor always
   returns host-byte-order 16-bit signed data, and uses integer
41
   milliseconds where libogg uses double seconds.
42 43 44 45 46 47
*/
#define ov_read(VF, BUFFER, LENGTH, BIGENDIANP, WORD, SGNED, BITSTREAM) \
        ov_read(VF, BUFFER, LENGTH, BITSTREAM)
#define ov_time_total(VF, I) ((double)ov_time_total(VF, I)/1000)
#define ov_time_tell(VF) ((double)ov_time_tell(VF)/1000)
#define ov_time_seek_page(VF, S) (ov_time_seek_page(VF, (S)*1000))
Avuton Olrich's avatar
Avuton Olrich committed
48
#endif /* HAVE_TREMOR */
49

50 51
#include <errno.h>

52
struct VorbisInputStream {
53
	Decoder *const decoder;
54

55
	InputStream &input_stream;
56
	bool seekable;
57 58

	VorbisInputStream(Decoder *_decoder, InputStream &_is)
59 60
		:decoder(_decoder), input_stream(_is),
		 seekable(input_stream.CheapSeeking()) {}
61
};
62

63
static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
64
{
65
	VorbisInputStream *vis = (VorbisInputStream *)data;
66
	size_t ret = decoder_read(vis->decoder, vis->input_stream,
67
				  ptr, size * nmemb);
Max Kellermann's avatar
Max Kellermann committed
68

Avuton Olrich's avatar
Avuton Olrich committed
69
	errno = 0;
70

Max Kellermann's avatar
Max Kellermann committed
71
	return ret / size;
72 73
}

74
static int ogg_seek_cb(void *data, ogg_int64_t _offset, int whence)
Avuton Olrich's avatar
Avuton Olrich committed
75
{
76
	VorbisInputStream *vis = (VorbisInputStream *)data;
77
	InputStream &is = vis->input_stream;
78

79 80 81 82 83
	if (!vis->seekable ||
	    (vis->decoder != nullptr &&
	     decoder_get_command(*vis->decoder) == DecoderCommand::STOP))
		return -1;

84
	offset_type offset = _offset;
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	switch (whence) {
	case SEEK_SET:
		break;

	case SEEK_CUR:
		offset += is.GetOffset();
		break;

	case SEEK_END:
		if (!is.KnownSize())
			return -1;

		offset += is.GetSize();
		break;

	default:
		return -1;
	}

	return is.LockSeek(offset, IgnoreError())
105
		? 0 : -1;
106 107
}

108
/* TODO: check Ogg libraries API and see if we can just not have this func */
109
static int ogg_close_cb(gcc_unused void *data)
Avuton Olrich's avatar
Avuton Olrich committed
110
{
111
	return 0;
112 113
}

114
static long ogg_tell_cb(void *data)
Avuton Olrich's avatar
Avuton Olrich committed
115
{
116
	VorbisInputStream *vis = (VorbisInputStream *)data;
117

118
	return (long)vis->input_stream.GetOffset();
119 120
}

121
static const ov_callbacks vorbis_is_callbacks = {
122 123 124 125
	ogg_read_cb,
	ogg_seek_cb,
	ogg_close_cb,
	ogg_tell_cb,
126 127
};

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
static const char *
vorbis_strerror(int code)
{
	switch (code) {
	case OV_EREAD:
		return "read error";

	case OV_ENOTVORBIS:
		return "not vorbis stream";

	case OV_EVERSION:
		return "vorbis version mismatch";

	case OV_EBADHEADER:
		return "invalid vorbis header";

	case OV_EFAULT:
		return "internal logic error";

	default:
		return "unknown error";
	}
}

152
static bool
153
vorbis_is_open(VorbisInputStream *vis, OggVorbis_File *vf)
154
{
155
	int ret = ov_open_callbacks(vis, vf, nullptr, 0, vorbis_is_callbacks);
156
	if (ret < 0) {
157 158
		if (vis->decoder == nullptr ||
		    decoder_get_command(*vis->decoder) == DecoderCommand::NONE)
159 160 161
			FormatWarning(vorbis_domain,
				      "Failed to open Ogg Vorbis stream: %s",
				      vorbis_strerror(ret));
162 163 164 165 166 167
		return false;
	}

	return true;
}

Max Kellermann's avatar
Max Kellermann committed
168
static void
169
vorbis_send_comments(Decoder &decoder, InputStream &is,
Max Kellermann's avatar
Max Kellermann committed
170
		     char **comments)
171
{
Max Kellermann's avatar
Max Kellermann committed
172
	Tag *tag = vorbis_comments_to_tag(comments);
Avuton Olrich's avatar
Avuton Olrich committed
173 174
	if (!tag)
		return;
175

176
	decoder_tag(decoder, is, std::move(*tag));
Max Kellermann's avatar
Max Kellermann committed
177
	delete tag;
178 179
}

180 181 182 183 184
#ifndef HAVE_TREMOR
static void
vorbis_interleave(float *dest, const float *const*src,
		  unsigned nframes, unsigned channels)
{
185 186
	PcmInterleaveFloat(dest, ConstBuffer<const float *>(src, channels),
			   nframes);
187 188 189
}
#endif

190
/* public */
191 192

static bool
193
vorbis_init(gcc_unused const ConfigBlock &block)
194 195 196 197 198 199 200
{
#ifndef HAVE_TREMOR
	LogDebug(vorbis_domain, vorbis_version_string());
#endif
	return true;
}

201 202 203 204 205 206 207 208 209 210
gcc_pure
static SignedSongTime
vorbis_duration(OggVorbis_File &vf)
{
	auto total = ov_time_total(&vf, -1);
	return total >= 0
		? SignedSongTime::FromS(total)
		: SignedSongTime::Negative();
}

211
static void
212
vorbis_stream_decode(Decoder &decoder,
213
		     InputStream &input_stream)
Warren Dukes's avatar
Warren Dukes committed
214
{
215
	if (ogg_codec_detect(&decoder, input_stream) != OGG_CODEC_VORBIS)
216
		return;
217

218
	/* rewind the stream, because ogg_codec_detect() has
219
	   moved it */
220
	input_stream.LockRewind(IgnoreError());
221

222
	VorbisInputStream vis(&decoder, input_stream);
223
	OggVorbis_File vf;
224
	if (!vorbis_is_open(&vis, &vf))
225
		return;
Warren Dukes's avatar
Warren Dukes committed
226

227
	const vorbis_info *vi = ov_info(&vf, -1);
228
	if (vi == nullptr) {
229
		LogWarning(vorbis_domain, "ov_info() has failed");
230 231 232
		return;
	}

233
	Error error;
234 235
	AudioFormat audio_format;
	if (!audio_format_init_checked(audio_format, vi->rate,
236
#ifdef HAVE_TREMOR
237
				       SampleFormat::S16,
238
#else
239
				       SampleFormat::FLOAT,
240
#endif
241
				       vi->channels, error)) {
242
		LogError(error);
243 244 245
		return;
	}

246 247
	decoder_initialized(decoder, audio_format, vis.seekable,
			    vorbis_duration(vf));
248

249
#ifdef HAVE_TREMOR
250
	char buffer[4096];
251 252 253
#else
	float buffer[2048];
	const int frames_per_buffer =
254
		ARRAY_SIZE(buffer) / audio_format.channels;
255 256 257
	const unsigned frame_size = sizeof(buffer[0]) * audio_format.channels;
#endif

258
	int prev_section = -1;
259
	unsigned kbit_rate = 0;
260

261
	DecoderCommand cmd = decoder_get_command(decoder);
262
	while (cmd != DecoderCommand::STOP) {
263
		if (cmd == DecoderCommand::SEEK) {
264 265
			auto seek_where = decoder_seek_where_frame(decoder);
			if (0 == ov_pcm_seek_page(&vf, seek_where)) {
266
				decoder_command_finished(decoder);
Avuton Olrich's avatar
Avuton Olrich committed
267
			} else
268
				decoder_seek_error(decoder);
Warren Dukes's avatar
Warren Dukes committed
269
		}
270

271
		int current_section;
272 273

#ifdef HAVE_TREMOR
274
		long nbytes = ov_read(&vf, buffer, sizeof(buffer),
275
				      IsBigEndian(), 2, 1,
276
				      &current_section);
277 278 279 280 281 282 283 284 285 286 287 288 289 290
#else
		float **per_channel;
		long nframes = ov_read_float(&vf, &per_channel,
					     frames_per_buffer,
					     &current_section);
		long nbytes = nframes;
		if (nframes > 0) {
			vorbis_interleave(buffer,
					  (const float*const*)per_channel,
					  nframes, audio_format.channels);
			nbytes *= frame_size;
		}
#endif

291 292 293
		if (nbytes == OV_HOLE) /* bad packet */
			nbytes = 0;
		else if (nbytes <= 0)
294 295 296
			/* break on EOF or other error */
			break;

Avuton Olrich's avatar
Avuton Olrich committed
297
		if (current_section != prev_section) {
298
			vi = ov_info(&vf, -1);
299
			if (vi == nullptr) {
300 301
				LogWarning(vorbis_domain,
					   "ov_info() has failed");
302 303 304
				break;
			}

305 306
			if (vi->rate != (long)audio_format.sample_rate ||
			    vi->channels != (int)audio_format.channels) {
307 308
				/* we don't support audio format
				   change yet */
309 310
				LogWarning(vorbis_domain,
					   "audio format change, stopping here");
311
				break;
Warren Dukes's avatar
Warren Dukes committed
312
			}
313

314
			char **comments = ov_comment(&vf, -1)->user_comments;
Max Kellermann's avatar
Max Kellermann committed
315
			vorbis_send_comments(decoder, input_stream, comments);
316

317 318
			ReplayGainInfo rgi;
			if (vorbis_comments_to_replay_gain(rgi, comments))
319
				decoder_replay_gain(decoder, &rgi);
Warren Dukes's avatar
Warren Dukes committed
320

321 322
			prev_section = current_section;
		}
Warren Dukes's avatar
Warren Dukes committed
323

324 325
		long test = ov_bitrate_instant(&vf);
		if (test > 0)
326
			kbit_rate = test / 1000;
Warren Dukes's avatar
Warren Dukes committed
327

Max Kellermann's avatar
Max Kellermann committed
328
		cmd = decoder_data(decoder, input_stream,
329 330
				   buffer, nbytes,
				   kbit_rate);
331
	}
Warren Dukes's avatar
Warren Dukes committed
332

Warren Dukes's avatar
Warren Dukes committed
333
	ov_clear(&vf);
Warren Dukes's avatar
Warren Dukes committed
334 335
}

336
static bool
337
vorbis_scan_stream(InputStream &is,
338
		   const TagHandler &handler, void *handler_ctx)
Avuton Olrich's avatar
Avuton Olrich committed
339
{
340
	VorbisInputStream vis(nullptr, is);
Warren Dukes's avatar
Warren Dukes committed
341 342
	OggVorbis_File vf;

343
	if (!vorbis_is_open(&vis, &vf))
344
		return false;
Warren Dukes's avatar
Warren Dukes committed
345

346 347 348 349
	const auto total = ov_time_total(&vf, -1);
	if (total >= 0)
		tag_handler_invoke_duration(handler, handler_ctx,
					    SongTime::FromS(total));
Warren Dukes's avatar
Warren Dukes committed
350

351 352
	vorbis_comments_scan(ov_comment(&vf, -1)->user_comments,
			     handler, handler_ctx);
Warren Dukes's avatar
Warren Dukes committed
353 354

	ov_clear(&vf);
355
	return true;
Warren Dukes's avatar
Warren Dukes committed
356 357
}

Max Kellermann's avatar
Max Kellermann committed
358
static const char *const vorbis_suffixes[] = {
359
	"ogg", "oga", nullptr
Max Kellermann's avatar
Max Kellermann committed
360 361 362
};

static const char *const vorbis_mime_types[] = {
363 364
	"application/ogg",
	"application/x-ogg",
365 366 367 368 369 370
	"audio/ogg",
	"audio/vorbis",
	"audio/vorbis+ogg",
	"audio/x-ogg",
	"audio/x-vorbis",
	"audio/x-vorbis+ogg",
371
	nullptr
372
};
Warren Dukes's avatar
Warren Dukes committed
373

374
const struct DecoderPlugin vorbis_decoder_plugin = {
375
	"vorbis",
376
	vorbis_init,
377 378 379 380 381 382 383 384
	nullptr,
	vorbis_stream_decode,
	nullptr,
	nullptr,
	vorbis_scan_stream,
	nullptr,
	vorbis_suffixes,
	vorbis_mime_types
Warren Dukes's avatar
Warren Dukes committed
385
};