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  }