Skip to content

Commit

Permalink
Fix detectAACProfile function
Browse files Browse the repository at this point in the history
  • Loading branch information
sunfish-shogi committed Feb 20, 2021
1 parent 84c9503 commit d23f5a8
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 41 deletions.
10 changes: 6 additions & 4 deletions mp4tool/probe/probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func buildReport(r io.ReadSeeker) (*report, error) {
switch tr.Codec {
case mp4.CodecAVC1:
if tr.AVC != nil {
t.Codec = fmt.Sprintf("avc1.%02x%02x%02x",
t.Codec = fmt.Sprintf("avc1.%02X%02X%02X",
tr.AVC.Profile,
tr.AVC.ProfileCompatibility,
tr.AVC.Level,
Expand All @@ -134,10 +134,12 @@ func buildReport(r io.ReadSeeker) (*report, error) {
}
t.IDRFrameNum = len(idxs)
case mp4.CodecMP4A:
if tr.MP4A != nil {
t.Codec = fmt.Sprintf("mp4a.%d.%d", tr.MP4A.OTI, tr.MP4A.AudOTI)
} else {
if tr.MP4A == nil || tr.MP4A.OTI == 0 {
t.Codec = "mp4a"
} else if tr.MP4A.AudOTI == 0 {
t.Codec = fmt.Sprintf("mp4a.%X", tr.MP4A.OTI)
} else {
t.Codec = fmt.Sprintf("mp4a.%X.%d", tr.MP4A.OTI, tr.MP4A.AudOTI)
}
default:
t.Codec = "unknown"
Expand Down
86 changes: 50 additions & 36 deletions probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package mp4
import (
"bytes"
"errors"
"fmt"
"io"

"github.com/abema/go-mp4/bitio"
Expand Down Expand Up @@ -192,6 +191,7 @@ func probeTrak(r io.ReadSeeker, bi *BoxInfo) (*Track, error) {
{BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeEncv(), BoxTypeAvcC()},
{BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeMp4a()},
{BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeMp4a(), BoxTypeEsds()},
{BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeMp4a(), BoxTypeWave(), BoxTypeEsds()},
{BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeEnca()},
{BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStsd(), BoxTypeEnca(), BoxTypeEsds()},
{BoxTypeMdia(), BoxTypeMinf(), BoxTypeStbl(), BoxTypeStco()},
Expand Down Expand Up @@ -372,10 +372,10 @@ func probeTrak(r io.ReadSeeker, bi *BoxInfo) (*Track, error) {
func detectAACProfile(esds *Esds) (oti, audOTI uint8, err error) {
configDscr := findDescriptorByTag(esds.Descriptors, DecoderConfigDescrTag)
if configDscr == nil || configDscr.DecoderConfigDescriptor == nil {
return 0, 0, errors.New("DecoderConfigDescriptor not found")
return 0, 0, nil
}
if configDscr.DecoderConfigDescriptor.ObjectTypeIndication != 0x40 {
return 0, 0, fmt.Errorf("unsupported object type indication: %d", int(configDscr.DecoderConfigDescriptor.ObjectTypeIndication))
return configDscr.DecoderConfigDescriptor.ObjectTypeIndication, 0, nil
}

specificDscr := findDescriptorByTag(esds.Descriptors, DecSpecificInfoTag)
Expand All @@ -388,41 +388,34 @@ func detectAACProfile(esds *Esds) (oti, audOTI uint8, err error) {

// audio object type
audioObjectType, read, err := getAudioObjectType(r)
remaining -= read
if err != nil {
return 0, 0, err
}
remaining -= read

// sampling frequency index
samplingFrequencyIndex, err := r.ReadBits(4)
remaining -= 4
if err != nil {
return 0, 0, err
}
remaining -= 4
if samplingFrequencyIndex[0] == 0x0f {
if _, err = r.ReadBits(24); err != nil {
return 0, 0, err
}
remaining -= 24
}

// channel configuration
if _, err = r.ReadBits(4); err != nil {
return 0, 0, err
}
remaining -= 4

if audioObjectType == 5 || audioObjectType == 29 {
// HE-AAC
return 40, 5, nil

}

if remaining >= 16 {
if audioObjectType == 2 && remaining >= 20 {
if _, err = r.ReadBits(4); err != nil {
return 0, 0, err
}
remaining -= 4
syncExtensionType, err := r.ReadBits(11)
if err != nil {
return 0, 0, err
}
remaining -= 11
if syncExtensionType[0] == 0x2 && syncExtensionType[1] == 0xb7 {
extAudioObjectType, _, err := getAudioObjectType(r)
if err != nil {
Expand All @@ -433,20 +426,42 @@ func detectAACProfile(esds *Esds) (oti, audOTI uint8, err error) {
if err != nil {
return 0, 0, err
}
remaining--
if sbr[0] != 0 {
// HE-AAC
return 40, 5, nil
if extAudioObjectType == 5 {
sfi, err := r.ReadBits(4)
if err != nil {
return 0, 0, err
}
remaining -= 4
if sfi[0] == 0xf {
if _, err := r.ReadBits(24); err != nil {
return 0, 0, err
}
remaining -= 24
}
if remaining >= 12 {
syncExtensionType, err := r.ReadBits(11)
if err != nil {
return 0, 0, err
}
if syncExtensionType[0] == 0x5 && syncExtensionType[1] == 0x48 {
ps, err := r.ReadBits(1)
if err != nil {
return 0, 0, err
}
if ps[0] != 0 {
return 0x40, 29, nil
}
}
}
}
return 0x40, 5, nil
}
}
}
}

if audioObjectType == 2 {
// AAC-LC
return 40, 2, nil
}

return 0, 0, fmt.Errorf("unsupported audio object type: %d", audioObjectType)
return 0x40, audioObjectType, nil
}

func findDescriptorByTag(dscrs []Descriptor, tag int8) *Descriptor {
Expand All @@ -460,18 +475,17 @@ func findDescriptorByTag(dscrs []Descriptor, tag int8) *Descriptor {

func getAudioObjectType(r bitio.Reader) (byte, int, error) {
audioObjectType, err := r.ReadBits(5)
read := 5
if err != nil {
return 0, read, err
return 0, 0, err
}
if audioObjectType[0] == 0x1f {
audioObjectType, err = r.ReadBits(6)
read += 6
if err != nil {
return 0, read, err
}
if audioObjectType[0] != 0x1f {
return audioObjectType[0], 5, nil
}
audioObjectType, err = r.ReadBits(6)
if err != nil {
return 0, 0, err
}
return audioObjectType[0], read, nil
return audioObjectType[0] + 32, 11, nil
}

func probeMoof(r io.ReadSeeker, bi *BoxInfo) (*Segment, error) {
Expand Down
116 changes: 115 additions & 1 deletion probe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestProbe(t *testing.T) {
assert.Equal(t, uint32(44100), info.Tracks[1].Timescale)
assert.Equal(t, uint64(45124), info.Tracks[1].Duration)
assert.Equal(t, CodecMP4A, info.Tracks[1].Codec)
assert.Equal(t, uint8(40), info.Tracks[1].MP4A.OTI)
assert.Equal(t, uint8(0x40), info.Tracks[1].MP4A.OTI)
assert.Equal(t, uint8(2), info.Tracks[1].MP4A.AudOTI)
assert.Equal(t, uint16(2), info.Tracks[1].MP4A.ChannelCount)
assert.False(t, info.Tracks[1].Encrypted)
Expand Down Expand Up @@ -178,6 +178,120 @@ func TestProbeFra(t *testing.T) {
assert.Equal(t, int32(0), info.Segments[3].CompositionTimeOffset)
}

func TestDetectAACProfile(t *testing.T) {
testCases := []struct {
name string
esds *Esds
expectedOTI uint8
expectedAudOTI uint8
}{
{
name: "40.2",
esds: &Esds{
Descriptors: []Descriptor{
{Tag: DecoderConfigDescrTag, DecoderConfigDescriptor: &DecoderConfigDescriptor{ObjectTypeIndication: 0x40}},
{Tag: DecSpecificInfoTag, Data: []byte{
// audio-object-type=0x2 (5bits), sample-frequency-index (4bits), padding (7bits)
0x10, 0x00,
}},
},
},
expectedOTI: 0x40,
expectedAudOTI: 2,
},
{
name: "40.5 ExtAudType=5 SBR=1 SFI=0x0",
esds: &Esds{
Descriptors: []Descriptor{
{Tag: DecoderConfigDescrTag, DecoderConfigDescriptor: &DecoderConfigDescriptor{ObjectTypeIndication: 0x40}},
{Tag: DecSpecificInfoTag, Data: []byte{
// audio-object-type=0x2 (5bits), sample-frequency-index (4bits),
// channel config (4bits), sync-extension-type=0x2b7 (11bits),
0x10, 0x02, 0xb7,
// audio-object-type=0x5 (5bits), sbr=1 (1bit), sfi=0x0 (4bits), padding (6bits)
0x2c, 0x00,
}},
},
},
expectedOTI: 0x40,
expectedAudOTI: 5,
},
{
name: "40.29 ExtAudType=5 SBR=1 SFI=0xf PS=1",
esds: &Esds{
Descriptors: []Descriptor{
{Tag: DecoderConfigDescrTag, DecoderConfigDescriptor: &DecoderConfigDescriptor{ObjectTypeIndication: 0x40}},
{Tag: DecSpecificInfoTag, Data: []byte{
// audio-object-type=0x2 (5bits), sample-frequency-index (4bits),
// channel config (4bits), sync-extension-type=0x2b7 (11bits),
0x10, 0x02, 0xb7,
// audio-object-type=0x5 (5bits), sbr=1 (1bit), sfi=0xf (4bits), ext (24bits),
// sync-extension-type=0x548 (11bits), ps=1 (1bit), padding (2bits)
0x2f, 0xc0, 0x00, 0x00, 0x2a, 0x44,
}},
},
},
expectedOTI: 0x40,
expectedAudOTI: 29,
},
{
name: "40.5 ExtAudType=5 SBR=1 SFI=0xf PS=0",
esds: &Esds{
Descriptors: []Descriptor{
{Tag: DecoderConfigDescrTag, DecoderConfigDescriptor: &DecoderConfigDescriptor{ObjectTypeIndication: 0x40}},
{Tag: DecSpecificInfoTag, Data: []byte{
// audio-object-type=0x2 (5bits), sample-frequency-index (4bits),
// channel config (4bits), sync-extension-type=0x2b7 (11bits),
0x10, 0x02, 0xb7,
// audio-object-type=0x5 (5bits), sbr=1 (1bit), sfi=0xf (4bits), ext (24bits),
// sync-extension-type=0x548 (11bits), ps=1 (1bit), padding (2bits)
0x2f, 0xc0, 0x00, 0x00, 0x2a, 0x40,
}},
},
},
expectedOTI: 0x40,
expectedAudOTI: 5,
},
{
name: "40.2 sample-frequency-index=0xf",
esds: &Esds{
Descriptors: []Descriptor{
{Tag: DecoderConfigDescrTag, DecoderConfigDescriptor: &DecoderConfigDescriptor{ObjectTypeIndication: 0x40}},
{Tag: DecSpecificInfoTag, Data: []byte{
// audio-object-type=0x2 (5bits), sample-frequency-index=0xf (4bits), ext(24bits), padding(7bits)
0x17, 0x80, 0x00, 0x00, 0x00,
}},
},
},
expectedOTI: 0x40,
expectedAudOTI: 2,
},
{
name: "40.42",
esds: &Esds{
Descriptors: []Descriptor{
{Tag: DecoderConfigDescrTag, DecoderConfigDescriptor: &DecoderConfigDescriptor{ObjectTypeIndication: 0x40}},
{Tag: DecSpecificInfoTag, Data: []byte{
// audio-object-type=0x1f (5bits), 0xa (6bits)
// sample-frequency-index (4bits), padding (1bits)
0x15, 0x00,
}},
},
},
expectedOTI: 0x40,
expectedAudOTI: 2,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
oti, audOTI, err := detectAACProfile(tc.esds)
require.NoError(t, err)
assert.Equal(t, tc.expectedOTI, oti)
assert.Equal(t, tc.expectedAudOTI, audOTI)
})
}
}

func TestSamplesGetBitrate(t *testing.T) {
assert.Equal(t, uint64(0), Samples{}.GetBitrate(100))

Expand Down

0 comments on commit d23f5a8

Please sign in to comment.