fix help message: show -I as shorthand for --ignorelength
[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
c5c45908 11#if HAVE_INTTYPES_H
12# include <inttypes.h>
13#elif defined(_MSC_VER)
14# define SCNd64 "I64d"
15#endif
48e2f01c 16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
68543176 19#include <ctype.h>
48e2f01c 20#include <locale.h>
21#include <errno.h>
7222b0b5 22#include <sys/stat.h>
48e2f01c 23#include <getopt.h>
7222b0b5 24#if HAVE_UNISTD_H
25#include <unistd.h>
26#endif
bd3b4b34 27#if HAVE_SIGACTION
28#include <signal.h>
29#endif
7222b0b5 30#ifdef _WIN32
31#include <io.h>
bd3b4b34 32#define WIN32_LEAN_AND_MEAN
33#include <windows.h>
7222b0b5 34#endif
48e2f01c 35#include "compat.h"
36#include "wav_reader.h"
1af8624b 37#include "caf_reader.h"
48e2f01c 38#include "aacenc.h"
39#include "m4af.h"
40#include "progress.h"
41#include "version.h"
cbb23cdb 42#include "metadata.h"
48e2f01c 43
44#define PROGNAME "fdkaac"
45
721d977f 46static volatile int g_interrupted = 0;
bd3b4b34 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
48e2f01c 80static
81int read_callback(void *cookie, void *data, uint32_t size)
82{
5e1168a4 83 size_t rc = fread(data, 1, size, (FILE*)cookie);
84 return ferror((FILE*)cookie) ? -1 : (int)rc;
48e2f01c 85}
86
87static
88int write_callback(void *cookie, const void *data, uint32_t size)
89{
5e1168a4 90 size_t rc = fwrite(data, 1, size, (FILE*)cookie);
91 return ferror((FILE*)cookie) ? -1 : (int)rc;
48e2f01c 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"
097ef9a8 132" -a, --afterburner <n> Afterburner\n"
48e2f01c 133" 0: Off\n"
134" 1: On(default)\n"
135" -L, --lowdelay-sbr Enable ELD-SBR (AAC ELD only)\n"
48e2f01c 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"
68543176 143" -C, --adts-crc-check Add CRC protection on ADTS header\n"
48e2f01c 144" -h, --header-period <n> StreamMuxConfig/PCE repetition period in\n"
145" transport layer\n"
146"\n"
147" -o <filename> Output filename\n"
d317e29d 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"
c9ac59e8 152" -I, --ignorelength Ignore length of WAV header\n"
aa2ca1e3 153" -S, --silent Don't print progress messages\n"
fb2b3635 154" --moov-before-mdat Place moov box before mdat box on m4a output\n"
48e2f01c 155"\n"
68543176 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"
48e2f01c 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"
c5c45908 183" --tag <fcc>:<value> Set iTunes predefined tag with four char code.\n"
a56831da 184" --tag-from-file <fcc>:<filename>\n"
185" Same as above, but value is read from file.\n"
c5c45908 186" --long-tag <name>:<value> Set arbitrary tag as iTunes custom metadata.\n"
cbb23cdb 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"
48e2f01c 196 , fdkaac_version);
197}
198
48e2f01c 199typedef struct aacenc_param_ex_t {
200 AACENC_PARAMS
201
202 char *input_filename;
2d744bd5 203 FILE *input_fp;
48e2f01c 204 char *output_filename;
2d744bd5 205 FILE *output_fp;
d317e29d 206 unsigned gapless_mode;
48e2f01c 207 unsigned ignore_length;
aa2ca1e3 208 int silent;
fb2b3635 209 int moov_before_mdat;
48e2f01c 210
68543176 211 int is_raw;
212 unsigned raw_channels;
213 unsigned raw_rate;
214 const char *raw_format;
215
3b666b75 216 aacenc_tag_store_t tags;
1af8624b 217 aacenc_tag_store_t source_tags;
218 aacenc_translate_generic_text_tag_ctx_t source_tag_ctx;
48e2f01c 219
cbb23cdb 220 char *json_filename;
221} aacenc_param_ex_t;
c5c45908 222
48e2f01c 223static
224int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
225{
226 int ch;
227 unsigned n;
48e2f01c 228
fb2b3635 229#define OPT_MOOV_BEFORE_MDAT M4AF_FOURCC('m','o','o','v')
a56831da 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')
cbb23cdb 236#define OPT_TAG_FROM_JSON M4AF_FOURCC('t','f','j','s')
68543176 237
48e2f01c 238 static struct option long_options[] = {
239 { "help", no_argument, 0, 'h' },
240 { "profile", required_argument, 0, 'p' },
241 { "bitrate", required_argument, 0, 'b' },
34b319e0 242 { "bitrate-mode", required_argument, 0, 'm' },
48e2f01c 243 { "bandwidth", required_argument, 0, 'w' },
244 { "afterburner", required_argument, 0, 'a' },
245 { "lowdelay-sbr", no_argument, 0, 'L' },
48e2f01c 246 { "transport-format", required_argument, 0, 'f' },
68543176 247 { "adts-crc-check", no_argument, 0, 'C' },
48e2f01c 248 { "header-period", required_argument, 0, 'P' },
249
d317e29d 250 { "gapless-mode", required_argument, 0, 'G' },
5c534696 251 { "ignorelength", no_argument, 0, 'I' },
aa2ca1e3 252 { "silent", no_argument, 0, 'S' },
fb2b3635 253 { "moov-before-mdat", no_argument, 0, OPT_MOOV_BEFORE_MDAT },
48e2f01c 254
68543176 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 },
c5c45908 272 { "tag", required_argument, 0, OPT_SHORT_TAG },
a56831da 273 { "tag-from-file", required_argument, 0, OPT_SHORT_TAG_FILE },
c5c45908 274 { "long-tag", required_argument, 0, OPT_LONG_TAG },
cbb23cdb 275 { "tag-from-json", required_argument, 0, OPT_TAG_FROM_JSON },
5c534696 276 { 0, 0, 0, 0 },
48e2f01c 277 };
278 params->afterburner = 1;
279
280 aacenc_getmainargs(&argc, &argv);
d317e29d 281 while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:CP:G:Io:SR",
48e2f01c 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;
48e2f01c 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;
229c3ead 331 case 'C':
48e2f01c 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;
d317e29d 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;
48e2f01c 351 case 'I':
352 params->ignore_length = 1;
353 break;
aa2ca1e3 354 case 'S':
355 params->silent = 1;
356 break;
fb2b3635 357 case OPT_MOOV_BEFORE_MDAT:
358 params->moov_before_mdat = 1;
359 break;
68543176 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;
48e2f01c 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:
3b666b75 392 aacenc_add_tag_to_store(&params->tags, ch, 0, optarg,
393 strlen(optarg), 0);
c5c45908 394 break;
395 case OPT_SHORT_TAG:
a56831da 396 case OPT_SHORT_TAG_FILE:
c5c45908 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';
a56831da 408 if (ch == OPT_SHORT_TAG || ch == OPT_SHORT_TAG_FILE) {
360cf7dc 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;
c5c45908 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 }
3b666b75 426 aacenc_add_tag_to_store(&params->tags, fcc, optarg,
427 val, strlen(val),
428 ch == OPT_SHORT_TAG_FILE);
48e2f01c 429 }
48e2f01c 430 break;
cbb23cdb 431 case OPT_TAG_FROM_JSON:
432 params->json_filename = optarg;
433 break;
48e2f01c 434 default:
435 return usage(), -1;
436 }
437 }
438 if (argc == optind)
439 return usage(), -1;
68543176 440
48e2f01c 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;
68543176 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 }
48e2f01c 461 params->input_filename = argv[optind];
462 return 0;
463};
464
465static
e4bbeeb0 466int write_sample(FILE *ofp, m4af_ctx_t *m4af,
48e2f01c 467 const void *data, uint32_t size, uint32_t duration)
468{
469 if (!m4af) {
5e1168a4 470 fwrite(data, 1, size, ofp);
471 if (ferror(ofp)) {
48e2f01c 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
2d744bd5 483int encode(pcm_reader_t *reader, HANDLE_AACENCODER encoder,
e4bbeeb0 484 uint32_t frame_length, FILE *ofp, m4af_ctx_t *m4af,
aa2ca1e3 485 int show_progress)
48e2f01c 486{
e8e9f79e 487 int16_t *ibuf = 0;
48e2f01c 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 };
2d744bd5 496 const pcm_sample_description_t *fmt = pcm_get_format(reader);
48e2f01c 497
2d744bd5 498 ibuf = malloc(frame_length * fmt->bytes_per_frame);
499 aacenc_progress_init(&progress, pcm_get_length(reader), fmt->sample_rate);
48e2f01c 500 do {
bd3b4b34 501 if (g_interrupted)
502 nread = 0;
503 else if (nread) {
2d744bd5 504 if ((nread = pcm_read_frames(reader, ibuf, frame_length)) < 0) {
48e2f01c 505 fprintf(stderr, "ERROR: read failed\n");
506 goto END;
48e2f01c 507 }
aa2ca1e3 508 if (show_progress)
2d744bd5 509 aacenc_progress_update(&progress, pcm_get_position(reader),
510 fmt->sample_rate * 2);
48e2f01c 511 }
e8e9f79e 512 if ((consumed = aac_encode_frame(encoder, fmt, ibuf, nread,
48e2f01c 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);
aa2ca1e3 521
522 if (show_progress)
2d744bd5 523 aacenc_progress_finish(&progress, pcm_get_position(reader));
48e2f01c 524 rc = frames_written;
525END:
526 if (ibuf) free(ibuf);
48e2f01c 527 if (obuf) free(obuf);
528 return rc;
529}
530
c5c45908 531static
e4bbeeb0 532void put_tool_tag(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
c5c45908 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
48e2f01c 559static
e4bbeeb0 560int finalize_m4a(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
48e2f01c 561 HANDLE_AACENCODER encoder)
562{
563 unsigned i;
1af8624b 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);
cbb23cdb 569
570 if (params->json_filename)
3b666b75 571 aacenc_write_tags_from_json(m4af, params->json_filename);
cbb23cdb 572
1af8624b 573 tag = params->tags.tag_table;
cbb23cdb 574 for (i = 0; i < params->tags.tag_count; ++i, ++tag)
3b666b75 575 aacenc_write_tag_entry(m4af, tag);
c5c45908 576
577 put_tool_tag(m4af, params, encoder);
48e2f01c 578
fb2b3635 579 if (m4af_finalize(m4af, params->moov_before_mdat) < 0) {
48e2f01c 580 fprintf(stderr, "ERROR: failed to finalize m4a\n");
581 return -1;
582 }
583 return 0;
584}
585
af8fa38d 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 {
5888fddc 596 const char *base = aacenc_basename(filename);
af8fa38d 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
68543176 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
29a8f73f 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
2d744bd5 647static
648pcm_reader_t *open_input(aacenc_param_ex_t *params)
649{
29a8f73f 650 pcm_io_context_t io = { 0 };
2d744bd5 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 }
29a8f73f 659 io.cookie = params->input_fp;
f48bf129 660 if (fstat(fileno(params->input_fp), &stb) == 0
29a8f73f 661 && (stb.st_mode & S_IFMT) == S_IFREG)
662 io.vtbl = &pcm_io_vtbl;
663 else
664 io.vtbl = &pcm_io_vtbl_noseek;
665
2d744bd5 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;
29a8f73f 677 if ((reader = raw_open(&io, &desc)) == 0) {
2d744bd5 678 fprintf(stderr, "ERROR: failed to open raw input\n");
679 goto END;
680 }
681 } else {
1af8624b 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:
2d744bd5 703 goto END;
704 }
705 }
e8e9f79e 706 return pcm_open_sint16_converter(reader);
2d744bd5 707END:
e8e9f79e 708 return 0;
2d744bd5 709}
710
48e2f01c 711int main(int argc, char **argv)
712{
2d744bd5 713 static m4af_io_callbacks_t m4af_io = {
714 read_callback, write_callback, seek_callback, tell_callback
715 };
48e2f01c 716 aacenc_param_ex_t params = { 0 };
717
718 int result = 2;
48e2f01c 719 char *output_filename = 0;
2d744bd5 720 pcm_reader_t *reader = 0;
48e2f01c 721 HANDLE_AACENCODER encoder = 0;
722 AACENC_InfoStruct aacinfo = { 0 };
e4bbeeb0 723 m4af_ctx_t *m4af = 0;
48e2f01c 724 const pcm_sample_description_t *sample_format;
725 int downsampled_timescale = 0;
726 int frame_count = 0;
9b8f9915 727 int sbr_mode = 0;
48e2f01c 728
729 setlocale(LC_CTYPE, "");
730 setbuf(stderr, 0);
731
732 if (parse_options(argc, argv, &params) < 0)
733 return 1;
734
2d744bd5 735 if ((reader = open_input(&params)) == 0)
48e2f01c 736 goto END;
2d744bd5 737
738 sample_format = pcm_get_format(reader);
48e2f01c 739
9b8f9915 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
48e2f01c 751 if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
752 &aacinfo) < 0)
753 goto END;
754
755 if (!params.output_filename) {
af8fa38d 756 const char *ext = params.transport_format ? ".aac" : ".m4a";
757 output_filename = generate_output_filename(params.input_filename, ext);
48e2f01c 758 params.output_filename = output_filename;
759 }
760
2d744bd5 761 if ((params.output_fp = aacenc_fopen(params.output_filename, "wb+")) == 0) {
48e2f01c 762 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
763 strerror(errno));
764 goto END;
765 }
bd3b4b34 766 handle_signals();
9b8f9915 767 sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params);
48e2f01c 768 if (!params.transport_format) {
769 uint32_t scale;
9b8f9915 770 uint8_t mp4asc[32];
771 uint32_t ascsize = sizeof(mp4asc);
48e2f01c 772 unsigned framelen = aacinfo.frameLength;
9b8f9915 773 if (sbr_mode)
7b1f2136 774 downsampled_timescale = 1;
48e2f01c 775 scale = sample_format->sample_rate >> downsampled_timescale;
2d744bd5 776 if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io,
777 params.output_fp)) < 0)
48e2f01c 778 goto END;
9b8f9915 779 aacenc_mp4asc((aacenc_param_t*)&params, aacinfo.confBuf,
780 aacinfo.confSize, mp4asc, &ascsize);
781 m4af_set_decoder_specific_info(m4af, 0, mp4asc, ascsize);
48e2f01c 782 m4af_set_fixed_frame_duration(m4af, 0,
783 framelen >> downsampled_timescale);
2f6fc566 784 m4af_set_vbr_mode(m4af, 0, params.bitrate_mode);
d317e29d 785 m4af_set_priming_mode(m4af, params.gapless_mode + 1);
48e2f01c 786 m4af_begin_write(m4af);
787 }
2d744bd5 788 frame_count = encode(reader, encoder, aacinfo.frameLength,
789 params.output_fp, m4af, !params.silent);
48e2f01c 790 if (frame_count < 0)
791 goto END;
792 if (m4af) {
793 uint32_t delay = aacinfo.encoderDelay;
2d744bd5 794 int64_t frames_read = pcm_get_position(reader);
48e2f01c 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:
2d744bd5 804 if (reader) pcm_teardown(&reader);
805 if (params.input_fp) fclose(params.input_fp);
48e2f01c 806 if (m4af) m4af_teardown(&m4af);
2d744bd5 807 if (params.output_fp) fclose(params.output_fp);
48e2f01c 808 if (encoder) aacEncClose(&encoder);
809 if (output_filename) free(output_filename);
1af8624b 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);
48e2f01c 814
815 return result;
816}
This page took 0.060994 seconds and 4 git commands to generate.