diff --git a/ChangeLog b/ChangeLog index 4992a48cd..4fe848d9a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2003-08-18 Moritz Bunkus + + * mkvmerge: Implemented a switch that has to be used for SBR AAC / + AAC+ / HE-AAC if the source file is an AAC file and the AAC file + contains SBR AAC data (no automatic detection possible in this case!). + 2003-08-14 Moritz Bunkus * mkvmerge: Low bitrate AC3 tracks from Real's DNET are identified diff --git a/doc/mkvmerge.1 b/doc/mkvmerge.1 index 2096f0d2c..53eecb074 100644 --- a/doc/mkvmerge.1 +++ b/doc/mkvmerge.1 @@ -226,6 +226,21 @@ tracks by selecting different track IDs each time. \fB\-t\fR, \fB\-\-tags\fR <\fITID\fR:\fIfile\fR> Read tags for the track with the number \fITID\fR from the \fIfile\fR. See the section about tags below for details. +.TP +\fB\-\-aab\-is\-sbr\fR <\fITID\fR> +Tells \fBmkvmerge\fR that the track with the ID \fITID\fR is SBR AAC (also +known as HE-AAC or AAC+). This options is needed if a) the source file is an +AAC file (NOT for a Matroska file) and b) the AAC file contains SBR AAC data. +The reason for this switch is that it is technically impossible to +automatically tell normal AAC data from SBR AAC data without decoding a +complete AAC frame. As there are several patent issues with AAC decoders I +won't implement this decoding stage. So for SBR AAC files this switch is +mandatory. The resulting file might not play back correctly or even not at +all if the switch was omitted. +.br +If the source file is a Matroska file then the CodecID should be enough to +detect SBR AAC. However, if the CodecID is wrong then this switch can be used +to correct that. .LP Options that only apply to video tracks: diff --git a/src/matroska.h b/src/matroska.h index aa46e1ef8..716ad64c7 100644 --- a/src/matroska.h +++ b/src/matroska.h @@ -26,11 +26,12 @@ #define MKV_A_AAC_2MAIN "A_AAC/MPEG2/MAIN" #define MKV_A_AAC_2LC "A_AAC/MPEG2/LC" #define MKV_A_AAC_2SSR "A_AAC/MPEG2/SSR" +#define MKV_A_AAC_2SBR "A_AAC/MPEG2/LC/SBR" #define MKV_A_AAC_4MAIN "A_AAC/MPEG4/MAIN" #define MKV_A_AAC_4LC "A_AAC/MPEG4/LC" #define MKV_A_AAC_4SSR "A_AAC/MPEG4/SSR" #define MKV_A_AAC_4LTP "A_AAC/MPEG4/LTP" -#define MKV_A_AAC_4SBR "A_AAC/MPEG4/SBR" +#define MKV_A_AAC_4SBR "A_AAC/MPEG4/LC/SBR" #define MKV_A_AC3 "A_AC3" #define MKV_A_DTS "A_DTS" #define MKV_A_MP3 "A_MPEG/L3" diff --git a/src/mkvmerge.cpp b/src/mkvmerge.cpp index 86846e7c4..17611edd0 100644 --- a/src/mkvmerge.cpp +++ b/src/mkvmerge.cpp @@ -279,6 +279,7 @@ static void usage() { " --language Sets the language for the track (ISO639-2\n" " code, see --list-languages).\n" " -t, --tags Read tags for the track from a XML file.\n" + " --aac-is-sbr Track with the ID is HE-AAC/AAC+/SBR-AAC.\n" "\n Options that only apply to video tracks:\n" " -f, --fourcc Forces the FourCC to the specified value.\n" " Works only for video tracks.\n" @@ -1043,6 +1044,7 @@ static void identify(const char *filename) { ti.atracks = new vector; ti.vtracks = new vector; ti.stracks = new vector; + ti.aac_is_sbr = new vector; file = (filelist_t *)safemalloc(sizeof(filelist_t)); @@ -1096,6 +1098,7 @@ static void parse_args(int argc, char **argv) { ti.languages = new vector; ti.sub_charsets = new vector; ti.all_tags = new vector; + ti.aac_is_sbr = new vector; ti.aspect_ratio = 0.0; ti.atracks = new vector; ti.vtracks = new vector; @@ -1474,6 +1477,18 @@ static void parse_args(int argc, char **argv) { parse_tags(argv[i + 1], tags); ti.all_tags->push_back(tags); i++; + + } else if (!strcmp(argv[i], "--aac-is-sbr")) { + if ((i + 1) >= argc) { + mxprint(stderr, "Error: %s lacks the track ID.\n", argv[i]); + exit(1); + } + if (!parse_int(argv[i + 1], id) || (id < 0)) { + mxprint(stderr, "Error: '%s' is not a valid track ID.\n", argv[i + 1]); + exit(1); + } + ti.aac_is_sbr->push_back(id); + i++; } // The argument is an input file. @@ -1523,6 +1538,7 @@ static void parse_args(int argc, char **argv) { delete ti.languages; delete ti.sub_charsets; delete ti.all_tags; + delete ti.aac_is_sbr; memset(&ti, 0, sizeof(track_info_t)); ti.audio_syncs = new vector; ti.cue_creations = new vector; @@ -1530,6 +1546,7 @@ static void parse_args(int argc, char **argv) { ti.languages = new vector; ti.sub_charsets = new vector; ti.all_tags = new vector; + ti.aac_is_sbr = new vector; ti.aspect_ratio = 0.0; ti.atracks = new vector; ti.vtracks = new vector; diff --git a/src/p_aac.cpp b/src/p_aac.cpp index 251ea6f49..781bcc898 100644 --- a/src/p_aac.cpp +++ b/src/p_aac.cpp @@ -184,6 +184,8 @@ void aac_packetizer_c::set_headers() { set_codec_id(MKV_A_AAC_4SSR); else if (profile == AAC_PROFILE_LTP) set_codec_id(MKV_A_AAC_4LTP); + else if (profile == AAC_PROFILE_SBR) + set_codec_id(MKV_A_AAC_4SBR); else die("aac_packetizer: Unknown AAC MPEG-4 object type %d.", profile); } else { @@ -193,6 +195,8 @@ void aac_packetizer_c::set_headers() { set_codec_id(MKV_A_AAC_2LC); else if (profile == AAC_PROFILE_SSR) set_codec_id(MKV_A_AAC_2SSR); + else if (profile == AAC_PROFILE_SBR) + set_codec_id(MKV_A_AAC_2SBR); else die("aac_packetizer: Unknown AAC MPEG-2 profile %d.", profile); } diff --git a/src/p_aac.h b/src/p_aac.h index a4916977d..b9a2f860d 100644 --- a/src/p_aac.h +++ b/src/p_aac.h @@ -34,6 +34,7 @@ #define AAC_PROFILE_LC 1 #define AAC_PROFILE_SSR 2 #define AAC_PROFILE_LTP 3 +#define AAC_PROFILE_SBR 4 class aac_packetizer_c: public generic_packetizer_c { private: diff --git a/src/pr_generic.cpp b/src/pr_generic.cpp index e51b4a541..4042d1f6e 100644 --- a/src/pr_generic.cpp +++ b/src/pr_generic.cpp @@ -614,6 +614,7 @@ track_info_t *duplicate_track_info(track_info_t *src) { dst->all_tags = new vector(*src->all_tags); for (i = 0; i < src->all_tags->size(); i++) (*dst->all_tags)[i].file_name = safestrdup((*src->all_tags)[i].file_name); + dst->aac_is_sbr = new vector(*src->aac_is_sbr); dst->private_data = (unsigned char *)safememdup(src->private_data, src->private_size); dst->sub_charset = safestrdup(src->sub_charset); @@ -643,6 +644,7 @@ void free_track_info(track_info_t *ti) { for (i = 0; i < ti->all_tags->size(); i++) safefree((*ti->all_tags)[i].file_name); delete ti->all_tags; + delete ti->aac_is_sbr; safefree(ti->language); safefree(ti->private_data); safefree(ti->sub_charset); diff --git a/src/pr_generic.h b/src/pr_generic.h index 531b2c79f..dbb8c0583 100644 --- a/src/pr_generic.h +++ b/src/pr_generic.h @@ -109,6 +109,8 @@ typedef struct { vector *all_tags; // As given on the command line tags_t *tags_ptr; // For this very track KaxTags *tags; // For this very track + + vector *aac_is_sbr; // For AAC+/HE-AAC/SBR } track_info_t; class generic_reader_c; diff --git a/src/r_aac.cpp b/src/r_aac.cpp index 119d28ad3..66b1fd5e8 100644 --- a/src/r_aac.cpp +++ b/src/r_aac.cpp @@ -56,7 +56,7 @@ int aac_reader_c::probe_file(mm_io_c *mm_io, int64_t size) { aac_reader_c::aac_reader_c(track_info_t *nti) throw (error_c): generic_reader_c(nti) { - int adif; + int adif, i; aac_header_t aacheader; try { @@ -77,12 +77,24 @@ aac_reader_c::aac_reader_c(track_info_t *nti) throw (error_c): throw error_c("aac_reader: No valid AAC packet found in the first " SINITCHUNKSIZE " bytes.\n"); guess_adts_version(); - mxprint(stdout, "emphasis_present: %s\n", emphasis_present ? "true" : - "false"); adif = 0; } bytes_processed = 0; ti->id = 0; // ID for this track. + + for (i = 0; i < ti->aac_is_sbr->size(); i++) + if ((*ti->aac_is_sbr)[i] == 0) { + aacheader.profile = AAC_PROFILE_SBR; + break; + } + if (aacheader.profile != AAC_PROFILE_SBR) + mxprint(stdout, + "WARNING! AAC files may contain HE-AAC / AAC+ / SBR AAC audio. " + "This can NOT be detected automatically. Therefore you have to " + "specifiy '--aac-is-sbr 0' manually for this input file if the " + "file actually contains SBR AAC. The file will be muxed in the " + "WRONG way otherwise. Also read mkvmerge's documentation.\n"); + aacpacketizer = new aac_packetizer_c(this, aacheader.id, aacheader.profile, aacheader.sample_rate, aacheader.channels, ti, diff --git a/src/r_matroska.cpp b/src/r_matroska.cpp index a880b75f3..ff7f3d6f7 100644 --- a/src/r_matroska.cpp +++ b/src/r_matroska.cpp @@ -392,6 +392,8 @@ void kax_reader_c::verify_tracks() { !strcmp(t->codec_id, MKV_A_AAC_4LC) || !strcmp(t->codec_id, MKV_A_AAC_4SSR) || !strcmp(t->codec_id, MKV_A_AAC_4LTP) || + !strcmp(t->codec_id, MKV_A_AAC_4SBR) || + !strcmp(t->codec_id, MKV_A_AAC_2SBR) || !strcmp(t->codec_id, MKV_A_AAC_4SBR)) t->a_formattag = FOURCC('M', 'P', '4', 'A'); else { @@ -1179,7 +1181,7 @@ void kax_reader_c::create_packetizers() { } else if (t->a_formattag == FOURCC('M', 'P', '4', 'A')) { // A_AAC/MPEG2/MAIN // 0123456789012345 - int id, profile; + int id, profile, sbridx; if (t->codec_id[10] == '2') id = AAC_ID_MPEG2; @@ -1198,11 +1200,18 @@ void kax_reader_c::create_packetizers() { profile = AAC_PROFILE_SSR; else if (!strcmp(&t->codec_id[12], "LTP")) profile = AAC_PROFILE_LTP; + else if (!strcmp(&t->codec_id[12], "LC/SBR")) + profile = AAC_PROFILE_SBR; else { mxprint(stderr, "Error: matroska_reader: Malformed codec id " "%s for track %d.\n", t->codec_id, t->tnum); exit(1); } + for (sbridx = 0; sbridx < ti->aac_is_sbr->size(); sbridx++) + if ((*ti->aac_is_sbr)[sbridx] == t->tnum) { + profile = AAC_PROFILE_SBR; + break; + } t->packetizer = new aac_packetizer_c(this, id, profile, (unsigned long)t->a_sfreq,