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