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