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  }