github.com/bluenviron/mediacommon@v1.9.3/pkg/codecs/vp9/header.go (about) 1 package vp9 2 3 import ( 4 "fmt" 5 6 "github.com/bluenviron/mediacommon/pkg/bits" 7 ) 8 9 // FrameType is a frame type. 10 type FrameType bool 11 12 // frame types. 13 const ( 14 FrameTypeKeyFrame FrameType = false 15 FrameTypeNonKeyFrame FrameType = true 16 ) 17 18 // Header_ColorConfig is the color_config member of an header. 19 type Header_ColorConfig struct { //nolint:revive 20 TenOrTwelveBit bool 21 BitDepth uint8 22 ColorSpace uint8 23 ColorRange bool 24 SubsamplingX bool 25 SubsamplingY bool 26 } 27 28 func (c *Header_ColorConfig) unmarshal(profile uint8, buf []byte, pos *int) error { 29 if profile >= 2 { 30 var err error 31 c.TenOrTwelveBit, err = bits.ReadFlag(buf, pos) 32 if err != nil { 33 return err 34 } 35 36 if c.TenOrTwelveBit { 37 c.BitDepth = 12 38 } else { 39 c.BitDepth = 10 40 } 41 } else { 42 c.BitDepth = 8 43 } 44 45 tmp, err := bits.ReadBits(buf, pos, 3) 46 if err != nil { 47 return err 48 } 49 c.ColorSpace = uint8(tmp) 50 51 if c.ColorSpace != 7 { 52 var err error 53 c.ColorRange, err = bits.ReadFlag(buf, pos) 54 if err != nil { 55 return err 56 } 57 58 if profile == 1 || profile == 3 { 59 err := bits.HasSpace(buf, *pos, 3) 60 if err != nil { 61 return err 62 } 63 64 c.SubsamplingX = bits.ReadFlagUnsafe(buf, pos) 65 c.SubsamplingY = bits.ReadFlagUnsafe(buf, pos) 66 *pos++ 67 } else { 68 c.SubsamplingX = true 69 c.SubsamplingY = true 70 } 71 } else { 72 c.ColorRange = true 73 74 if profile == 1 || profile == 3 { 75 c.SubsamplingX = false 76 c.SubsamplingY = false 77 78 err := bits.HasSpace(buf, *pos, 1) 79 if err != nil { 80 return err 81 } 82 *pos++ 83 } 84 } 85 86 return nil 87 } 88 89 // Header_FrameSize is the frame_size member of an header. 90 type Header_FrameSize struct { //nolint:revive 91 FrameWidthMinus1 uint16 92 FrameHeightMinus1 uint16 93 } 94 95 func (s *Header_FrameSize) unmarshal(buf []byte, pos *int) error { 96 err := bits.HasSpace(buf, *pos, 32) 97 if err != nil { 98 return err 99 } 100 101 s.FrameWidthMinus1 = uint16(bits.ReadBitsUnsafe(buf, pos, 16)) 102 s.FrameHeightMinus1 = uint16(bits.ReadBitsUnsafe(buf, pos, 16)) 103 return nil 104 } 105 106 // Header is a VP9 Frame header. 107 // Specification: 108 // https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf 109 type Header struct { 110 Profile uint8 111 ShowExistingFrame bool 112 FrameToShowMapIdx uint8 113 FrameType FrameType 114 ShowFrame bool 115 ErrorResilientMode bool 116 ColorConfig *Header_ColorConfig 117 FrameSize *Header_FrameSize 118 } 119 120 // Unmarshal decodes a Header. 121 func (h *Header) Unmarshal(buf []byte) error { 122 pos := 0 123 124 err := bits.HasSpace(buf, pos, 4) 125 if err != nil { 126 return err 127 } 128 129 frameMarker := bits.ReadBitsUnsafe(buf, &pos, 2) 130 if frameMarker != 2 { 131 return fmt.Errorf("invalid frame marker") 132 } 133 134 profileLowBit := uint8(bits.ReadBitsUnsafe(buf, &pos, 1)) 135 profileHighBit := uint8(bits.ReadBitsUnsafe(buf, &pos, 1)) 136 h.Profile = profileHighBit<<1 + profileLowBit 137 138 if h.Profile == 3 { 139 err := bits.HasSpace(buf, pos, 1) 140 if err != nil { 141 return err 142 } 143 pos++ 144 } 145 146 h.ShowExistingFrame, err = bits.ReadFlag(buf, &pos) 147 if err != nil { 148 return err 149 } 150 151 if h.ShowExistingFrame { 152 tmp, err := bits.ReadBits(buf, &pos, 3) 153 if err != nil { 154 return err 155 } 156 h.FrameToShowMapIdx = uint8(tmp) 157 158 return nil 159 } 160 161 err = bits.HasSpace(buf, pos, 3) 162 if err != nil { 163 return err 164 } 165 166 h.FrameType = FrameType(bits.ReadFlagUnsafe(buf, &pos)) 167 h.ShowFrame = bits.ReadFlagUnsafe(buf, &pos) 168 h.ErrorResilientMode = bits.ReadFlagUnsafe(buf, &pos) 169 170 if !h.FrameType { 171 err := bits.HasSpace(buf, pos, 24) 172 if err != nil { 173 return err 174 } 175 176 frameSyncByte0 := uint8(bits.ReadBitsUnsafe(buf, &pos, 8)) 177 if frameSyncByte0 != 0x49 { 178 return fmt.Errorf("wrong frame_sync_byte_0") 179 } 180 181 frameSyncByte1 := uint8(bits.ReadBitsUnsafe(buf, &pos, 8)) 182 if frameSyncByte1 != 0x83 { 183 return fmt.Errorf("wrong frame_sync_byte_1") 184 } 185 186 frameSyncByte2 := uint8(bits.ReadBitsUnsafe(buf, &pos, 8)) 187 if frameSyncByte2 != 0x42 { 188 return fmt.Errorf("wrong frame_sync_byte_2") 189 } 190 191 h.ColorConfig = &Header_ColorConfig{} 192 err = h.ColorConfig.unmarshal(h.Profile, buf, &pos) 193 if err != nil { 194 return err 195 } 196 197 h.FrameSize = &Header_FrameSize{} 198 err = h.FrameSize.unmarshal(buf, &pos) 199 if err != nil { 200 return err 201 } 202 } 203 204 return nil 205 } 206 207 // Width returns the video width. 208 func (h Header) Width() int { 209 return int(h.FrameSize.FrameWidthMinus1) + 1 210 } 211 212 // Height returns the video height. 213 func (h Header) Height() int { 214 return int(h.FrameSize.FrameHeightMinus1) + 1 215 } 216 217 // ChromaSubsampling returns the chroma subsampling format, in ISO-BMFF/vpcC format. 218 func (h Header) ChromaSubsampling() uint8 { 219 switch { 220 case !h.ColorConfig.SubsamplingX && !h.ColorConfig.SubsamplingY: 221 return 3 // 4:4:4 222 case h.ColorConfig.SubsamplingX && !h.ColorConfig.SubsamplingY: 223 return 2 // 4:2:2 224 default: 225 return 1 // 4:2:0 colocated with luma 226 } 227 }