OpusEncoderPlugin.cxx 7.66 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"
#include "OpusEncoderPlugin.hxx"
22
#include "OggEncoder.hxx"
23
#include "AudioFormat.hxx"
24
#include "config/ConfigError.hxx"
25
#include "util/Alloc.hxx"
26
#include "system/ByteOrder.hxx"
27 28 29 30

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

31 32
#include <stdexcept>

33
#include <assert.h>
34
#include <stdlib.h>
35

36
namespace {
37

38
class OpusEncoder final : public OggEncoder {
39
	const AudioFormat audio_format;
40

41
	const size_t frame_size;
42

43 44 45
	const size_t buffer_frames, buffer_size;
	size_t buffer_position = 0;
	uint8_t *const buffer;
46

47
	::OpusEncoder *const enc;
48 49 50

	unsigned char buffer2[1275 * 3 + 7];

51 52
	int lookahead;

53
	ogg_int64_t packetno = 0;
54 55

	ogg_int64_t granulepos;
56

57 58 59
public:
	OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc);
	~OpusEncoder() override;
60

61
	/* virtual methods from class Encoder */
62 63
	void End() override;
	void Write(const void *data, size_t length) override;
64

65
	size_t Read(void *dest, size_t length) override;
66

67
private:
68 69
	void DoEncode(bool eos);
	void WriteSilence(unsigned fill_frames);
70 71 72

	void GenerateHead();
	void GenerateTags();
73 74
};

75
class PreparedOpusEncoder final : public PreparedEncoder {
76 77 78 79
	opus_int32 bitrate;
	int complexity;
	int signal;

80
public:
81
	PreparedOpusEncoder(const ConfigBlock &block);
82 83

	/* virtual methods from class PreparedEncoder */
84
	Encoder *Open(AudioFormat &audio_format) override;
85 86 87 88

	const char *GetMimeType() const override {
		return "audio/ogg";
	}
89 90
};

91
PreparedOpusEncoder::PreparedOpusEncoder(const ConfigBlock &block)
92
{
93
	const char *value = block.GetBlockValue("bitrate", "auto");
94
	if (strcmp(value, "auto") == 0)
95
		bitrate = OPUS_AUTO;
96
	else if (strcmp(value, "max") == 0)
97
		bitrate = OPUS_BITRATE_MAX;
98 99
	else {
		char *endptr;
100
		bitrate = strtoul(value, &endptr, 10);
101
		if (endptr == value || *endptr != 0 ||
102 103
		    bitrate < 500 || bitrate > 512000)
			throw std::runtime_error("Invalid bit rate");
104 105
	}

106
	complexity = block.GetBlockValue("complexity", 10u);
107 108
	if (complexity > 10)
		throw std::runtime_error("Invalid complexity");
109

110
	value = block.GetBlockValue("signal", "auto");
111
	if (strcmp(value, "auto") == 0)
112
		signal = OPUS_AUTO;
113
	else if (strcmp(value, "voice") == 0)
114
		signal = OPUS_SIGNAL_VOICE;
115
	else if (strcmp(value, "music") == 0)
116
		signal = OPUS_SIGNAL_MUSIC;
117 118
	else
		throw std::runtime_error("Invalid signal");
119 120
}

121
static PreparedEncoder *
122
opus_encoder_init(const ConfigBlock &block)
123
{
124
	return new PreparedOpusEncoder(block);
125 126
}

127
OpusEncoder::OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc)
128
	:OggEncoder(false),
129 130 131 132 133 134 135 136 137 138 139
	 audio_format(_audio_format),
	 frame_size(_audio_format.GetFrameSize()),
	 buffer_frames(_audio_format.sample_rate / 50),
	 buffer_size(frame_size * buffer_frames),
	 buffer((unsigned char *)xalloc(buffer_size)),
	 enc(_enc)
{
	opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
}

Encoder *
140
PreparedOpusEncoder::Open(AudioFormat &audio_format)
141 142
{
	/* libopus supports only 48 kHz */
143
	audio_format.sample_rate = 48000;
144

145 146
	if (audio_format.channels > 2)
		audio_format.channels = 1;
147

148
	switch (audio_format.format) {
149 150
	case SampleFormat::S16:
	case SampleFormat::FLOAT:
151 152
		break;

153
	case SampleFormat::S8:
154
		audio_format.format = SampleFormat::S16;
155 156 157
		break;

	default:
158
		audio_format.format = SampleFormat::FLOAT;
159 160 161
		break;
	}

162
	int error_code;
163 164 165 166
	auto *enc = opus_encoder_create(audio_format.sample_rate,
					audio_format.channels,
					OPUS_APPLICATION_AUDIO,
					&error_code);
167 168
	if (enc == nullptr)
		throw std::runtime_error(opus_strerror(error_code));
169

170 171 172
	opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
	opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
	opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal));
173

174
	return new OpusEncoder(audio_format, enc);
175 176
}

