FastLED 3.9.15
Loading...
Searching...
No Matches
h264.cpp.hpp
Go to the documentation of this file.
1#include "fl/codec/h264.h"
3#include "fl/stl/utility.h"
4
5namespace fl {
6
7namespace {
8
9// Exp-Golomb bitstream reader for H.264 SPS parsing.
10struct BitReader {
12 fl::size bytePos = 0;
13 fl::u8 bitPos = 0; // 0-7, MSB first
14
16
17 bool hasBits(fl::size n) const {
18 fl::size totalBits = data.size() * 8;
19 fl::size currentBit = bytePos * 8 + bitPos;
20 return currentBit + n <= totalBits;
21 }
22
23 fl::u32 readBits(fl::u8 n) {
24 fl::u32 val = 0;
25 for (fl::u8 i = 0; i < n; i++) {
26 if (bytePos >= data.size()) return val;
27 val = (val << 1) | ((data[bytePos] >> (7 - bitPos)) & 1);
28 bitPos++;
29 if (bitPos == 8) {
30 bitPos = 0;
31 bytePos++;
32 }
33 }
34 return val;
35 }
36
37 // Read unsigned Exp-Golomb coded value (ue(v))
38 fl::u32 readUE() {
39 int leadingZeros = 0;
40 while (hasBits(1) && readBits(1) == 0) {
41 leadingZeros++;
42 if (leadingZeros > 32) return 0; // prevent infinite loop
43 }
44 if (leadingZeros == 0) return 0;
45 fl::u32 suffix = readBits(leadingZeros);
46 return (1u << leadingZeros) - 1 + suffix;
47 }
48
49 // Read signed Exp-Golomb coded value (se(v))
50 fl::i32 readSE() {
51 fl::u32 val = readUE();
52 if (val & 1) {
53 return fl::i32((val + 1) / 2);
54 } else {
55 return -fl::i32(val / 2);
56 }
57 }
58
59 void skipBits(fl::size n) {
60 for (fl::size i = 0; i < n; i++) {
61 bitPos++;
62 if (bitPos == 8) {
63 bitPos = 0;
64 bytePos++;
65 }
66 }
67 }
68};
69
70// Parse scaling list (skip it — we only need dimensions).
71void skipScalingList(BitReader& br, int size) {
72 int lastScale = 8;
73 int nextScale = 8;
74 for (int j = 0; j < size; j++) {
75 if (nextScale != 0) {
76 fl::i32 delta = br.readSE();
77 nextScale = (lastScale + delta + 256) % 256;
78 }
79 lastScale = (nextScale == 0) ? lastScale : nextScale;
80 }
81}
82
83} // namespace
84
86 H264Info info;
87
88 if (spsData.size() < 4) {
89 if (error_message) *error_message = "SPS data too small";
90 return info;
91 }
92
93 BitReader br(spsData);
94
95 // NAL unit header: forbidden_zero_bit(1) + nal_ref_idc(2) + nal_unit_type(5)
96 fl::u8 nalHeader = (fl::u8)br.readBits(8);
97 fl::u8 nalType = nalHeader & 0x1F;
98 FL_UNUSED(nalType); // should be 7 for SPS
99
100 // SPS header
101 fl::u8 profile_idc = (fl::u8)br.readBits(8);
102 br.readBits(8); // constraint_set flags + reserved
103 fl::u8 level_idc = (fl::u8)br.readBits(8);
104 br.readUE(); // seq_parameter_set_id
105
106 info.profile = profile_idc;
107 info.level = level_idc;
108
109 // High profile extensions
110 fl::u32 chromaFormatIdc = 1; // default
111 if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 ||
112 profile_idc == 244 || profile_idc == 44 || profile_idc == 83 ||
113 profile_idc == 86 || profile_idc == 118 || profile_idc == 128 ||
114 profile_idc == 138 || profile_idc == 139 || profile_idc == 134 ||
115 profile_idc == 135) {
116 chromaFormatIdc = br.readUE();
117 if (chromaFormatIdc == 3) {
118 br.readBits(1); // separate_colour_plane_flag
119 }
120 br.readUE(); // bit_depth_luma_minus8
121 br.readUE(); // bit_depth_chroma_minus8
122 br.readBits(1); // qpprime_y_zero_transform_bypass_flag
123
124 fl::u32 seqScalingMatrixPresent = br.readBits(1);
125 if (seqScalingMatrixPresent) {
126 int limit = (chromaFormatIdc != 3) ? 8 : 12;
127 for (int i = 0; i < limit; i++) {
128 if (br.readBits(1)) { // seq_scaling_list_present_flag
129 skipScalingList(br, (i < 6) ? 16 : 64);
130 }
131 }
132 }
133 }
134
135 br.readUE(); // log2_max_frame_num_minus4
136 fl::u32 picOrderCntType = br.readUE();
137 if (picOrderCntType == 0) {
138 br.readUE(); // log2_max_pic_order_cnt_lsb_minus4
139 } else if (picOrderCntType == 1) {
140 br.readBits(1); // delta_pic_order_always_zero_flag
141 br.readSE(); // offset_for_non_ref_pic
142 br.readSE(); // offset_for_top_to_bottom_field
143 fl::u32 numRefFramesInPoc = br.readUE();
144 for (fl::u32 i = 0; i < numRefFramesInPoc; i++) {
145 br.readSE(); // offset_for_ref_frame
146 }
147 }
148
149 info.numRefFrames = (fl::u8)br.readUE(); // max_num_ref_frames
150 br.readBits(1); // gaps_in_frame_num_value_allowed_flag
151
152 fl::u32 picWidthInMbs = br.readUE() + 1;
153 fl::u32 picHeightInMapUnits = br.readUE() + 1;
154
155 fl::u32 frameMbsOnly = br.readBits(1);
156 if (!frameMbsOnly) {
157 br.readBits(1); // mb_adaptive_frame_field_flag
158 }
159
160 br.readBits(1); // direct_8x8_inference_flag
161
162 // Frame cropping
163 fl::u32 cropLeft = 0, cropRight = 0, cropTop = 0, cropBottom = 0;
164 fl::u32 frameCropping = br.readBits(1);
165 if (frameCropping) {
166 cropLeft = br.readUE();
167 cropRight = br.readUE();
168 cropTop = br.readUE();
169 cropBottom = br.readUE();
170 }
171
172 // Calculate dimensions
173 fl::u32 width = picWidthInMbs * 16;
174 fl::u32 height = (2 - frameMbsOnly) * picHeightInMapUnits * 16;
175
176 // Apply cropping
177 fl::u32 cropUnitX = 1, cropUnitY = 2 - frameMbsOnly;
178 if (chromaFormatIdc == 1) {
179 cropUnitX = 2;
180 cropUnitY *= 2;
181 } else if (chromaFormatIdc == 2) {
182 cropUnitX = 2;
183 cropUnitY *= 1;
184 }
185
186 width -= (cropLeft + cropRight) * cropUnitX;
187 height -= (cropTop + cropBottom) * cropUnitY;
188
189 info.width = (fl::u16)width;
190 info.height = (fl::u16)height;
191 info.isValid = true;
192
193 return info;
194}
195
197 H264Info info;
198
199 Mp4TrackInfo track = parseMp4(mp4Data, error_message);
200 if (!track.isValid) return info;
201
202 // Use dimensions from avc1 box
203 info.width = track.width;
204 info.height = track.height;
205 info.profile = track.profile;
206 info.level = track.level;
207
208 // Parse SPS for more detailed info if available
209 if (!track.sps.empty()) {
210 fl::string spsError;
211 H264Info spsInfo = parseSPS(track.sps[0], &spsError);
212 if (spsInfo.isValid) {
213 // SPS-parsed dimensions are more authoritative
214 info.width = spsInfo.width;
215 info.height = spsInfo.height;
216 info.profile = spsInfo.profile;
217 info.level = spsInfo.level;
218 info.numRefFrames = spsInfo.numRefFrames;
219 }
220 }
221
222 info.isValid = true;
223 return info;
224}
225
226} // namespace fl
227
228// allow-include-after-namespace
229// Platform-specific factory: createDecoder() and isSupported()
230// IWYU pragma: begin_keep
231#include "platforms/esp/is_esp.h" // ok platform headers
232#if defined(FL_IS_ESP_32P4)
233#include "platforms/esp/32/codec/h264_hw_decoder.hpp" // ok platform headers
234#else
235#include "platforms/shared/codec/h264_noop.hpp" // ok platform headers
236#endif
237// IWYU pragma: end_keep
static H264Info parseSPS(fl::span< const fl::u8 > spsData, fl::string *error_message=nullptr)
Definition h264.cpp.hpp:85
static H264Info parseH264Info(fl::span< const fl::u8 > mp4Data, fl::string *error_message=nullptr)
Definition h264.cpp.hpp:196
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458
bool empty() const FL_NOEXCEPT
void skipScalingList(BitReader &br, int size)
Definition h264.cpp.hpp:71
unsigned char u8
Definition s16x16x4.h:132
u8 u8 height
Definition blur.h:186
u8 width
Definition blur.h:186
Mp4TrackInfo parseMp4(fl::span< const fl::u8 > data, fl::string *error)
Base definition for an LED controller.
Definition crgb.hpp:179
fl::vector< fl::vector< fl::u8 > > sps
Definition mp4_parser.h:20
#define FL_UNUSED(x)
fl::u8 profile
Definition h264.h:13
fl::u8 level
Definition h264.h:14
fl::u8 numRefFrames
Definition h264.h:15
bool isValid
Definition h264.h:16
fl::u16 height
Definition h264.h:12
fl::u16 width
Definition h264.h:11