#endif
#include "compat.h"
#include "wav_reader.h"
+#include "caf_reader.h"
#include "aacenc.h"
#include "m4af.h"
#include "progress.h"
" 0: Off\n"
" 1: On(default)\n"
" -L, --lowdelay-sbr Enable ELD-SBR (AAC ELD only)\n"
-" -s, --sbr-signaling <n> SBR signaling mode\n"
-" 0: Implicit, backward compatible(default)\n"
-" 1: Explicit SBR and implicit PS\n"
-" 2: Explicit hierarchical signaling\n"
" -f, --transport-format <n> Transport format\n"
" 0: RAW (default, muxed into M4A)\n"
" 1: ADIF\n"
" 0: iTunSMPB (default)\n"
" 1: ISO standard (edts + sgpd)\n"
" 2: Both\n"
-" --ignorelength Ignore length of WAV header\n"
+" --include-sbr-delay Count SBR decoder delay in encoder delay\n"
+" This is not iTunes compatible, but is default\n"
+" behavior of FDK library.\n"
+" -I, --ignorelength Ignore length of WAV header\n"
" -S, --silent Don't print progress messages\n"
" --moov-before-mdat Place moov box before mdat box on m4a output\n"
"\n"
char *output_filename;
FILE *output_fp;
unsigned gapless_mode;
+ unsigned include_sbr_delay;
unsigned ignore_length;
int silent;
int moov_before_mdat;
const char *raw_format;
aacenc_tag_store_t tags;
+ aacenc_tag_store_t source_tags;
+ aacenc_translate_generic_text_tag_ctx_t source_tag_ctx;
char *json_filename;
} aacenc_param_ex_t;
int ch;
unsigned n;
+#define OPT_INCLUDE_SBR_DELAY M4AF_FOURCC('s','d','l','y')
#define OPT_MOOV_BEFORE_MDAT M4AF_FOURCC('m','o','o','v')
#define OPT_RAW_CHANNELS M4AF_FOURCC('r','c','h','n')
#define OPT_RAW_RATE M4AF_FOURCC('r','r','a','t')
{ "bandwidth", required_argument, 0, 'w' },
{ "afterburner", required_argument, 0, 'a' },
{ "lowdelay-sbr", no_argument, 0, 'L' },
- { "sbr-signaling", required_argument, 0, 's' },
{ "transport-format", required_argument, 0, 'f' },
{ "adts-crc-check", no_argument, 0, 'C' },
{ "header-period", required_argument, 0, 'P' },
{ "gapless-mode", required_argument, 0, 'G' },
+ { "include-sbr-delay", no_argument, 0, OPT_INCLUDE_SBR_DELAY },
{ "ignorelength", no_argument, 0, 'I' },
{ "silent", no_argument, 0, 'S' },
{ "moov-before-mdat", no_argument, 0, OPT_MOOV_BEFORE_MDAT },
case 'L':
params->lowdelay_sbr = 1;
break;
- case 's':
- if (sscanf(optarg, "%u", &n) != 1 || n > 2) {
- fprintf(stderr, "invalid arg for sbr-signaling\n");
- return -1;
- }
- params->sbr_signaling = n;
- break;
case 'f':
if (sscanf(optarg, "%u", &n) != 1) {
fprintf(stderr, "invalid arg for transport-format\n");
}
params->gapless_mode = n;
break;
+ case OPT_INCLUDE_SBR_DELAY:
+ params->include_sbr_delay = 1;
+ break;
case 'I':
params->ignore_length = 1;
break;
HANDLE_AACENCODER encoder)
{
unsigned i;
- aacenc_tag_entry_t *tag = params->tags.tag_table;
+ aacenc_tag_entry_t *tag;
+
+ tag = params->source_tags.tag_table;
+ for (i = 0; i < params->source_tags.tag_count; ++i, ++tag)
+ aacenc_write_tag_entry(m4af, tag);
if (params->json_filename)
aacenc_write_tags_from_json(m4af, params->json_filename);
+ tag = params->tags.tag_table;
for (i = 0; i < params->tags.tag_count; ++i, ++tag)
aacenc_write_tag_entry(m4af, tag);
goto END;
}
io.cookie = params->input_fp;
- if (fstat(fileno(io.cookie), &stb) == 0
+ if (fstat(fileno(params->input_fp), &stb) == 0
&& (stb.st_mode & S_IFMT) == S_IFREG)
io.vtbl = &pcm_io_vtbl;
else
goto END;
}
} else {
- if ((reader = wav_open(&io, params->ignore_length)) == 0) {
- fprintf(stderr, "ERROR: broken / unsupported input file\n");
+ int c;
+ ungetc(c = getc(params->input_fp), params->input_fp);
+
+ switch (c) {
+ case 'R':
+ if ((reader = wav_open(&io, params->ignore_length)) == 0) {
+ fprintf(stderr, "ERROR: broken / unsupported input file\n");
+ goto END;
+ }
+ break;
+ case 'c':
+ params->source_tag_ctx.add = aacenc_add_tag_entry_to_store;
+ params->source_tag_ctx.add_ctx = ¶ms->source_tags;
+ if ((reader = caf_open(&io,
+ aacenc_translate_generic_text_tag,
+ ¶ms->source_tag_ctx)) == 0) {
+ fprintf(stderr, "ERROR: broken / unsupported input file\n");
+ goto END;
+ }
+ break;
+ default:
goto END;
}
}
const pcm_sample_description_t *sample_format;
int downsampled_timescale = 0;
int frame_count = 0;
+ int sbr_mode = 0;
setlocale(LC_CTYPE, "");
setbuf(stderr, 0);
sample_format = pcm_get_format(reader);
+ /*
+ * We use explicit/hierarchical signaling for LOAS.
+ * Other than that, we request implicit signaling to FDK library, then
+ * append explicit/backward-compatible signaling to ASC in case of MP4FF.
+ *
+ * Explicit/backward-compatible signaling of SBR is the most recommended
+ * way in MPEG4 part3 spec, and seems the only way supported by iTunes.
+ * Since FDK library does not support it, we have to do it on our side.
+ */
+ params.sbr_signaling = (params.transport_format == TT_MP4_LOAS) ? 2 : 0;
+
if (aacenc_init(&encoder, (aacenc_param_t*)¶ms, sample_format,
&aacinfo) < 0)
goto END;
goto END;
}
handle_signals();
+ sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)¶ms);
if (!params.transport_format) {
uint32_t scale;
+ uint8_t mp4asc[32];
+ uint32_t ascsize = sizeof(mp4asc);
unsigned framelen = aacinfo.frameLength;
- int sbr_mode = aacenc_is_sbr_active((aacenc_param_t*)¶ms);
- int sig_mode = aacEncoder_GetParam(encoder, AACENC_SIGNALING_MODE);
- if (sbr_mode && !sig_mode)
+ if (sbr_mode)
downsampled_timescale = 1;
scale = sample_format->sample_rate >> downsampled_timescale;
if ((m4af = m4af_create(M4AF_CODEC_MP4A, scale, &m4af_io,
params.output_fp)) < 0)
goto END;
- m4af_set_decoder_specific_info(m4af, 0, aacinfo.confBuf,
- aacinfo.confSize);
+ aacenc_mp4asc((aacenc_param_t*)¶ms, aacinfo.confBuf,
+ aacinfo.confSize, mp4asc, &ascsize);
+ m4af_set_decoder_specific_info(m4af, 0, mp4asc, ascsize);
m4af_set_fixed_frame_duration(m4af, 0,
framelen >> downsampled_timescale);
m4af_set_vbr_mode(m4af, 0, params.bitrate_mode);
goto END;
if (m4af) {
uint32_t delay = aacinfo.encoderDelay;
+ uint32_t padding;
int64_t frames_read = pcm_get_position(reader);
- uint32_t padding = frame_count * aacinfo.frameLength
- - frames_read - aacinfo.encoderDelay;
+
+ if (sbr_mode && params.profile != AOT_ER_AAC_ELD &&
+ !params.include_sbr_delay)
+ delay -= 481 << 1;
+ if (sbr_mode && (delay & 1))
+ ++delay;
+ padding = frame_count * aacinfo.frameLength - frames_read - delay;
m4af_set_priming(m4af, 0, delay >> downsampled_timescale,
padding >> downsampled_timescale);
if (finalize_m4a(m4af, ¶ms, encoder) < 0)
if (params.output_fp) fclose(params.output_fp);
if (encoder) aacEncClose(&encoder);
if (output_filename) free(output_filename);
- if (params.tags.tag_table) aacenc_free_tag_store(¶ms.tags);
+ if (params.tags.tag_table)
+ aacenc_free_tag_store(¶ms.tags);
+ if (params.source_tags.tag_table)
+ aacenc_free_tag_store(¶ms.source_tags);
return result;
}