]> iEval git - fdkaac.git/blame_incremental - src/main.c
--tag-from-json: properly support number/total format in json track field
[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 "aacenc.h"
38#include "m4af.h"
39#include "progress.h"
40#include "version.h"
41#include "metadata.h"
42
43#define PROGNAME "fdkaac"
44
45static volatile int g_interrupted = 0;
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
79static
80int read_callback(void *cookie, void *data, uint32_t size)
81{
82 size_t rc = fread(data, 1, size, (FILE*)cookie);
83 return ferror((FILE*)cookie) ? -1 : (int)rc;
84}
85
86static
87int write_callback(void *cookie, const void *data, uint32_t size)
88{
89 size_t rc = fwrite(data, 1, size, (FILE*)cookie);
90 return ferror((FILE*)cookie) ? -1 : (int)rc;
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"
131" -a, --afterburner <n> Afterburner\n"
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"
146" -C, --adts-crc-check Add CRC protection on ADTS header\n"
147" -h, --header-period <n> StreamMuxConfig/PCE repetition period in\n"
148" transport layer\n"
149"\n"
150" -o <filename> Output filename\n"
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"
155" --ignorelength Ignore length of WAV header\n"
156" -S, --silent Don't print progress messages\n"
157" --moov-before-mdat Place moov box before mdat box on m4a output\n"
158"\n"
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"
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"
186" --tag <fcc>:<value> Set iTunes predefined tag with four char code.\n"
187" --tag-from-file <fcc>:<filename>\n"
188" Same as above, but value is read from file.\n"
189" --long-tag <name>:<value> Set arbitrary tag as iTunes custom metadata.\n"
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"
199 , fdkaac_version);
200}
201
202typedef struct aacenc_param_ex_t {
203 AACENC_PARAMS
204
205 char *input_filename;
206 FILE *input_fp;
207 char *output_filename;
208 FILE *output_fp;
209 unsigned gapless_mode;
210 unsigned ignore_length;
211 int silent;
212 int moov_before_mdat;
213
214 int is_raw;
215 unsigned raw_channels;
216 unsigned raw_rate;
217 const char *raw_format;
218
219 aacenc_tag_param_t tags;
220
221 char *json_filename;
222} aacenc_param_ex_t;
223
224static
225int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
226{
227 int ch;
228 unsigned n;
229
230#define OPT_MOOV_BEFORE_MDAT M4AF_FOURCC('m','o','o','v')
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')
237#define OPT_TAG_FROM_JSON M4AF_FOURCC('t','f','j','s')
238
239 static struct option long_options[] = {
240 { "help", no_argument, 0, 'h' },
241 { "profile", required_argument, 0, 'p' },
242 { "bitrate", required_argument, 0, 'b' },
243 { "bitrate-mode", required_argument, 0, 'm' },
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' },
249 { "adts-crc-check", no_argument, 0, 'C' },
250 { "header-period", required_argument, 0, 'P' },
251
252 { "gapless-mode", required_argument, 0, 'G' },
253 { "ignorelength", no_argument, 0, 'I' },
254 { "silent", no_argument, 0, 'S' },
255 { "moov-before-mdat", no_argument, 0, OPT_MOOV_BEFORE_MDAT },
256
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 },
274 { "tag", required_argument, 0, OPT_SHORT_TAG },
275 { "tag-from-file", required_argument, 0, OPT_SHORT_TAG_FILE },
276 { "long-tag", required_argument, 0, OPT_LONG_TAG },
277 { "tag-from-json", required_argument, 0, OPT_TAG_FROM_JSON },
278 { 0, 0, 0, 0 },
279 };
280 params->afterburner = 1;
281
282 aacenc_getmainargs(&argc, &argv);
283 while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:CP:G:Io:SR",
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;
340 case 'C':
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;
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;
360 case 'I':
361 params->ignore_length = 1;
362 break;
363 case 'S':
364 params->silent = 1;
365 break;
366 case OPT_MOOV_BEFORE_MDAT:
367 params->moov_before_mdat = 1;
368 break;
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;
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:
401 aacenc_param_add_itmf_entry(&params->tags, ch, 0, optarg,
402 strlen(optarg), 0);
403 break;
404 case OPT_SHORT_TAG:
405 case OPT_SHORT_TAG_FILE:
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';
417 if (ch == OPT_SHORT_TAG || ch == OPT_SHORT_TAG_FILE) {
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;
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 }
435 aacenc_param_add_itmf_entry(&params->tags, fcc, optarg,
436 val, strlen(val),
437 ch == OPT_SHORT_TAG_FILE);
438 }
439 break;
440 case OPT_TAG_FROM_JSON:
441 params->json_filename = optarg;
442 break;
443 default:
444 return usage(), -1;
445 }
446 }
447 if (argc == optind)
448 return usage(), -1;
449
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;
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 }
470 params->input_filename = argv[optind];
471 return 0;
472};
473
474static
475int write_sample(FILE *ofp, m4af_ctx_t *m4af,
476 const void *data, uint32_t size, uint32_t duration)
477{
478 if (!m4af) {
479 fwrite(data, 1, size, ofp);
480 if (ferror(ofp)) {
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
492int encode(pcm_reader_t *reader, HANDLE_AACENCODER encoder,
493 uint32_t frame_length, FILE *ofp, m4af_ctx_t *m4af,
494 int show_progress)
495{
496 int16_t *ibuf = 0;
497 uint8_t *obuf = 0;
498 uint32_t olen;
499 uint32_t osize = 0;
500 int nread = 1;
501 int consumed;
502 int rc = -1;
503 int frames_written = 0;
504 aacenc_progress_t progress = { 0 };
505 const pcm_sample_description_t *fmt = pcm_get_format(reader);
506
507 ibuf = malloc(frame_length * fmt->bytes_per_frame);
508 aacenc_progress_init(&progress, pcm_get_length(reader), fmt->sample_rate);
509 do {
510 if (g_interrupted)
511 nread = 0;
512 else if (nread) {
513 if ((nread = pcm_read_frames(reader, ibuf, frame_length)) < 0) {
514 fprintf(stderr, "ERROR: read failed\n");
515 goto END;
516 }
517 if (show_progress)
518 aacenc_progress_update(&progress, pcm_get_position(reader),
519 fmt->sample_rate * 2);
520 }
521 if ((consumed = aac_encode_frame(encoder, fmt, ibuf, nread,
522 &obuf, &olen, &osize)) < 0)
523 goto END;
524 if (olen > 0) {
525 if (write_sample(ofp, m4af, obuf, olen, frame_length) < 0)
526 goto END;
527 ++frames_written;
528 }
529 } while (nread > 0 || olen > 0);
530
531 if (show_progress)
532 aacenc_progress_finish(&progress, pcm_get_position(reader));
533 rc = frames_written;
534END:
535 if (ibuf) free(ibuf);
536 if (obuf) free(obuf);
537 return rc;
538}
539
540static
541void put_tool_tag(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
542 HANDLE_AACENCODER encoder)
543{
544 char tool_info[256];
545 char *p = tool_info;
546 LIB_INFO *lib_info = 0;
547
548 p += sprintf(p, PROGNAME " %s, ", fdkaac_version);
549
550 lib_info = calloc(FDK_MODULE_LAST, sizeof(LIB_INFO));
551 if (aacEncGetLibInfo(lib_info) == AACENC_OK) {
552 int i;
553 for (i = 0; i < FDK_MODULE_LAST; ++i)
554 if (lib_info[i].module_id == FDK_AACENC)
555 break;
556 p += sprintf(p, "libfdk-aac %s, ", lib_info[i].versionStr);
557 }
558 free(lib_info);
559 if (params->bitrate_mode)
560 sprintf(p, "VBR mode %d", params->bitrate_mode);
561 else
562 sprintf(p, "CBR %dkbps",
563 aacEncoder_GetParam(encoder, AACENC_BITRATE) / 1000);
564
565 m4af_add_itmf_string_tag(m4af, M4AF_TAG_TOOL, tool_info);
566}
567
568static
569int finalize_m4a(m4af_ctx_t *m4af, const aacenc_param_ex_t *params,
570 HANDLE_AACENCODER encoder)
571{
572 unsigned i;
573 aacenc_tag_entry_t *tag = params->tags.tag_table;
574
575 if (params->json_filename)
576 aacenc_put_tags_from_json(m4af, params->json_filename);
577
578 for (i = 0; i < params->tags.tag_count; ++i, ++tag)
579 aacenc_put_tag_entry(m4af, tag);
580
581 put_tool_tag(m4af, params, encoder);
582
583 if (m4af_finalize(m4af, params->moov_before_mdat) < 0) {
584 fprintf(stderr, "ERROR: failed to finalize m4a\n");
585 return -1;
586 }
587 return 0;
588}
589
590static
591char *generate_output_filename(const char *filename, const char *ext)
592{
593 char *p = 0;
594 size_t ext_len = strlen(ext);
595
596 if (strcmp(filename, "-") == 0) {
597 p = malloc(ext_len + 6);
598 sprintf(p, "stdin%s", ext);
599 } else {
600 const char *base = aacenc_basename(filename);
601 size_t ilen = strlen(base);
602 const char *ext_org = strrchr(base, '.');
603 if (ext_org) ilen = ext_org - base;
604 p = malloc(ilen + ext_len + 1);
605 sprintf(p, "%.*s%s", ilen, base, ext);
606 }
607 return p;
608}
609
610static
611int parse_raw_spec(const char *spec, pcm_sample_description_t *desc)
612{
613 unsigned bits;
614 unsigned char c_type, c_endian = 'L';
615 int type;
616
617 if (sscanf(spec, "%c%u%c", &c_type, &bits, &c_endian) < 2)
618 return -1;
619 c_type = toupper(c_type);
620 c_endian = toupper(c_endian);
621
622 if (c_type == 'S')
623 type = 1;
624 else if (c_type == 'U')
625 type = 2;
626 else if (c_type == 'F')
627 type = 4;
628 else
629 return -1;
630
631 if (c_endian == 'B')
632 type |= 8;
633 else if (c_endian != 'L')
634 return -1;
635
636 if (c_type == 'F' && bits != 32 && bits != 64)
637 return -1;
638 if (c_type != 'F' && (bits < 8 || bits > 32))
639 return -1;
640
641 desc->sample_type = type;
642 desc->bits_per_channel = bits;
643 return 0;
644}
645
646static
647pcm_reader_t *open_input(aacenc_param_ex_t *params)
648{
649 wav_io_context_t wav_io = {
650 read_callback, seek_callback, tell_callback
651 };
652 pcm_reader_t *reader = 0;
653 struct stat stb = { 0 };
654
655 if ((params->input_fp = aacenc_fopen(params->input_filename, "rb")) == 0) {
656 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params->input_filename,
657 strerror(errno));
658 goto END;
659 }
660 if (fstat(fileno(params->input_fp), &stb) == 0
661 && (stb.st_mode & S_IFMT) != S_IFREG) {
662 wav_io.seek = 0;
663 wav_io.tell = 0;
664 }
665 if (params->is_raw) {
666 int bytes_per_channel;
667 pcm_sample_description_t desc = { 0 };
668 if (parse_raw_spec(params->raw_format, &desc) < 0) {
669 fprintf(stderr, "ERROR: invalid raw-format spec\n");
670 goto END;
671 }
672 desc.sample_rate = params->raw_rate;
673 desc.channels_per_frame = params->raw_channels;
674 bytes_per_channel = (desc.bits_per_channel + 7) / 8;
675 desc.bytes_per_frame = params->raw_channels * bytes_per_channel;
676 if ((reader = raw_open(&wav_io, params->input_fp, &desc)) == 0) {
677 fprintf(stderr, "ERROR: failed to open raw input\n");
678 goto END;
679 }
680 } else {
681 if ((reader = wav_open(&wav_io, params->input_fp,
682 params->ignore_length)) == 0) {
683 fprintf(stderr, "ERROR: broken / unsupported input file\n");
684 goto END;
685 }
686 }
687 return pcm_open_sint16_converter(reader);
688END:
689 return 0;
690}
691
692int main(int argc, char **argv)
693{
694 static m4af_io_callbacks_t m4af_io = {
695 read_callback, write_callback, seek_callback, tell_callback
696 };
697 aacenc_param_ex_t params = { 0 };
698
699 int result = 2;
700 char *output_filename = 0;
701 pcm_reader_t *reader = 0;
702 HANDLE_AACENCODER encoder = 0;
703 AACENC_InfoStruct aacinfo = { 0 };
704 m4af_ctx_t *m4af = 0;
705 const pcm_sample_description_t *sample_format;
706 int downsampled_timescale = 0;
707 int frame_count = 0;
708
709 setlocale(LC_CTYPE, "");
710 setbuf(stderr, 0);
711
712 if (parse_options(argc, argv, &params) < 0)
713 return 1;
714
715 if ((reader = open_input(&params)) == 0)
716 goto END;
717
718 sample_format = pcm_get_format(reader);
719
720 if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
721 &aacinfo) < 0)
722 goto END;
723
724 if (!params.output_filename) {
725 const char *ext = params.transport_format ? ".aac" : ".m4a";
726 output_filename = generate_output_filename(params.input_filename, ext);
727 params.output_filename = output_filename;
728 }
729
730 if ((params.output_fp = aacenc_fopen(params.output_filename, "wb+")) == 0) {
731 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
732 strerror(errno));
733 goto END;
734 }
735 handle_signals();
736 if (!params.transport_format) {
737 uint32_t scale;
738 unsigned framelen = aacinfo.frameLength;
739 int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params);
740 int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE);
741 if (sbr_mode && !sig_mode)
742 downsampled_timescale = 1;
743 scale = sample_format->sample_rate >> downsampled_timescale;
744 if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io,
745 params.output_fp)) < 0)
746 goto END;
747 m4af_set_decoder_specific_info(m4af, 0, aacinfo.confBuf,
748 aacinfo.confSize);
749 m4af_set_fixed_frame_duration(m4af, 0,
750 framelen >> downsampled_timescale);
751 m4af_set_vbr_mode(m4af, 0, params.bitrate_mode);
752 m4af_set_priming_mode(m4af, params.gapless_mode + 1);
753 m4af_begin_write(m4af);
754 }
755 frame_count = encode(reader, encoder, aacinfo.frameLength,
756 params.output_fp, m4af, !params.silent);
757 if (frame_count < 0)
758 goto END;
759 if (m4af) {
760 uint32_t delay = aacinfo.encoderDelay;
761 int64_t frames_read = pcm_get_position(reader);
762 uint32_t padding = frame_count * aacinfo.frameLength
763 - frames_read - aacinfo.encoderDelay;
764 m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
765 padding >> downsampled_timescale);
766 if (finalize_m4a(m4af, &params, encoder) < 0)
767 goto END;
768 }
769 result = 0;
770END:
771 if (reader) pcm_teardown(&reader);
772 if (params.input_fp) fclose(params.input_fp);
773 if (m4af) m4af_teardown(&m4af);
774 if (params.output_fp) fclose(params.output_fp);
775 if (encoder) aacEncClose(&encoder);
776 if (output_filename) free(output_filename);
777 if (params.tags.tag_table) free(params.tags.tag_table);
778
779 return result;
780}
This page took 0.027466 seconds and 4 git commands to generate.