177
OpusEncoder::~OpusEncoder()
178 179 180 181 182
{
	free(buffer);
	opus_encoder_destroy(enc);
}

183 184
void
OpusEncoder::DoEncode(bool eos)
185
{
186
	assert(buffer_position == buffer_size);
187 188

	opus_int32 result =
189 190 191 192 193 194 195 196 197 198 199
		audio_format.format == SampleFormat::S16
		? opus_encode(enc,
			      (const opus_int16 *)buffer,
			      buffer_frames,
			      buffer2,
			      sizeof(buffer2))
		: opus_encode_float(enc,
				    (const float *)buffer,
				    buffer_frames,
				    buffer2,
				    sizeof(buffer2));
200 201
	if (result < 0)
		throw std::runtime_error("Opus encoder error");
202

203
	granulepos += buffer_frames;
204

205
	ogg_packet packet;
206
	packet.packet = buffer2;
207 208 209
	packet.bytes = result;
	packet.b_o_s = false;
	packet.e_o_s = eos;
210 211 212
	packet.granulepos = granulepos;
	packet.packetno = packetno++;
	stream.PacketIn(packet);
213

214
	buffer_position = 0;
215 216
}

217 218
void
OpusEncoder::End()
219
{
220
	Flush();
221

222 223 224
	memset(buffer + buffer_position, 0,
	       buffer_size - buffer_position);
	buffer_position = buffer_size;
225

226
	DoEncode(true);
227 228
}

229 230
void
OpusEncoder::WriteSilence(unsigned fill_frames)
231 232
{
	size_t fill_bytes = fill_frames * frame_size;
233 234

	while (fill_bytes > 0) {
235
		size_t nbytes = buffer_size - buffer_position;
236 237 238
		if (nbytes > fill_bytes)
			nbytes = fill_bytes;

239 240
		memset(buffer + buffer_position, 0, nbytes);
		buffer_position += nbytes;
241 242
		fill_bytes -= nbytes;

243 244
		if (buffer_position == buffer_size)
			DoEncode(false);
245 246 247
	}
}

248 249
void
OpusEncoder::Write(const void *_data, size_t length)
250 251 252
{
	const uint8_t *data = (const uint8_t *)_data;

253
	if (lookahead > 0) {
254 255 256
		/* generate some silence at the beginning of the
		   stream */

257
		assert(buffer_position == 0);
258

259
		WriteSilence(lookahead);
260
		lookahead = 0;
261 262
	}

263
	while (length > 0) {
264
		size_t nbytes = buffer_size - buffer_position;
265 266 267
		if (nbytes > length)
			nbytes = length;

268
		memcpy(buffer + buffer_position, data, nbytes);
269 270
		data += nbytes;
		length -= nbytes;
271
		buffer_position += nbytes;
272

273 274
		if (buffer_position == buffer_size)
			DoEncode(false);
275 276 277
	}
}

278 279
void
OpusEncoder::GenerateHead()
280 281 282 283
{
	unsigned char header[19];
	memcpy(header, "OpusHead", 8);
	header[8] = 1;
284 285 286
	header[9] = audio_format.channels;
	*(uint16_t *)(header + 10) = ToLE16(lookahead);
	*(uint32_t *)(header + 12) = ToLE32(audio_format.sample_rate);
287 288 289 290 291 292 293 294 295 296
	header[16] = 0;
	header[17] = 0;
	header[18] = 0;

	ogg_packet packet;
	packet.packet = header;
	packet.bytes = 19;
	packet.b_o_s = true;
	packet.e_o_s = false;
	packet.granulepos = 0;
297 298
	packet.packetno = packetno++;
	stream.PacketIn(packet);
299
	Flush();
300 301
}

302 303
void
OpusEncoder::GenerateTags()
304 305 306 307 308
{
	const char *version = opus_get_version_string();
	size_t version_length = strlen(version);

	size_t comments_size = 8 + 4 + version_length + 4;
309
	unsigned char *comments = (unsigned char *)xalloc(comments_size);
310
	memcpy(comments, "OpusTags", 8);
311
	*(uint32_t *)(comments + 8) = ToLE32(version_length);
312
	memcpy(comments + 12, version, version_length);
313
	*(uint32_t *)(comments + 12 + version_length) = ToLE32(0);
314 315 316 317 318 319 320

	ogg_packet packet;
	packet.packet = comments;
	packet.bytes = comments_size;
	packet.b_o_s = false;
	packet.e_o_s = false;
	packet.granulepos = 0;
321 322
	packet.packetno = packetno++;
	stream.PacketIn(packet);
323
	Flush();
324

325
	free(comments);
326 327
}

328 329
size_t
OpusEncoder::Read(void *dest, size_t length)
330
{
331 332 333 334
	if (packetno == 0)
		GenerateHead();
	else if (packetno == 1)
		GenerateTags();
335

336
	return OggEncoder::Read(dest, length);
337
}
338

339 340
}

341
const EncoderPlugin opus_encoder_plugin = {
342 343 344
	"opus",
	opus_encoder_init,
};