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