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