h264_sps_pps_tracker_unittest.cc (13899B)
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 "modules/video_coding/h264_sps_pps_tracker.h" 12 13 #include <cstdint> 14 #include <vector> 15 16 #include "api/array_view.h" 17 #include "api/video/video_codec_type.h" 18 #include "common_video/h264/h264_common.h" 19 #include "modules/rtp_rtcp/source/rtp_video_header.h" 20 #include "modules/video_coding/codecs/h264/include/h264_globals.h" 21 #include "test/gmock.h" 22 #include "test/gtest.h" 23 24 namespace webrtc { 25 namespace video_coding { 26 namespace { 27 28 using ::testing::ElementsAreArray; 29 using ::testing::SizeIs; 30 31 const uint8_t start_code[] = {0, 0, 0, 1}; 32 33 ArrayView<const uint8_t> Bitstream( 34 const H264SpsPpsTracker::FixedBitstream& fixed) { 35 return fixed.bitstream; 36 } 37 38 void ExpectSpsPpsIdr(const RTPVideoHeaderH264& codec_header, 39 uint8_t sps_id, 40 uint8_t pps_id) { 41 bool contains_sps = false; 42 bool contains_pps = false; 43 bool contains_idr = false; 44 for (const auto& nalu : codec_header.nalus) { 45 if (nalu.type == H264::NaluType::kSps) { 46 EXPECT_EQ(sps_id, nalu.sps_id); 47 contains_sps = true; 48 } else if (nalu.type == H264::NaluType::kPps) { 49 EXPECT_EQ(sps_id, nalu.sps_id); 50 EXPECT_EQ(pps_id, nalu.pps_id); 51 contains_pps = true; 52 } else if (nalu.type == H264::NaluType::kIdr) { 53 EXPECT_EQ(pps_id, nalu.pps_id); 54 contains_idr = true; 55 } 56 } 57 EXPECT_TRUE(contains_sps); 58 EXPECT_TRUE(contains_pps); 59 EXPECT_TRUE(contains_idr); 60 } 61 62 class H264VideoHeader : public RTPVideoHeader { 63 public: 64 H264VideoHeader() { 65 codec = kVideoCodecH264; 66 is_first_packet_in_frame = false; 67 auto& h264_header = video_type_header.emplace<RTPVideoHeaderH264>(); 68 h264_header.packetization_type = kH264SingleNalu; 69 } 70 71 RTPVideoHeaderH264& h264() { 72 return std::get<RTPVideoHeaderH264>(video_type_header); 73 } 74 }; 75 76 } // namespace 77 78 class TestH264SpsPpsTracker : public ::testing::Test { 79 public: 80 void AddSps(H264VideoHeader* header, 81 uint8_t sps_id, 82 std::vector<uint8_t>* data) { 83 NaluInfo info; 84 info.type = H264::NaluType::kSps; 85 info.sps_id = sps_id; 86 info.pps_id = -1; 87 data->push_back(H264::NaluType::kSps); 88 data->push_back(sps_id); // The sps data, just a single byte. 89 90 header->h264().nalus.push_back(info); 91 } 92 93 void AddPps(H264VideoHeader* header, 94 uint8_t sps_id, 95 uint8_t pps_id, 96 std::vector<uint8_t>* data) { 97 NaluInfo info; 98 info.type = H264::NaluType::kPps; 99 info.sps_id = sps_id; 100 info.pps_id = pps_id; 101 data->push_back(H264::NaluType::kPps); 102 data->push_back(pps_id); // The pps data, just a single byte. 103 104 header->h264().nalus.push_back(info); 105 } 106 107 void AddIdr(H264VideoHeader* header, int pps_id) { 108 NaluInfo info; 109 info.type = H264::NaluType::kIdr; 110 info.sps_id = -1; 111 info.pps_id = pps_id; 112 113 header->h264().nalus.push_back(info); 114 } 115 116 protected: 117 H264SpsPpsTracker tracker_; 118 }; 119 120 TEST_F(TestH264SpsPpsTracker, NoNalus) { 121 uint8_t data[] = {1, 2, 3}; 122 H264VideoHeader header; 123 header.h264().packetization_type = kH264FuA; 124 125 H264SpsPpsTracker::FixedBitstream fixed = 126 tracker_.CopyAndFixBitstream(data, &header); 127 128 EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert); 129 EXPECT_THAT(Bitstream(fixed), ElementsAreArray(data)); 130 } 131 132 TEST_F(TestH264SpsPpsTracker, FuAFirstPacket) { 133 uint8_t data[] = {1, 2, 3}; 134 H264VideoHeader header; 135 header.h264().packetization_type = kH264FuA; 136 header.h264().nalus.resize(1); 137 header.is_first_packet_in_frame = true; 138 139 H264SpsPpsTracker::FixedBitstream fixed = 140 tracker_.CopyAndFixBitstream(data, &header); 141 142 EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert); 143 std::vector<uint8_t> expected; 144 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 145 expected.insert(expected.end(), {1, 2, 3}); 146 EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected)); 147 } 148 149 TEST_F(TestH264SpsPpsTracker, StapAIncorrectSegmentLength) { 150 uint8_t data[] = {0, 0, 2, 0}; 151 H264VideoHeader header; 152 header.h264().packetization_type = kH264StapA; 153 header.is_first_packet_in_frame = true; 154 155 EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &header).action, 156 H264SpsPpsTracker::kDrop); 157 } 158 159 TEST_F(TestH264SpsPpsTracker, ConsecutiveStapA) { 160 // When the GenericFrameDescriptor or DependencyDescriptor RTP header 161 // extensions are used, we may receive a series of StapA packets where only 162 // the first packet has is_first_packet_in_frame = true set. 163 std::vector<uint8_t> data; 164 H264VideoHeader first_header; 165 first_header.h264().packetization_type = kH264StapA; 166 first_header.is_first_packet_in_frame = true; 167 168 // SPS in first packet. 169 data.insert(data.end(), {0}); // First byte is ignored 170 data.insert(data.end(), {0, 2}); // Length of segment 171 AddSps(&first_header, 13, &data); 172 H264SpsPpsTracker::FixedBitstream first_fixed = 173 tracker_.CopyAndFixBitstream(data, &first_header); 174 EXPECT_THAT(first_fixed.action, H264SpsPpsTracker::kInsert); 175 176 H264VideoHeader second_header; 177 second_header.h264().packetization_type = kH264StapA; 178 second_header.is_first_packet_in_frame = false; 179 180 // PPS and IDR in second packet. 181 data.insert(data.end(), {0, 2}); // Length of segment 182 AddPps(&second_header, 13, 27, &data); 183 data.insert(data.end(), {0, 5}); // Length of segment 184 AddIdr(&second_header, 27); 185 data.insert(data.end(), {1, 2, 3, 2, 1}); 186 187 H264SpsPpsTracker::FixedBitstream fixed = 188 tracker_.CopyAndFixBitstream(data, &second_header); 189 190 EXPECT_THAT(fixed.action, H264SpsPpsTracker::kInsert); 191 std::vector<uint8_t> expected; 192 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 193 expected.insert(expected.end(), {H264::NaluType::kSps, 13}); 194 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 195 expected.insert(expected.end(), {H264::NaluType::kPps, 27}); 196 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 197 expected.insert(expected.end(), {1, 2, 3, 2, 1}); 198 EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected)); 199 } 200 201 TEST_F(TestH264SpsPpsTracker, SingleNaluInsertStartCode) { 202 uint8_t data[] = {1, 2, 3}; 203 H264VideoHeader header; 204 header.h264().nalus.resize(1); 205 206 H264SpsPpsTracker::FixedBitstream fixed = 207 tracker_.CopyAndFixBitstream(data, &header); 208 209 EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert); 210 std::vector<uint8_t> expected; 211 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 212 expected.insert(expected.end(), {1, 2, 3}); 213 EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected)); 214 } 215 216 TEST_F(TestH264SpsPpsTracker, NoStartCodeInsertedForSubsequentFuAPacket) { 217 std::vector<uint8_t> data = {1, 2, 3}; 218 H264VideoHeader header; 219 header.h264().packetization_type = kH264FuA; 220 // Since no NALU begin in this packet the nalus are empty. 221 header.h264().nalus.clear(); 222 223 H264SpsPpsTracker::FixedBitstream fixed = 224 tracker_.CopyAndFixBitstream(data, &header); 225 226 EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert); 227 EXPECT_THAT(Bitstream(fixed), ElementsAreArray(data)); 228 } 229 230 TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoSpsPpsInserted) { 231 std::vector<uint8_t> data = {1, 2, 3}; 232 H264VideoHeader header; 233 header.is_first_packet_in_frame = true; 234 AddIdr(&header, 0); 235 236 EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &header).action, 237 H264SpsPpsTracker::kRequestKeyframe); 238 } 239 240 TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoPpsInserted) { 241 std::vector<uint8_t> data = {1, 2, 3}; 242 H264VideoHeader header; 243 header.is_first_packet_in_frame = true; 244 AddSps(&header, 0, &data); 245 AddIdr(&header, 0); 246 247 EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &header).action, 248 H264SpsPpsTracker::kRequestKeyframe); 249 } 250 251 TEST_F(TestH264SpsPpsTracker, IdrFirstPacketNoSpsInserted) { 252 std::vector<uint8_t> data = {1, 2, 3}; 253 H264VideoHeader header; 254 header.is_first_packet_in_frame = true; 255 AddPps(&header, 0, 0, &data); 256 AddIdr(&header, 0); 257 258 EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &header).action, 259 H264SpsPpsTracker::kRequestKeyframe); 260 } 261 262 TEST_F(TestH264SpsPpsTracker, SpsPpsPacketThenIdrFirstPacket) { 263 std::vector<uint8_t> data; 264 H264VideoHeader sps_pps_header; 265 // Insert SPS/PPS 266 AddSps(&sps_pps_header, 0, &data); 267 AddPps(&sps_pps_header, 0, 1, &data); 268 269 EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &sps_pps_header).action, 270 H264SpsPpsTracker::kInsert); 271 272 // Insert first packet of the IDR 273 H264VideoHeader idr_header; 274 idr_header.is_first_packet_in_frame = true; 275 AddIdr(&idr_header, 1); 276 data = {1, 2, 3}; 277 278 H264SpsPpsTracker::FixedBitstream fixed = 279 tracker_.CopyAndFixBitstream(data, &idr_header); 280 EXPECT_EQ(fixed.action, H264SpsPpsTracker::kInsert); 281 282 std::vector<uint8_t> expected; 283 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 284 expected.insert(expected.end(), {1, 2, 3}); 285 EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected)); 286 } 287 288 TEST_F(TestH264SpsPpsTracker, SpsPpsIdrInStapA) { 289 std::vector<uint8_t> data; 290 H264VideoHeader header; 291 header.h264().packetization_type = kH264StapA; 292 header.is_first_packet_in_frame = true; // Always true for StapA 293 294 data.insert(data.end(), {0}); // First byte is ignored 295 data.insert(data.end(), {0, 2}); // Length of segment 296 AddSps(&header, 13, &data); 297 data.insert(data.end(), {0, 2}); // Length of segment 298 AddPps(&header, 13, 27, &data); 299 data.insert(data.end(), {0, 5}); // Length of segment 300 AddIdr(&header, 27); 301 data.insert(data.end(), {1, 2, 3, 2, 1}); 302 303 H264SpsPpsTracker::FixedBitstream fixed = 304 tracker_.CopyAndFixBitstream(data, &header); 305 306 EXPECT_THAT(fixed.action, H264SpsPpsTracker::kInsert); 307 308 std::vector<uint8_t> expected; 309 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 310 expected.insert(expected.end(), {H264::NaluType::kSps, 13}); 311 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 312 expected.insert(expected.end(), {H264::NaluType::kPps, 27}); 313 expected.insert(expected.end(), start_code, start_code + sizeof(start_code)); 314 expected.insert(expected.end(), {1, 2, 3, 2, 1}); 315 EXPECT_THAT(Bitstream(fixed), ElementsAreArray(expected)); 316 } 317 318 TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBand) { 319 constexpr uint8_t kData[] = {1, 2, 3}; 320 321 // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos. 322 // width: 320, height: 240 323 const std::vector<uint8_t> sps( 324 {0x67, 0x7a, 0x00, 0x0d, 0xbc, 0xd9, 0x41, 0x41, 0xfa, 0x10, 0x00, 0x00, 325 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0xc0, 0xf1, 0x42, 0x99, 0x60}); 326 const std::vector<uint8_t> pps({0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0}); 327 tracker_.InsertSpsPpsNalus(sps, pps); 328 329 // Insert first packet of the IDR. 330 H264VideoHeader idr_header; 331 idr_header.is_first_packet_in_frame = true; 332 AddIdr(&idr_header, 0); 333 EXPECT_THAT(idr_header.h264().nalus, SizeIs(1)); 334 335 H264SpsPpsTracker::FixedBitstream fixed = 336 tracker_.CopyAndFixBitstream(kData, &idr_header); 337 338 EXPECT_THAT(idr_header.h264().nalus, SizeIs(3)); 339 EXPECT_EQ(idr_header.width, 320u); 340 EXPECT_EQ(idr_header.height, 240u); 341 ExpectSpsPpsIdr(idr_header.h264(), 0, 0); 342 } 343 344 TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBandWrongNaluHeader) { 345 constexpr uint8_t kData[] = {1, 2, 3}; 346 347 // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos. 348 // Nalu headers manupilated afterwards. 349 const std::vector<uint8_t> sps( 350 {0xff, 0x7a, 0x00, 0x0d, 0xbc, 0xd9, 0x41, 0x41, 0xfa, 0x10, 0x00, 0x00, 351 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0xc0, 0xf1, 0x42, 0x99, 0x60}); 352 const std::vector<uint8_t> pps({0xff, 0xeb, 0xe3, 0xcb, 0x22, 0xc0}); 353 tracker_.InsertSpsPpsNalus(sps, pps); 354 355 // Insert first packet of the IDR. 356 H264VideoHeader idr_header; 357 idr_header.is_first_packet_in_frame = true; 358 AddIdr(&idr_header, 0); 359 360 EXPECT_EQ(tracker_.CopyAndFixBitstream(kData, &idr_header).action, 361 H264SpsPpsTracker::kRequestKeyframe); 362 } 363 364 TEST_F(TestH264SpsPpsTracker, SpsPpsOutOfBandIncompleteNalu) { 365 constexpr uint8_t kData[] = {1, 2, 3}; 366 367 // Generated by "ffmpeg -r 30 -f avfoundation -i "default" out.h264" on macos. 368 // Nalus damaged afterwards. 369 const std::vector<uint8_t> sps({0x67, 0x7a, 0x00, 0x0d, 0xbc, 0xd9}); 370 const std::vector<uint8_t> pps({0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0}); 371 tracker_.InsertSpsPpsNalus(sps, pps); 372 373 // Insert first packet of the IDR. 374 H264VideoHeader idr_header; 375 idr_header.is_first_packet_in_frame = true; 376 AddIdr(&idr_header, 0); 377 378 EXPECT_EQ(tracker_.CopyAndFixBitstream(kData, &idr_header).action, 379 H264SpsPpsTracker::kRequestKeyframe); 380 } 381 382 TEST_F(TestH264SpsPpsTracker, SaveRestoreWidthHeight) { 383 std::vector<uint8_t> data; 384 385 // Insert an SPS/PPS packet with width/height and make sure 386 // that information is set on the first IDR packet. 387 H264VideoHeader sps_pps_header; 388 AddSps(&sps_pps_header, 0, &data); 389 AddPps(&sps_pps_header, 0, 1, &data); 390 sps_pps_header.width = 320; 391 sps_pps_header.height = 240; 392 393 EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &sps_pps_header).action, 394 H264SpsPpsTracker::kInsert); 395 396 H264VideoHeader idr_header; 397 idr_header.is_first_packet_in_frame = true; 398 AddIdr(&idr_header, 1); 399 data.insert(data.end(), {1, 2, 3}); 400 401 EXPECT_EQ(tracker_.CopyAndFixBitstream(data, &idr_header).action, 402 H264SpsPpsTracker::kInsert); 403 404 EXPECT_EQ(idr_header.width, 320); 405 EXPECT_EQ(idr_header.height, 240); 406 } 407 408 } // namespace video_coding 409 } // namespace webrtc