1 /*
2 * encode_lame.c - encode audio frames using lame
3 * Written by Andrew Church <achurch@achurch.org>
4 *
5 * This file is part of transcode, a video stream processing tool.
6 * transcode is free software, distributable under the terms of the GNU
7 * General Public License (version 2 or later). See the file COPYING
8 * for details.
9 */
10
11 #include "transcode.h"
12 #include "libtc/libtc.h"
13 #include "libtc/optstr.h"
14 #include "libtc/tcmodule-plugin.h"
15
16 #include <lame/lame.h>
17
18 #define MOD_NAME "encode_lame.so"
19 #define MOD_VERSION "v1.1 (2006-11-01)"
20 #define MOD_CAP "Encodes audio to MP3 using LAME"
21 #define MOD_AUTHOR "Andrew Church"
22
23 #define MOD_FEATURES \
24 TC_MODULE_FEATURE_ENCODE|TC_MODULE_FEATURE_AUDIO
25
26 #define MOD_FLAGS \
27 TC_MODULE_FLAG_RECONFIGURABLE
28
29
30 /*************************************************************************/
31
32 /* Local data structure: */
33
34 typedef struct {
35 lame_global_flags *lgf;
36 int bps; /* bytes per sample */
37 int channels;
38 int flush_flag;
39 } PrivateData;
40
41 /*************************************************************************/
42
43 /**
44 * lame_log_error, lame_log_msg, lame_log_debug: Internal logging
45 * functions for LAME.
46 *
47 * Parameters:
48 * format: Log message format string.
49 * args: Log message format arguments.
50 * Return value:
51 * None.
52 */
53
lame_log_error(const char * format,va_list args)54 static void lame_log_error(const char *format, va_list args)
55 {
56 char buf[TC_BUF_MAX];
57 tc_vsnprintf(buf, sizeof(buf), format, args);
58 tc_log_error(MOD_NAME, "%s", buf);
59 }
60
lame_log_msg(const char * format,va_list args)61 static void lame_log_msg(const char *format, va_list args)
62 {
63 if (verbose & TC_INFO) {
64 char buf[TC_BUF_MAX];
65 tc_vsnprintf(buf, sizeof(buf), format, args);
66 tc_log_info(MOD_NAME, "%s", buf);
67 }
68 }
69
lame_log_debug(const char * format,va_list args)70 static void lame_log_debug(const char *format, va_list args)
71 {
72 if (verbose & TC_DEBUG) {
73 char buf[TC_BUF_MAX];
74 tc_vsnprintf(buf, sizeof(buf), format, args);
75 tc_log_msg(MOD_NAME, "%s", buf);
76 }
77 }
78
79 /*************************************************************************/
80 /*************************************************************************/
81
82 /* Module interface routines and data. */
83
84 /*************************************************************************/
85
86 /**
87 * lamemod_init: Initialize this instance of the module. See
88 * tcmodule-data.h for function details. Note the name of this function--
89 * we don't want to conflict with libmp3lame's lame_init().
90 */
91
lamemod_init(TCModuleInstance * self,uint32_t features)92 static int lamemod_init(TCModuleInstance *self, uint32_t features)
93 {
94 PrivateData *pd;
95
96 TC_MODULE_SELF_CHECK(self, "init");
97 TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
98
99 self->userdata = pd = tc_malloc(sizeof(PrivateData));
100 if (!pd) {
101 tc_log_error(MOD_NAME, "init: out of memory!");
102 return TC_ERROR;
103 }
104 pd->lgf = NULL;
105
106 /* FIXME: shouldn't this test a specific flag? */
107 if (verbose) {
108 tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
109 if (verbose & TC_INFO)
110 tc_log_info(MOD_NAME, "Using LAME %s", get_lame_version());
111 }
112 return TC_OK;
113 }
114
115 /*************************************************************************/
116
117 /**
118 * lame_configure: Configure this instance of the module. See
119 * tcmodule-data.h for function details.
120 */
121
lame_configure(TCModuleInstance * self,const char * options,vob_t * vob)122 static int lame_configure(TCModuleInstance *self,
123 const char *options, vob_t *vob)
124 {
125 PrivateData *pd;
126 int samplerate = vob->mp3frequency ? vob->mp3frequency : vob->a_rate;
127 int quality;
128 MPEG_mode mode;
129
130 TC_MODULE_SELF_CHECK(self, "configure");
131 pd = self->userdata;
132
133 pd->flush_flag = vob->encoder_flush;
134 /* Save bytes per sample */
135 pd->bps = (vob->dm_chan * vob->dm_bits) / 8;
136 /* And audio channels */
137 pd->channels = vob->dm_chan;
138
139 /* Create LAME object (freeing any old one that might be left over) */
140 if (pd->lgf)
141 lame_close(pd->lgf);
142 pd->lgf = lame_init();
143 if (!pd->lgf) {
144 tc_log_error(MOD_NAME, "LAME initialization failed");
145 return TC_ERROR;
146 }
147
148 /* Set up logging functions (assume no failure) */
149 lame_set_errorf(pd->lgf, lame_log_error);
150 lame_set_msgf (pd->lgf, lame_log_msg );
151 lame_set_debugf(pd->lgf, lame_log_debug);
152
153 /* Set up audio parameters */
154 if (vob->dm_bits != 16) {
155 tc_log_error(MOD_NAME, "Only 16-bit samples supported");
156 return TC_ERROR;
157 }
158 if (lame_set_in_samplerate(pd->lgf, samplerate) < 0) {
159 tc_log_error(MOD_NAME, "lame_set_in_samplerate(%d) failed",samplerate);
160 return TC_ERROR;
161 }
162 if (lame_set_num_channels(pd->lgf, pd->channels) < 0) {
163 tc_log_error(MOD_NAME, "lame_set_num_channels(%d) failed",
164 pd->channels);
165 return TC_ERROR;
166 }
167 if (lame_set_scale(pd->lgf, vob->volume) < 0) {
168 tc_log_error(MOD_NAME, "lame_set_scale(%f) failed", vob->volume);
169 return TC_ERROR;
170 }
171 if (lame_set_bWriteVbrTag(pd->lgf, (vob->a_vbr!=0)) < 0) {
172 tc_log_error(MOD_NAME, "lame_set_bWriteVbrTag(%d) failed",
173 (vob->a_vbr!=0));
174 return TC_ERROR;
175 }
176 quality = (int)TC_CLAMP(vob->mp3quality, 0.0, 9.0);
177 if (lame_set_quality(pd->lgf, quality) < 0) {
178 tc_log_error(MOD_NAME, "lame_set_quality(%d) failed", quality);
179 return TC_ERROR;
180 }
181 switch (vob->mp3mode) {
182 case 0: mode = JOINT_STEREO; break;
183 case 1: mode = STEREO; break;
184 case 2: mode = MONO; break;
185 default:
186 tc_log_warn(MOD_NAME,"Invalid audio mode, defaulting to joint stereo");
187 mode = JOINT_STEREO;
188 break;
189 }
190 /* FIXME: add coherency check with given audio channels? */
191 if (lame_set_mode(pd->lgf, mode) < 0) {
192 tc_log_error(MOD_NAME, "lame_set_mode(%d) failed", mode);
193 return TC_ERROR;
194 }
195 if (lame_set_brate(pd->lgf, vob->mp3bitrate) < 0) {
196 tc_log_error(MOD_NAME, "lame_set_brate(%d) failed", vob->mp3bitrate);
197 return TC_ERROR;
198 }
199 /* Ugly preset handling */
200 if (vob->lame_preset) {
201 preset_mode preset;
202 int fast = 0;
203 char *s = strchr(vob->lame_preset, ',');
204 if (s) {
205 *s++ = 0;
206 if (strcmp(s, "fast") == 0)
207 fast = 1;
208 }
209 if (strcmp(vob->lame_preset, "standard") == 0) {
210 preset = (fast ? STANDARD_FAST : STANDARD);
211 vob->a_vbr = 1;
212 } else if (strcmp(vob->lame_preset, "medium") == 0) {
213 preset = (fast ? MEDIUM_FAST : MEDIUM);
214 vob->a_vbr = 1;
215 } else if (strcmp(vob->lame_preset, "extreme") == 0) {
216 preset = (fast ? EXTREME_FAST : EXTREME);
217 vob->a_vbr = 1;
218 } else if (strcmp(vob->lame_preset, "insane") == 0) {
219 preset = INSANE;
220 vob->a_vbr = 1;
221 } else {
222 preset = strtol(vob->lame_preset, &s, 10);
223 if (*s || preset < 8 || preset > 320) {
224 tc_log_error(MOD_NAME, "Invalid preset \"%s\"",
225 vob->lame_preset);
226 return TC_ERROR;
227 } else {
228 vob->a_vbr = 1;
229 }
230 }
231 if (lame_set_preset(pd->lgf, preset) < 0) {
232 tc_log_error(MOD_NAME, "lame_set_preset(%d) failed", preset);
233 return TC_ERROR;
234 }
235 } // if (vob->lame_preset)
236 /* Acceleration setting failures aren't fatal */
237 if (lame_set_asm_optimizations(pd->lgf, MMX, (tc_accel&AC_MMX )?1:0) < 0)
238 tc_log_warn(MOD_NAME, "lame_set_asm_optimizations(MMX,%d) failed",
239 (tc_accel&AC_MMX)?1:0);
240 if (lame_set_asm_optimizations(pd->lgf, AMD_3DNOW,
241 (tc_accel&AC_3DNOW)?1:0) < 0)
242 tc_log_warn(MOD_NAME, "lame_set_asm_optimizations(3DNOW,%d) failed",
243 (tc_accel&AC_3DNOW)?1:0);
244 if (lame_set_asm_optimizations(pd->lgf, SSE, (tc_accel&AC_SSE )?1:0) < 0)
245 tc_log_warn(MOD_NAME, "lame_set_asm_optimizations(SSE,%d) failed",
246 (tc_accel&AC_SSE)?1:0);
247 /* FIXME: this function is documented as "for testing only"--should we
248 * really expose it to the user? */
249 if (!vob->bitreservoir && lame_set_disable_reservoir(pd->lgf, 1) < 0) {
250 tc_log_error(MOD_NAME, "lame_set_disable_reservoir(1) failed");
251 return TC_ERROR;
252 }
253 if (lame_set_VBR(pd->lgf, vob->a_vbr ? vbr_default : vbr_off) < 0) {
254 tc_log_error(MOD_NAME, "lame_set_VBR(%d) failed",
255 vob->a_vbr ? vbr_default : vbr_off);
256 return TC_ERROR;
257 }
258 if (vob->a_vbr) {
259 /* FIXME: we should have a separate VBR quality control */
260 if (lame_set_VBR_q(pd->lgf, quality) < 0) {
261 tc_log_error(MOD_NAME, "lame_set_VBR_q(%d) failed", quality);
262 return TC_ERROR;
263 }
264 }
265
266 /* Initialize encoder */
267 if (lame_init_params(pd->lgf) < 0) {
268 tc_log_error(MOD_NAME, "lame_init_params() failed");
269 return TC_ERROR;
270 }
271
272 return TC_OK;
273 }
274
275 /*************************************************************************/
276
277 /**
278 * lame_inspect: Return the value of an option in this instance of the
279 * module. See tcmodule-data.h for function details.
280 */
281
lame_inspect(TCModuleInstance * self,const char * param,const char ** value)282 static int lame_inspect(TCModuleInstance *self,
283 const char *param, const char **value)
284 {
285 static char buf[TC_BUF_MAX];
286
287 TC_MODULE_SELF_CHECK(self, "inspect");
288 TC_MODULE_SELF_CHECK(param, "inspect");
289
290 if (optstr_lookup(param, "help")) {
291 tc_snprintf(buf, sizeof(buf),
292 "Overview:\n"
293 " Encodes audio to MP3 using the LAME library.\n"
294 "No options available.\n");
295 *value = buf;
296 }
297 return TC_OK;
298 }
299
300 /*************************************************************************/
301
302 /**
303 * lame_stop: Reset this instance of the module. See tcmodule-data.h for
304 * function details.
305 */
306
lame_stop(TCModuleInstance * self)307 static int lame_stop(TCModuleInstance *self)
308 {
309 PrivateData *pd;
310
311 TC_MODULE_SELF_CHECK(self, "stop");
312
313 pd = self->userdata;
314
315 if (pd->lgf) {
316 lame_close(pd->lgf);
317 pd->lgf = NULL;
318 }
319
320 return TC_OK;
321 }
322
323 /*************************************************************************/
324
325 /**
326 * lame_fini: Clean up after this instance of the module. See
327 * tcmodule-data.h for function details.
328 */
329
lame_fini(TCModuleInstance * self)330 static int lame_fini(TCModuleInstance *self)
331 {
332 TC_MODULE_SELF_CHECK(self, "fini");
333
334 lame_stop(self);
335 tc_free(self->userdata);
336 self->userdata = NULL;
337 return TC_OK;
338 }
339
340 /*************************************************************************/
341
342 /**
343 * lame_encode: Encode a frame of data. See tcmodule-data.h for
344 * function details.
345 */
346
347 #define LAME_FLUSH_BUFFER_SIZE 7200 /* from lame/lame.h */
348
lame_encode(TCModuleInstance * self,aframe_list_t * in,aframe_list_t * out)349 static int lame_encode(TCModuleInstance *self,
350 aframe_list_t *in, aframe_list_t *out)
351 {
352 PrivateData *pd;
353 int res;
354
355 TC_MODULE_SELF_CHECK(self, "encode");
356 if (out == NULL) {
357 tc_log_error(MOD_NAME, "no output buffer supplied");
358 return TC_ERROR;
359 }
360
361 pd = self->userdata;
362
363 if (in == NULL) {
364 /* flush request */
365 if (!pd->flush_flag) {
366 /* No-flush option given, so don't do anything */
367 out->audio_len = 0;
368 return TC_OK;
369 }
370 if (out->audio_size < LAME_FLUSH_BUFFER_SIZE) {
371 /* paranoia is a virtue */
372 tc_log_error(MOD_NAME, "output buffer too small for"
373 " flushing (%i|%i)",
374 out->audio_size,
375 LAME_FLUSH_BUFFER_SIZE);
376 return TC_ERROR;
377 }
378
379 /*
380 * Looks like _nogap should behave better when
381 * splitting/rotating output files.
382 * Moreover, our streams should'nt contain any ID3 tag,
383 * -- FR
384 */
385 res = lame_encode_flush_nogap(pd->lgf, out->audio_buf, 0);
386 if (verbose & TC_DEBUG) {
387 tc_log_info(MOD_NAME, "flushing %d audio bytes", res);
388 }
389 } else {
390 /* regular encoding */
391 if (pd->channels == 1) { /* mono */
392 res = lame_encode_buffer(pd->lgf,
393 (short *)(in->audio_buf),
394 (short *)(in->audio_buf),
395 in->audio_size / pd->bps,
396 out->audio_buf,
397 out->audio_size);
398 } else { /* all stereo flavours */
399 res = lame_encode_buffer_interleaved(pd->lgf,
400 (short *)in->audio_buf,
401 in->audio_size / pd->bps,
402 out->audio_buf,
403 out->audio_size);
404 }
405
406 if (res < 0) {
407 if (verbose & TC_DEBUG) {
408 tc_log_error(MOD_NAME, "lame_encode_buffer_interleaved() failed"
409 " (%d: %s)", res,
410 res==-1 ? "output buffer overflow" :
411 res==-2 ? "out of memory" :
412 res==-3 ? "not initialized" :
413 res==-4 ? "psychoacoustic problems" : "unknown");
414 } else {
415 tc_log_error(MOD_NAME, "Audio encoding failed!");
416 }
417 return TC_ERROR;
418 }
419 }
420 out->audio_len = res;
421 return TC_OK;
422 }
423
424 /*************************************************************************/
425
426 static const TCCodecID lame_codecs_in[] = { TC_CODEC_PCM, TC_CODEC_ERROR };
427 static const TCCodecID lame_codecs_out[] = { TC_CODEC_MP3, TC_CODEC_ERROR };
428 TC_MODULE_CODEC_FORMATS(lame);
429
430 TC_MODULE_INFO(lame);
431
432 static const TCModuleClass lame_class = {
433 TC_MODULE_CLASS_HEAD(lame),
434
435 .init = lamemod_init,
436 .fini = lame_fini,
437 .configure = lame_configure,
438 .stop = lame_stop,
439 .inspect = lame_inspect,
440
441 .encode_audio = lame_encode,
442 };
443
444 TC_MODULE_ENTRY_POINT(lame);
445
446 /*************************************************************************/
447
448 /*
449 * Local variables:
450 * c-file-style: "stroustrup"
451 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
452 * indent-tabs-mode: nil
453 * End:
454 *
455 * vim: expandtab shiftwidth=4:
456 */
457