tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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