reimplement int16 conversion as pcm_reader
[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;
2d744bd5 206 FILE *input_fp;
48e2f01c 207 char *output_filename;
2d744bd5 208 FILE *output_fp;
d317e29d 209 unsigned gapless_mode;
48e2f01c 210 unsigned ignore_length;
aa2ca1e3 211 int silent;
fb2b3635 212 int moov_before_mdat;
48e2f01c 213
68543176 214 int is_raw;
215 unsigned raw_channels;
216 unsigned raw_rate;
217 const char *raw_format;
218
cbb23cdb 219 aacenc_tag_param_t tags;
48e2f01c 220
cbb23cdb 221 char *json_filename;
222} aacenc_param_ex_t;
c5c45908 223
48e2f01c 224static
225int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
226{
227 int ch;
228 unsigned n;
48e2f01c 229
fb2b3635 230#define OPT_MOOV_BEFORE_MDAT M4AF_FOURCC('m','o','o','v')
a56831da 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')
cbb23cdb 237#define OPT_TAG_FROM_JSON M4AF_FOURCC('t','f','j','s')
68543176 238
48e2f01c 239 static struct option long_options[] = {
240 { "help", no_argument, 0, 'h' },
241 { "profile", required_argument, 0, 'p' },
242 { "bitrate", required_argument, 0, 'b' },
34b319e0 243 { "bitrate-mode", required_argument, 0, 'm' },
48e2f01c 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' },
68543176 249 { "adts-crc-check", no_argument, 0, 'C' },
48e2f01c 250 { "header-period", required_argument, 0, 'P' },
251
d317e29d 252 { "gapless-mode", required_argument, 0, 'G' },
5c534696 253 { "ignorelength", no_argument, 0, 'I' },
aa2ca1e3 254 { "silent", no_argument, 0, 'S' },
fb2b3635 255 { "moov-before-mdat", no_argument, 0, OPT_MOOV_BEFORE_MDAT },
48e2f01c 256
68543176 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 },
c5c45908 274 { "tag", required_argument, 0, OPT_SHORT_TAG },
a56831da 275 { "tag-from-file", required_argument, 0, OPT_SHORT_TAG_FILE },
c5c45908 276 { "long-tag", required_argument, 0, OPT_LONG_TAG },
cbb23cdb 277 { "tag-from-json", required_argument, 0, OPT_TAG_FROM_JSON },
5c534696 278 { 0, 0, 0, 0 },
48e2f01c 279 };
280 params->afterburner = 1;
281
282 aacenc_getmainargs(&argc, &argv);
d317e29d 283 while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:CP:G:Io:SR",
48e2f01c 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;
229c3ead 340 case 'C':
48e2f01c 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;
d317e29d 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;
48e2f01c 360 case 'I':
361 params->ignore_length = 1;
362 break;
aa2ca1e3 363 case 'S':
364 params->silent = 1;
365 break;
fb2b3635 366 case OPT_MOOV_BEFORE_MDAT:
367 params->moov_before_mdat = 1;
368 break;
68543176 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;
48e2f01c 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:
cbb23cdb 401 aacenc_param_add_itmf_entry(&params->tags, ch, 0, optarg,
402 strlen(optarg), 0);
c5c45908 403 break;
404 case OPT_SHORT_TAG:
a56831da 405 case OPT_SHORT_TAG_FILE:
c5c45908 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';
a56831da 417 if (ch == OPT_SHORT_TAG || ch == OPT_SHORT_TAG_FILE) {
360cf7dc 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;
c5c45908 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 }
cbb23cdb 435 aacenc_param_add_itmf_entry(&params->tags, fcc, optarg,
436 val, strlen(val),
437 ch == OPT_SHORT_TAG_FILE);
48e2f01c 438 }
48e2f01c 439 break;
cbb23cdb 440 case OPT_TAG_FROM_JSON:
441 params->json_filename = optarg;
442 break;
48e2f01c 443 default:
444 return usage(), -1;
445 }
446 }
447 if (argc == optind)
448 return usage(), -1;
68543176 449
48e2f01c 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;
68543176 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 }
48e2f01c 470 params->input_filename = argv[optind];
471 return 0;
472};
473
474static
e4bbeeb0 475int write_sample(FILE *ofp, m4af_ctx_t *m4af,
48e2f01c 476 const void *data, uint32_t size, uint32_t duration)
477{
478 if (!m4af) {
5e1168a4 479 fwrite(data, 1, size, ofp);
480 if (ferror(ofp)) {
48e2f01c 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
2d744bd5 492int encode(pcm_reader_t *reader, HANDLE_AACENCODER encoder,
e4bbeeb0 493 uint32_t frame_length, FILE *ofp, m4af_ctx_t *m4af,
aa2ca1e3 494 int show_progress)
48e2f01c 495{
e8e9f79e 496 int16_t *ibuf = 0;
48e2f01c 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 };
2d744bd5 505 const pcm_sample_description_t *fmt = pcm_get_format(reader);
48e2f01c 506
2d744bd5 507 ibuf = malloc(frame_length * fmt->bytes_per_frame);
508 aacenc_progress_init(&progress, pcm_get_length(reader), fmt->sample_rate);
48e2f01c 509 do {
bd3b4b34 510 if (g_interrupted)
511 nread = 0;
512 else if (nread) {
2d744bd5 513 if ((nread = pcm_read_frames(reader, ibuf, frame_length)) < 0) {
48e2f01c 514 fprintf(stderr, "ERROR: read failed\n");
515 goto END;
48e2f01c 516 }
aa2ca1e3 517 if (show_progress)
2d744bd5 518 aacenc_progress_update(&progress, pcm_get_position(reader),
519 fmt->sample_rate * 2);
48e2f01c 520 }
e8e9f79e 521 if ((consumed = aac_encode_frame(encoder, fmt, ibuf, nread,
48e2f01c 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);
aa2ca1e3 530
531 if (show_progress)
2d744bd5 532 aacenc_progress_finish(&progress, pcm_get_position(reader));
48e2f01c 533 rc = frames_written;
534END:
535 if (ibuf) free(ibuf);
48e2f01c 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
fb2b3635 583 if (m4af_finalize(m4af, params->moov_before_mdat) < 0) {
48e2f01c 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
2d744bd5 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 }
e8e9f79e 687 return pcm_open_sint16_converter(reader);
2d744bd5 688END:
e8e9f79e 689 return 0;
2d744bd5 690}
691
48e2f01c 692int main(int argc, char **argv)
693{
2d744bd5 694 static m4af_io_callbacks_t m4af_io = {
695 read_callback, write_callback, seek_callback, tell_callback
696 };
48e2f01c 697 aacenc_param_ex_t params = { 0 };
698
699 int result = 2;
48e2f01c 700 char *output_filename = 0;
2d744bd5 701 pcm_reader_t *reader = 0;
48e2f01c 702 HANDLE_AACENCODER encoder = 0;
703 AACENC_InfoStruct aacinfo = { 0 };
e4bbeeb0 704 m4af_ctx_t *m4af = 0;
48e2f01c 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
2d744bd5 715 if ((reader = open_input(&params)) == 0)
48e2f01c 716 goto END;
2d744bd5 717
718 sample_format = pcm_get_format(reader);
48e2f01c 719
720 if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
721 &aacinfo) < 0)
722 goto END;
723
724 if (!params.output_filename) {
af8fa38d 725 const char *ext = params.transport_format ? ".aac" : ".m4a";
726 output_filename = generate_output_filename(params.input_filename, ext);
48e2f01c 727 params.output_filename = output_filename;
728 }
729
2d744bd5 730 if ((params.output_fp = aacenc_fopen(params.output_filename, "wb+")) == 0) {
48e2f01c 731 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
732 strerror(errno));
733 goto END;
734 }
bd3b4b34 735 handle_signals();
48e2f01c 736 if (!params.transport_format) {
737 uint32_t scale;
738 unsigned framelen = aacinfo.frameLength;
7b1f2136 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;
48e2f01c 743 scale = sample_format->sample_rate >> downsampled_timescale;
2d744bd5 744 if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io,
745 params.output_fp)) < 0)
48e2f01c 746 goto END;
e4bbeeb0 747 m4af_set_decoder_specific_info(m4af, 0, aacinfo.confBuf,
748 aacinfo.confSize);
48e2f01c 749 m4af_set_fixed_frame_duration(m4af, 0,
750 framelen >> downsampled_timescale);
2f6fc566 751 m4af_set_vbr_mode(m4af, 0, params.bitrate_mode);
d317e29d 752 m4af_set_priming_mode(m4af, params.gapless_mode + 1);
48e2f01c 753 m4af_begin_write(m4af);
754 }
2d744bd5 755 frame_count = encode(reader, encoder, aacinfo.frameLength,
756 params.output_fp, m4af, !params.silent);
48e2f01c 757 if (frame_count < 0)
758 goto END;
759 if (m4af) {
760 uint32_t delay = aacinfo.encoderDelay;
2d744bd5 761 int64_t frames_read = pcm_get_position(reader);
48e2f01c 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:
2d744bd5 771 if (reader) pcm_teardown(&reader);
772 if (params.input_fp) fclose(params.input_fp);
48e2f01c 773 if (m4af) m4af_teardown(&m4af);
2d744bd5 774 if (params.output_fp) fclose(params.output_fp);
48e2f01c 775 if (encoder) aacEncClose(&encoder);
776 if (output_filename) free(output_filename);
cbb23cdb 777 if (params.tags.tag_table) free(params.tags.tag_table);
48e2f01c 778
779 return result;
780}
This page took 0.056978 seconds and 4 git commands to generate.