shout_ogg.c 7.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* the Music Player Daemon (MPD)
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
 * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

19
#include "shout_plugin.h"
20

21
#include <vorbis/vorbisenc.h>
22
#include <stdlib.h>
23

24
struct ogg_vorbis_data {
25 26 27 28 29 30 31 32 33 34 35
	ogg_stream_state os;
	ogg_page og;
	ogg_packet op;
	ogg_packet header_main;
	ogg_packet header_comments;
	ogg_packet header_codebooks;

	vorbis_dsp_state vd;
	vorbis_block vb;
	vorbis_info vi;
	vorbis_comment vc;
36
};
37

38
static void add_tag(struct ogg_vorbis_data *od, const char *name, char *value)
39 40
{
	if (value) {
41 42 43 44
		union {
			const char *in;
			char *out;
		} u = { .in = name };
45
		vorbis_comment_add_tag(&od->vc, u.out, value);
46 47 48
	}
}

49
static void copy_tag_to_vorbis_comment(struct shout_data *sd)
50
{
51
	struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
52

53 54 55 56 57 58
	if (sd->tag) {
		int i;

		for (i = 0; i < sd->tag->numOfItems; i++) {
			switch (sd->tag->items[i]->type) {
			case TAG_ITEM_ARTIST:
59
				add_tag(od, "ARTIST", sd->tag->items[i]->value);
60 61
				break;
			case TAG_ITEM_ALBUM:
62
				add_tag(od, "ALBUM", sd->tag->items[i]->value);
63 64
				break;
			case TAG_ITEM_TITLE:
65
				add_tag(od, "TITLE", sd->tag->items[i]->value);
66
				break;
67

68 69 70 71 72 73 74
			default:
				break;
			}
		}
	}
}

75
static int copy_ogg_buffer_to_shout_buffer(ogg_page *og,
76
					   struct shout_buffer *buf)
77
{
78
	if (sizeof(buf->data) - buf->len >= (size_t)og->header_len) {
79 80 81 82
		memcpy(buf->data + buf->len,
		       og->header, og->header_len);
		buf->len += og->header_len;
	} else {
83
		g_warning("%s: not enough buffer space!\n", __func__);
84 85 86
		return -1;
	}

87
	if (sizeof(buf->data) - buf->len >= (size_t)og->body_len) {
88 89 90 91
		memcpy(buf->data + buf->len,
		       og->body, og->body_len);
		buf->len += og->body_len;
	} else {
92
		g_warning("%s: not enough buffer space!\n", __func__);
93 94 95 96 97 98 99 100
		return -1;
	}

	return 0;
}

static int flush_ogg_buffer(struct shout_data *sd)
{
101 102
	struct shout_buffer *buf = &sd->buf;
	struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
103 104
	int ret = 0;

105 106
	if (ogg_stream_flush(&od->os, &od->og))
		ret = copy_ogg_buffer_to_shout_buffer(&od->og, buf);
107 108 109 110

	return ret;
}

111
static int send_ogg_vorbis_header(struct shout_data *sd)
112
{
113
	struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
114 115 116 117 118

	vorbis_analysis_headerout(&od->vd, &od->vc,
				  &od->header_main,
				  &od->header_comments,
				  &od->header_codebooks);
119

120 121 122
	ogg_stream_packetin(&od->os, &od->header_main);
	ogg_stream_packetin(&od->os, &od->header_comments);
	ogg_stream_packetin(&od->os, &od->header_codebooks);
123 124

	return flush_ogg_buffer(sd);
125 126
}

127
static void finish_encoder(struct ogg_vorbis_data *od)
128
{
129
	vorbis_analysis_wrote(&od->vd, 0);
130

131 132 133 134 135
	while (vorbis_analysis_blockout(&od->vd, &od->vb) == 1) {
		vorbis_analysis(&od->vb, NULL);
		vorbis_bitrate_addblock(&od->vb);
		while (vorbis_bitrate_flushpacket(&od->vd, &od->op)) {
			ogg_stream_packetin(&od->os, &od->op);
136 137 138 139
		}
	}
}

140
static int shout_ogg_encoder_clear_encoder(struct shout_data *sd)
141
{
142
	struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
143
	int ret;
144

145 146 147
	finish_encoder(od);
	if ((ret = ogg_stream_pageout(&od->os, &od->og)))
		copy_ogg_buffer_to_shout_buffer(&od->og, &sd->buf);
148

149 150 151 152 153
	vorbis_comment_clear(&od->vc);
	ogg_stream_clear(&od->os);
	vorbis_block_clear(&od->vb);
	vorbis_dsp_clear(&od->vd);
	vorbis_info_clear(&od->vi);
154 155

	return ret;
156 157
}

158 159
static void shout_ogg_encoder_finish(struct shout_data *sd)
{
160
	struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
161 162 163 164 165 166 167 168 169

	if (od) {
		free(od);
		sd->encoder_data = NULL;
	}
}

