github.com/cnotch/ipchub@v1.1.0/av/format/flv/tag.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 flv
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"io"
    11  
    12  	"github.com/cnotch/ipchub/av/format/amf"
    13  )
    14  
    15  // flv 标记类型ID
    16  const (
    17  	TagTypeAudio    = 0x08
    18  	TagTypeVideo    = 0x09
    19  	TagTypeAmf0Data = 0x12 // 18
    20  )
    21  
    22  // flv Tag Header Size, total is 11Byte.
    23  // 	filter + type 1Byte
    24  // 	data size 	3Byte
    25  // 	timestamp 	3Byte
    26  // 	timestampEx 1Byte
    27  // 	streamID 	3Byte always is 0
    28  const (
    29  	TagHeaderSize = 11
    30  )
    31  
    32  // Tag FLV Tag
    33  type Tag struct {
    34  	Reserved  byte   // 2 bits; 用于 FMS 的保留字段值为 0
    35  	Filter    byte   // 1 bits; 未加密文件中此值为 0,加密文件中此值为 1
    36  	TagType   byte   // 5 bits
    37  	DataSize  uint32 // 24 bits; Tag 中 Data 长度
    38  	Timestamp uint32 // 24 bits(Timestamp) + 8 bits(TimestampExtended); 单位是毫秒的时间戳,FLV 文件中第一个 Tag 的 DTS 总为 0
    39  	StreamID  uint32 // 24 bits; 总为 0
    40  	Data      []byte // Tag 包含的数据
    41  }
    42  
    43  // TagWriter 包装 WriteTag 方法的接口
    44  type TagWriter interface {
    45  	WriteFlvTag(tag *Tag) error
    46  }
    47  
    48  // Size tag 的总大小(包括 Header + Data)
    49  func (tag *Tag) Size() int {
    50  	return TagHeaderSize + len(tag.Data)
    51  }
    52  
    53  // IsVideo 判断是否是视频 Tag
    54  func (tag *Tag) IsVideo() bool {
    55  	return tag.TagType == TagTypeVideo
    56  }
    57  
    58  // IsAudio 判断是否是音频 Tag
    59  func (tag *Tag) IsAudio() bool {
    60  	return tag.TagType == TagTypeAudio
    61  }
    62  
    63  // IsMetadata 判断是否是元数据 tag
    64  func (tag *Tag) IsMetadata() bool {
    65  	if tag.TagType != TagTypeAmf0Data {
    66  		return false
    67  	}
    68  
    69  	buff := bytes.NewReader(tag.Data)
    70  	if name, err := amf.ReadString(buff); err == nil {
    71  		return name == ScriptOnMetaData
    72  	}
    73  	return false
    74  }
    75  
    76  // IsH2645KeyFrame 判断是否是 H264/H265 关键帧 Tag
    77  func (tag *Tag) IsH2645KeyFrame() bool {
    78  	if len(tag.Data) < 2 {
    79  		return false
    80  	}
    81  
    82  	return tag.TagType == TagTypeVideo &&
    83  		((tag.Data[0]&0x0f) == CodecIDAVC || ((tag.Data[0] & 0x0f) == CodecIDHEVC)) &&
    84  		((tag.Data[0]>>4)&0x0f) == FrameTypeKeyFrame
    85  }
    86  
    87  // IsH2645SequenceHeader 判断是否是 H264/H265 序列头 Tag
    88  func (tag *Tag) IsH2645SequenceHeader() bool {
    89  	if len(tag.Data) < 2 {
    90  		return false
    91  	}
    92  
    93  	return tag.TagType == TagTypeVideo &&
    94  		((tag.Data[0]&0x0f) == CodecIDAVC || ((tag.Data[0] & 0x0f) == CodecIDHEVC)) &&
    95  		((tag.Data[0]>>4)&0x0f) == FrameTypeKeyFrame &&
    96  		tag.Data[1] == H2645PacketTypeSequenceHeader
    97  }
    98  
    99  // IsAACSequenceHeader 判断是否是 AAC 序列头 Tag
   100  func (tag *Tag) IsAACSequenceHeader() bool {
   101  	if len(tag.Data) < 2 {
   102  		return false
   103  	}
   104  
   105  	return tag.TagType == TagTypeAudio &&
   106  		((tag.Data[0]>>4)&0x0f == SoundFormatAAC) &&
   107  		tag.Data[1] == AACPacketTypeSequenceHeader
   108  }
   109  
   110  // Read 根据规范的格式从 r 中读取 flv Tag。
   111  func (tag *Tag) Read(r io.Reader) error {
   112  	var tagHeader [TagHeaderSize]byte
   113  	if _, err := io.ReadFull(r, tagHeader[:]); err != nil {
   114  		return err
   115  	}
   116  
   117  	offset := 0
   118  
   119  	// filter & type
   120  	tag.Filter = (tagHeader[offset] << 2) >> 7
   121  	tag.TagType = tagHeader[offset] & 0x1F
   122  	offset++
   123  
   124  	// data size
   125  	tag.DataSize = binary.BigEndian.Uint32(tagHeader[offset:])
   126  	tag.DataSize = tag.DataSize >> 8
   127  	offset += 3
   128  
   129  	// timestamp & timestamp extended
   130  	timestamp := binary.BigEndian.Uint32(tagHeader[offset:])
   131  	tag.Timestamp = (timestamp >> 8) | (timestamp << 24)
   132  	offset += 3 // 为 stream id 多留一个高位字节
   133  
   134  	// stream id
   135  	tag.StreamID = binary.BigEndian.Uint32(tagHeader[offset:]) & 0xffffff
   136  
   137  	tag.Data = make([]byte, tag.DataSize)
   138  	if _, err := io.ReadFull(r, tag.Data); err != nil {
   139  		return err
   140  	}
   141  	return nil
   142  }
   143  
   144  // Write 根据规范将 flv Tag 输出到 w。
   145  func (tag *Tag) Write(w io.Writer) error {
   146  	return writeTag(w, tag, 0)
   147  }
   148  
   149  func writeTag(w io.Writer, tag *Tag, timestampDelta uint32) error {
   150  	var tagHeader [TagHeaderSize + 1]byte // 为 stream id 多留一个高位字节
   151  	offset := 0
   152  
   153  	// data size
   154  	binary.BigEndian.PutUint32(tagHeader[offset:], uint32(len(tag.Data)))
   155  
   156  	// type
   157  	tagHeader[offset] = ((tag.Filter & 0x1) << 5) | (tag.TagType & 0x1f)
   158  	offset += 4
   159  
   160  	// timestamp
   161  	timestamp := tag.Timestamp - timestampDelta
   162  	binary.BigEndian.PutUint32(tagHeader[offset:], (timestamp<<8)|(timestamp>>24))
   163  	offset += 4
   164  
   165  	// stream id
   166  	binary.BigEndian.PutUint32(tagHeader[offset:], tag.StreamID<<8)
   167  	offset += 3
   168  
   169  	// write tag header
   170  	if _, err := w.Write(tagHeader[:offset]); err != nil {
   171  		return err
   172  	}
   173  
   174  	// write tag data
   175  	if _, err := w.Write(tag.Data); err != nil {
   176  		return err
   177  	}
   178  
   179  	return nil
   180  }
   181  
   182  // TagData tag data
   183  type TagData interface {
   184  	Marshal() ([]byte, error)
   185  	Unmarshal(data []byte) error
   186  }