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