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