github.com/cnotch/ipchub@v1.1.0/av/format/rtp/h264_depacketizer.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 rtp 6 7 import ( 8 "fmt" 9 "time" 10 11 "github.com/cnotch/ipchub/av/codec" 12 "github.com/cnotch/ipchub/av/codec/h264" 13 ) 14 15 type h264Depacketizer struct { 16 fragments []*Packet // 分片包 17 meta *codec.VideoMeta 18 metaReady bool 19 nextDts float64 20 dtsStep float64 21 startOn time.Time 22 w codec.FrameWriter 23 syncClock SyncClock 24 } 25 26 // NewH264Depacketizer 实例化 H264 帧提取器 27 func NewH264Depacketizer(meta *codec.VideoMeta, w codec.FrameWriter) Depacketizer { 28 h264dp := &h264Depacketizer{ 29 meta: meta, 30 fragments: make([]*Packet, 0, 16), 31 w: w, 32 } 33 h264dp.syncClock.RTPTimeUnit = float64(time.Second) / float64(meta.ClockRate) 34 return h264dp 35 } 36 37 func (h264dp *h264Depacketizer) Control(basePts *int64, p *Packet) error { 38 if ok := h264dp.syncClock.Decode(p.Data); ok { 39 if *basePts == 0 { 40 *basePts = h264dp.syncClock.NTPTime 41 } 42 } 43 return nil 44 } 45 46 func (h264dp *h264Depacketizer) Depacketize(basePts int64, packet *Packet) (err error) { 47 if h264dp.syncClock.NTPTime == 0 { // 未收到同步时钟信息,忽略任意包 48 return 49 } 50 51 payload := packet.Payload() 52 if len(payload) < 3 { 53 return 54 } 55 56 // +---------------+ 57 // |0|1|2|3|4|5|6|7| 58 // +-+-+-+-+-+-+-+-+ 59 // |F|NRI| Type | 60 // +---------------+ 61 naluType := payload[0] & h264.NalTypeBitmask 62 63 switch { 64 case naluType < h264.NalStapaInRtp: 65 // h264 原生 nal 包 66 // 0 1 2 3 67 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 68 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 69 // |F|NRI| type | | 70 // +-+-+-+-+-+-+-+-+ | 71 // | | 72 // | Bytes 2..n of a Single NAL unit | 73 // | | 74 // | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 75 // | :...OPTIONAL RTP padding | 76 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 77 frame := &codec.Frame{ 78 MediaType: codec.MediaTypeVideo, 79 Payload: payload, 80 } 81 err = h264dp.writeFrame(basePts, packet.Timestamp, frame) 82 case naluType == h264.NalStapaInRtp: 83 err = h264dp.depacketizeStapa(basePts, packet) 84 case naluType == h264.NalFuAInRtp: 85 err = h264dp.depacketizeFuA(basePts, packet) 86 default: 87 err = fmt.Errorf("nalu type %d is currently not handled", naluType) 88 } 89 return 90 } 91 92 func (h264dp *h264Depacketizer) depacketizeStapa(basePts int64, packet *Packet) (err error) { 93 payload := packet.Payload() 94 header := payload[0] 95 96 // 0 1 2 3 97 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 98 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 99 // |STAP-A NAL HDR | NALU 1 Size | NALU 1 HDR | 100 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 101 // | NALU 1 Data | 102 // : : 103 // + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 104 // | | NALU 2 Size | NALU 2 HDR | 105 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 106 // | NALU 2 Data | 107 // : : 108 // | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 109 // | :...OPTIONAL RTP padding | 110 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 111 off := 1 // 跳过 STAP-A NAL HDR 112 // 循环读取被封装的NAL 113 for { 114 // nal长度 115 nalSize := ((uint16(payload[off])) << 8) | uint16(payload[off+1]) 116 if nalSize < 1 { 117 return 118 } 119 120 off += 2 121 frame := &codec.Frame{ 122 MediaType: codec.MediaTypeVideo, 123 Payload: make([]byte, nalSize), 124 } 125 copy(frame.Payload, payload[off:]) 126 frame.Payload[0] = 0 | (header & 0x60) | (frame.Payload[0] & 0x1F) 127 if err = h264dp.writeFrame(basePts, packet.Timestamp,frame); err != nil { 128 return 129 } 130 131 off += int(nalSize) 132 if off >= len(payload) { // 扫描完成 133 break 134 } 135 } 136 return 137 } 138 139 func (h264dp *h264Depacketizer) depacketizeFuA(basePts int64, packet *Packet) (err error) { 140 payload := packet.Payload() 141 header := payload[0] 142 143 // 0 1 2 3 144 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 145 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 146 // | FU indicator | FU header | | 147 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 148 // | | 149 // | FU payload | 150 // | | 151 // | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 152 // | :...OPTIONAL RTP padding | 153 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 154 // +---------------+ 155 // |0|1|2|3|4|5|6|7| 156 // +-+-+-+-+-+-+-+-+ 157 // |S|E|R| Type | 158 // +---------------+ 159 fuHeader := payload[1] 160 161 if (fuHeader>>7)&1 == 1 { // 第一个分片包 162 h264dp.fragments = h264dp.fragments[:0] 163 } 164 if len(h264dp.fragments) != 0 && 165 h264dp.fragments[len(h264dp.fragments)-1].SequenceNumber != packet.SequenceNumber-1 { 166 // Packet loss ? 167 h264dp.fragments = h264dp.fragments[:0] 168 return 169 } 170 171 // 缓存片段 172 h264dp.fragments = append(h264dp.fragments, packet) 173 174 if (fuHeader>>6)&1 == 1 { // 最后一个片段 175 frameLen := 1 // 计数帧总长,初始 naluType header len 176 for _, fragment := range h264dp.fragments { 177 frameLen += len(fragment.Payload()) - 2 178 } 179 180 frame := &codec.Frame{ 181 MediaType: codec.MediaTypeVideo, 182 Payload: make([]byte, frameLen)} 183 184 frame.Payload[0] = (header & 0x60) | (fuHeader & 0x1F) 185 offset := 1 186 for _, fragment := range h264dp.fragments { 187 payload := fragment.Payload()[2:] 188 copy(frame.Payload[offset:], payload) 189 offset += len(payload) 190 } 191 // 清空分片缓存 192 h264dp.fragments = h264dp.fragments[:0] 193 194 err = h264dp.writeFrame(basePts, packet.Timestamp,frame) 195 } 196 197 return 198 } 199 200 func (h264dp *h264Depacketizer) rtp2ntp(timestamp uint32) int64 { 201 return h264dp.syncClock.Rtp2Ntp(timestamp) 202 } 203 204 func (h264dp *h264Depacketizer) writeFrame(basePts int64, rtpTimestamp uint32, frame *codec.Frame) error { 205 nalType := frame.Payload[0] & 0x1f 206 switch nalType { 207 case h264.NalSps: 208 if len(h264dp.meta.Sps) == 0 { 209 h264dp.meta.Sps = frame.Payload 210 } 211 case h264.NalPps: 212 if len(h264dp.meta.Pps) == 0 { 213 h264dp.meta.Pps = frame.Payload 214 } 215 case h264.NalFillerData: // ?ignore... 216 return nil 217 } 218 219 if !h264dp.metaReady { 220 if !h264.MetadataIsReady(h264dp.meta) { 221 return nil 222 } 223 if h264dp.meta.FixedFrameRate { 224 h264dp.dtsStep = float64(time.Second) / h264dp.meta.FrameRate 225 } else { 226 h264dp.startOn = time.Now() 227 } 228 h264dp.metaReady = true 229 } 230 231 frame.Pts = h264dp.rtp2ntp(rtpTimestamp) - basePts+ptsDelay 232 if h264dp.dtsStep > 0 { 233 frame.Dts = int64(h264dp.nextDts) 234 h264dp.nextDts += h264dp.dtsStep 235 } else { 236 frame.Dts = int64(time.Now().Sub(h264dp.startOn)) 237 } 238 return h264dp.w.WriteFrame(frame) 239 }