static int shout_ogg_encoder_init(struct shout_data *sd)
{
170
	struct ogg_vorbis_data *od = g_new(struct ogg_vorbis_data, 1);
171 172 173 174 175 176

	sd->encoder_data = od;

	return 0;
}

177
static int reinit_encoder(struct shout_data *sd)
178
{
179
	struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
180 181

	vorbis_info_init(&od->vi);
182 183

	if (sd->quality >= -1.0) {
184
		if (0 != vorbis_encode_init_vbr(&od->vi,
185
						sd->audio_format.channels,
186
						sd->audio_format.sample_rate,
187
						sd->quality * 0.1)) {
188
			g_warning("error initializing vorbis vbr\n");
189
			vorbis_info_clear(&od->vi);
190 191 192
			return -1;
		}
	} else {
193
		if (0 != vorbis_encode_init(&od->vi,
194
					    sd->audio_format.channels,
195
					    sd->audio_format.sample_rate, -1.0,
196
					    sd->bitrate * 1000, -1.0)) {
197
			g_warning("error initializing vorbis encoder\n");
198
			vorbis_info_clear(&od->vi);
199 200 201 202
			return -1;
		}
	}

203 204 205 206
	vorbis_analysis_init(&od->vd, &od->vi);
	vorbis_block_init(&od->vd, &od->vb);
	ogg_stream_init(&od->os, rand());
	vorbis_comment_init(&od->vc);
207 208 209 210

	return 0;
}

211
static int shout_ogg_encoder_init_encoder(struct shout_data *sd)
212 213 214 215 216
{
	if (reinit_encoder(sd))
		return -1;

	if (send_ogg_vorbis_header(sd)) {
217
		g_warning("error sending ogg vorbis header for shout\n");
218 219 220 221 222 223
		return -1;
	}

	return 0;
}

224
static int shout_ogg_encoder_send_metadata(struct shout_data *sd,
225 226
					   G_GNUC_UNUSED char * song,
					   G_GNUC_UNUSED size_t size)
227
{
228
	struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
229

230
	shout_ogg_encoder_clear_encoder(sd);
231
	if (reinit_encoder(sd))
232 233 234 235
		return 0;

	copy_tag_to_vorbis_comment(sd);

236 237 238 239
	vorbis_analysis_headerout(&od->vd, &od->vc,
				  &od->header_main,
				  &od->header_comments,
				  &od->header_codebooks);
240

241 242 243
	ogg_stream_packetin(&od->os, &od->header_main);
	ogg_stream_packetin(&od->os, &od->header_comments);
	ogg_stream_packetin(&od->os, &od->header_codebooks);
244

245
	flush_ogg_buffer(sd);
246 247 248 249

	return 0;
}

250 251
static int shout_ogg_encoder_encode(struct shout_data *sd,
				    const char *chunk, size_t size)
252
{
253
	struct shout_buffer *buf = &sd->buf;
254 255 256 257
	unsigned int i;
	int j;
	float **vorbbuf;
	unsigned int samples;
258
	int bytes = audio_format_sample_size(&sd->audio_format);
259
	struct ogg_vorbis_data *od = (struct ogg_vorbis_data *)sd->encoder_data;
260 261

	samples = size / (bytes * sd->audio_format.channels);
262
	vorbbuf = vorbis_analysis_buffer(&od->vd, samples);
263 264 265 266 267

	/* this is for only 16-bit audio */

	for (i = 0; i < samples; i++) {
		for (j = 0; j < sd->audio_format.channels; j++) {
268
			vorbbuf[j][i] = (*((const int16_t *) chunk)) / 32768.0;
269 270 271 272
			chunk += bytes;
		}
	}

273
	vorbis_analysis_wrote(&od->vd, samples);
274

275 276 277
	while (1 == vorbis_analysis_blockout(&od->vd, &od->vb)) {
		vorbis_analysis(&od->vb, NULL);
		vorbis_bitrate_addblock(&od->vb);
278

279 280
		while (vorbis_bitrate_flushpacket(&od->vd, &od->op)) {
			ogg_stream_packetin(&od->os, &od->op);
281 282
		}
	}
283

284 285
	if (ogg_stream_pageout(&od->os, &od->og))
		copy_ogg_buffer_to_shout_buffer(&od->og, buf);
286 287

	return 0;
288 289
}

290
const struct shout_encoder_plugin shout_ogg_encoder = {
291 292 293 294 295 296 297 298 299 300
	"ogg",
	SHOUT_FORMAT_VORBIS,

	shout_ogg_encoder_clear_encoder,
	shout_ogg_encoder_encode,
	shout_ogg_encoder_finish,
	shout_ogg_encoder_init,
	shout_ogg_encoder_init_encoder,
	shout_ogg_encoder_send_metadata,
};