package mpeg // https://github.com/mstorsjo/fdk-aac/blob/f285813ec15e7c6f8e4839c9eb4f6b0cd2da1990/libMpegTPEnc/src/tpenc_asc.cpp // https://www.iis.fraunhofer.de/content/dam/iis/de/doc/ame/wp/FraunhoferIIS_Application-Bulletin_AAC-Transport-Formats.pdf // https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/aac_adtstoasc_bsf.c import ( "github.com/wader/fq/format" "github.com/wader/fq/format/registry" "github.com/wader/fq/pkg/decode" ) var aacFrameFormat decode.Group func init() { registry.MustRegister(decode.Format{ Name: format.ADTS_FRAME, Description: "Audio Data Transport Stream frame", DecodeFn: adtsFrameDecoder, Dependencies: []decode.Dependency{ {Names: []string{format.AAC_FRAME}, Group: &aacFrameFormat}, }, }) } var protectionAbsentNames = decode.BoolToScalar{ true: {Description: "No CRC"}, false: {Description: "Has CRC"}, } func adtsFrameDecoder(d *decode.D, in interface{}) interface{} { /* adts_frame() { adts_fixed_header(); adts_variable_header(); if (number_of_raw_data_blocks_in_frame == 0) { adts_error_check(); raw_data_block(); } else { adts_header_error_check(); for( i = 0; i <= number_of_raw_data_blocks_in_frame; i++ ) { raw_data_block(); adts_raw_data_block_error_check(); } } } */ // A 12 syncword 0xFFF, all bits must be 1 // B 1 MPEG Version: 0 for MPEG-4, 1 for MPEG-2 // C 2 Layer: always 0 // D 1 protection absent, Warning, set to 1 if there is no CRC and 0 if there is CRC // E 2 profile, the MPEG-4 Audio Object Type minus 1 // F 4 MPEG-4 Sampling Frequency Index (15 is forbidden) // G 1 private bit, guaranteed never to be used by MPEG, set to 0 when encoding, ignore when decoding // H 3 MPEG-4 Channel Configuration (in the case of 0, the channel configuration is sent via an inband PCE) // I 1 originality, set to 0 when encoding, ignore when decoding // J 1 home, set to 0 when encoding, ignore when decoding // K 1 copyrighted id bit, the next bit of a centrally registered copyright identifier, set to 0 when encoding, ignore when decoding // L 1 copyright id start, signals that this frame's copyright id bit is the first bit of the copyright id, set to 0 when encoding, ignore when decoding // M 13 frame length, this value must include 7 or 9 bytes of header length: FrameLength = (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame) // O 11 Buffer fullness // P 2 Number of AAC frames (RDBs) in ADTS frame minus 1, for maximum compatibility always use 1 AAC frame per ADTS frame // Q 16 CRC if protection absent is 0 d.FieldU12("syncword", d.AssertU(0b1111_1111_1111), d.Bin) d.FieldU1("mpeg_version", d.MapUToStrSym(decode.UToStr{0: "MPEG-4", 1: "MPEG2- AAC"})) d.FieldU2("layer", d.AssertU(0)) protectionAbsent := d.FieldBool("protection_absent", d.MapBoolToScalar(protectionAbsentNames)) // TODO: better sym names objectType := d.FieldUFn("profile", func(d *decode.D) uint64 { return d.U2() + 1 }, d.MapUToStrSym(format.MPEGAudioObjectTypeNames)) d.FieldUScalarFn("sampling_frequency", func(d *decode.D) decode.Scalar { v := d.U4() if v == 15 { return decode.Scalar{Actual: d.U24()} } if f, ok := frequencyIndexHz[v]; ok { return decode.Scalar{Actual: v, Sym: f} } return decode.Scalar{Description: "invalid"} }) d.FieldU1("private_bit") d.FieldU3("channel_configuration", d.MapUToStrSym(channelConfigurationNames)) d.FieldU1("originality") d.FieldU1("home") d.FieldU1("copyrighted") d.FieldU1("copyright") frameLength := d.FieldU13("frame_length") dataLength := int64(frameLength - 7) if !protectionAbsent { // TODO: multuple RDBs CRCs dataLength -= 2 } d.FieldU11("buffer_fullness") numberOfRDBs := d.FieldUFn("number_of_rdbs", func(d *decode.D) uint64 { return d.U2() + 1 }) if !protectionAbsent { d.FieldU16("crc") } if dataLength < 0 { d.Fatalf("dataLength < 0") } d.FieldArray("raw_data_blocks", func(d *decode.D) { for i := uint64(0); i < numberOfRDBs; i++ { d.FieldFormatLen("raw_data_block", dataLength*8, aacFrameFormat, format.AACFrameIn{ObjectType: int(objectType)}) } }) return nil }