github.com/cnotch/ipchub@v1.1.0/av/format/mpegts/frame.go (about)

     1  // Copyright calabashdad. https://github.com/calabashdad/seal.git
     2  //
     3  // Copyright (c) 2019,CAOHONGJU All rights reserved.
     4  // Use of this source code is governed by a MIT-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package mpegts
     8  
     9  import (
    10  	"github.com/cnotch/ipchub/av/codec/aac"
    11  	"github.com/cnotch/ipchub/av/codec/h264"
    12  )
    13  
    14  // the mpegts header specifed the video/audio pid.
    15  const (
    16  	tsVideoPid = 256
    17  	tsAudioPid = 257
    18  )
    19  
    20  // the mpegts header specifed the stream id.
    21  const (
    22  	tsAudioAac = 0xc0 // ts aac stream id.
    23  	tsVideoAvc = 0xe0 // ts avc stream id.
    24  )
    25  
    26  // Frame mpegts frame
    27  type Frame struct {
    28  	Pid      int
    29  	StreamID int
    30  	Dts      int64
    31  	Pts      int64
    32  	Header   []byte // 1. AAC-ADTS Header; 2. aud nal [+sps nal+pps nal]+sample nal start code
    33  	Payload  []byte // data without startcode
    34  	key      bool
    35  }
    36  
    37  // IsVideo .
    38  func (frame *Frame) IsVideo() bool {
    39  	return frame.Pid == tsVideoPid
    40  }
    41  
    42  // IsAudio .
    43  func (frame *Frame) IsAudio() bool {
    44  	return frame.Pid == tsAudioPid
    45  }
    46  
    47  // IsKeyFrame 判断是否是 video key frame
    48  func (frame *Frame) IsKeyFrame() bool {
    49  	return frame.key
    50  }
    51  
    52  func (frame *Frame) prepareAvcHeader(sps, pps []byte) {
    53  	// a ts sample is format as:
    54  	// 00 00 00 01 // header
    55  	//       xxxxxxx // data bytes
    56  	// 00 00 01 // continue header
    57  	//       xxxxxxx // data bytes.
    58  	// so, for each sample, we append header in aud_nal, then appends the bytes in sample.
    59  	// for type1/5/6, insert aud packet.
    60  	audNal := []byte{0x00, 0x00, 0x00, 0x01, 0x09, 0xf0}
    61  
    62  	// step 1:
    63  	// first, before each "real" sample,
    64  	// we add some packets according to the nal_unit_type,
    65  	// for example, when got nal_unit_type=5, insert SPS/PPS before sample.
    66  
    67  	// 5bits, 7.3.1 NAL unit syntax,
    68  	// H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
    69  	var nalUnitType uint8
    70  	nalUnitType = frame.Payload[0]
    71  	nalUnitType &= 0x1f
    72  
    73  	// 6: Supplemental enhancement information (SEI) sei_rbsp( ), page 61
    74  	// @see: ngx_rtmp_hls_append_aud
    75  	// @remark, when got type 9, we donot send aud_nal, but it will make ios unhappy, so we remove it.
    76  	if h264.NalSlice == nalUnitType || h264.NalIdrSlice == nalUnitType || h264.NalSei == nalUnitType {
    77  		frame.Header = append(frame.Header, audNal...)
    78  	}
    79  
    80  	// 5: Coded slice of an IDR picture.
    81  	// insert sps/pps before IDR or key frame is ok.
    82  	if h264.NalIdrSlice == nalUnitType {
    83  		// @see: ngx_rtmp_hls_append_sps_pps
    84  		if len(sps) > 0 {
    85  			// AnnexB prefix, for sps always 4 bytes header
    86  			frame.Header = append(frame.Header, audNal[:4]...)
    87  			// sps
    88  			frame.Header = append(frame.Header, sps...)
    89  		}
    90  
    91  		if len(pps) > 0 {
    92  			// AnnexB prefix, for pps always 4 bytes header
    93  			frame.Header = append(frame.Header, audNal[:4]...)
    94  			// pps
    95  			frame.Header = append(frame.Header, pps...)
    96  		}
    97  	}
    98  
    99  	// 7-9, ignore, @see: ngx_rtmp_hls_video
   100  	if nalUnitType >= h264.NalSps && nalUnitType <= h264.NalAud {
   101  		return
   102  	}
   103  
   104  	// step 2:
   105  	// output the "real" sample, in buf.
   106  	// when we output some special assist packets according to nal_unit_type
   107  
   108  	// sample start prefix, '00 00 00 01' or '00 00 01'
   109  	pAudnal := 0 + 1
   110  	endAudnal := pAudnal + 3
   111  
   112  	// first AnnexB prefix is long (4 bytes)
   113  	if 0 == len(frame.Header) {
   114  		pAudnal = 0
   115  	}
   116  	frame.Header = append(frame.Header, audNal[pAudnal:pAudnal+endAudnal-pAudnal]...)
   117  
   118  	return
   119  }
   120  
   121  func (frame *Frame) prepareAacHeader(sps *aac.RawSPS) {
   122  	adtsHeader := sps.ToAdtsHeader(len(frame.Payload))
   123  	frame.Header = adtsHeader[:]
   124  	return
   125  }
   126  
   127  // FrameWriter 包装 WriteMpegtsFrame 方法的接口
   128  type FrameWriter interface {
   129  	WriteMpegtsFrame(frame *Frame) error
   130  }