xref: /aosp_15_r20/external/webrtc/common_video/h264/sps_parser.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "common_video/h264/sps_parser.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <cstdint>
14*d9f75844SAndroid Build Coastguard Worker #include <vector>
15*d9f75844SAndroid Build Coastguard Worker 
16*d9f75844SAndroid Build Coastguard Worker #include "common_video/h264/h264_common.h"
17*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/bitstream_reader.h"
18*d9f75844SAndroid Build Coastguard Worker 
19*d9f75844SAndroid Build Coastguard Worker namespace {
20*d9f75844SAndroid Build Coastguard Worker constexpr int kScalingDeltaMin = -128;
21*d9f75844SAndroid Build Coastguard Worker constexpr int kScaldingDeltaMax = 127;
22*d9f75844SAndroid Build Coastguard Worker }  // namespace
23*d9f75844SAndroid Build Coastguard Worker 
24*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
25*d9f75844SAndroid Build Coastguard Worker 
26*d9f75844SAndroid Build Coastguard Worker SpsParser::SpsState::SpsState() = default;
27*d9f75844SAndroid Build Coastguard Worker SpsParser::SpsState::SpsState(const SpsState&) = default;
28*d9f75844SAndroid Build Coastguard Worker SpsParser::SpsState::~SpsState() = default;
29*d9f75844SAndroid Build Coastguard Worker 
30*d9f75844SAndroid Build Coastguard Worker // General note: this is based off the 02/2014 version of the H.264 standard.
31*d9f75844SAndroid Build Coastguard Worker // You can find it on this page:
32*d9f75844SAndroid Build Coastguard Worker // http://www.itu.int/rec/T-REC-H.264
33*d9f75844SAndroid Build Coastguard Worker 
34*d9f75844SAndroid Build Coastguard Worker // Unpack RBSP and parse SPS state from the supplied buffer.
ParseSps(const uint8_t * data,size_t length)35*d9f75844SAndroid Build Coastguard Worker absl::optional<SpsParser::SpsState> SpsParser::ParseSps(const uint8_t* data,
36*d9f75844SAndroid Build Coastguard Worker                                                         size_t length) {
37*d9f75844SAndroid Build Coastguard Worker   std::vector<uint8_t> unpacked_buffer = H264::ParseRbsp(data, length);
38*d9f75844SAndroid Build Coastguard Worker   BitstreamReader reader(unpacked_buffer);
39*d9f75844SAndroid Build Coastguard Worker   return ParseSpsUpToVui(reader);
40*d9f75844SAndroid Build Coastguard Worker }
41*d9f75844SAndroid Build Coastguard Worker 
ParseSpsUpToVui(BitstreamReader & reader)42*d9f75844SAndroid Build Coastguard Worker absl::optional<SpsParser::SpsState> SpsParser::ParseSpsUpToVui(
43*d9f75844SAndroid Build Coastguard Worker     BitstreamReader& reader) {
44*d9f75844SAndroid Build Coastguard Worker   // Now, we need to use a bitstream reader to parse through the actual AVC SPS
45*d9f75844SAndroid Build Coastguard Worker   // format. See Section 7.3.2.1.1 ("Sequence parameter set data syntax") of the
46*d9f75844SAndroid Build Coastguard Worker   // H.264 standard for a complete description.
47*d9f75844SAndroid Build Coastguard Worker   // Since we only care about resolution, we ignore the majority of fields, but
48*d9f75844SAndroid Build Coastguard Worker   // we still have to actively parse through a lot of the data, since many of
49*d9f75844SAndroid Build Coastguard Worker   // the fields have variable size.
50*d9f75844SAndroid Build Coastguard Worker   // We're particularly interested in:
51*d9f75844SAndroid Build Coastguard Worker   // chroma_format_idc -> affects crop units
52*d9f75844SAndroid Build Coastguard Worker   // pic_{width,height}_* -> resolution of the frame in macroblocks (16x16).
53*d9f75844SAndroid Build Coastguard Worker   // frame_crop_*_offset -> crop information
54*d9f75844SAndroid Build Coastguard Worker 
55*d9f75844SAndroid Build Coastguard Worker   SpsState sps;
56*d9f75844SAndroid Build Coastguard Worker 
57*d9f75844SAndroid Build Coastguard Worker   // chroma_format_idc will be ChromaArrayType if separate_colour_plane_flag is
58*d9f75844SAndroid Build Coastguard Worker   // 0. It defaults to 1, when not specified.
59*d9f75844SAndroid Build Coastguard Worker   uint32_t chroma_format_idc = 1;
60*d9f75844SAndroid Build Coastguard Worker 
61*d9f75844SAndroid Build Coastguard Worker   // profile_idc: u(8). We need it to determine if we need to read/skip chroma
62*d9f75844SAndroid Build Coastguard Worker   // formats.
63*d9f75844SAndroid Build Coastguard Worker   uint8_t profile_idc = reader.Read<uint8_t>();
64*d9f75844SAndroid Build Coastguard Worker   // constraint_set0_flag through constraint_set5_flag + reserved_zero_2bits
65*d9f75844SAndroid Build Coastguard Worker   // 1 bit each for the flags + 2 bits + 8 bits for level_idc = 16 bits.
66*d9f75844SAndroid Build Coastguard Worker   reader.ConsumeBits(16);
67*d9f75844SAndroid Build Coastguard Worker   // seq_parameter_set_id: ue(v)
68*d9f75844SAndroid Build Coastguard Worker   sps.id = reader.ReadExponentialGolomb();
69*d9f75844SAndroid Build Coastguard Worker   sps.separate_colour_plane_flag = 0;
70*d9f75844SAndroid Build Coastguard Worker   // See if profile_idc has chroma format information.
71*d9f75844SAndroid Build Coastguard Worker   if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 ||
72*d9f75844SAndroid Build Coastguard Worker       profile_idc == 244 || profile_idc == 44 || profile_idc == 83 ||
73*d9f75844SAndroid Build Coastguard Worker       profile_idc == 86 || profile_idc == 118 || profile_idc == 128 ||
74*d9f75844SAndroid Build Coastguard Worker       profile_idc == 138 || profile_idc == 139 || profile_idc == 134) {
75*d9f75844SAndroid Build Coastguard Worker     // chroma_format_idc: ue(v)
76*d9f75844SAndroid Build Coastguard Worker     chroma_format_idc = reader.ReadExponentialGolomb();
77*d9f75844SAndroid Build Coastguard Worker     if (chroma_format_idc == 3) {
78*d9f75844SAndroid Build Coastguard Worker       // separate_colour_plane_flag: u(1)
79*d9f75844SAndroid Build Coastguard Worker       sps.separate_colour_plane_flag = reader.ReadBit();
80*d9f75844SAndroid Build Coastguard Worker     }
81*d9f75844SAndroid Build Coastguard Worker     // bit_depth_luma_minus8: ue(v)
82*d9f75844SAndroid Build Coastguard Worker     reader.ReadExponentialGolomb();
83*d9f75844SAndroid Build Coastguard Worker     // bit_depth_chroma_minus8: ue(v)
84*d9f75844SAndroid Build Coastguard Worker     reader.ReadExponentialGolomb();
85*d9f75844SAndroid Build Coastguard Worker     // qpprime_y_zero_transform_bypass_flag: u(1)
86*d9f75844SAndroid Build Coastguard Worker     reader.ConsumeBits(1);
87*d9f75844SAndroid Build Coastguard Worker     // seq_scaling_matrix_present_flag: u(1)
88*d9f75844SAndroid Build Coastguard Worker     if (reader.Read<bool>()) {
89*d9f75844SAndroid Build Coastguard Worker       // Process the scaling lists just enough to be able to properly
90*d9f75844SAndroid Build Coastguard Worker       // skip over them, so we can still read the resolution on streams
91*d9f75844SAndroid Build Coastguard Worker       // where this is included.
92*d9f75844SAndroid Build Coastguard Worker       int scaling_list_count = (chroma_format_idc == 3 ? 12 : 8);
93*d9f75844SAndroid Build Coastguard Worker       for (int i = 0; i < scaling_list_count; ++i) {
94*d9f75844SAndroid Build Coastguard Worker         // seq_scaling_list_present_flag[i]  : u(1)
95*d9f75844SAndroid Build Coastguard Worker         if (reader.Read<bool>()) {
96*d9f75844SAndroid Build Coastguard Worker           int last_scale = 8;
97*d9f75844SAndroid Build Coastguard Worker           int next_scale = 8;
98*d9f75844SAndroid Build Coastguard Worker           int size_of_scaling_list = i < 6 ? 16 : 64;
99*d9f75844SAndroid Build Coastguard Worker           for (int j = 0; j < size_of_scaling_list; j++) {
100*d9f75844SAndroid Build Coastguard Worker             if (next_scale != 0) {
101*d9f75844SAndroid Build Coastguard Worker               // delta_scale: se(v)
102*d9f75844SAndroid Build Coastguard Worker               int delta_scale = reader.ReadSignedExponentialGolomb();
103*d9f75844SAndroid Build Coastguard Worker               if (!reader.Ok() || delta_scale < kScalingDeltaMin ||
104*d9f75844SAndroid Build Coastguard Worker                   delta_scale > kScaldingDeltaMax) {
105*d9f75844SAndroid Build Coastguard Worker                 return absl::nullopt;
106*d9f75844SAndroid Build Coastguard Worker               }
107*d9f75844SAndroid Build Coastguard Worker               next_scale = (last_scale + delta_scale + 256) % 256;
108*d9f75844SAndroid Build Coastguard Worker             }
109*d9f75844SAndroid Build Coastguard Worker             if (next_scale != 0)
110*d9f75844SAndroid Build Coastguard Worker               last_scale = next_scale;
111*d9f75844SAndroid Build Coastguard Worker           }
112*d9f75844SAndroid Build Coastguard Worker         }
113*d9f75844SAndroid Build Coastguard Worker       }
114*d9f75844SAndroid Build Coastguard Worker     }
115*d9f75844SAndroid Build Coastguard Worker   }
116*d9f75844SAndroid Build Coastguard Worker   // log2_max_frame_num and log2_max_pic_order_cnt_lsb are used with
117*d9f75844SAndroid Build Coastguard Worker   // BitstreamReader::ReadBits, which can read at most 64 bits at a time. We
118*d9f75844SAndroid Build Coastguard Worker   // also have to avoid overflow when adding 4 to the on-wire golomb value,
119*d9f75844SAndroid Build Coastguard Worker   // e.g., for evil input data, ReadExponentialGolomb might return 0xfffc.
120*d9f75844SAndroid Build Coastguard Worker   const uint32_t kMaxLog2Minus4 = 32 - 4;
121*d9f75844SAndroid Build Coastguard Worker 
122*d9f75844SAndroid Build Coastguard Worker   // log2_max_frame_num_minus4: ue(v)
123*d9f75844SAndroid Build Coastguard Worker   uint32_t log2_max_frame_num_minus4 = reader.ReadExponentialGolomb();
124*d9f75844SAndroid Build Coastguard Worker   if (!reader.Ok() || log2_max_frame_num_minus4 > kMaxLog2Minus4) {
125*d9f75844SAndroid Build Coastguard Worker     return absl::nullopt;
126*d9f75844SAndroid Build Coastguard Worker   }
127*d9f75844SAndroid Build Coastguard Worker   sps.log2_max_frame_num = log2_max_frame_num_minus4 + 4;
128*d9f75844SAndroid Build Coastguard Worker 
129*d9f75844SAndroid Build Coastguard Worker   // pic_order_cnt_type: ue(v)
130*d9f75844SAndroid Build Coastguard Worker   sps.pic_order_cnt_type = reader.ReadExponentialGolomb();
131*d9f75844SAndroid Build Coastguard Worker   if (sps.pic_order_cnt_type == 0) {
132*d9f75844SAndroid Build Coastguard Worker     // log2_max_pic_order_cnt_lsb_minus4: ue(v)
133*d9f75844SAndroid Build Coastguard Worker     uint32_t log2_max_pic_order_cnt_lsb_minus4 = reader.ReadExponentialGolomb();
134*d9f75844SAndroid Build Coastguard Worker     if (!reader.Ok() || log2_max_pic_order_cnt_lsb_minus4 > kMaxLog2Minus4) {
135*d9f75844SAndroid Build Coastguard Worker       return absl::nullopt;
136*d9f75844SAndroid Build Coastguard Worker     }
137*d9f75844SAndroid Build Coastguard Worker     sps.log2_max_pic_order_cnt_lsb = log2_max_pic_order_cnt_lsb_minus4 + 4;
138*d9f75844SAndroid Build Coastguard Worker   } else if (sps.pic_order_cnt_type == 1) {
139*d9f75844SAndroid Build Coastguard Worker     // delta_pic_order_always_zero_flag: u(1)
140*d9f75844SAndroid Build Coastguard Worker     sps.delta_pic_order_always_zero_flag = reader.ReadBit();
141*d9f75844SAndroid Build Coastguard Worker     // offset_for_non_ref_pic: se(v)
142*d9f75844SAndroid Build Coastguard Worker     reader.ReadExponentialGolomb();
143*d9f75844SAndroid Build Coastguard Worker     // offset_for_top_to_bottom_field: se(v)
144*d9f75844SAndroid Build Coastguard Worker     reader.ReadExponentialGolomb();
145*d9f75844SAndroid Build Coastguard Worker     // num_ref_frames_in_pic_order_cnt_cycle: ue(v)
146*d9f75844SAndroid Build Coastguard Worker     uint32_t num_ref_frames_in_pic_order_cnt_cycle =
147*d9f75844SAndroid Build Coastguard Worker         reader.ReadExponentialGolomb();
148*d9f75844SAndroid Build Coastguard Worker     for (size_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
149*d9f75844SAndroid Build Coastguard Worker       // offset_for_ref_frame[i]: se(v)
150*d9f75844SAndroid Build Coastguard Worker       reader.ReadExponentialGolomb();
151*d9f75844SAndroid Build Coastguard Worker       if (!reader.Ok()) {
152*d9f75844SAndroid Build Coastguard Worker         return absl::nullopt;
153*d9f75844SAndroid Build Coastguard Worker       }
154*d9f75844SAndroid Build Coastguard Worker     }
155*d9f75844SAndroid Build Coastguard Worker   }
156*d9f75844SAndroid Build Coastguard Worker   // max_num_ref_frames: ue(v)
157*d9f75844SAndroid Build Coastguard Worker   sps.max_num_ref_frames = reader.ReadExponentialGolomb();
158*d9f75844SAndroid Build Coastguard Worker   // gaps_in_frame_num_value_allowed_flag: u(1)
159*d9f75844SAndroid Build Coastguard Worker   reader.ConsumeBits(1);
160*d9f75844SAndroid Build Coastguard Worker   //
161*d9f75844SAndroid Build Coastguard Worker   // IMPORTANT ONES! Now we're getting to resolution. First we read the pic
162*d9f75844SAndroid Build Coastguard Worker   // width/height in macroblocks (16x16), which gives us the base resolution,
163*d9f75844SAndroid Build Coastguard Worker   // and then we continue on until we hit the frame crop offsets, which are used
164*d9f75844SAndroid Build Coastguard Worker   // to signify resolutions that aren't multiples of 16.
165*d9f75844SAndroid Build Coastguard Worker   //
166*d9f75844SAndroid Build Coastguard Worker   // pic_width_in_mbs_minus1: ue(v)
167*d9f75844SAndroid Build Coastguard Worker   sps.width = 16 * (reader.ReadExponentialGolomb() + 1);
168*d9f75844SAndroid Build Coastguard Worker   // pic_height_in_map_units_minus1: ue(v)
169*d9f75844SAndroid Build Coastguard Worker   uint32_t pic_height_in_map_units_minus1 = reader.ReadExponentialGolomb();
170*d9f75844SAndroid Build Coastguard Worker   // frame_mbs_only_flag: u(1)
171*d9f75844SAndroid Build Coastguard Worker   sps.frame_mbs_only_flag = reader.ReadBit();
172*d9f75844SAndroid Build Coastguard Worker   if (!sps.frame_mbs_only_flag) {
173*d9f75844SAndroid Build Coastguard Worker     // mb_adaptive_frame_field_flag: u(1)
174*d9f75844SAndroid Build Coastguard Worker     reader.ConsumeBits(1);
175*d9f75844SAndroid Build Coastguard Worker   }
176*d9f75844SAndroid Build Coastguard Worker   sps.height =
177*d9f75844SAndroid Build Coastguard Worker       16 * (2 - sps.frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1);
178*d9f75844SAndroid Build Coastguard Worker   // direct_8x8_inference_flag: u(1)
179*d9f75844SAndroid Build Coastguard Worker   reader.ConsumeBits(1);
180*d9f75844SAndroid Build Coastguard Worker   //
181*d9f75844SAndroid Build Coastguard Worker   // MORE IMPORTANT ONES! Now we're at the frame crop information.
182*d9f75844SAndroid Build Coastguard Worker   //
183*d9f75844SAndroid Build Coastguard Worker   uint32_t frame_crop_left_offset = 0;
184*d9f75844SAndroid Build Coastguard Worker   uint32_t frame_crop_right_offset = 0;
185*d9f75844SAndroid Build Coastguard Worker   uint32_t frame_crop_top_offset = 0;
186*d9f75844SAndroid Build Coastguard Worker   uint32_t frame_crop_bottom_offset = 0;
187*d9f75844SAndroid Build Coastguard Worker   // frame_cropping_flag: u(1)
188*d9f75844SAndroid Build Coastguard Worker   if (reader.Read<bool>()) {
189*d9f75844SAndroid Build Coastguard Worker     // frame_crop_{left, right, top, bottom}_offset: ue(v)
190*d9f75844SAndroid Build Coastguard Worker     frame_crop_left_offset = reader.ReadExponentialGolomb();
191*d9f75844SAndroid Build Coastguard Worker     frame_crop_right_offset = reader.ReadExponentialGolomb();
192*d9f75844SAndroid Build Coastguard Worker     frame_crop_top_offset = reader.ReadExponentialGolomb();
193*d9f75844SAndroid Build Coastguard Worker     frame_crop_bottom_offset = reader.ReadExponentialGolomb();
194*d9f75844SAndroid Build Coastguard Worker   }
195*d9f75844SAndroid Build Coastguard Worker   // vui_parameters_present_flag: u(1)
196*d9f75844SAndroid Build Coastguard Worker   sps.vui_params_present = reader.ReadBit();
197*d9f75844SAndroid Build Coastguard Worker 
198*d9f75844SAndroid Build Coastguard Worker   // Far enough! We don't use the rest of the SPS.
199*d9f75844SAndroid Build Coastguard Worker   if (!reader.Ok()) {
200*d9f75844SAndroid Build Coastguard Worker     return absl::nullopt;
201*d9f75844SAndroid Build Coastguard Worker   }
202*d9f75844SAndroid Build Coastguard Worker 
203*d9f75844SAndroid Build Coastguard Worker   // Figure out the crop units in pixels. That's based on the chroma format's
204*d9f75844SAndroid Build Coastguard Worker   // sampling, which is indicated by chroma_format_idc.
205*d9f75844SAndroid Build Coastguard Worker   if (sps.separate_colour_plane_flag || chroma_format_idc == 0) {
206*d9f75844SAndroid Build Coastguard Worker     frame_crop_bottom_offset *= (2 - sps.frame_mbs_only_flag);
207*d9f75844SAndroid Build Coastguard Worker     frame_crop_top_offset *= (2 - sps.frame_mbs_only_flag);
208*d9f75844SAndroid Build Coastguard Worker   } else if (!sps.separate_colour_plane_flag && chroma_format_idc > 0) {
209*d9f75844SAndroid Build Coastguard Worker     // Width multipliers for formats 1 (4:2:0) and 2 (4:2:2).
210*d9f75844SAndroid Build Coastguard Worker     if (chroma_format_idc == 1 || chroma_format_idc == 2) {
211*d9f75844SAndroid Build Coastguard Worker       frame_crop_left_offset *= 2;
212*d9f75844SAndroid Build Coastguard Worker       frame_crop_right_offset *= 2;
213*d9f75844SAndroid Build Coastguard Worker     }
214*d9f75844SAndroid Build Coastguard Worker     // Height multipliers for format 1 (4:2:0).
215*d9f75844SAndroid Build Coastguard Worker     if (chroma_format_idc == 1) {
216*d9f75844SAndroid Build Coastguard Worker       frame_crop_top_offset *= 2;
217*d9f75844SAndroid Build Coastguard Worker       frame_crop_bottom_offset *= 2;
218*d9f75844SAndroid Build Coastguard Worker     }
219*d9f75844SAndroid Build Coastguard Worker   }
220*d9f75844SAndroid Build Coastguard Worker   // Subtract the crop for each dimension.
221*d9f75844SAndroid Build Coastguard Worker   sps.width -= (frame_crop_left_offset + frame_crop_right_offset);
222*d9f75844SAndroid Build Coastguard Worker   sps.height -= (frame_crop_top_offset + frame_crop_bottom_offset);
223*d9f75844SAndroid Build Coastguard Worker 
224*d9f75844SAndroid Build Coastguard Worker   return sps;
225*d9f75844SAndroid Build Coastguard Worker }
226*d9f75844SAndroid Build Coastguard Worker 
227*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
228