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