refactor pcm io routines
[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
3b666b75 219 aacenc_tag_store_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:
3b666b75 401 aacenc_add_tag_to_store(&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 }
3b666b75 435 aacenc_add_tag_to_store(&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)
3b666b75 576 aacenc_write_tags_from_json(m4af, params->json_filename);
cbb23cdb 577
578 for (i = 0; i < params->tags.tag_count; ++i, ++tag)
3b666b75 579 aacenc_write_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
29a8f73f 646static pcm_io_vtbl_t pcm_io_vtbl = {
647 read_callback, seek_callback, tell_callback
648};
649static pcm_io_vtbl_t pcm_io_vtbl_noseek = { read_callback, 0, 0 };
650
2d744bd5 651static
652pcm_reader_t *open_input(aacenc_param_ex_t *params)
653{
29a8f73f 654 pcm_io_context_t io = { 0 };
2d744bd5 655 pcm_reader_t *reader = 0;
656 struct stat stb = { 0 };
657
658 if ((params->input_fp = aacenc_fopen(params->input_filename, "rb")) == 0) {
659 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params->input_filename,
660 strerror(errno));
661 goto END;
662 }
29a8f73f 663 io.cookie = params->input_fp;
664 if (fstat(fileno(io.cookie), &stb) == 0
665 && (stb.st_mode & S_IFMT) == S_IFREG)
666 io.vtbl = &pcm_io_vtbl;
667 else
668 io.vtbl = &pcm_io_vtbl_noseek;
669
2d744bd5 670 if (params->is_raw) {
671 int bytes_per_channel;
672 pcm_sample_description_t desc = { 0 };
673 if (parse_raw_spec(params->raw_format, &desc) < 0) {
674 fprintf(stderr, "ERROR: invalid raw-format spec\n");
675 goto END;
676 }
677 desc.sample_rate = params->raw_rate;
678 desc.channels_per_frame = params->raw_channels;
679 bytes_per_channel = (desc.bits_per_channel + 7) / 8;
680 desc.bytes_per_frame = params->raw_channels * bytes_per_channel;
29a8f73f 681 if ((reader = raw_open(&io, &desc)) == 0) {
2d744bd5 682 fprintf(stderr, "ERROR: failed to open raw input\n");
683 goto END;
684 }
685 } else {
29a8f73f 686 if ((reader = wav_open(&io, params->ignore_length)) == 0) {
2d744bd5 687 fprintf(stderr, "ERROR: broken / unsupported input file\n");
688 goto END;
689 }
690 }
e8e9f79e 691 return pcm_open_sint16_converter(reader);
2d744bd5 692END:
e8e9f79e 693 return 0;
2d744bd5 694}
695
48e2f01c 696int main(int argc, char **argv)
697{
2d744bd5 698 static m4af_io_callbacks_t m4af_io = {
699 read_callback, write_callback, seek_callback, tell_callback
700 };
48e2f01c 701 aacenc_param_ex_t params = { 0 };
702
703 int result = 2;
48e2f01c 704 char *output_filename = 0;
2d744bd5 705 pcm_reader_t *reader = 0;
48e2f01c 706 HANDLE_AACENCODER encoder = 0;
707 AACENC_InfoStruct aacinfo = { 0 };
e4bbeeb0 708 m4af_ctx_t *m4af = 0;
48e2f01c 709 const pcm_sample_description_t *sample_format;
710 int downsampled_timescale = 0;
711 int frame_count = 0;
712
713 setlocale(LC_CTYPE, "");
714 setbuf(stderr, 0);
715
716 if (parse_options(argc, argv, &params) < 0)
717 return 1;
718
2d744bd5 719 if ((reader = open_input(&params)) == 0)
48e2f01c 720 goto END;
2d744bd5 721
722 sample_format = pcm_get_format(reader);
48e2f01c 723
724 if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
725 &aacinfo) < 0)
726 goto END;
727
728 if (!params.output_filename) {
af8fa38d 729 const char *ext = params.transport_format ? ".aac" : ".m4a";
730 output_filename = generate_output_filename(params.input_filename, ext);
48e2f01c 731 params.output_filename = output_filename;
732 }
733
2d744bd5 734 if ((params.output_fp = aacenc_fopen(params.output_filename, "wb+")) == 0) {
48e2f01c 735 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
736 strerror(errno));
737 goto END;
738 }
bd3b4b34 739 handle_signals();
48e2f01c 740 if (!params.transport_format) {
741 uint32_t scale;
742 unsigned framelen = aacinfo.frameLength;
7b1f2136 743 int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params);
744 int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE);
745 if (sbr_mode && !sig_mode)
746 downsampled_timescale = 1;
48e2f01c 747 scale = sample_format->sample_rate >> downsampled_timescale;
2d744bd5 748 if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io,
749 params.output_fp)) < 0)
48e2f01c 750 goto END;
e4bbeeb0 751 m4af_set_decoder_specific_info(m4af, 0, aacinfo.confBuf,
752 aacinfo.confSize);
48e2f01c 753 m4af_set_fixed_frame_duration(m4af, 0,
754 framelen >> downsampled_timescale);
2f6fc566 755 m4af_set_vbr_mode(m4af, 0, params.bitrate_mode);
d317e29d 756 m4af_set_priming_mode(m4af, params.gapless_mode + 1);
48e2f01c 757 m4af_begin_write(m4af);
758 }
2d744bd5 759 frame_count = encode(reader, encoder, aacinfo.frameLength,
760 params.output_fp, m4af, !params.silent);
48e2f01c 761 if (frame_count < 0)
762 goto END;
763 if (m4af) {
764 uint32_t delay = aacinfo.encoderDelay;
2d744bd5 765 int64_t frames_read = pcm_get_position(reader);
48e2f01c 766 uint32_t padding = frame_count * aacinfo.frameLength
767 - frames_read - aacinfo.encoderDelay;
768 m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
769 padding >> downsampled_timescale);
770 if (finalize_m4a(m4af, &params, encoder) < 0)
771 goto END;
772 }
773 result = 0;
774END:
2d744bd5 775 if (reader) pcm_teardown(&reader);
776 if (params.input_fp) fclose(params.input_fp);
48e2f01c 777 if (m4af) m4af_teardown(&m4af);
2d744bd5 778 if (params.output_fp) fclose(params.output_fp);
48e2f01c 779 if (encoder) aacEncClose(&encoder);
780 if (output_filename) free(output_filename);
3b666b75 781 if (params.tags.tag_table) aacenc_free_tag_store(&params.tags);
48e2f01c 782
783 return result;
784}
This page took 0.059069 seconds and 4 git commands to generate.