github.com/cnotch/ipchub@v1.1.0/av/format/sdp/parsemeta.go (about) 1 // Copyright (c) 2019,CAOHONGJU All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE file. 4 5 package sdp 6 7 import ( 8 "encoding/base64" 9 "encoding/hex" 10 "strings" 11 12 "github.com/cnotch/ipchub/av/codec" 13 "github.com/cnotch/ipchub/av/codec/aac" 14 "github.com/cnotch/ipchub/av/codec/h264" 15 "github.com/cnotch/ipchub/av/codec/hevc" 16 "github.com/cnotch/ipchub/utils/scan" 17 "github.com/pixelbender/go-sdp/sdp" 18 ) 19 20 func ParseMetadata(rawsdp string, video *codec.VideoMeta, audio *codec.AudioMeta) error { 21 sdp, err := sdp.ParseString(rawsdp) 22 if err != nil { 23 return err 24 } 25 26 for _, media := range sdp.Media { 27 switch media.Type { 28 case "video": 29 video.Codec = media.Format[0].Name 30 if video.Codec != "" { 31 for _, bw := range media.Bandwidth { 32 if bw.Type == "AS" { 33 video.DataRate = float64(bw.Value) 34 } 35 } 36 parseVideoMeta(media.Format[0], video) 37 } 38 39 case "audio": 40 audio.Codec = media.Format[0].Name 41 if audio.Codec == "MPEG4-GENERIC" { 42 audio.Codec = "AAC" 43 } 44 45 if audio.Codec != "" { 46 for _, bw := range media.Bandwidth { 47 if bw.Type == "AS" { 48 audio.DataRate = float64(bw.Value) 49 } 50 } 51 parseAudioMeta(media.Format[0], audio) 52 } 53 } 54 } 55 return nil 56 } 57 58 func parseAudioMeta(m *sdp.Format, audio *codec.AudioMeta) { 59 audio.SampleSize = 16 60 audio.Channels = 2 61 audio.SampleRate = 44100 62 if m.ClockRate > 0 { 63 audio.SampleRate = m.ClockRate 64 } 65 if m.Channels > 0 { 66 audio.Channels = m.Channels 67 } 68 69 // parse AAC config 70 if len(m.Params) == 0 { 71 return 72 } 73 if audio.Codec == "AAC" { 74 for _, p := range m.Params { 75 i := strings.Index(p, "config=") 76 if i < 0 { 77 continue 78 } 79 p = p[i+len("config="):] 80 81 endi := strings.IndexByte(p, ';') 82 if endi > -1 { 83 p = p[:endi] 84 } 85 86 var config []byte 87 var err error 88 if config, err = hex.DecodeString(p); err != nil { 89 config = aac.Encode2BytesASC(2, 90 byte(aac.SamplingIndex(audio.SampleRate)), 91 byte(audio.Channels)) 92 } 93 94 // audio.SetParameterSet(aac.ParameterSetConfig, config) 95 audio.Sps = config 96 _ = aac.MetadataIsReady(audio) 97 break 98 } 99 } 100 101 } 102 103 func parseVideoMeta(m *sdp.Format, video *codec.VideoMeta) { 104 if m.ClockRate > 0 { 105 video.ClockRate = m.ClockRate 106 } 107 108 if len(m.Params) == 0 { 109 return 110 } 111 switch video.Codec { 112 case "h264", "H264": 113 video.Codec = "H264" 114 for _, p := range m.Params { 115 i := strings.Index(p, "sprop-parameter-sets=") 116 if i < 0 { 117 continue 118 } 119 p = p[i+len("sprop-parameter-sets="):] 120 121 endi := strings.IndexByte(p, ';') 122 if endi > -1 { 123 p = p[:endi] 124 } 125 parseH264SpsPps(p, video) 126 break 127 } 128 case "h265", "H265", "hevc", "HEVC": 129 video.Codec = "H265" 130 for _, p := range m.Params { 131 i := strings.Index(p, "sprop-") 132 if i < 0 { 133 continue 134 } 135 parseH265VpsSpsPps(p[i:], video) 136 break 137 } 138 } 139 } 140 141 func parseH264SpsPps(s string, video *codec.VideoMeta) { 142 ppsStr, spsStr, ok := scan.Comma.Scan(s) 143 if !ok { 144 return 145 } 146 147 sps, err := base64.StdEncoding.DecodeString(spsStr) 148 if err == nil { 149 // video.SetParameterSet(h264.ParameterSetSps, sps) 150 video.Sps = sps 151 } 152 153 pps, err := base64.StdEncoding.DecodeString(ppsStr) 154 if err == nil { 155 // video.SetParameterSet(h264.ParameterSetPps, pps) 156 video.Pps = pps 157 } 158 159 _ = h264.MetadataIsReady(video) 160 } 161 162 func parseH265VpsSpsPps(s string, video *codec.VideoMeta) { 163 var advance, token string 164 continueScan := true 165 advance = s 166 for continueScan { 167 advance, token, continueScan = scan.Semicolon.Scan(advance) 168 name, value, ok := scan.EqualPair.Scan(token) 169 if ok { 170 var ps *[]byte 171 var err error 172 switch name { 173 case "sprop-vps": 174 ps = &video.Vps 175 case "sprop-sps": 176 ps = &video.Sps 177 case "sprop-pps": 178 ps = &video.Pps 179 } 180 if ps == nil { 181 continue 182 } 183 184 if *ps, err = base64.StdEncoding.DecodeString(value); err != nil { 185 return 186 } 187 } 188 } 189 190 _ = hevc.MetadataIsReady(video) 191 }