github.com/bluenviron/mediacommon@v1.9.3/pkg/formats/mpegts/track.go (about) 1 package mpegts 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/asticode/go-astits" 8 "github.com/bluenviron/mediacommon/pkg/codecs/ac3" 9 "github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio" 10 ) 11 12 const ( 13 h264Identifier = 'H'<<24 | 'D'<<16 | 'M'<<8 | 'V' 14 h265Identifier = 'H'<<24 | 'E'<<16 | 'V'<<8 | 'C' 15 opusIdentifier = 'O'<<24 | 'p'<<16 | 'u'<<8 | 's' 16 ) 17 18 var errUnsupportedCodec = errors.New("unsupported codec") 19 20 func findMPEG4AudioConfig(dem *astits.Demuxer, pid uint16) (*mpeg4audio.Config, error) { 21 for { 22 data, err := dem.NextData() 23 if err != nil { 24 return nil, err 25 } 26 27 if data.PES == nil || data.PID != pid { 28 continue 29 } 30 31 var adtsPkts mpeg4audio.ADTSPackets 32 err = adtsPkts.Unmarshal(data.PES.Data) 33 if err != nil { 34 return nil, fmt.Errorf("unable to decode ADTS: %w", err) 35 } 36 37 pkt := adtsPkts[0] 38 return &mpeg4audio.Config{ 39 Type: pkt.Type, 40 SampleRate: pkt.SampleRate, 41 ChannelCount: pkt.ChannelCount, 42 }, nil 43 } 44 } 45 46 func findAC3Parameters(dem *astits.Demuxer, pid uint16) (int, int, error) { 47 for { 48 data, err := dem.NextData() 49 if err != nil { 50 return 0, 0, err 51 } 52 53 if data.PES == nil || data.PID != pid { 54 continue 55 } 56 57 var syncInfo ac3.SyncInfo 58 err = syncInfo.Unmarshal(data.PES.Data) 59 if err != nil { 60 return 0, 0, fmt.Errorf("invalid AC-3 frame: %w", err) 61 } 62 63 var bsi ac3.BSI 64 err = bsi.Unmarshal(data.PES.Data[5:]) 65 if err != nil { 66 return 0, 0, fmt.Errorf("invalid AC-3 frame: %w", err) 67 } 68 69 return syncInfo.SampleRate(), bsi.ChannelCount(), nil 70 } 71 } 72 73 func findOpusRegistration(descriptors []*astits.Descriptor) bool { 74 for _, sd := range descriptors { 75 if sd.Registration != nil { 76 if sd.Registration.FormatIdentifier == opusIdentifier { 77 return true 78 } 79 } 80 } 81 return false 82 } 83 84 func findOpusChannelCount(descriptors []*astits.Descriptor) int { 85 for _, sd := range descriptors { 86 if sd.Extension != nil && sd.Extension.Tag == 0x80 && 87 sd.Extension.Unknown != nil && len(*sd.Extension.Unknown) >= 1 { 88 return int((*sd.Extension.Unknown)[0]) 89 } 90 } 91 return 0 92 } 93 94 func findOpusCodec(descriptors []*astits.Descriptor) *CodecOpus { 95 if !findOpusRegistration(descriptors) { 96 return nil 97 } 98 99 channelCount := findOpusChannelCount(descriptors) 100 if channelCount <= 0 { 101 return nil 102 } 103 104 return &CodecOpus{ 105 ChannelCount: channelCount, 106 } 107 } 108 109 // Track is a MPEG-TS track. 110 type Track struct { 111 PID uint16 112 Codec Codec 113 114 isLeading bool // Writer-only 115 mp3Checked bool // Writer-only 116 } 117 118 func (t *Track) marshal() (*astits.PMTElementaryStream, error) { 119 return t.Codec.marshal(t.PID) 120 } 121 122 func (t *Track) unmarshal(dem *astits.Demuxer, es *astits.PMTElementaryStream) error { 123 t.PID = es.ElementaryPID 124 125 switch es.StreamType { 126 case astits.StreamTypeH265Video: 127 t.Codec = &CodecH265{} 128 return nil 129 130 case astits.StreamTypeH264Video: 131 t.Codec = &CodecH264{} 132 return nil 133 134 case astits.StreamTypeMPEG4Video: 135 t.Codec = &CodecMPEG4Video{} 136 return nil 137 138 case astits.StreamTypeMPEG2Video, astits.StreamTypeMPEG1Video: 139 t.Codec = &CodecMPEG1Video{} 140 return nil 141 142 case astits.StreamTypeAACAudio: 143 conf, err := findMPEG4AudioConfig(dem, es.ElementaryPID) 144 if err != nil { 145 return err 146 } 147 148 t.Codec = &CodecMPEG4Audio{ 149 Config: *conf, 150 } 151 return nil 152 153 case astits.StreamTypeMPEG1Audio: 154 t.Codec = &CodecMPEG1Audio{} 155 return nil 156 157 case astits.StreamTypeAC3Audio: 158 sampleRate, channelCount, err := findAC3Parameters(dem, es.ElementaryPID) 159 if err != nil { 160 return err 161 } 162 163 t.Codec = &CodecAC3{ 164 SampleRate: sampleRate, 165 ChannelCount: channelCount, 166 } 167 return nil 168 169 case astits.StreamTypePrivateData: 170 codec := findOpusCodec(es.ElementaryStreamDescriptors) 171 if codec != nil { 172 t.Codec = codec 173 return nil 174 } 175 } 176 177 return errUnsupportedCodec 178 }