github.com/cnotch/ipchub@v1.1.0/av/format/flv/flv.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 "encoding/binary" 9 "errors" 10 "fmt" 11 "io" 12 ) 13 14 // flv Header Size, total is 9Byte. 15 // Signatures 3Byte 'FLV' = 0x46 0x4c 0x56 16 // Version 1Byte 0x01 17 // TypeFlags 1Byte bit0:audio bit2:video 18 // DataOffset 4Byte FLV Header Length 19 const ( 20 FlvHeaderSize = 9 21 TypeFlagsVideo = 0x04 22 TypeFlagsAudio = 0x01 23 TypeFlagsOffset = 4 24 ) 25 26 var flvHeaderTemplate = []byte{0x46, 0x4c, 0x56, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09} 27 28 // Reader flv Reader 29 type Reader struct { 30 r io.Reader 31 Header [FlvHeaderSize]byte // flv header 32 } 33 34 // NewReader . 35 func NewReader(r io.Reader) (*Reader, error) { 36 reader := &Reader{ 37 r: r, 38 } 39 40 // read flv header 41 if _, err := io.ReadFull(r, reader.Header[:]); err != nil { 42 return nil, err 43 } 44 // 简单验证 45 if reader.Header[0] != 'F' || 46 reader.Header[1] != 'L' || 47 reader.Header[2] != 'V' { 48 return nil, errors.New("Signatures must is 'FLV'") 49 } 50 51 if previousTagSize, err := reader.readTagSize(); err != nil { 52 return nil, err 53 } else if previousTagSize != 0 { 54 return nil, errors.New("First 'PreviousTagSize' must is 0") 55 } 56 57 return reader, nil 58 } 59 60 func (r *Reader) readTagSize() (tagSize uint32, err error) { 61 var buff [4]byte 62 if _, err := io.ReadFull(r.r, buff[:]); err != nil { 63 return 0, err 64 } 65 66 tagSize = binary.BigEndian.Uint32(buff[:]) 67 return 68 } 69 70 // ReadFlvTag read flv tag 71 func (r *Reader) ReadFlvTag() (*Tag, error) { 72 73 var tag Tag 74 if err := tag.Read(r.r); err != nil { 75 return nil, err 76 } 77 78 if tagSize, err := r.readTagSize(); err != nil { 79 return nil, err 80 } else if tagSize != uint32(tag.Size()) { 81 return nil, fmt.Errorf("PreviousTagSize mismatches, expect '%d' but actual '%d'", 82 tag.Size(), tagSize) 83 } 84 return &tag, nil 85 } 86 87 // HasVideo flv include video stream. 88 func (r *Reader) HasVideo() bool { 89 return r.Header[TypeFlagsOffset]&TypeFlagsVideo != 0 90 } 91 92 // HasAudio flv include audio stream. 93 func (r *Reader) HasAudio() bool { 94 return r.Header[TypeFlagsOffset]&TypeFlagsAudio != 0 95 } 96 97 const uninitializedTimestampDelta = 0xffffffff 98 99 // Writer flv Writer 100 type Writer struct { 101 w io.Writer 102 timestampDelta uint32 // 流在中间输出时的相对时间戳 103 } 104 105 // NewWriter . 106 func NewWriter(w io.Writer, typeFlags byte) (*Writer, error) { 107 if typeFlags&0x05 == 0 { 108 return nil, errors.New("TypeFlags not include any streams") 109 } 110 111 writer := &Writer{ 112 w: w, 113 timestampDelta: uninitializedTimestampDelta, 114 } 115 116 var flvHeader [FlvHeaderSize]byte 117 copy(flvHeader[:], flvHeaderTemplate[:]) 118 flvHeader[TypeFlagsOffset] = typeFlags & (TypeFlagsVideo | TypeFlagsAudio) 119 if _, err := w.Write(flvHeader[:]); err != nil { 120 return nil, err 121 } 122 123 if err := writer.writeTagSize(0); err != nil { 124 return nil, err 125 } 126 127 return writer, nil 128 } 129 130 func (w *Writer) writeTagSize(tagSize uint32) error { 131 var buff [4]byte 132 // write PreviousTagSize 133 binary.BigEndian.PutUint32(buff[:], tagSize) 134 if _, err := w.w.Write(buff[:]); err != nil { 135 return err 136 } 137 return nil 138 } 139 140 // WriteFlvTag write flv tag 141 func (w *Writer) WriteFlvTag(tag *Tag) error { 142 // 记录第一个Tag的时间戳 143 if w.timestampDelta == uninitializedTimestampDelta { 144 w.timestampDelta = tag.Timestamp 145 } 146 147 if err := writeTag(w.w, tag, w.timestampDelta); err != nil { 148 return err 149 } 150 151 return w.writeTagSize(uint32(tag.Size())) 152 }