fix help message: show -I as shorthand for --ignorelength
[fdkaac.git] / src / main.c
... / ...
CommitLineData
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#if HAVE_INTTYPES_H
12# include <inttypes.h>
13#elif defined(_MSC_VER)
14# define SCNd64 "I64d"
15#endif
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <ctype.h>
20#include <locale.h>
21#include <errno.h>
22#include <sys/stat.h>
23#include <getopt.h>
24#if HAVE_UNISTD_H
25#include <unistd.h>
26#endif
27#if HAVE_SIGACTION
28#include <signal.h>
29#endif
30#ifdef _WIN32
31#include <io.h>
32#define WIN32_LEAN_AND_MEAN
33#include <windows.h>
34#endif
35#include "compat.h"
36#include "wav_reader.h"
37#include "caf_reader.h"
38#include "aacenc.h"
39#include "m4af.h"
40#include "progress.h"
41#include "version.h"
42#include "metadata.h"
43
44#define PROGNAME "fdkaac"
45
46static volatile int g_interrupted = 0;
47
48#if HAVE_SIGACTION
49static void signal_handler(int signum)
50{
51 g_interrupted = 1;
52}
53static void handle_signals(void)
54{
55 int i, sigs[] = { SIGINT, SIGHUP, SIGTERM };
56 for (i = 0; i < sizeof(sigs)/sizeof(sigs[0]); ++i) {
57 struct sigaction sa = { 0 };
58 sa.sa_handler = signal_handler;
59 sa.sa_flags |= SA_RESTART;
60 sigaction(sigs[i], &sa, 0);
61 }
62}
63#elif defined(_WIN32)
64static BOOL WINAPI signal_handler(DWORD type)
65{
66 g_interrupted = 1;
67 return TRUE;
68}
69
70static void handle_signals(void)
71{
72 SetConsoleCtrlHandler(signal_handler, TRUE);
73}
74#else
75static void handle_signals(void)
76{
77}
78#endif
79
80static
81int read_callback(void *cookie, void *data, uint32_t size)
82{
83 size_t rc = fread(data, 1, size, (FILE*)cookie);
84 return ferror((FILE*)cookie) ? -1 : (int)rc;
85}
86
87static
88int write_callback(void *cookie, const void *data, uint32_t size)
89{
90 size_t rc = fwrite(data, 1, size, (FILE*)cookie);
91 return ferror((FILE*)cookie) ? -1 : (int)rc;
92}
93
94static
95int seek_callback(void *cookie, int64_t off, int whence)
96{
97 return fseeko((FILE*)cookie, off, whence);
98}
99
100static
101int64_t tell_callback(void *cookie)
102{
103 return ftello((FILE*)cookie);
104}
105
106static
107void usage(void)
108{
109 printf(
110PROGNAME " %s\n"
111"Usage: " PROGNAME " [options] input_file\n"
112"Options:\n"
113" -h, --help Print this help message\n"
114" -p, --profile <n> Profile (audio object type)\n"
115" 2: MPEG-4 AAC LC (default)\n"
116" 5: MPEG-4 HE-AAC (SBR)\n"
117" 29: MPEG-4 HE-AAC v2 (SBR+PS)\n"
118" 23: MPEG-4 AAC LD\n"
119" 39: MPEG-4 AAC ELD\n"
120" 129: MPEG-2 AAC LC\n"
121" 132: MPEG-2 HE-AAC (SBR)\n"
122" 156: MPEG-2 HE-AAC v2 (SBR+PS)\n"
123" -b, --bitrate <n> Bitrate in bits per seconds (for CBR)\n"
124" -m, --bitrate-mode <n> Bitrate configuration\n"
125" 0: CBR (default)\n"
126" 1-5: VBR\n"
127" (VBR mode is not officially supported, and\n"
128" works only on a certain combination of\n"
129" parameter settings, sample rate, and\n"
130" channel configuration)\n"
131" -w, --bandwidth <n> Frequency bandwidth in Hz (AAC LC only)\n"
132" -a, --afterburner <n> Afterburner\n"
133" 0: Off\n"
134" 1: On(default)\n"
135" -L, --lowdelay-sbr Enable ELD-SBR (AAC ELD only)\n"
136" -f, --transport-format <n> Transport format\n"
137" 0: RAW (default, muxed into M4A)\n"
138" 1: ADIF\n"
139" 2: ADTS\n"
140" 6: LATM MCP=1\n"
141" 7: LATM MCP=0\n"
142" 10: LOAS/LATM (LATM within LOAS)\n"
143" -C, --adts-crc-check Add CRC protection on ADTS header\n"
144" -h, --header-period <n> StreamMuxConfig/PCE repetition period in\n"
145" transport layer\n"
146"\n"
147" -o <filename> Output filename\n"
148" -G, --gapless-mode <n> Encoder delay signaling for gapless playback\n"
149" 0: iTunSMPB (default)\n"
150" 1: ISO standard (edts + sgpd)\n"
151" 2: Both\n"
152" -I, --ignorelength Ignore length of WAV header\n"
153" -S, --silent Don't print progress messages\n"
154" --moov-before-mdat Place moov box before mdat box on m4a output\n"
155"\n"
156"Options for raw (headerless) input:\n"
157" -R, --raw Treat input as raw (by default WAV is\n"
158" assumed)\n"
159" --raw-channels <n> Number of channels (default: 2)\n"
160" --raw-rate <n> Sample rate (default: 44100)\n"
161" --raw-format <spec> Sample format, default is \"S16L\".\n"
162" Spec is as follows:\n"
163" 1st char: S(igned)|U(nsigned)|F(loat)\n"
164" 2nd part: bits per channel\n"
165" Last char: L(ittle)|B(ig)\n"
166" Last char can be omitted, in which case L is\n"
167" assumed. Spec is case insensitive, therefore\n"
168" \"u16b\" is same as \"U16B\".\n"
169"\n"
170"Tagging options:\n"
171" --title <string>\n"
172" --artist <string>\n"
173" --album <string>\n"
174" --genre <string>\n"
175" --date <string>\n"
176" --composer <string>\n"
177" --grouping <string>\n"
178" --comment <string>\n"
179" --album-artist <string>\n"
180" --track <number[/total]>\n"
181" --disk <number[/total]>\n"
182" --tempo <n>\n"
183" --tag <fcc>:<value> Set iTunes predefined tag with four char code.\n"
184" --tag-from-file <fcc>:<filename>\n"
185" Same as above, but value is read from file.\n"
186" --long-tag <name>:<value> Set arbitrary tag as iTunes custom metadata.\n"
187" --tag-from-json <filename[?dot_notation]>\n"
188" Read tags from JSON. By default, tags are\n"
189" assumed to be direct children of the root\n"
190" object(dictionary).\n"
191" Optionally, position of the dictionary\n"
192" that contains tags can be specified with\n"
193" dotted notation.\n"
194" Example:\n"
195" --tag-from-json /path/to/json?format.tags\n"
196 , fdkaac_version);
197}
198
199typedef struct aacenc_param_ex_t {
200 AACENC_PARAMS
201
202 char *input_filename;
203 FILE *input_fp;
204 char *output_filename;
205 FILE *output_fp;
206 unsigned gapless_mode;
207 unsigned ignore_length;
208 int silent;
209 int moov_before_mdat;
210
211 int is_raw;
212 unsigned raw_channels;
213 unsigned raw_rate;
214 const char *raw_format;
215
216 aacenc_tag_store_t tags;
217 aacenc_tag_store_t source_tags;
218 aacenc_translate_generic_text_tag_ctx_t source_tag_ctx;
219
220 char *json_filename;
221} aacenc_param_ex_t;
222
223static
224int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
225{
226 int ch;
227 unsigned n;
228
229#define OPT_MOOV_BEFORE_MDAT M4AF_FOURCC('m','o','o','v')
230#define OPT_RAW_CHANNELS M4AF_FOURCC('r','c','h','n')
231#define OPT_RAW_RATE M4AF_FOURCC('r','r','a','t')
232#define OPT_RAW_FORMAT M4AF_FOURCC('r','f','m','t')
233#define OPT_SHORT_TAG M4AF_FOURCC('s','t','a','g')
234#define OPT_SHORT_TAG_FILE M4AF_FOURCC('s','t','g','f')
235#define OPT_LONG_TAG M4AF_FOURCC('l','t','a','g')
236#define OPT_TAG_FROM_JSON M4AF_FOURCC('t','f','j','s')
237
238 static struct option long_options[] = {
239 { "help", no_argument, 0, 'h' },
240 { "profile", required_argument, 0, 'p' },
241 { "bitrate", required_argument, 0, 'b' },
242 { "bitrate-mode", required_argument, 0, 'm' },
243 { "bandwidth", required_argument, 0, 'w' },
244 { "afterburner", required_argument, 0, 'a' },
245 { "lowdelay-sbr", no_argument, 0, 'L' },
246 { "transport-format", required_argument, 0, 'f' },
247 { "adts-crc-check", no_argument, 0, 'C' },
248 { "header-period", required_argument, 0, 'P' },
249
250 { "gapless-mode", required_argument, 0, 'G' },
251 { "ignorelength", no_argument, 0, 'I' },
252 { "silent", no_argument, 0, 'S' },
253 { "moov-before-mdat", no_argument, 0, OPT_MOOV_BEFORE_MDAT },
254
255 { "raw", no_argument, 0, 'R' },
256 { "raw-channels", required_argument, 0, OPT_RAW_CHANNELS },
257 { "raw-rate", required_argument, 0, OPT_RAW_RATE },
258 { "raw-format", required_argument, 0, OPT_RAW_FORMAT },
259
260 { "title", required_argument, 0, M4AF_TAG_TITLE },
261 { "artist", required_argument, 0, M4AF_TAG_ARTIST },
262 { "album", required_argument, 0, M4AF_TAG_ALBUM },
263 { "genre", required_argument, 0, M4AF_TAG_GENRE },
264 { "date", required_argument, 0, M4AF_TAG_DATE },
265 { "composer", required_argument, 0, M4AF_TAG_COMPOSER },
266 { "grouping", required_argument, 0, M4AF_TAG_GROUPING },
267 { "comment", required_argument, 0, M4AF_TAG_COMMENT },
268 { "album-artist", required_argument, 0, M4AF_TAG_ALBUM_ARTIST },
269 { "track", required_argument, 0, M4AF_TAG_TRACK },
270 { "disk", required_argument, 0, M4AF_TAG_DISK },
271 { "tempo", required_argument, 0, M4AF_TAG_TEMPO },
272 { "tag", required_argument, 0, OPT_SHORT_TAG },
273 { "tag-from-file", required_argument, 0, OPT_SHORT_TAG_FILE },
274 { "long-tag", required_argument, 0, OPT_LONG_TAG },
275 { "tag-from-json", required_argument, 0, OPT_TAG_FROM_JSON },
276 { 0, 0, 0, 0 },
277 };
278 params->afterburner = 1;
279
280 aacenc_getmainargs(&argc, &argv);
281 while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:CP:G:Io:SR",
282 long_options, 0)) != EOF) {
283 switch (ch) {
284 case 'h':
285 return usage(), -1;
286 case 'p':
287 if (sscanf(optarg, "%u", &n) != 1) {
288 fprintf(stderr, "invalid arg for profile\n");
289 return -1;
290 }
291 params->profile = n;
292 break;
293 case 'b':
294 if (sscanf(optarg, "%u", &n) != 1) {
295 fprintf(stderr, "invalid arg for bitrate\n");
296 return -1;
297 }
298 params->bitrate = n;
299 break;
300 case 'm':
301 if (sscanf(optarg, "%u", &n) != 1 || n > 5) {
302 fprintf(stderr, "invalid arg for bitrate-mode\n");
303 return -1;
304 }
305 params->bitrate_mode = n;
306 break;
307 case 'w':
308 if (sscanf(optarg, "%u", &n) != 1) {
309 fprintf(stderr, "invalid arg for bandwidth\n");
310 return -1;
311 }
312 params->bandwidth = n;
313 break;
314 case 'a':
315 if (sscanf(optarg, "%u", &n) != 1 || n > 1) {
316 fprintf(stderr, "invalid arg for afterburner\n");
317 return -1;
318 }
319 params->afterburner = n;
320 break;
321 case 'L':
322 params->lowdelay_sbr = 1;
323 break;
324 case 'f':
325 if (sscanf(optarg, "%u", &n) != 1) {
326 fprintf(stderr, "invalid arg for transport-format\n");
327 return -1;
328 }
329 params->transport_format = n;
330 break;
331 case 'C':
332 params->adts_crc_check = 1;
333 break;
334 case 'P':
335 if (sscanf(optarg, "%u", &n) != 1) {
336 fprintf(stderr, "invalid arg for header-period\n");
337 return -1;
338 }
339 params->header_period = n;
340 break;
341 case 'o':
342 params->output_filename = optarg;
343 break;
344 case 'G':
345 if (sscanf(optarg, "%u", &n) != 1 || n > 2) {
346 fprintf(stderr, "invalid arg for gapless-mode\n");
347 return -1;
348 }
349 params->gapless_mode = n;
350 break;
351 case 'I':
352 params->ignore_length = 1;
353 break;
354 case 'S':
355 params->silent = 1;
356 break;
357 case OPT_MOOV_BEFORE_MDAT:
358 params->moov_before_mdat = 1;
359 break;
360 case 'R':
361 params->is_raw = 1;
362 break;
363 case OPT_RAW_CHANNELS:
364 if (sscanf(optarg, "%u", &n) != 1) {
365 fprintf(stderr, "invalid arg for raw-channels\n");
366 return -1;
367 }
368 params->raw_channels = n;
369 break;
370 case OPT_RAW_RATE:
371 if (sscanf(optarg, "%u", &n) != 1) {
372 fprintf(stderr, "invalid arg for raw-rate\n");
373 return -1;
374 }
375 params->raw_rate = n;
376 break;
377 case OPT_RAW_FORMAT:
378 params->raw_format = optarg;
379 break;
380 case M4AF_TAG_TITLE:
381 case M4AF_TAG_ARTIST:
382 case M4AF_TAG_ALBUM:
383 case M4AF_TAG_GENRE:
384 case M4AF_TAG_DATE:
385 case M4AF_TAG_COMPOSER:
386 case M4AF_TAG_GROUPING:
387 case M4AF_TAG_COMMENT:
388 case M4AF_TAG_ALBUM_ARTIST:
389 case M4AF_TAG_TRACK:
390 case M4AF_TAG_DISK:
391 case M4AF_TAG_TEMPO:
392 aacenc_add_tag_to_store(&params->tags, ch, 0, optarg,
393 strlen(optarg), 0);
394 break;
395 case OPT_SHORT_TAG:
396 case OPT_SHORT_TAG_FILE:
397 case OPT_LONG_TAG:
398 {
399 char *val;
400 size_t klen;
401 unsigned fcc = M4AF_FOURCC('-','-','-','-');
402
403 if ((val = strchr(optarg, ':')) == 0) {
404 fprintf(stderr, "invalid arg for tag\n");
405 return -1;
406 }
407 *val++ = '\0';
408 if (ch == OPT_SHORT_TAG || ch == OPT_SHORT_TAG_FILE) {
409 /*
410 * take care of U+00A9(COPYRIGHT SIGN).
411 * 1) if length of fcc is 3, we prepend '\xa9'.
412 * 2) U+00A9 becomes "\xc2\xa9" in UTF-8. Therefore
413 * we remove first '\xc2'.
414 */
415 if (optarg[0] == '\xc2')
416 ++optarg;
417 if ((klen = strlen(optarg))== 3)
418 fcc = 0xa9;
419 else if (klen != 4) {
420 fprintf(stderr, "invalid arg for tag\n");
421 return -1;
422 }
423 for (; *optarg; ++optarg)
424 fcc = ((fcc << 8) | (*optarg & 0xff));
425 }
426 aacenc_add_tag_to_store(&params->tags, fcc, optarg,
427 val, strlen(val),
428 ch == OPT_SHORT_TAG_FILE);
429 }
430 break;
431 case OPT_TAG_FROM_JSON:
432 params->json_filename = optarg;
433 break;
434 default:
435 return usage(), -1;
436 }
437 }
438 if (argc == optind)
439 return usage(), -1;
440
441 if (!params->bitrate && !params->bitrate_mode) {
442 fprintf(stderr, "bitrate or bitrate-mode is mandatory\n");
443 return -1;
444 }
445 if (params->output_filename && !strcmp(params->output_filename, "-") &&
446 !params->transport_format) {
447 fprintf(stderr, "stdout streaming is not available on M4A output\n");
448 return -1;
449 }
450 if (params->bitrate && params->bitrate < 10000)
451 params->bitrate *= 1000;
452
453 if (params->is_raw) {
454 if (!params->raw_channels)
455 params->raw_channels = 2;
456 if (!params->raw_rate)
457 params->raw_rate = 44100;
458 if (!params->raw_format)
459 params->raw_format = "S16L";
460 }
461 params->input_filename = argv[optind];
462 return 0;
463};
464
465static
466int write_sample(FILE *ofp, m4af_ctx_t *m4af,
467 const void *data, uint32_t size, uint32_t duration)
468{
469 if (!m4af) {
470 fwrite(data, 1, size, ofp);
471 if (ferror(ofp)) {
472 fprintf(stderr, "ERROR: fwrite(): %s\n", strerror(errno));
473 return -1;
474 }
475 } else if (m4af_write_sample(m4af, 0, data, size, duration) < 0) {
476 fprintf(stderr, "ERROR: failed to write m4a sample\n");
477 return -1;
478 }
479 return 0;
480}
481
482static
483int encode(pcm_reader_t *reader, HANDLE_AACENCODER encoder,
484 uint32_t frame_length, FILE *ofp, m4af_ctx_t *m4af,
485 int show_progress)
486{
487 int16_t *ibuf = 0;
488 uint8_t *obuf = 0;
489 uint32_t olen;
490 uint32_t osize = 0;
491 int nread = 1;
492 int consumed;
493 int rc = -1;
494 int frames_written = 0;
495 aacenc_progress_t progress = { 0 };
496 const pcm_sample_description_t *fmt = pcm_get_format(reader);
497
498 ibuf = malloc(frame_length * fmt->bytes_per_frame);
499 aacenc_progress_init(&progress, pcm_get_length(reader), fmt->sample_rate);
500 do {
501 if (g_interrupted)
502 nread = 0;
503 else if (nread) {
504 if ((nread = pcm_read_frames(reader, ibuf, frame_length)) < 0) {
505 fprintf(stderr, "ERROR: read failed\n");
506 goto END;
507 }
508 if (show_progress)
509 aacenc_progress_update(&progress, pcm_get_position(reader),
510 fmt->sample_rate * 2);
511 }
512 if ((consumed = aac_encode_frame(encoder, fmt, ibuf, nread,
513 &obuf, &olen, &osize)) < 0)
514 goto END;
515 if (olen > 0) {
516 if (write_sample(ofp, m4af, obuf, olen, frame_length) < 0)
517 goto END;
518 ++frames_written;
519 }
520 } while (nread > 0 || olen > 0);
521
522 if (show_progress)
523 aacenc_progress_finish(&progress, pcm_get_position(reader));
524 rc = frames_written;
525END:
526 if (ibuf) free(ibuf);
527 if (obuf) free(obuf);
528 return rc;
529}
530
531static
532void put_tool_tag(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
533 HANDLE_AACENCODER encoder)
534{
535 char tool_info[256];
536 char *p = tool_info;
537 LIB_INFO *lib_info = 0;
538
539 p += sprintf(p, PROGNAME " %s, ", fdkaac_version);
540
541 lib_info = calloc(FDK_MODULE_LAST, sizeof(LIB_INFO));
542 if (aacEncGetLibInfo(lib_info) == AACENC_OK) {
543 int i;
544 for (i = 0; i < FDK_MODULE_LAST; ++i)
545 if (lib_info[i].module_id == FDK_AACENC)
546 break;
547 p += sprintf(p, "libfdk-aac %s, ", lib_info[i].versionStr);
548 }
549 free(lib_info);
550 if (params->bitrate_mode)
551 sprintf(p, "VBR mode %d", params->bitrate_mode);
552 else
553 sprintf(p, "CBR %dkbps",
554 aacEncoder_GetParam(encoder, AACENC_BITRATE) / 1000);
555
556 m4af_add_itmf_string_tag(m4af, M4AF_TAG_TOOL, tool_info);
557}
558
559static
560int finalize_m4a(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
561 HANDLE_AACENCODER encoder)
562{
563 unsigned i;
564 aacenc_tag_entry_t *tag;
565
566 tag = params->source_tags.tag_table;
567 for (i = 0; i < params->source_tags.tag_count; ++i, ++tag)
568 aacenc_write_tag_entry(m4af, tag);
569
570 if (params->json_filename)
571 aacenc_write_tags_from_json(m4af, params->json_filename);
572
573 tag = params->tags.tag_table;
574 for (i = 0; i < params->tags.tag_count; ++i, ++tag)
575 aacenc_write_tag_entry(m4af, tag);
576
577 put_tool_tag(m4af, params, encoder);
578
579 if (m4af_finalize(m4af, params->moov_before_mdat) < 0) {
580 fprintf(stderr, "ERROR: failed to finalize m4a\n");
581 return -1;
582 }
583 return 0;
584}
585
586static
587char *generate_output_filename(const char *filename, const char *ext)
588{
589 char *p = 0;
590 size_t ext_len = strlen(ext);
591
592 if (strcmp(filename, "-") == 0) {
593 p = malloc(ext_len + 6);
594 sprintf(p, "stdin%s", ext);
595 } else {
596 const char *base = aacenc_basename(filename);
597 size_t ilen = strlen(base);
598 const char *ext_org = strrchr(base, '.');
599 if (ext_org) ilen = ext_org - base;
600 p = malloc(ilen + ext_len + 1);
601 sprintf(p, "%.*s%s", ilen, base, ext);
602 }
603 return p;
604}
605
606static
607int parse_raw_spec(const char *spec, pcm_sample_description_t *desc)
608{
609 unsigned bits;
610 unsigned char c_type, c_endian = 'L';
611 int type;
612
613 if (sscanf(spec, "%c%u%c", &c_type, &bits, &c_endian) < 2)
614 return -1;
615 c_type = toupper(c_type);
616 c_endian = toupper(c_endian);
617
618 if (c_type == 'S')
619 type = 1;
620 else if (c_type == 'U')
621 type = 2;
622 else if (c_type == 'F')
623 type = 4;
624 else
625 return -1;
626
627 if (c_endian == 'B')
628 type |= 8;
629 else if (c_endian != 'L')
630 return -1;
631
632 if (c_type == 'F' && bits != 32 && bits != 64)
633 return -1;
634 if (c_type != 'F' && (bits < 8 || bits > 32))
635 return -1;
636
637 desc->sample_type = type;
638 desc->bits_per_channel = bits;
639 return 0;
640}
641
642static pcm_io_vtbl_t pcm_io_vtbl = {
643 read_callback, seek_callback, tell_callback
644};
645static pcm_io_vtbl_t pcm_io_vtbl_noseek = { read_callback, 0, 0 };
646
647static
648pcm_reader_t *open_input(aacenc_param_ex_t *params)
649{
650 pcm_io_context_t io = { 0 };
651 pcm_reader_t *reader = 0;
652 struct stat stb = { 0 };
653
654 if ((params->input_fp = aacenc_fopen(params->input_filename, "rb")) == 0) {
655 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params->input_filename,
656 strerror(errno));
657 goto END;
658 }
659 io.cookie = params->input_fp;
660 if (fstat(fileno(params->input_fp), &stb) == 0
661 && (stb.st_mode & S_IFMT) == S_IFREG)
662 io.vtbl = &pcm_io_vtbl;
663 else
664 io.vtbl = &pcm_io_vtbl_noseek;
665
666 if (params->is_raw) {
667 int bytes_per_channel;
668 pcm_sample_description_t desc = { 0 };
669 if (parse_raw_spec(params->raw_format, &desc) < 0) {
670 fprintf(stderr, "ERROR: invalid raw-format spec\n");
671 goto END;
672 }
673 desc.sample_rate = params->raw_rate;
674 desc.channels_per_frame = params->raw_channels;
675 bytes_per_channel = (desc.bits_per_channel + 7) / 8;
676 desc.bytes_per_frame = params->raw_channels * bytes_per_channel;
677 if ((reader = raw_open(&io, &desc)) == 0) {
678 fprintf(stderr, "ERROR: failed to open raw input\n");
679 goto END;
680 }
681 } else {
682 int c;
683 ungetc(c = getc(params->input_fp), params->input_fp);
684
685 switch (c) {
686 case 'R':
687 if ((reader = wav_open(&io, params->ignore_length)) == 0) {
688 fprintf(stderr, "ERROR: broken / unsupported input file\n");
689 goto END;
690 }
691 break;
692 case 'c':
693 params->source_tag_ctx.add = aacenc_add_tag_entry_to_store;
694 params->source_tag_ctx.add_ctx = &params->source_tags;
695 if ((reader = caf_open(&io,
696 aacenc_translate_generic_text_tag,
697 &params->source_tag_ctx)) == 0) {
698 fprintf(stderr, "ERROR: broken / unsupported input file\n");
699 goto END;
700 }
701 break;
702 default:
703 goto END;
704 }
705 }
706 return pcm_open_sint16_converter(reader);
707END:
708 return 0;
709}
710
711int main(int argc, char **argv)
712{
713 static m4af_io_callbacks_t m4af_io = {
714 read_callback, write_callback, seek_callback, tell_callback
715 };
716 aacenc_param_ex_t params = { 0 };
717
718 int result = 2;
719 char *output_filename = 0;
720 pcm_reader_t *reader = 0;
721 HANDLE_AACENCODER encoder = 0;
722 AACENC_InfoStruct aacinfo = { 0 };
723 m4af_ctx_t *m4af = 0;
724 const pcm_sample_description_t *sample_format;
725 int downsampled_timescale = 0;
726 int frame_count = 0;
727 int sbr_mode = 0;
728
729 setlocale(LC_CTYPE, "");
730 setbuf(stderr, 0);
731
732 if (parse_options(argc, argv, &params) < 0)
733 return 1;
734
735 if ((reader = open_input(&params)) == 0)
736 goto END;
737
738 sample_format = pcm_get_format(reader);
739
740 /*
741 * We use explicit/hierarchical signaling for LOAS.
742 * Other than that, we request implicit signaling to FDK library, then
743 * append explicit/backward-compatible signaling to ASC in case of MP4FF.
744 *
745 * Explicit/backward-compatible signaling of SBR is the most recommended
746 * way in MPEG4 part3 spec, and seems the only way supported by iTunes.
747 * Since FDK library does not support it, we have to do it on our side.
748 */
749 params.sbr_signaling = (params.transport_format == TT_MP4_LOAS) ? 2 : 0;
750
751 if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
752 &aacinfo) < 0)
753 goto END;
754
755 if (!params.output_filename) {
756 const char *ext = params.transport_format ? ".aac" : ".m4a";
757 output_filename = generate_output_filename(params.input_filename, ext);
758 params.output_filename = output_filename;
759 }
760
761 if ((params.output_fp = aacenc_fopen(params.output_filename, "wb+")) == 0) {
762 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
763 strerror(errno));
764 goto END;
765 }
766 handle_signals();
767 sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params);
768 if (!params.transport_format) {
769 uint32_t scale;
770 uint8_t mp4asc[32];
771 uint32_t ascsize = sizeof(mp4asc);
772 unsigned framelen = aacinfo.frameLength;
773 if (sbr_mode)
774 downsampled_timescale = 1;
775 scale = sample_format->sample_rate >> downsampled_timescale;
776 if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io,
777 params.output_fp)) < 0)
778 goto END;
779 aacenc_mp4asc((aacenc_param_t*)&params, aacinfo.confBuf,
780 aacinfo.confSize, mp4asc, &ascsize);
781 m4af_set_decoder_specific_info(m4af, 0, mp4asc, ascsize);
782 m4af_set_fixed_frame_duration(m4af, 0,
783 framelen >> downsampled_timescale);
784 m4af_set_vbr_mode(m4af, 0, params.bitrate_mode);
785 m4af_set_priming_mode(m4af, params.gapless_mode + 1);
786 m4af_begin_write(m4af);
787 }
788 frame_count = encode(reader, encoder, aacinfo.frameLength,
789 params.output_fp, m4af, !params.silent);
790 if (frame_count < 0)
791 goto END;
792 if (m4af) {
793 uint32_t delay = aacinfo.encoderDelay;
794 int64_t frames_read = pcm_get_position(reader);
795 uint32_t padding = frame_count * aacinfo.frameLength
796 - frames_read - aacinfo.encoderDelay;
797 m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
798 padding >> downsampled_timescale);
799 if (finalize_m4a(m4af, &params, encoder) < 0)
800 goto END;
801 }
802 result = 0;
803END:
804 if (reader) pcm_teardown(&reader);
805 if (params.input_fp) fclose(params.input_fp);
806 if (m4af) m4af_teardown(&m4af);
807 if (params.output_fp) fclose(params.output_fp);
808 if (encoder) aacEncClose(&encoder);
809 if (output_filename) free(output_filename);
810 if (params.tags.tag_table)
811 aacenc_free_tag_store(&params.tags);
812 if (params.source_tags.tag_table)
813 aacenc_free_tag_store(&params.source_tags);
814
815 return result;
816}
This page took 0.012993 seconds and 4 git commands to generate.