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 }