]> iEval git - fdkaac.git/blame - src/main.c
tweak configure.ac and Makefile.am
[fdkaac.git] / src / main.c
CommitLineData
48e2f01c 1/*
2 * Copyright (C) 2013 nu774
3 * For conditions of distribution and use, see copyright notice in COPYING
4 */
5#if HAVE_CONFIG_H
6# include "config.h"
7#endif
8#if HAVE_STDINT_H
9# include <stdint.h>
10#endif
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <locale.h>
15#include <errno.h>
16#include <getopt.h>
17#include "compat.h"
18#include "wav_reader.h"
19#include "aacenc.h"
20#include "m4af.h"
21#include "progress.h"
22#include "version.h"
23
24#define PROGNAME "fdkaac"
25
26static
27int read_callback(void *cookie, void *data, uint32_t size)
28{
29 return fread(data, 1, size, (FILE*)cookie);
30}
31
32static
33int write_callback(void *cookie, const void *data, uint32_t size)
34{
35 return fwrite(data, 1, size, (FILE*)cookie);
36}
37
38static
39int seek_callback(void *cookie, int64_t off, int whence)
40{
41 return fseeko((FILE*)cookie, off, whence);
42}
43
44static
45int64_t tell_callback(void *cookie)
46{
47 return ftello((FILE*)cookie);
48}
49
50static
51void usage(void)
52{
53 printf(
54PROGNAME " %s\n"
55"Usage: " PROGNAME " [options] input_file\n"
56"Options:\n"
57" -h, --help Print this help message\n"
58" -p, --profile <n> Profile (audio object type)\n"
59" 2: MPEG-4 AAC LC (default)\n"
60" 5: MPEG-4 HE-AAC (SBR)\n"
61" 29: MPEG-4 HE-AAC v2 (SBR+PS)\n"
62" 23: MPEG-4 AAC LD\n"
63" 39: MPEG-4 AAC ELD\n"
64" 129: MPEG-2 AAC LC\n"
65" 132: MPEG-2 HE-AAC (SBR)\n"
66" 156: MPEG-2 HE-AAC v2 (SBR+PS)\n"
67" -b, --bitrate <n> Bitrate in bits per seconds (for CBR)\n"
68" -m, --bitrate-mode <n> Bitrate configuration\n"
69" 0: CBR (default)\n"
70" 1-5: VBR\n"
71" (VBR mode is not officially supported, and\n"
72" works only on a certain combination of\n"
73" parameter settings, sample rate, and\n"
74" channel configuration)\n"
75" -w, --bandwidth <n> Frequency bandwidth in Hz (AAC LC only)\n"
76" -a, --afterurner <n> Afterburner\n"
77" 0: Off\n"
78" 1: On(default)\n"
79" -L, --lowdelay-sbr Enable ELD-SBR (AAC ELD only)\n"
80" -s, --sbr-signaling <n> SBR signaling mode\n"
81" 0: Implicit, backward compatible(default)\n"
82" 1: Explicit SBR and implicit PS\n"
83" 2: Explicit hierarchical signaling\n"
84" -f, --transport-format <n> Transport format\n"
85" 0: RAW (default, muxed into M4A)\n"
86" 1: ADIF\n"
87" 2: ADTS\n"
88" 6: LATM MCP=1\n"
89" 7: LATM MCP=0\n"
90" 10: LOAS/LATM (LATM within LOAS)\n"
91" -c, --adts-crc-check Add CRC protection on ADTS header\n"
92" -h, --header-period <n> StreamMuxConfig/PCE repetition period in\n"
93" transport layer\n"
94"\n"
95" -o <filename> Output filename\n"
96" --ignore-length Ignore length of WAV header\n"
97"\n"
98"Tagging options:\n"
99" --title <string>\n"
100" --artist <string>\n"
101" --album <string>\n"
102" --genre <string>\n"
103" --date <string>\n"
104" --composer <string>\n"
105" --grouping <string>\n"
106" --comment <string>\n"
107" --album-artist <string>\n"
108" --track <number[/total]>\n"
109" --disk <number[/total]>\n"
110" --tempo <n>\n"
111 , fdkaac_version);
112}
113
114typedef struct aacenc_tag_entry_t {
115 uint32_t tag;
116 const char *data;
117} aacenc_tag_entry_t;
118
119typedef struct aacenc_param_ex_t {
120 AACENC_PARAMS
121
122 char *input_filename;
123 char *output_filename;
124 unsigned ignore_length;
125
126 aacenc_tag_entry_t *tag_table;
127 unsigned tag_count;
128 unsigned tag_table_capacity;
129} aacenc_param_ex_t;
130
131static
132int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
133{
134 int ch;
135 unsigned n;
136 aacenc_tag_entry_t *tag;
137
138 static struct option long_options[] = {
139 { "help", no_argument, 0, 'h' },
140 { "profile", required_argument, 0, 'p' },
141 { "bitrate", required_argument, 0, 'b' },
142 { "biterate-mode", required_argument, 0, 'm' },
143 { "bandwidth", required_argument, 0, 'w' },
144 { "afterburner", required_argument, 0, 'a' },
145 { "lowdelay-sbr", no_argument, 0, 'L' },
146 { "sbr-signaling", required_argument, 0, 's' },
147 { "transport-format", required_argument, 0, 'f' },
148 { "adts-crc-check", no_argument, 0, 'c' },
149 { "header-period", required_argument, 0, 'P' },
150
151 { "ignore-length", no_argument, 0, 'I' },
152
153 { "title", required_argument, 0, M4AF_TAG_TITLE },
154 { "artist", required_argument, 0, M4AF_TAG_ARTIST },
155 { "album", required_argument, 0, M4AF_TAG_ALBUM },
156 { "genre", required_argument, 0, M4AF_TAG_GENRE },
157 { "date", required_argument, 0, M4AF_TAG_DATE },
158 { "composer", required_argument, 0, M4AF_TAG_COMPOSER },
159 { "grouping", required_argument, 0, M4AF_TAG_GROUPING },
160 { "comment", required_argument, 0, M4AF_TAG_COMMENT },
161 { "album-artist", required_argument, 0, M4AF_TAG_ALBUM_ARTIST },
162 { "track", required_argument, 0, M4AF_TAG_TRACK },
163 { "disk", required_argument, 0, M4AF_TAG_DISK },
164 { "tempo", required_argument, 0, M4AF_TAG_TEMPO },
165 };
166 params->afterburner = 1;
167
168 aacenc_getmainargs(&argc, &argv);
169 while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:cP:Io:",
170 long_options, 0)) != EOF) {
171 switch (ch) {
172 case 'h':
173 return usage(), -1;
174 case 'p':
175 if (sscanf(optarg, "%u", &n) != 1) {
176 fprintf(stderr, "invalid arg for profile\n");
177 return -1;
178 }
179 params->profile = n;
180 break;
181 case 'b':
182 if (sscanf(optarg, "%u", &n) != 1) {
183 fprintf(stderr, "invalid arg for bitrate\n");
184 return -1;
185 }
186 params->bitrate = n;
187 break;
188 case 'm':
189 if (sscanf(optarg, "%u", &n) != 1 || n > 5) {
190 fprintf(stderr, "invalid arg for bitrate-mode\n");
191 return -1;
192 }
193 params->bitrate_mode = n;
194 break;
195 case 'w':
196 if (sscanf(optarg, "%u", &n) != 1) {
197 fprintf(stderr, "invalid arg for bandwidth\n");
198 return -1;
199 }
200 params->bandwidth = n;
201 break;
202 case 'a':
203 if (sscanf(optarg, "%u", &n) != 1 || n > 1) {
204 fprintf(stderr, "invalid arg for afterburner\n");
205 return -1;
206 }
207 params->afterburner = n;
208 break;
209 case 'L':
210 params->lowdelay_sbr = 1;
211 break;
212 case 's':
213 if (sscanf(optarg, "%u", &n) != 1 || n > 2) {
214 fprintf(stderr, "invalid arg for sbr-signaling\n");
215 return -1;
216 }
217 params->sbr_signaling = n;
218 break;
219 case 'f':
220 if (sscanf(optarg, "%u", &n) != 1) {
221 fprintf(stderr, "invalid arg for transport-format\n");
222 return -1;
223 }
224 params->transport_format = n;
225 break;
226 case 'c':
227 params->adts_crc_check = 1;
228 break;
229 case 'P':
230 if (sscanf(optarg, "%u", &n) != 1) {
231 fprintf(stderr, "invalid arg for header-period\n");
232 return -1;
233 }
234 params->header_period = n;
235 break;
236 case 'o':
237 params->output_filename = optarg;
238 break;
239 case 'I':
240 params->ignore_length = 1;
241 break;
242 case M4AF_TAG_TITLE:
243 case M4AF_TAG_ARTIST:
244 case M4AF_TAG_ALBUM:
245 case M4AF_TAG_GENRE:
246 case M4AF_TAG_DATE:
247 case M4AF_TAG_COMPOSER:
248 case M4AF_TAG_GROUPING:
249 case M4AF_TAG_COMMENT:
250 case M4AF_TAG_ALBUM_ARTIST:
251 case M4AF_TAG_TRACK:
252 case M4AF_TAG_DISK:
253 case M4AF_TAG_TEMPO:
254 if (params->tag_count == params->tag_table_capacity) {
255 unsigned newsize = params->tag_table_capacity;
256 newsize = newsize ? newsize * 2 : 1;
257 params->tag_table =
258 realloc(params->tag_table,
259 newsize * sizeof(aacenc_tag_entry_t));
260 params->tag_table_capacity = newsize;
261 }
262 tag = params->tag_table + params->tag_count;
263 tag->tag = ch;
264 tag->data = optarg;
265 params->tag_count++;
266 break;
267 default:
268 return usage(), -1;
269 }
270 }
271 if (argc == optind)
272 return usage(), -1;
273 if (!params->bitrate && !params->bitrate_mode) {
274 fprintf(stderr, "bitrate or bitrate-mode is mandatory\n");
275 return -1;
276 }
277 if (params->output_filename && !strcmp(params->output_filename, "-") &&
278 !params->transport_format) {
279 fprintf(stderr, "stdout streaming is not available on M4A output\n");
280 return -1;
281 }
282 if (params->bitrate && params->bitrate < 10000)
283 params->bitrate *= 1000;
284 params->input_filename = argv[optind];
285 return 0;
286};
287
288static
289int write_sample(FILE *ofp, m4af_writer_t *m4af,
290 const void *data, uint32_t size, uint32_t duration)
291{
292 if (!m4af) {
293 if (fwrite(data, 1, size, ofp) < 0) {
294 fprintf(stderr, "ERROR: fwrite(): %s\n", strerror(errno));
295 return -1;
296 }
297 } else if (m4af_write_sample(m4af, 0, data, size, duration) < 0) {
298 fprintf(stderr, "ERROR: failed to write m4a sample\n");
299 return -1;
300 }
301 return 0;
302}
303
304static
305int encode(wav_reader_t *wavf, HANDLE_AACENCODER encoder,
306 uint32_t frame_length, FILE *ofp, m4af_writer_t *m4af)
307{
308 uint8_t *ibuf = 0;
309 int16_t *pcmbuf = 0;
310 uint32_t pcmsize = 0;
311 uint8_t *obuf = 0;
312 uint32_t olen;
313 uint32_t osize = 0;
314 int nread = 1;
315 int consumed;
316 int rc = -1;
317 int frames_written = 0;
318 aacenc_progress_t progress = { 0 };
319 const pcm_sample_description_t *format = wav_get_format(wavf);
320
321 ibuf = malloc(frame_length * format->bytes_per_frame);
322 aacenc_progress_init(&progress, wav_get_length(wavf), format->sample_rate);
323 do {
324 if (nread) {
325 if ((nread = wav_read_frames(wavf, ibuf, frame_length)) < 0) {
326 fprintf(stderr, "ERROR: read failed\n");
327 goto END;
328 } else if (nread > 0) {
329 if (pcm_convert_to_native_sint16(format, ibuf, nread,
330 &pcmbuf, &pcmsize) < 0) {
331 fprintf(stderr, "ERROR: unsupported sample format\n");
332 goto END;
333 }
334 }
335 aacenc_progress_update(&progress, wav_get_position(wavf),
336 format->sample_rate * 2);
337 }
338 if ((consumed = aac_encode_frame(encoder, format, pcmbuf, nread,
339 &obuf, &olen, &osize)) < 0)
340 goto END;
341 if (olen > 0) {
342 if (write_sample(ofp, m4af, obuf, olen, frame_length) < 0)
343 goto END;
344 ++frames_written;
345 }
346 } while (nread > 0 || olen > 0);
347 aacenc_progress_finish(&progress, wav_get_position(wavf));
348 rc = frames_written;
349END:
350 if (ibuf) free(ibuf);
351 if (pcmbuf) free(pcmbuf);
352 if (obuf) free(obuf);
353 return rc;
354}
355
356static
357int finalize_m4a(m4af_writer_t *m4af, const aacenc_param_ex_t *params,
358 HANDLE_AACENCODER encoder)
359{
360 unsigned i;
361 aacenc_tag_entry_t *tag = params->tag_table;
362
363 for (i = 0; i < params->tag_count; ++i, ++tag) {
364 switch (tag->tag) {
365 case M4AF_TAG_TRACK:
366 {
367 unsigned m, n = 0;
368 if (sscanf(tag->data, "%u/%u", &m, &n) >= 1)
369 m4af_add_itmf_track_tag(m4af, m, n);
370 break;
371 }
372 case M4AF_TAG_DISK:
373 {
374 unsigned m, n = 0;
375 if (sscanf(tag->data, "%u/%u", &m, &n) >= 1)
376 m4af_add_itmf_disk_tag(m4af, m, n);
377 break;
378 }
379 case M4AF_TAG_TEMPO:
380 {
381 unsigned n;
382 if (sscanf(tag->data, "%u", &n) == 1)
383 m4af_add_itmf_int16_tag(m4af, tag->tag, n);
384 break;
385 }
386 default:
387 {
388 char *u8 = aacenc_to_utf8(tag->data);
389 m4af_add_itmf_string_tag(m4af, tag->tag, u8);
390 free(u8);
391 }
392 }
393 }
394 {
395 char tool_info[256];
396 char *p = tool_info;
397 LIB_INFO *lib_info = 0;
398
399 p += sprintf(p, PROGNAME " %s, ", fdkaac_version);
400
401 lib_info = malloc(FDK_MODULE_LAST * sizeof(LIB_INFO));
402 /* XXX: aacEncGetLibInfo() seems buggy and sometimes fails */
403 if (aacEncGetLibInfo(lib_info) == AACENC_OK) {
404 for (i = 0; i < FDK_MODULE_LAST; ++i)
405 if (lib_info[i].module_id == FDK_AACENC)
406 break;
407 p += sprintf(p, "libfdk-aac %s, ", lib_info[i].versionStr);
408 }
409 free(lib_info);
410 if (params->bitrate_mode)
411 sprintf(p, "VBR mode %d", params->bitrate_mode);
412 else
413 sprintf(p, "CBR %dkbps", params->bitrate / 1000);
414
415 m4af_add_itmf_string_tag(m4af, M4AF_TAG_TOOL, tool_info);
416 }
417 if (m4af_finalize(m4af) < 0) {
418 fprintf(stderr, "ERROR: failed to finalize m4a\n");
419 return -1;
420 }
421 return 0;
422}
423
424int main(int argc, char **argv)
425{
426 wav_io_context_t wav_io = { read_callback, seek_callback };
427 m4af_io_callbacks_t m4af_io = {
428 write_callback, seek_callback, tell_callback };
429 aacenc_param_ex_t params = { 0 };
430
431 int result = 2;
432 FILE *ifp = 0;
433 FILE *ofp = 0;
434 char *output_filename = 0;
435 wav_reader_t *wavf = 0;
436 HANDLE_AACENCODER encoder = 0;
437 AACENC_InfoStruct aacinfo = { 0 };
438 m4af_writer_t *m4af = 0;
439 const pcm_sample_description_t *sample_format;
440 int downsampled_timescale = 0;
441 int frame_count = 0;
442
443 setlocale(LC_CTYPE, "");
444 setbuf(stderr, 0);
445
446 if (parse_options(argc, argv, &params) < 0)
447 return 1;
448
449 if ((ifp = aacenc_fopen(params.input_filename, "rb")) == 0) {
450 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.input_filename,
451 strerror(errno));
452 goto END;
453 }
454
455 if (ifp == stdin)
456 wav_io.seek = 0;
457
458 if ((wavf = wav_open(&wav_io, ifp, params.ignore_length)) == 0) {
459 fprintf(stderr, "ERROR: broken / unsupported input file\n");
460 goto END;
461 }
462 sample_format = wav_get_format(wavf);
463
464 if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
465 &aacinfo) < 0)
466 goto END;
467
468 if (!params.output_filename) {
469 size_t ilen = strlen(params.input_filename);
470 const char *ext = strrchr(params.input_filename, '.');
471 if (ext) ilen = ext - params.input_filename;
472 output_filename = malloc(ilen + 5);
473 sprintf(output_filename, "%.*s%s", ilen, params.input_filename,
474 params.transport_format ? ".aac" : ".m4a");
475 params.output_filename = output_filename;
476 }
477
478 if ((ofp = aacenc_fopen(params.output_filename, "wb")) == 0) {
479 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
480 strerror(errno));
481 goto END;
482 }
483 if (!params.transport_format) {
484 uint32_t scale;
485 unsigned framelen = aacinfo.frameLength;
486 int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params);
487 int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE);
488 if (sbr_mode && !sig_mode)
489 downsampled_timescale = 1;
490 scale = sample_format->sample_rate >> downsampled_timescale;
491 if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io, ofp)) < 0)
492 goto END;
493 m4af_decoder_specific_info(m4af, 0, aacinfo.confBuf, aacinfo.confSize);
494 m4af_set_fixed_frame_duration(m4af, 0,
495 framelen >> downsampled_timescale);
496 m4af_begin_write(m4af);
497 }
498 frame_count = encode(wavf, encoder, aacinfo.frameLength, ofp, m4af);
499 if (frame_count < 0)
500 goto END;
501 if (m4af) {
502 uint32_t delay = aacinfo.encoderDelay;
503 int64_t frames_read = wav_get_position(wavf);
504 uint32_t padding = frame_count * aacinfo.frameLength
505 - frames_read - aacinfo.encoderDelay;
506 m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
507 padding >> downsampled_timescale);
508 if (finalize_m4a(m4af, &params, encoder) < 0)
509 goto END;
510 }
511 result = 0;
512END:
513 if (wavf) wav_teardown(&wavf);
514 if (ifp) fclose(ifp);
515 if (m4af) m4af_teardown(&m4af);
516 if (ofp) fclose(ofp);
517 if (encoder) aacEncClose(&encoder);
518 if (output_filename) free(output_filename);
519 if (params.tag_table) free(params.tag_table);
520
521 return result;
522}
This page took 0.093821 seconds and 4 git commands to generate.