OLD | NEW |
(Empty) | |
| 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 #include "webrtc/common_video/h264/pps_parser.h" |
| 12 |
| 13 #include <limits> |
| 14 |
| 15 #include "testing/gtest/include/gtest/gtest.h" |
| 16 |
| 17 #include "webrtc/base/bitbuffer.h" |
| 18 #include "webrtc/base/buffer.h" |
| 19 #include "webrtc/common_video/h264/h264_common.h" |
| 20 |
| 21 namespace webrtc { |
| 22 |
| 23 static const size_t kPpsBufferMaxSize = 256; |
| 24 static const uint32_t kIgnored = 0; |
| 25 |
| 26 void WritePps(const PpsParser::PpsState& pps, |
| 27 int slice_group_map_type, |
| 28 int num_slice_groups, |
| 29 int pic_size_in_map_units, |
| 30 rtc::Buffer* out_buffer) { |
| 31 uint8_t data[kPpsBufferMaxSize] = {0}; |
| 32 rtc::BitBufferWriter bit_buffer(data, kPpsBufferMaxSize); |
| 33 |
| 34 // pic_parameter_set_id: ue(v) |
| 35 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 36 // seq_parameter_set_id: ue(v) |
| 37 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 38 // entropy_coding_mode_flag: u(1) |
| 39 bit_buffer.WriteBits(kIgnored, 1); |
| 40 // bottom_field_pic_order_in_frame_present_flag: u(1) |
| 41 bit_buffer.WriteBits(pps.bottom_field_pic_order_in_frame_present_flag ? 1 : 0, |
| 42 1); |
| 43 // num_slice_groups_minus1: ue(v) |
| 44 RTC_CHECK_GT(num_slice_groups, 0); |
| 45 bit_buffer.WriteExponentialGolomb(num_slice_groups - 1); |
| 46 |
| 47 if (num_slice_groups > 1) { |
| 48 // slice_group_map_type: ue(v) |
| 49 bit_buffer.WriteExponentialGolomb(slice_group_map_type); |
| 50 switch (slice_group_map_type) { |
| 51 case 0: |
| 52 for (int i = 0; i < num_slice_groups; ++i) { |
| 53 // run_length_minus1[iGroup]: ue(v) |
| 54 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 55 } |
| 56 break; |
| 57 case 2: |
| 58 for (int i = 0; i < num_slice_groups; ++i) { |
| 59 // top_left[iGroup]: ue(v) |
| 60 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 61 // bottom_right[iGroup]: ue(v) |
| 62 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 63 } |
| 64 break; |
| 65 case 3: |
| 66 case 4: |
| 67 case 5: |
| 68 // slice_group_change_direction_flag: u(1) |
| 69 bit_buffer.WriteBits(kIgnored, 1); |
| 70 // slice_group_change_rate_minus1: ue(v) |
| 71 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 72 break; |
| 73 case 6: { |
| 74 bit_buffer.WriteExponentialGolomb(pic_size_in_map_units - 1); |
| 75 |
| 76 uint32_t slice_group_id_bits = 0; |
| 77 // If num_slice_groups is not a power of two an additional bit is |
| 78 // required |
| 79 // to account for the ceil() of log2() below. |
| 80 if ((num_slice_groups & (num_slice_groups - 1)) != 0) |
| 81 ++slice_group_id_bits; |
| 82 while (num_slice_groups > 0) { |
| 83 num_slice_groups >>= 1; |
| 84 ++slice_group_id_bits; |
| 85 } |
| 86 |
| 87 for (int i = 0; i < pic_size_in_map_units; ++i) { |
| 88 // slice_group_id[i]: u(v) |
| 89 // Represented by ceil(log2(num_slice_groups_minus1 + 1)) bits. |
| 90 bit_buffer.WriteBits(kIgnored, slice_group_id_bits); |
| 91 } |
| 92 break; |
| 93 } |
| 94 default: |
| 95 RTC_NOTREACHED(); |
| 96 } |
| 97 } |
| 98 |
| 99 // num_ref_idx_l0_default_active_minus1: ue(v) |
| 100 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 101 // num_ref_idx_l1_default_active_minus1: ue(v) |
| 102 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 103 // weighted_pred_flag: u(1) |
| 104 bit_buffer.WriteBits(pps.weighted_pred_flag ? 1 : 0, 1); |
| 105 // weighted_bipred_idc: u(2) |
| 106 bit_buffer.WriteBits(pps.weighted_bipred_idc, 2); |
| 107 |
| 108 // pic_init_qp_minus26: se(v) |
| 109 bit_buffer.WriteSignedExponentialGolomb(pps.pic_init_qp_minus26); |
| 110 // pic_init_qs_minus26: se(v) |
| 111 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 112 // chroma_qp_index_offset: se(v) |
| 113 bit_buffer.WriteExponentialGolomb(kIgnored); |
| 114 // deblocking_filter_control_present_flag: u(1) |
| 115 // constrained_intra_pred_flag: u(1) |
| 116 bit_buffer.WriteBits(kIgnored, 2); |
| 117 // redundant_pic_cnt_present_flag: u(1) |
| 118 bit_buffer.WriteBits(pps.redundant_pic_cnt_present_flag, 1); |
| 119 |
| 120 size_t byte_offset; |
| 121 size_t bit_offset; |
| 122 bit_buffer.GetCurrentOffset(&byte_offset, &bit_offset); |
| 123 if (bit_offset > 0) { |
| 124 bit_buffer.WriteBits(0, 8 - bit_offset); |
| 125 bit_buffer.GetCurrentOffset(&byte_offset, &bit_offset); |
| 126 } |
| 127 |
| 128 H264::WriteRbsp(data, byte_offset, out_buffer); |
| 129 } |
| 130 |
| 131 class PpsParserTest : public ::testing::Test { |
| 132 public: |
| 133 PpsParserTest() {} |
| 134 virtual ~PpsParserTest() {} |
| 135 |
| 136 void RunTest() { |
| 137 VerifyParsing(generated_pps_, 0, 1, 0); |
| 138 const int kMaxSliceGroups = 17; // Arbitrarily large. |
| 139 const int kMaxMapType = 6; |
| 140 int slice_group_bits = 0; |
| 141 for (int slice_group = 2; slice_group < kMaxSliceGroups; ++slice_group) { |
| 142 if ((slice_group & (slice_group - 1)) == 0) { |
| 143 // Slice group at a new power of two - increase slice_group_bits. |
| 144 ++slice_group_bits; |
| 145 } |
| 146 for (int map_type = 0; map_type <= kMaxMapType; ++map_type) { |
| 147 if (map_type == 1) { |
| 148 // TODO(sprang): Implement support for dispersed slice group map type. |
| 149 // See 8.2.2.2 Specification for dispersed slice group map type. |
| 150 continue; |
| 151 } else if (map_type == 6) { |
| 152 int max_pic_size = 1 << slice_group_bits; |
| 153 for (int pic_size = 1; pic_size < max_pic_size; ++pic_size) |
| 154 VerifyParsing(generated_pps_, map_type, slice_group, pic_size); |
| 155 } else { |
| 156 VerifyParsing(generated_pps_, map_type, slice_group, 0); |
| 157 } |
| 158 } |
| 159 } |
| 160 } |
| 161 |
| 162 void VerifyParsing(const PpsParser::PpsState& pps, |
| 163 int slice_group_map_type, |
| 164 int num_slice_groups, |
| 165 int pic_size_in_map_units) { |
| 166 buffer_.Clear(); |
| 167 WritePps(pps, slice_group_map_type, num_slice_groups, pic_size_in_map_units, |
| 168 &buffer_); |
| 169 parsed_pps_ = PpsParser::ParsePps(buffer_.data(), buffer_.size()); |
| 170 EXPECT_TRUE(static_cast<bool>(parsed_pps_)); |
| 171 EXPECT_EQ(pps.bottom_field_pic_order_in_frame_present_flag, |
| 172 parsed_pps_->bottom_field_pic_order_in_frame_present_flag); |
| 173 EXPECT_EQ(pps.weighted_pred_flag, parsed_pps_->weighted_pred_flag); |
| 174 EXPECT_EQ(pps.weighted_bipred_idc, parsed_pps_->weighted_bipred_idc); |
| 175 EXPECT_EQ(pps.redundant_pic_cnt_present_flag, |
| 176 parsed_pps_->redundant_pic_cnt_present_flag); |
| 177 EXPECT_EQ(pps.pic_init_qp_minus26, parsed_pps_->pic_init_qp_minus26); |
| 178 } |
| 179 |
| 180 PpsParser::PpsState generated_pps_; |
| 181 rtc::Buffer buffer_; |
| 182 rtc::Optional<PpsParser::PpsState> parsed_pps_; |
| 183 }; |
| 184 |
| 185 TEST_F(PpsParserTest, ZeroPps) { |
| 186 RunTest(); |
| 187 } |
| 188 |
| 189 TEST_F(PpsParserTest, MaxPps) { |
| 190 generated_pps_.bottom_field_pic_order_in_frame_present_flag = true; |
| 191 generated_pps_.pic_init_qp_minus26 = std::numeric_limits<int32_t>::max(); |
| 192 generated_pps_.redundant_pic_cnt_present_flag = 1; // 1 bit value. |
| 193 generated_pps_.weighted_bipred_idc = (1 << 2) - 1; // 2 bit value. |
| 194 generated_pps_.weighted_pred_flag = true; |
| 195 RunTest(); |
| 196 |
| 197 generated_pps_.pic_init_qp_minus26 = std::numeric_limits<int32_t>::min() + 1; |
| 198 RunTest(); |
| 199 } |
| 200 |
| 201 } // namespace webrtc |
OLD | NEW |