retrieve bitrate for tool tag with aacEncoder_GetParam()
[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
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <locale.h>
15#include <errno.h>
16#include <getopt.h>
17#include "compat.h"
18#include "wav_reader.h"
19#include "aacenc.h"
20#include "m4af.h"
21#include "progress.h"
22#include "version.h"
23
24#define PROGNAME "fdkaac"
25
26static
27int read_callback(void *cookie, void *data, uint32_t size)
28{
29 return fread(data, 1, size, (FILE*)cookie);
30}
31
32static
33int write_callback(void *cookie, const void *data, uint32_t size)
34{
35 return fwrite(data, 1, size, (FILE*)cookie);
36}
37
38static
39int seek_callback(void *cookie, int64_t off, int whence)
40{
41 return fseeko((FILE*)cookie, off, whence);
42}
43
44static
45int64_t tell_callback(void *cookie)
46{
47 return ftello((FILE*)cookie);
48}
49
50static
51void usage(void)
52{
53 printf(
54PROGNAME " %s\n"
55"Usage: " PROGNAME " [options] input_file\n"
56"Options:\n"
57" -h, --help Print this help message\n"
58" -p, --profile <n> Profile (audio object type)\n"
59" 2: MPEG-4 AAC LC (default)\n"
60" 5: MPEG-4 HE-AAC (SBR)\n"
61" 29: MPEG-4 HE-AAC v2 (SBR+PS)\n"
62" 23: MPEG-4 AAC LD\n"
63" 39: MPEG-4 AAC ELD\n"
64" 129: MPEG-2 AAC LC\n"
65" 132: MPEG-2 HE-AAC (SBR)\n"
66" 156: MPEG-2 HE-AAC v2 (SBR+PS)\n"
67" -b, --bitrate <n> Bitrate in bits per seconds (for CBR)\n"
68" -m, --bitrate-mode <n> Bitrate configuration\n"
69" 0: CBR (default)\n"
70" 1-5: VBR\n"
71" (VBR mode is not officially supported, and\n"
72" works only on a certain combination of\n"
73" parameter settings, sample rate, and\n"
74" channel configuration)\n"
75" -w, --bandwidth <n> Frequency bandwidth in Hz (AAC LC only)\n"
76" -a, --afterurner <n> Afterburner\n"
77" 0: Off\n"
78" 1: On(default)\n"
79" -L, --lowdelay-sbr Enable ELD-SBR (AAC ELD only)\n"
80" -s, --sbr-signaling <n> SBR signaling mode\n"
81" 0: Implicit, backward compatible(default)\n"
82" 1: Explicit SBR and implicit PS\n"
83" 2: Explicit hierarchical signaling\n"
84" -f, --transport-format <n> Transport format\n"
85" 0: RAW (default, muxed into M4A)\n"
86" 1: ADIF\n"
87" 2: ADTS\n"
88" 6: LATM MCP=1\n"
89" 7: LATM MCP=0\n"
90" 10: LOAS/LATM (LATM within LOAS)\n"
91" -c, --adts-crc-check Add CRC protection on ADTS header\n"
92" -h, --header-period <n> StreamMuxConfig/PCE repetition period in\n"
93" transport layer\n"
94"\n"
95" -o <filename> Output filename\n"
96" --ignore-length Ignore length of WAV header\n"
97"\n"
98"Tagging options:\n"
99" --title <string>\n"
100" --artist <string>\n"
101" --album <string>\n"
102" --genre <string>\n"
103" --date <string>\n"
104" --composer <string>\n"
105" --grouping <string>\n"
106" --comment <string>\n"
107" --album-artist <string>\n"
108" --track <number[/total]>\n"
109" --disk <number[/total]>\n"
110" --tempo <n>\n"
111 , fdkaac_version);
112}
113
114typedef struct aacenc_tag_entry_t {
115 uint32_t tag;
116 const char *data;
117} aacenc_tag_entry_t;
118
119typedef struct aacenc_param_ex_t {
120 AACENC_PARAMS
121
122 char *input_filename;
123 char *output_filename;
124 unsigned ignore_length;
125
126 aacenc_tag_entry_t *tag_table;
127 unsigned tag_count;
128 unsigned tag_table_capacity;
129} aacenc_param_ex_t;
130
131static
132int parse_options(int argc, char **argv, aacenc_param_ex_t *params)
133{
134 int ch;
135 unsigned n;
136 aacenc_tag_entry_t *tag;
137
138 static struct option long_options[] = {
139 { "help", no_argument, 0, 'h' },
140 { "profile", required_argument, 0, 'p' },
141 { "bitrate", required_argument, 0, 'b' },
142 { "biterate-mode", required_argument, 0, 'm' },
143 { "bandwidth", required_argument, 0, 'w' },
144 { "afterburner", required_argument, 0, 'a' },
145 { "lowdelay-sbr", no_argument, 0, 'L' },
146 { "sbr-signaling", required_argument, 0, 's' },
147 { "transport-format", required_argument, 0, 'f' },
148 { "adts-crc-check", no_argument, 0, 'c' },
149 { "header-period", required_argument, 0, 'P' },
150
151 { "ignore-length", no_argument, 0, 'I' },
152
153 { "title", required_argument, 0, M4AF_TAG_TITLE },
154 { "artist", required_argument, 0, M4AF_TAG_ARTIST },
155 { "album", required_argument, 0, M4AF_TAG_ALBUM },
156 { "genre", required_argument, 0, M4AF_TAG_GENRE },
157 { "date", required_argument, 0, M4AF_TAG_DATE },
158 { "composer", required_argument, 0, M4AF_TAG_COMPOSER },
159 { "grouping", required_argument, 0, M4AF_TAG_GROUPING },
160 { "comment", required_argument, 0, M4AF_TAG_COMMENT },
161 { "album-artist", required_argument, 0, M4AF_TAG_ALBUM_ARTIST },
162 { "track", required_argument, 0, M4AF_TAG_TRACK },
163 { "disk", required_argument, 0, M4AF_TAG_DISK },
164 { "tempo", required_argument, 0, M4AF_TAG_TEMPO },
165 };
166 params->afterburner = 1;
167
168 aacenc_getmainargs(&argc, &argv);
169 while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:cP:Io:",
170 long_options, 0)) != EOF) {
171 switch (ch) {
172 case 'h':
173 return usage(), -1;
174 case 'p':
175 if (sscanf(optarg, "%u", &n) != 1) {
176 fprintf(stderr, "invalid arg for profile\n");
177 return -1;
178 }
179 params->profile = n;
180 break;
181 case 'b':
182 if (sscanf(optarg, "%u", &n) != 1) {
183 fprintf(stderr, "invalid arg for bitrate\n");
184 return -1;
185 }
186 params->bitrate = n;
187 break;
188 case 'm':
189 if (sscanf(optarg, "%u", &n) != 1 || n > 5) {
190 fprintf(stderr, "invalid arg for bitrate-mode\n");
191 return -1;
192 }
193 params->bitrate_mode = n;
194 break;
195 case 'w':
196 if (sscanf(optarg, "%u", &n) != 1) {
197 fprintf(stderr, "invalid arg for bandwidth\n");
198 return -1;
199 }
200 params->bandwidth = n;
201 break;
202 case 'a':
203 if (sscanf(optarg, "%u", &n) != 1 || n > 1) {
204 fprintf(stderr, "invalid arg for afterburner\n");
205 return -1;
206 }
207 params->afterburner = n;
208 break;
209 case 'L':
210 params->lowdelay_sbr = 1;
211 break;
212 case 's':
213 if (sscanf(optarg, "%u", &n) != 1 || n > 2) {
214 fprintf(stderr, "invalid arg for sbr-signaling\n");
215 return -1;
216 }
217 params->sbr_signaling = n;
218 break;
219 case 'f':
220 if (sscanf(optarg, "%u", &n) != 1) {
221 fprintf(stderr, "invalid arg for transport-format\n");
222 return -1;
223 }
224 params->transport_format = n;
225 break;
226 case 'c':
227 params->adts_crc_check = 1;
228 break;
229 case 'P':
230 if (sscanf(optarg, "%u", &n) != 1) {
231 fprintf(stderr, "invalid arg for header-period\n");
232 return -1;
233 }
234 params->header_period = n;
235 break;
236 case 'o':
237 params->output_filename = optarg;
238 break;
239 case 'I':
240 params->ignore_length = 1;
241 break;
242 case M4AF_TAG_TITLE:
243 case M4AF_TAG_ARTIST:
244 case M4AF_TAG_ALBUM:
245 case M4AF_TAG_GENRE:
246 case M4AF_TAG_DATE:
247 case M4AF_TAG_COMPOSER:
248 case M4AF_TAG_GROUPING:
249 case M4AF_TAG_COMMENT:
250 case M4AF_TAG_ALBUM_ARTIST:
251 case M4AF_TAG_TRACK:
252 case M4AF_TAG_DISK:
253 case M4AF_TAG_TEMPO:
254 if (params->tag_count == params->tag_table_capacity) {
255 unsigned newsize = params->tag_table_capacity;
256 newsize = newsize ? newsize * 2 : 1;
257 params->tag_table =
258 realloc(params->tag_table,
259 newsize * sizeof(aacenc_tag_entry_t));
260 params->tag_table_capacity = newsize;
261 }
262 tag = params->tag_table + params->tag_count;
263 tag->tag = ch;
264 tag->data = optarg;
265 params->tag_count++;
266 break;
267 default:
268 return usage(), -1;
269 }
270 }
271 if (argc == optind)
272 return usage(), -1;
273 if (!params->bitrate && !params->bitrate_mode) {
274 fprintf(stderr, "bitrate or bitrate-mode is mandatory\n");
275 return -1;
276 }
277 if (params->output_filename && !strcmp(params->output_filename, "-") &&
278 !params->transport_format) {
279 fprintf(stderr, "stdout streaming is not available on M4A output\n");
280 return -1;
281 }
282 if (params->bitrate && params->bitrate < 10000)
283 params->bitrate *= 1000;
284 params->input_filename = argv[optind];
285 return 0;
286};
287
288static
289int write_sample(FILE *ofp, m4af_writer_t *m4af,
290 const void *data, uint32_t size, uint32_t duration)
291{
292 if (!m4af) {
293 if (fwrite(data, 1, size, ofp) < 0) {
294 fprintf(stderr, "ERROR: fwrite(): %s\n", strerror(errno));
295 return -1;
296 }
297 } else if (m4af_write_sample(m4af, 0, data, size, duration) < 0) {
298 fprintf(stderr, "ERROR: failed to write m4a sample\n");
299 return -1;
300 }
301 return 0;
302}
303
304static
305int encode(wav_reader_t *wavf, HANDLE_AACENCODER encoder,
306 uint32_t frame_length, FILE *ofp, m4af_writer_t *m4af)
307{
308 uint8_t *ibuf = 0;
309 int16_t *pcmbuf = 0;
310 uint32_t pcmsize = 0;
311 uint8_t *obuf = 0;
312 uint32_t olen;
313 uint32_t osize = 0;
314 int nread = 1;
315 int consumed;
316 int rc = -1;
317 int frames_written = 0;
318 aacenc_progress_t progress = { 0 };
319 const pcm_sample_description_t *format = wav_get_format(wavf);
320
321 ibuf = malloc(frame_length * format->bytes_per_frame);
322 aacenc_progress_init(&progress, wav_get_length(wavf), format->sample_rate);
323 do {
324 if (nread) {
325 if ((nread = wav_read_frames(wavf, ibuf, frame_length)) < 0) {
326 fprintf(stderr, "ERROR: read failed\n");
327 goto END;
328 } else if (nread > 0) {
329 if (pcm_convert_to_native_sint16(format, ibuf, nread,
330 &pcmbuf, &pcmsize) < 0) {
331 fprintf(stderr, "ERROR: unsupported sample format\n");
332 goto END;
333 }
334 }
335 aacenc_progress_update(&progress, wav_get_position(wavf),
336 format->sample_rate * 2);
337 }
338 if ((consumed = aac_encode_frame(encoder, format, pcmbuf, nread,
339 &obuf, &olen, &osize)) < 0)
340 goto END;
341 if (olen > 0) {
342 if (write_sample(ofp, m4af, obuf, olen, frame_length) < 0)
343 goto END;
344 ++frames_written;
345 }
346 } while (nread > 0 || olen > 0);
347 aacenc_progress_finish(&progress, wav_get_position(wavf));
348 rc = frames_written;
349END:
350 if (ibuf) free(ibuf);
351 if (pcmbuf) free(pcmbuf);
352 if (obuf) free(obuf);
353 return rc;
354}
355
356static
357int finalize_m4a(m4af_writer_t *m4af, const aacenc_param_ex_t *params,
358 HANDLE_AACENCODER encoder)
359{
360 unsigned i;
361 aacenc_tag_entry_t *tag = params->tag_table;
362
363 for (i = 0; i < params->tag_count; ++i, ++tag) {
364 switch (tag->tag) {
365 case M4AF_TAG_TRACK:
366 {
367 unsigned m, n = 0;
368 if (sscanf(tag->data, "%u/%u", &m, &n) >= 1)
369 m4af_add_itmf_track_tag(m4af, m, n);
370 break;
371 }
372 case M4AF_TAG_DISK:
373 {
374 unsigned m, n = 0;
375 if (sscanf(tag->data, "%u/%u", &m, &n) >= 1)
376 m4af_add_itmf_disk_tag(m4af, m, n);
377 break;
378 }
379 case M4AF_TAG_TEMPO:
380 {
381 unsigned n;
382 if (sscanf(tag->data, "%u", &n) == 1)
383 m4af_add_itmf_int16_tag(m4af, tag->tag, n);
384 break;
385 }
386 default:
387 {
388 char *u8 = aacenc_to_utf8(tag->data);
389 m4af_add_itmf_string_tag(m4af, tag->tag, u8);
390 free(u8);
391 }
392 }
393 }
394 {
395 char tool_info[256];
396 char *p = tool_info;
397 LIB_INFO *lib_info = 0;
398
399 p += sprintf(p, PROGNAME " %s, ", fdkaac_version);
400
d1ce2536 401 lib_info = calloc(FDK_MODULE_LAST, sizeof(LIB_INFO));
48e2f01c 402 if (aacEncGetLibInfo(lib_info) == AACENC_OK) {
403 for (i = 0; i < FDK_MODULE_LAST; ++i)
404 if (lib_info[i].module_id == FDK_AACENC)
405 break;
406 p += sprintf(p, "libfdk-aac %s, ", lib_info[i].versionStr);
407 }
408 free(lib_info);
409 if (params->bitrate_mode)
410 sprintf(p, "VBR mode %d", params->bitrate_mode);
411 else
112fdaf4 412 sprintf(p, "CBR %dkbps",
413 aacEncoder_GetParam(encoder, AACENC_BITRATE) / 1000);
48e2f01c 414
415 m4af_add_itmf_string_tag(m4af, M4AF_TAG_TOOL, tool_info);
416 }
417 if (m4af_finalize(m4af) < 0) {
418 fprintf(stderr, "ERROR: failed to finalize m4a\n");
419 return -1;
420 }
421 return 0;
422}
423
af8fa38d 424static
425const char *basename(const char *filename)
426{
427 char *p = strrchr(filename, '/');
428#ifdef _WIN32
429 char *q = strrchr(filename, '\\');
430 if (p < q) p = q;
431#endif
432 return p ? p + 1 : filename;
433}
434
435static
436char *generate_output_filename(const char *filename, const char *ext)
437{
438 char *p = 0;
439 size_t ext_len = strlen(ext);
440
441 if (strcmp(filename, "-") == 0) {
442 p = malloc(ext_len + 6);
443 sprintf(p, "stdin%s", ext);
444 } else {
445 const char *base = basename(filename);
446 size_t ilen = strlen(base);
447 const char *ext_org = strrchr(base, '.');
448 if (ext_org) ilen = ext_org - base;
449 p = malloc(ilen + ext_len + 1);
450 sprintf(p, "%.*s%s", ilen, base, ext);
451 }
452 return p;
453}
454
48e2f01c 455int main(int argc, char **argv)
456{
457 wav_io_context_t wav_io = { read_callback, seek_callback };
458 m4af_io_callbacks_t m4af_io = {
459 write_callback, seek_callback, tell_callback };
460 aacenc_param_ex_t params = { 0 };
461
462 int result = 2;
463 FILE *ifp = 0;
464 FILE *ofp = 0;
465 char *output_filename = 0;
466 wav_reader_t *wavf = 0;
467 HANDLE_AACENCODER encoder = 0;
468 AACENC_InfoStruct aacinfo = { 0 };
469 m4af_writer_t *m4af = 0;
470 const pcm_sample_description_t *sample_format;
471 int downsampled_timescale = 0;
472 int frame_count = 0;
473
474 setlocale(LC_CTYPE, "");
475 setbuf(stderr, 0);
476
477 if (parse_options(argc, argv, &params) < 0)
478 return 1;
479
480 if ((ifp = aacenc_fopen(params.input_filename, "rb")) == 0) {
481 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.input_filename,
482 strerror(errno));
483 goto END;
484 }
485
486 if (ifp == stdin)
487 wav_io.seek = 0;
488
489 if ((wavf = wav_open(&wav_io, ifp, params.ignore_length)) == 0) {
490 fprintf(stderr, "ERROR: broken / unsupported input file\n");
491 goto END;
492 }
493 sample_format = wav_get_format(wavf);
494
495 if (aacenc_init(&encoder, (aacenc_param_t*)&params, sample_format,
496 &aacinfo) < 0)
497 goto END;
498
499 if (!params.output_filename) {
af8fa38d 500 const char *ext = params.transport_format ? ".aac" : ".m4a";
501 output_filename = generate_output_filename(params.input_filename, ext);
48e2f01c 502 params.output_filename = output_filename;
503 }
504
505 if ((ofp = aacenc_fopen(params.output_filename, "wb")) == 0) {
506 aacenc_fprintf(stderr, "ERROR: %s: %s\n", params.output_filename,
507 strerror(errno));
508 goto END;
509 }
510 if (!params.transport_format) {
511 uint32_t scale;
512 unsigned framelen = aacinfo.frameLength;
513 int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)&params);
514 int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE);
515 if (sbr_mode && !sig_mode)
516 downsampled_timescale = 1;
517 scale = sample_format->sample_rate >> downsampled_timescale;
518 if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io, ofp)) < 0)
519 goto END;
520 m4af_decoder_specific_info(m4af, 0, aacinfo.confBuf, aacinfo.confSize);
521 m4af_set_fixed_frame_duration(m4af, 0,
522 framelen >> downsampled_timescale);
523 m4af_begin_write(m4af);
524 }
525 frame_count = encode(wavf, encoder, aacinfo.frameLength, ofp, m4af);
526 if (frame_count < 0)
527 goto END;
528 if (m4af) {
529 uint32_t delay = aacinfo.encoderDelay;
530 int64_t frames_read = wav_get_position(wavf);
531 uint32_t padding = frame_count * aacinfo.frameLength
532 - frames_read - aacinfo.encoderDelay;
533 m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
534 padding >> downsampled_timescale);
535 if (finalize_m4a(m4af, &params, encoder) < 0)
536 goto END;
537 }
538 result = 0;
539END:
540 if (wavf) wav_teardown(&wavf);
541 if (ifp) fclose(ifp);
542 if (m4af) m4af_teardown(&m4af);
543 if (ofp) fclose(ofp);
544 if (encoder) aacEncClose(&encoder);
545 if (output_filename) free(output_filename);
546 if (params.tag_table) free(params.tag_table);
547
548 return result;
549}
This page took 0.039028 seconds and 4 git commands to generate.