From d317e29d467e1e3290a9a2e8c2bbb21b56c854cf Mon Sep 17 00:00:00 2001 From: nu774 Date: Sun, 3 Mar 2013 01:12:28 +0900 Subject: [PATCH] add --gapless-mode --- src/m4af.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/m4af.h | 8 +++++ src/main.c | 16 ++++++++- 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src/m4af.c b/src/m4af.c index bbb1e29..7a53df8 100644 --- a/src/m4af.c +++ b/src/m4af.c @@ -79,6 +79,7 @@ struct m4af_ctx_t { int64_t modification_time; int64_t mdat_pos; int64_t mdat_size; + int priming_mode; int last_error; m4af_itmf_entry_t *itmf_table; @@ -148,6 +149,13 @@ int m4af_write(m4af_ctx_t *ctx, const void *data, uint32_t size) return rc; } +static +int m4af_write16(m4af_ctx_t *ctx, uint32_t data) +{ + data = m4af_htob16(data); + return m4af_write(ctx, &data, 2); +} + static int m4af_write32(m4af_ctx_t *ctx, uint32_t data) { @@ -277,6 +285,11 @@ void m4af_set_priming(m4af_ctx_t *ctx, uint32_t track_idx, track->padding = padding; } +void m4af_set_priming_mode(m4af_ctx_t *ctx, int mode) +{ + ctx->priming_mode = mode; +} + static int m4af_add_sample_entry(m4af_ctx_t *ctx, uint32_t track_idx, uint32_t size, uint32_t delta) @@ -823,12 +836,50 @@ void m4af_write_stsd_box(m4af_ctx_t *ctx, uint32_t track_idx) m4af_update_box_size(ctx, pos); } +static +void m4af_write_sbgp_box(m4af_ctx_t *ctx, uint32_t track_idx) +{ + m4af_track_t *track = &ctx->track[track_idx]; + m4af_write(ctx, + "\0\0\0\034" /* size: 28 */ + "sbgp" /* type */ + "\0" /* version */ + "\0\0\0" /* flags */ + "roll" /* grouping_type */ + "\0\0\0\001" /* entry_count: 1 */ + , 20); + m4af_write32(ctx, track->num_samples); + m4af_write32(ctx, 1); /* group_description_index */ +} + +static +void m4af_write_sgpd_box(m4af_ctx_t *ctx, uint32_t track_idx) +{ + m4af_track_t *track = &ctx->track[track_idx]; + m4af_write(ctx, + "\0\0\0\032" /* size */ + "sgpd" /* type */ + "\001" /* version */ + "\0\0\0" /* flags */ + "roll" /* grouping_type */ + "\0\0\0\002" /* default_length: 2 */ + "\0\0\0\001" /* entry_count: 1 */ + "\377\377" /* payload_data: -1 */ + , 26); +} + static void m4af_write_stbl_box(m4af_ctx_t *ctx, uint32_t track_idx) { + m4af_track_t *track = &ctx->track[track_idx]; int64_t pos = m4af_tell(ctx); m4af_write(ctx, "\0\0\0\0stbl", 8); m4af_write_stsd_box(ctx, track_idx); + if ((ctx->priming_mode & M4AF_PRIMING_MODE_EDTS) && + (track->encoder_delay || track->padding)) { + m4af_write_sbgp_box(ctx, track_idx); + m4af_write_sgpd_box(ctx, track_idx); + } m4af_write_stts_box(ctx, track_idx); m4af_write_stsc_box(ctx, track_idx); m4af_write_stsz_box(ctx, track_idx); @@ -962,6 +1013,42 @@ void m4af_write_mdia_box(m4af_ctx_t *ctx, uint32_t track_idx) m4af_update_box_size(ctx, pos); } +static +void m4af_write_elst_box(m4af_ctx_t *ctx, uint32_t track_idx) +{ + m4af_track_t *track = &ctx->track[track_idx]; + uint8_t version; + int64_t duration = track->duration - track->encoder_delay - track->padding; + int64_t pos = m4af_tell(ctx); + duration = (double)duration / track->timescale * ctx->timescale + .5; + version = (duration > UINT32_MAX); + + m4af_write(ctx, "\0\0\0\0elst", 8); + m4af_write(ctx, &version, 1); + m4af_write(ctx, "\0\0\0", 3); /* flags */ + m4af_write32(ctx, 1); /* entry_count: 1 */ + if (version) { + m4af_write64(ctx, duration); + m4af_write64(ctx, track->encoder_delay); + } else { + m4af_write32(ctx, duration); + m4af_write32(ctx, track->encoder_delay); + } + m4af_write16(ctx, 1); /* media_rate_integer */ + m4af_write16(ctx, 0); /* media_rate_fraction */ + m4af_update_box_size(ctx, pos); +} + +static +void m4af_write_edts_box(m4af_ctx_t *ctx, uint32_t track_idx) +{ + m4af_track_t *track = &ctx->track[track_idx]; + int64_t pos = m4af_tell(ctx); + m4af_write(ctx, "\0\0\0\0edts", 8); + m4af_write_elst_box(ctx, track_idx); + m4af_update_box_size(ctx, pos); +} + static void m4af_write_tkhd_box(m4af_ctx_t *ctx, uint32_t track_idx) { @@ -1015,9 +1102,13 @@ void m4af_write_tkhd_box(m4af_ctx_t *ctx, uint32_t track_idx) static void m4af_write_trak_box(m4af_ctx_t *ctx, uint32_t track_idx) { + m4af_track_t *track = &ctx->track[track_idx]; int64_t pos = m4af_tell(ctx); m4af_write(ctx, "\0\0\0\0trak", 8); m4af_write_tkhd_box(ctx, track_idx); + if ((ctx->priming_mode & M4AF_PRIMING_MODE_EDTS) && + (track->encoder_delay || track->padding)) + m4af_write_edts_box(ctx, track_idx); m4af_write_mdia_box(ctx, track_idx); m4af_update_box_size(ctx, pos); } @@ -1227,7 +1318,9 @@ int m4af_finalize(m4af_ctx_t *ctx) } m4af_flush_chunk(ctx, i); } - if (ctx->track[0].encoder_delay || ctx->track[0].padding) + track = ctx->track; + if ((ctx->priming_mode & M4AF_PRIMING_MODE_ITUNSMPB) && + (track->encoder_delay || track->padding)) m4af_set_iTunSMPB(ctx); m4af_finalize_mdat(ctx); m4af_write_moov_box(ctx); diff --git a/src/m4af.h b/src/m4af.h index 91f4cf8..6c3ba4d 100644 --- a/src/m4af.h +++ b/src/m4af.h @@ -52,6 +52,12 @@ enum m4af_codec_type { M4AF_CODEC_TEXT = M4AF_FOURCC('t','e','x','t'), }; +enum m4af_priming_mode { + M4AF_PRIMING_MODE_ITUNSMPB = 1, + M4AF_PRIMING_MODE_EDTS = 2, + M4AF_PRIMING_MODE_BOTH = 3 +}; + typedef int (*m4af_read_callback)(void *cookie, void *buffer, uint32_t size); typedef int (*m4af_write_callback)(void *cookie, const void *data, uint32_t size); @@ -94,6 +100,8 @@ int m4af_set_decoder_specific_info(m4af_ctx_t *ctx, uint32_t track_idx, void m4af_set_priming(m4af_ctx_t *ctx, uint32_t track_idx, uint32_t encoder_delay, uint32_t padding); +void m4af_set_priming_mode(m4af_ctx_t *ctx, int mode); + void m4af_set_fixed_frame_duration(m4af_ctx_t *ctx, uint32_t track_idx, uint32_t length); diff --git a/src/main.c b/src/main.c index 84690a1..b6eaacb 100644 --- a/src/main.c +++ b/src/main.c @@ -148,6 +148,10 @@ PROGNAME " %s\n" " transport layer\n" "\n" " -o Output filename\n" +" -G, --gapless-mode Encoder delay signaling for gapless playback\n" +" 0: iTunSMPB (default)\n" +" 1: ISO standard (edts + sgpd)\n" +" 2: Both\n" " --ignorelength Ignore length of WAV header\n" " -S, --silent Don't print progress messages\n" "\n" @@ -199,6 +203,7 @@ typedef struct aacenc_param_ex_t { char *input_filename; char *output_filename; + unsigned gapless_mode; unsigned ignore_length; int silent; @@ -239,6 +244,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params) { "adts-crc-check", no_argument, 0, 'C' }, { "header-period", required_argument, 0, 'P' }, + { "gapless-mode", required_argument, 0, 'G' }, { "ignorelength", no_argument, 0, 'I' }, { "silent", no_argument, 0, 'S' }, @@ -268,7 +274,7 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params) params->afterburner = 1; aacenc_getmainargs(&argc, &argv); - while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:CP:Io:SR", + while ((ch = getopt_long(argc, argv, "hp:b:m:w:a:Ls:f:CP:G:Io:SR", long_options, 0)) != EOF) { switch (ch) { case 'h': @@ -338,6 +344,13 @@ int parse_options(int argc, char **argv, aacenc_param_ex_t *params) case 'o': params->output_filename = optarg; break; + case 'G': + if (sscanf(optarg, "%u", &n) != 1 || n > 2) { + fprintf(stderr, "invalid arg for gapless-mode\n"); + return -1; + } + params->gapless_mode = n; + break; case 'I': params->ignore_length = 1; break; @@ -718,6 +731,7 @@ int main(int argc, char **argv) aacinfo.confSize); m4af_set_fixed_frame_duration(m4af, 0, framelen >> downsampled_timescale); + m4af_set_priming_mode(m4af, params.gapless_mode + 1); m4af_begin_write(m4af); } frame_count = encode(wavf, encoder, aacinfo.frameLength, ofp, m4af, -- 2.30.2