github.com/cnotch/ipchub@v1.1.0/av/format/rtp/h265_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 "time" 9 10 "github.com/cnotch/ipchub/av/codec" 11 "github.com/cnotch/ipchub/av/codec/hevc" 12 ) 13 14 type h265Depacketizer struct { 15 fragments []*Packet // 分片包 16 meta *codec.VideoMeta 17 metaReady bool 18 nextDts float64 19 dtsStep float64 20 startOn time.Time 21 w codec.FrameWriter 22 syncClock SyncClock 23 } 24 25 // NewH265Depacketizer 实例化 H265 帧提取器 26 func NewH265Depacketizer(meta *codec.VideoMeta, w codec.FrameWriter) Depacketizer { 27 h265dp := &h265Depacketizer{ 28 meta: meta, 29 fragments: make([]*Packet, 0, 16), 30 w: w, 31 } 32 h265dp.syncClock.RTPTimeUnit = float64(time.Second) / float64(meta.ClockRate) 33 return h265dp 34 } 35 36 func (h265dp *h265Depacketizer) Control(basePts *int64, p *Packet) error { 37 if ok := h265dp.syncClock.Decode(p.Data); ok { 38 if *basePts == 0 { 39 *basePts = h265dp.syncClock.NTPTime 40 } 41 } 42 return nil 43 } 44 45 /* 46 * decode the HEVC payload header according to section 4 of draft version 6: 47 * 48 * 0 1 49 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 50 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 51 * |F| Type | LayerId | TID | 52 * +-------------+-----------------+ 53 * 54 * Forbidden zero (F): 1 bit 55 * NAL unit type (Type): 6 bits 56 * NUH layer ID (LayerId): 6 bits 57 * NUH temporal ID plus 1 (TID): 3 bits 58 * decode the FU header 59 * 60 * 0 1 2 3 4 5 6 7 61 * +-+-+-+-+-+-+-+-+ 62 * |S|E| FuType | 63 * +---------------+ 64 * 65 * Start fragment (S): 1 bit 66 * End fragment (E): 1 bit 67 * FuType: 6 bits 68 */ 69 func (h265dp *h265Depacketizer) Depacketize(basePts int64, packet *Packet) (err error) { 70 if h265dp.syncClock.NTPTime == 0 { // 未收到同步时钟信息,忽略任意包 71 return 72 } 73 74 payload := packet.Payload() 75 if len(payload) < 3 { 76 return 77 } 78 79 naluType := (payload[0] >> 1) & 0x3f 80 81 switch naluType { 82 case hevc.NalStapInRtp: // 在RTP中的聚合(AP) 83 return h265dp.depacketizeStap(basePts, packet) 84 case hevc.NalFuInRtp: // 在RTP中的扩展,分片(FU) 85 return h265dp.depacketizeFu(basePts, packet) 86 default: 87 frame := &codec.Frame{ 88 MediaType: codec.MediaTypeVideo, 89 Payload: payload, 90 } 91 err = h265dp.writeFrame(basePts, packet.Timestamp, frame) 92 return 93 } 94 } 95 96 func (h265dp *h265Depacketizer) depacketizeStap(basePts int64, packet *Packet) (err error) { 97 payload := packet.Payload() 98 off := 2 // 跳过 STAP NAL HDR 99 100 // 循环读取被封装的NAL 101 for { 102 // nal长度 103 nalSize := ((uint16(payload[off])) << 8) | uint16(payload[off+1]) 104 if nalSize < 1 { 105 return 106 } 107 108 off += 2 109 frame := &codec.Frame{ 110 MediaType: codec.MediaTypeVideo, 111 Payload: make([]byte, nalSize), 112 } 113 copy(frame.Payload, payload[off:]) 114 if err = h265dp.writeFrame(basePts, packet.Timestamp, frame); err != nil { 115 return 116 } 117 off += int(nalSize) 118 if off >= len(payload) { // 扫描完成 119 break 120 } 121 } 122 return 123 } 124 125 func (h265dp *h265Depacketizer) depacketizeFu(basePts int64, packet *Packet) (err error) { 126 payload := packet.Payload() 127 rawDataOffset := 3 // 原始数据的偏移 = FU indicator + header 128 129 // 0 1 2 3 4 5 6 7 130 // +-+-+-+-+-+-+-+-+ 131 // |S|E| FuType | 132 // +---------------+ 133 fuHeader := payload[2] 134 135 if (fuHeader>>7)&1 == 1 { // 第一个分片包 136 h265dp.fragments = h265dp.fragments[:0] 137 // 缓存片段 138 h265dp.fragments = append(h265dp.fragments, packet) 139 return 140 } 141 142 if len(h265dp.fragments) == 0 || (len(h265dp.fragments) != 0 && 143 h265dp.fragments[len(h265dp.fragments)-1].SequenceNumber != packet.SequenceNumber-1) { 144 // Packet loss ? 145 h265dp.fragments = h265dp.fragments[:0] 146 return 147 } 148 149 // 缓存其他片段 150 h265dp.fragments = append(h265dp.fragments, packet) 151 152 if (fuHeader>>6)&1 == 1 { // 最后一个片段 153 frameLen := 2 // 计算帧总长,初始 naluType header len 154 for _, fragment := range h265dp.fragments { 155 frameLen += len(fragment.Payload()) - rawDataOffset 156 } 157 158 frame := &codec.Frame{ 159 MediaType: codec.MediaTypeVideo, 160 Payload: make([]byte, frameLen), 161 } 162 163 frame.Payload[0] = (payload[0] & 0x81) | (fuHeader&0x3f)<<1 164 frame.Payload[1] = payload[1] 165 offset := 2 166 for _, fragment := range h265dp.fragments { 167 payload := fragment.Payload()[rawDataOffset:] 168 copy(frame.Payload[offset:], payload) 169 offset += len(payload) 170 } 171 // 清空分片缓存 172 h265dp.fragments = h265dp.fragments[:0] 173 174 err = h265dp.writeFrame(basePts, packet.Timestamp, frame) 175 } 176 177 return 178 } 179 180 func (h265dp *h265Depacketizer) rtp2ntp(timestamp uint32) int64 { 181 return h265dp.syncClock.Rtp2Ntp(timestamp) 182 } 183 184 func (h265dp *h265Depacketizer) writeFrame(basePts int64, rtpTimestamp uint32, frame *codec.Frame) error { 185 nalType := (frame.Payload[0] >> 1) & 0x3f 186 switch nalType { 187 case hevc.NalVps: 188 if len(h265dp.meta.Vps) == 0 { 189 h265dp.meta.Vps = frame.Payload 190 } 191 case hevc.NalSps: 192 if len(h265dp.meta.Sps) == 0 { 193 h265dp.meta.Sps = frame.Payload 194 } 195 case hevc.NalPps: 196 if len(h265dp.meta.Pps) == 0 { 197 h265dp.meta.Pps = frame.Payload 198 } 199 } 200 201 if !h265dp.metaReady { 202 if !hevc.MetadataIsReady(h265dp.meta) { 203 return nil 204 } 205 if h265dp.meta.FixedFrameRate { 206 h265dp.dtsStep = float64(time.Second) / h265dp.meta.FrameRate 207 } else { 208 h265dp.startOn = time.Now() 209 } 210 h265dp.metaReady = true 211 } 212 213 frame.Pts = h265dp.rtp2ntp(rtpTimestamp) - basePts + ptsDelay 214 if h265dp.dtsStep > 0 { 215 frame.Dts = int64(h265dp.nextDts) 216 h265dp.nextDts += h265dp.dtsStep 217 } else { 218 frame.Dts = int64(time.Now().Sub(h265dp.startOn)) 219 } 220 return h265dp.w.WriteFrame(frame) 221 }