1 /*
2  *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  *
10  */
11 
12 #include "common_video/h264/sps_vui_rewriter.h"
13 
14 #include <algorithm>
15 #include <memory>
16 #include <vector>
17 
18 #include "rtc_base/bitbuffer.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/logging.h"
21 #include "rtc_base/numerics/safe_minmax.h"
22 
23 #include "common_video/h264/h264_common.h"
24 #include "common_video/h264/sps_parser.h"
25 
26 namespace webrtc {
27 
28 // The maximum expected growth from adding a VUI to the SPS. It's actually
29 // closer to 24 or so, but better safe than sorry.
30 const size_t kMaxVuiSpsIncrease = 64;
31 
32 #define RETURN_FALSE_ON_FAIL(x)                                      \
33   if (!(x)) {                                                        \
34     RTC_LOG_F(LS_ERROR) << " (line:" << __LINE__ << ") FAILED: " #x; \
35     return false;                                                    \
36   }
37 
38 #define COPY_UINT8(src, dest, tmp)                   \
39   do {                                               \
40     RETURN_FALSE_ON_FAIL((src)->ReadUInt8(&tmp));    \
41     if (dest)                                        \
42       RETURN_FALSE_ON_FAIL((dest)->WriteUInt8(tmp)); \
43   } while (0)
44 
45 #define COPY_EXP_GOLOMB(src, dest, tmp)                          \
46   do {                                                           \
47     RETURN_FALSE_ON_FAIL((src)->ReadExponentialGolomb(&tmp));    \
48     if (dest)                                                    \
49       RETURN_FALSE_ON_FAIL((dest)->WriteExponentialGolomb(tmp)); \
50   } while (0)
51 
52 #define COPY_BITS(src, dest, tmp, bits)                   \
53   do {                                                    \
54     RETURN_FALSE_ON_FAIL((src)->ReadBits(&tmp, bits));    \
55     if (dest)                                             \
56       RETURN_FALSE_ON_FAIL((dest)->WriteBits(tmp, bits)); \
57   } while (0)
58 
59 typedef const SpsParser::SpsState& Sps;
60 
61 bool CopyAndRewriteVui(Sps sps,
62                        rtc::BitBuffer* source,
63                        rtc::BitBufferWriter* destination,
64                        SpsVuiRewriter::ParseResult* out_vui_rewritten);
65 bool CopyHrdParameters(rtc::BitBuffer* source,
66                        rtc::BitBufferWriter* destination);
67 bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
68                              uint32_t max_num_ref_frames);
69 bool CopyRemainingBits(rtc::BitBuffer* source,
70                        rtc::BitBufferWriter* destination);
71 
ParseAndRewriteSps(const uint8_t * buffer,size_t length,rtc::Optional<SpsParser::SpsState> * sps,rtc::Buffer * destination)72 SpsVuiRewriter::ParseResult SpsVuiRewriter::ParseAndRewriteSps(
73     const uint8_t* buffer,
74     size_t length,
75     rtc::Optional<SpsParser::SpsState>* sps,
76     rtc::Buffer* destination) {
77   // Create temporary RBSP decoded buffer of the payload (exlcuding the
78   // leading nalu type header byte (the SpsParser uses only the payload).
79   std::vector<uint8_t> rbsp_buffer = H264::ParseRbsp(buffer, length);
80   rtc::BitBuffer source_buffer(rbsp_buffer.data(), rbsp_buffer.size());
81   rtc::Optional<SpsParser::SpsState> sps_state =
82       SpsParser::ParseSpsUpToVui(&source_buffer);
83   if (!sps_state)
84     return ParseResult::kFailure;
85 
86   *sps = sps_state;
87 
88   if (sps_state->pic_order_cnt_type >= 2) {
89     // No need to rewrite VUI in this case.
90     return ParseResult::kPocOk;
91   }
92 
93   // We're going to completely muck up alignment, so we need a BitBuffer to
94   // write with.
95   rtc::Buffer out_buffer(length + kMaxVuiSpsIncrease);
96   rtc::BitBufferWriter sps_writer(out_buffer.data(), out_buffer.size());
97 
98   // Check how far the SpsParser has read, and copy that data in bulk.
99   size_t byte_offset;
100   size_t bit_offset;
101   source_buffer.GetCurrentOffset(&byte_offset, &bit_offset);
102   memcpy(out_buffer.data(), rbsp_buffer.data(),
103          byte_offset + (bit_offset > 0 ? 1 : 0));  // OK to copy the last bits.
104 
105   // SpsParser will have read the vui_params_present flag, which we want to
106   // modify, so back off a bit;
107   if (bit_offset == 0) {
108     --byte_offset;
109     bit_offset = 7;
110   } else {
111     --bit_offset;
112   }
113   sps_writer.Seek(byte_offset, bit_offset);
114 
115   ParseResult vui_updated;
116   if (!CopyAndRewriteVui(*sps_state, &source_buffer, &sps_writer,
117                          &vui_updated)) {
118     RTC_LOG(LS_ERROR) << "Failed to parse/copy SPS VUI.";
119     return ParseResult::kFailure;
120   }
121 
122   if (vui_updated == ParseResult::kVuiOk) {
123     // No update necessary after all, just return.
124     return vui_updated;
125   }
126 
127   if (!CopyRemainingBits(&source_buffer, &sps_writer)) {
128     RTC_LOG(LS_ERROR) << "Failed to parse/copy SPS VUI.";
129     return ParseResult::kFailure;
130   }
131 
132   // Pad up to next byte with zero bits.
133   sps_writer.GetCurrentOffset(&byte_offset, &bit_offset);
134   if (bit_offset > 0) {
135     sps_writer.WriteBits(0, 8 - bit_offset);
136     ++byte_offset;
137     bit_offset = 0;
138   }
139 
140   RTC_DCHECK(byte_offset <= length + kMaxVuiSpsIncrease);
141   RTC_CHECK(destination != nullptr);
142 
143   out_buffer.SetSize(byte_offset);
144 
145   // Write updates SPS to destination with added RBSP
146   H264::WriteRbsp(out_buffer.data(), out_buffer.size(), destination);
147 
148   return ParseResult::kVuiRewritten;
149 }
150 
CopyAndRewriteVui(Sps sps,rtc::BitBuffer * source,rtc::BitBufferWriter * destination,SpsVuiRewriter::ParseResult * out_vui_rewritten)151 bool CopyAndRewriteVui(Sps sps,
152                        rtc::BitBuffer* source,
153                        rtc::BitBufferWriter* destination,
154                        SpsVuiRewriter::ParseResult* out_vui_rewritten) {
155   uint32_t golomb_tmp;
156   uint32_t bits_tmp;
157 
158   //
159   // vui_parameters_present_flag: u(1)
160   //
161   RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
162 
163   // ********* IMPORTANT! **********
164   // Now we're at the VUI, so we want to (1) add it if it isn't present, and
165   // (2) rewrite frame reordering values so no reordering is allowed.
166   if (!sps.vui_params_present) {
167     // Write a simple VUI with the parameters we want and 0 for all other flags.
168     // There are 8 flags to be off before the bitstream restriction flag.
169     RETURN_FALSE_ON_FAIL(destination->WriteBits(0, 8));
170     // bitstream_restriction_flag: u(1)
171     RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
172     RETURN_FALSE_ON_FAIL(
173         AddBitstreamRestriction(destination, sps.max_num_ref_frames));
174   } else {
175     // Parse out the full VUI.
176     // aspect_ratio_info_present_flag: u(1)
177     COPY_BITS(source, destination, bits_tmp, 1);
178     if (bits_tmp == 1) {
179       // aspect_ratio_idc: u(8)
180       COPY_BITS(source, destination, bits_tmp, 8);
181       if (bits_tmp == 255u) {  // Extended_SAR
182         // sar_width/sar_height: u(16) each.
183         COPY_BITS(source, destination, bits_tmp, 32);
184       }
185     }
186     // overscan_info_present_flag: u(1)
187     COPY_BITS(source, destination, bits_tmp, 1);
188     if (bits_tmp == 1) {
189       // overscan_appropriate_flag: u(1)
190       COPY_BITS(source, destination, bits_tmp, 1);
191     }
192     // video_signal_type_present_flag: u(1)
193     COPY_BITS(source, destination, bits_tmp, 1);
194     if (bits_tmp == 1) {
195       // video_format + video_full_range_flag: u(3) + u(1)
196       COPY_BITS(source, destination, bits_tmp, 4);
197       // colour_description_present_flag: u(1)
198       COPY_BITS(source, destination, bits_tmp, 1);
199       if (bits_tmp == 1) {
200         // colour_primaries, transfer_characteristics, matrix_coefficients:
201         // u(8) each.
202         COPY_BITS(source, destination, bits_tmp, 24);
203       }
204     }
205     // chroma_loc_info_present_flag: u(1)
206     COPY_BITS(source, destination, bits_tmp, 1);
207     if (bits_tmp == 1) {
208       // chroma_sample_loc_type_(top|bottom)_field: ue(v) each.
209       COPY_EXP_GOLOMB(source, destination, golomb_tmp);
210       COPY_EXP_GOLOMB(source, destination, golomb_tmp);
211     }
212     // timing_info_present_flag: u(1)
213     COPY_BITS(source, destination, bits_tmp, 1);
214     if (bits_tmp == 1) {
215       // num_units_in_tick, time_scale: u(32) each
216       COPY_BITS(source, destination, bits_tmp, 32);
217       COPY_BITS(source, destination, bits_tmp, 32);
218       // fixed_frame_rate_flag: u(1)
219       COPY_BITS(source, destination, bits_tmp, 1);
220     }
221     // nal_hrd_parameters_present_flag: u(1)
222     uint32_t nal_hrd_parameters_present_flag;
223     COPY_BITS(source, destination, nal_hrd_parameters_present_flag, 1);
224     if (nal_hrd_parameters_present_flag == 1) {
225       RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination));
226     }
227     // vcl_hrd_parameters_present_flag: u(1)
228     uint32_t vcl_hrd_parameters_present_flag;
229     COPY_BITS(source, destination, vcl_hrd_parameters_present_flag, 1);
230     if (vcl_hrd_parameters_present_flag == 1) {
231       RETURN_FALSE_ON_FAIL(CopyHrdParameters(source, destination));
232     }
233     if (nal_hrd_parameters_present_flag == 1 ||
234         vcl_hrd_parameters_present_flag == 1) {
235       // low_delay_hrd_flag: u(1)
236       COPY_BITS(source, destination, bits_tmp, 1);
237     }
238     // pic_struct_present_flag: u(1)
239     COPY_BITS(source, destination, bits_tmp, 1);
240 
241     // bitstream_restriction_flag: u(1)
242     uint32_t bitstream_restriction_flag;
243     RETURN_FALSE_ON_FAIL(source->ReadBits(&bitstream_restriction_flag, 1));
244     RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
245     if (bitstream_restriction_flag == 0) {
246       // We're adding one from scratch.
247       RETURN_FALSE_ON_FAIL(
248           AddBitstreamRestriction(destination, sps.max_num_ref_frames));
249     } else {
250       // We're replacing.
251       // motion_vectors_over_pic_boundaries_flag: u(1)
252       COPY_BITS(source, destination, bits_tmp, 1);
253       // max_bytes_per_pic_denom: ue(v)
254       COPY_EXP_GOLOMB(source, destination, golomb_tmp);
255       // max_bits_per_mb_denom: ue(v)
256       COPY_EXP_GOLOMB(source, destination, golomb_tmp);
257       // log2_max_mv_length_horizontal: ue(v)
258       COPY_EXP_GOLOMB(source, destination, golomb_tmp);
259       // log2_max_mv_length_vertical: ue(v)
260       COPY_EXP_GOLOMB(source, destination, golomb_tmp);
261       // ********* IMPORTANT! **********
262       // The next two are the ones we need to set to low numbers:
263       // max_num_reorder_frames: ue(v)
264       // max_dec_frame_buffering: ue(v)
265       // However, if they are already set to no greater than the numbers we
266       // want, then we don't need to be rewriting.
267       uint32_t max_num_reorder_frames, max_dec_frame_buffering;
268       RETURN_FALSE_ON_FAIL(
269           source->ReadExponentialGolomb(&max_num_reorder_frames));
270       RETURN_FALSE_ON_FAIL(
271           source->ReadExponentialGolomb(&max_dec_frame_buffering));
272       if (max_num_reorder_frames == 0 &&
273           max_dec_frame_buffering <= sps.max_num_ref_frames) {
274         RTC_LOG(LS_INFO) << "VUI bitstream already contains an optimal VUI.";
275         *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiOk;
276         return true;
277       }
278       RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
279       RETURN_FALSE_ON_FAIL(
280           destination->WriteExponentialGolomb(sps.max_num_ref_frames));
281     }
282   }
283   *out_vui_rewritten = SpsVuiRewriter::ParseResult::kVuiRewritten;
284   return true;
285 }
286 
287 // Copies a VUI HRD parameters segment.
CopyHrdParameters(rtc::BitBuffer * source,rtc::BitBufferWriter * destination)288 bool CopyHrdParameters(rtc::BitBuffer* source,
289                        rtc::BitBufferWriter* destination) {
290   uint32_t golomb_tmp;
291   uint32_t bits_tmp;
292 
293   // cbp_cnt_minus1: ue(v)
294   uint32_t cbp_cnt_minus1;
295   COPY_EXP_GOLOMB(source, destination, cbp_cnt_minus1);
296   // bit_rate_scale and cbp_size_scale: u(4) each
297   COPY_BITS(source, destination, bits_tmp, 8);
298   for (size_t i = 0; i <= cbp_cnt_minus1; ++i) {
299     // bit_rate_value_minus1 and cbp_size_value_minus1: ue(v) each
300     COPY_EXP_GOLOMB(source, destination, golomb_tmp);
301     COPY_EXP_GOLOMB(source, destination, golomb_tmp);
302     // cbr_flag: u(1)
303     COPY_BITS(source, destination, bits_tmp, 1);
304   }
305   // initial_cbp_removal_delay_length_minus1: u(5)
306   COPY_BITS(source, destination, bits_tmp, 5);
307   // cbp_removal_delay_length_minus1: u(5)
308   COPY_BITS(source, destination, bits_tmp, 5);
309   // dbp_output_delay_length_minus1: u(5)
310   COPY_BITS(source, destination, bits_tmp, 5);
311   // time_offset_length: u(5)
312   COPY_BITS(source, destination, bits_tmp, 5);
313   return true;
314 }
315 
316 // These functions are similar to webrtc::H264SpsParser::Parse, and based on the
317 // same version of the H.264 standard. You can find it here:
318 // http://www.itu.int/rec/T-REC-H.264
319 
320 // Adds a bitstream restriction VUI segment.
AddBitstreamRestriction(rtc::BitBufferWriter * destination,uint32_t max_num_ref_frames)321 bool AddBitstreamRestriction(rtc::BitBufferWriter* destination,
322                              uint32_t max_num_ref_frames) {
323   // motion_vectors_over_pic_boundaries_flag: u(1)
324   // Default is 1 when not present.
325   RETURN_FALSE_ON_FAIL(destination->WriteBits(1, 1));
326   // max_bytes_per_pic_denom: ue(v)
327   // Default is 2 when not present.
328   RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(2));
329   // max_bits_per_mb_denom: ue(v)
330   // Default is 1 when not present.
331   RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(1));
332   // log2_max_mv_length_horizontal: ue(v)
333   // log2_max_mv_length_vertical: ue(v)
334   // Both default to 16 when not present.
335   RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
336   RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(16));
337 
338   // ********* IMPORTANT! **********
339   // max_num_reorder_frames: ue(v)
340   RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(0));
341   // max_dec_frame_buffering: ue(v)
342   RETURN_FALSE_ON_FAIL(destination->WriteExponentialGolomb(max_num_ref_frames));
343   return true;
344 }
345 
CopyRemainingBits(rtc::BitBuffer * source,rtc::BitBufferWriter * destination)346 bool CopyRemainingBits(rtc::BitBuffer* source,
347                        rtc::BitBufferWriter* destination) {
348   uint32_t bits_tmp;
349   // Try to get at least the destination aligned.
350   if (source->RemainingBitCount() > 0 && source->RemainingBitCount() % 8 != 0) {
351     size_t misaligned_bits = source->RemainingBitCount() % 8;
352     COPY_BITS(source, destination, bits_tmp, misaligned_bits);
353   }
354   while (source->RemainingBitCount() > 0) {
355     auto count = rtc::SafeMin<size_t>(32u, source->RemainingBitCount());
356     COPY_BITS(source, destination, bits_tmp, count);
357   }
358   // TODO(noahric): The last byte could be all zeroes now, which we should just
359   // strip.
360   return true;
361 }
362 
363 }  // namespace webrtc
364