xref: /aosp_15_r20/external/webrtc/modules/video_coding/utility/ivf_file_writer_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/utility/ivf_file_writer.h"
12 
13 #include <string.h>
14 
15 #include <memory>
16 #include <string>
17 
18 #include "modules/rtp_rtcp/source/byte_io.h"
19 #include "test/gtest.h"
20 #include "test/testsupport/file_utils.h"
21 
22 namespace webrtc {
23 
24 namespace {
25 static const int kHeaderSize = 32;
26 static const int kFrameHeaderSize = 12;
27 static uint8_t dummy_payload[4] = {0, 1, 2, 3};
28 // As the default parameter when the width and height of encodedImage are 0,
29 // the values are copied from ivf_file_writer.cc
30 constexpr int kDefaultWidth = 1280;
31 constexpr int kDefaultHeight = 720;
32 }  // namespace
33 
34 class IvfFileWriterTest : public ::testing::Test {
35  protected:
SetUp()36   void SetUp() override {
37     file_name_ =
38         webrtc::test::TempFilename(webrtc::test::OutputPath(), "test_file");
39   }
TearDown()40   void TearDown() override { webrtc::test::RemoveFile(file_name_); }
41 
WriteDummyTestFrames(VideoCodecType codec_type,int width,int height,int num_frames,bool use_capture_tims_ms)42   bool WriteDummyTestFrames(VideoCodecType codec_type,
43                             int width,
44                             int height,
45                             int num_frames,
46                             bool use_capture_tims_ms) {
47     EncodedImage frame;
48     frame.SetEncodedData(
49         EncodedImageBuffer::Create(dummy_payload, sizeof(dummy_payload)));
50     frame._encodedWidth = width;
51     frame._encodedHeight = height;
52     for (int i = 1; i <= num_frames; ++i) {
53       frame.set_size(i % sizeof(dummy_payload));
54       if (use_capture_tims_ms) {
55         frame.capture_time_ms_ = i;
56       } else {
57         frame.SetTimestamp(i);
58       }
59       if (!file_writer_->WriteFrame(frame, codec_type))
60         return false;
61     }
62     return true;
63   }
64 
VerifyIvfHeader(FileWrapper * file,const uint8_t fourcc[4],int width,int height,uint32_t num_frames,bool use_capture_tims_ms)65   void VerifyIvfHeader(FileWrapper* file,
66                        const uint8_t fourcc[4],
67                        int width,
68                        int height,
69                        uint32_t num_frames,
70                        bool use_capture_tims_ms) {
71     ASSERT_TRUE(file->is_open());
72     uint8_t data[kHeaderSize];
73     ASSERT_EQ(static_cast<size_t>(kHeaderSize), file->Read(data, kHeaderSize));
74 
75     uint8_t dkif[4] = {'D', 'K', 'I', 'F'};
76     EXPECT_EQ(0, memcmp(dkif, data, 4));
77     EXPECT_EQ(0u, ByteReader<uint16_t>::ReadLittleEndian(&data[4]));
78     EXPECT_EQ(32u, ByteReader<uint16_t>::ReadLittleEndian(&data[6]));
79     EXPECT_EQ(0, memcmp(fourcc, &data[8], 4));
80     EXPECT_EQ(width, ByteReader<uint16_t>::ReadLittleEndian(&data[12]));
81     EXPECT_EQ(height, ByteReader<uint16_t>::ReadLittleEndian(&data[14]));
82     EXPECT_EQ(use_capture_tims_ms ? 1000u : 90000u,
83               ByteReader<uint32_t>::ReadLittleEndian(&data[16]));
84     EXPECT_EQ(1u, ByteReader<uint32_t>::ReadLittleEndian(&data[20]));
85     EXPECT_EQ(num_frames, ByteReader<uint32_t>::ReadLittleEndian(&data[24]));
86     EXPECT_EQ(0u, ByteReader<uint32_t>::ReadLittleEndian(&data[28]));
87   }
88 
VerifyDummyTestFrames(FileWrapper * file,uint32_t num_frames)89   void VerifyDummyTestFrames(FileWrapper* file, uint32_t num_frames) {
90     const int kMaxFrameSize = 4;
91     for (uint32_t i = 1; i <= num_frames; ++i) {
92       uint8_t frame_header[kFrameHeaderSize];
93       ASSERT_EQ(static_cast<unsigned int>(kFrameHeaderSize),
94                 file->Read(frame_header, kFrameHeaderSize));
95       uint32_t frame_length =
96           ByteReader<uint32_t>::ReadLittleEndian(&frame_header[0]);
97       EXPECT_EQ(i % 4, frame_length);
98       uint64_t timestamp =
99           ByteReader<uint64_t>::ReadLittleEndian(&frame_header[4]);
100       EXPECT_EQ(i, timestamp);
101 
102       uint8_t data[kMaxFrameSize] = {};
103       ASSERT_EQ(frame_length,
104                 static_cast<uint32_t>(file->Read(data, frame_length)));
105       EXPECT_EQ(0, memcmp(data, dummy_payload, frame_length));
106     }
107   }
108 
RunBasicFileStructureTest(VideoCodecType codec_type,const uint8_t fourcc[4],bool use_capture_tims_ms)109   void RunBasicFileStructureTest(VideoCodecType codec_type,
110                                  const uint8_t fourcc[4],
111                                  bool use_capture_tims_ms) {
112     file_writer_ =
113         IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(file_name_), 0);
114     ASSERT_TRUE(file_writer_.get());
115     const int kWidth = 320;
116     const int kHeight = 240;
117     const int kNumFrames = 257;
118     ASSERT_TRUE(WriteDummyTestFrames(codec_type, kWidth, kHeight, kNumFrames,
119                                      use_capture_tims_ms));
120     EXPECT_TRUE(file_writer_->Close());
121 
122     FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
123     VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFrames,
124                     use_capture_tims_ms);
125     VerifyDummyTestFrames(&out_file, kNumFrames);
126 
127     out_file.Close();
128   }
129 
130   std::string file_name_;
131   std::unique_ptr<IvfFileWriter> file_writer_;
132 };
133 
TEST_F(IvfFileWriterTest,WritesBasicVP8FileNtpTimestamp)134 TEST_F(IvfFileWriterTest, WritesBasicVP8FileNtpTimestamp) {
135   const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
136   RunBasicFileStructureTest(kVideoCodecVP8, fourcc, false);
137 }
138 
TEST_F(IvfFileWriterTest,WritesBasicVP8FileMsTimestamp)139 TEST_F(IvfFileWriterTest, WritesBasicVP8FileMsTimestamp) {
140   const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
141   RunBasicFileStructureTest(kVideoCodecVP8, fourcc, true);
142 }
143 
TEST_F(IvfFileWriterTest,WritesBasicVP9FileNtpTimestamp)144 TEST_F(IvfFileWriterTest, WritesBasicVP9FileNtpTimestamp) {
145   const uint8_t fourcc[4] = {'V', 'P', '9', '0'};
146   RunBasicFileStructureTest(kVideoCodecVP9, fourcc, false);
147 }
148 
TEST_F(IvfFileWriterTest,WritesBasicVP9FileMsTimestamp)149 TEST_F(IvfFileWriterTest, WritesBasicVP9FileMsTimestamp) {
150   const uint8_t fourcc[4] = {'V', 'P', '9', '0'};
151   RunBasicFileStructureTest(kVideoCodecVP9, fourcc, true);
152 }
153 
TEST_F(IvfFileWriterTest,WritesBasicAv1FileNtpTimestamp)154 TEST_F(IvfFileWriterTest, WritesBasicAv1FileNtpTimestamp) {
155   const uint8_t fourcc[4] = {'A', 'V', '0', '1'};
156   RunBasicFileStructureTest(kVideoCodecAV1, fourcc, false);
157 }
158 
TEST_F(IvfFileWriterTest,WritesBasicAv1FileMsTimestamp)159 TEST_F(IvfFileWriterTest, WritesBasicAv1FileMsTimestamp) {
160   const uint8_t fourcc[4] = {'A', 'V', '0', '1'};
161   RunBasicFileStructureTest(kVideoCodecAV1, fourcc, true);
162 }
163 
TEST_F(IvfFileWriterTest,WritesBasicH264FileNtpTimestamp)164 TEST_F(IvfFileWriterTest, WritesBasicH264FileNtpTimestamp) {
165   const uint8_t fourcc[4] = {'H', '2', '6', '4'};
166   RunBasicFileStructureTest(kVideoCodecH264, fourcc, false);
167 }
168 
TEST_F(IvfFileWriterTest,WritesBasicH264FileMsTimestamp)169 TEST_F(IvfFileWriterTest, WritesBasicH264FileMsTimestamp) {
170   const uint8_t fourcc[4] = {'H', '2', '6', '4'};
171   RunBasicFileStructureTest(kVideoCodecH264, fourcc, true);
172 }
173 
TEST_F(IvfFileWriterTest,WritesBasicUnknownCodecFileMsTimestamp)174 TEST_F(IvfFileWriterTest, WritesBasicUnknownCodecFileMsTimestamp) {
175   const uint8_t fourcc[4] = {'*', '*', '*', '*'};
176   RunBasicFileStructureTest(kVideoCodecGeneric, fourcc, true);
177 }
178 
TEST_F(IvfFileWriterTest,ClosesWhenReachesLimit)179 TEST_F(IvfFileWriterTest, ClosesWhenReachesLimit) {
180   const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
181   const int kWidth = 320;
182   const int kHeight = 240;
183   const int kNumFramesToWrite = 2;
184   const int kNumFramesToFit = 1;
185 
186   file_writer_ = IvfFileWriter::Wrap(
187       FileWrapper::OpenWriteOnly(file_name_),
188       kHeaderSize +
189           kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload)));
190   ASSERT_TRUE(file_writer_.get());
191 
192   ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight,
193                                     kNumFramesToWrite, true));
194   ASSERT_FALSE(file_writer_->Close());
195 
196   FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
197   VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFramesToFit, true);
198   VerifyDummyTestFrames(&out_file, kNumFramesToFit);
199 
200   out_file.Close();
201 }
202 
TEST_F(IvfFileWriterTest,UseDefaultValueWhenWidthAndHeightAreZero)203 TEST_F(IvfFileWriterTest, UseDefaultValueWhenWidthAndHeightAreZero) {
204   const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
205   const int kWidth = 0;
206   const int kHeight = 0;
207   const int kNumFramesToWrite = 2;
208   const int kNumFramesToFit = 1;
209 
210   file_writer_ = IvfFileWriter::Wrap(
211       FileWrapper::OpenWriteOnly(file_name_),
212       kHeaderSize +
213           kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload)));
214   ASSERT_TRUE(file_writer_.get());
215 
216   ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight,
217                                     kNumFramesToWrite, true));
218   ASSERT_FALSE(file_writer_->Close());
219 
220   FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
221   // When the width and height are zero, we should expect the width and height
222   // in IvfHeader to be kDefaultWidth and kDefaultHeight instead of kWidth and
223   // kHeight.
224   VerifyIvfHeader(&out_file, fourcc, kDefaultWidth, kDefaultHeight,
225                   kNumFramesToFit, true);
226   VerifyDummyTestFrames(&out_file, kNumFramesToFit);
227 
228   out_file.Close();
229 }
230 
TEST_F(IvfFileWriterTest,UseDefaultValueWhenOnlyWidthIsZero)231 TEST_F(IvfFileWriterTest, UseDefaultValueWhenOnlyWidthIsZero) {
232   const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
233   const int kWidth = 0;
234   const int kHeight = 360;
235   const int kNumFramesToWrite = 2;
236   const int kNumFramesToFit = 1;
237 
238   file_writer_ = IvfFileWriter::Wrap(
239       FileWrapper::OpenWriteOnly(file_name_),
240       kHeaderSize +
241           kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload)));
242   ASSERT_TRUE(file_writer_.get());
243 
244   ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight,
245                                     kNumFramesToWrite, true));
246   ASSERT_FALSE(file_writer_->Close());
247 
248   FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
249   // When the width and height are zero, we should expect the width and height
250   // in IvfHeader to be kDefaultWidth and kDefaultHeight instead of kWidth and
251   // kHeight.
252   VerifyIvfHeader(&out_file, fourcc, kDefaultWidth, kDefaultHeight,
253                   kNumFramesToFit, true);
254   VerifyDummyTestFrames(&out_file, kNumFramesToFit);
255 
256   out_file.Close();
257 }
258 
TEST_F(IvfFileWriterTest,UseDefaultValueWhenOnlyHeightIsZero)259 TEST_F(IvfFileWriterTest, UseDefaultValueWhenOnlyHeightIsZero) {
260   const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
261   const int kWidth = 240;
262   const int kHeight = 0;
263   const int kNumFramesToWrite = 2;
264   const int kNumFramesToFit = 1;
265 
266   file_writer_ = IvfFileWriter::Wrap(
267       FileWrapper::OpenWriteOnly(file_name_),
268       kHeaderSize +
269           kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload)));
270   ASSERT_TRUE(file_writer_.get());
271 
272   ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight,
273                                     kNumFramesToWrite, true));
274   ASSERT_FALSE(file_writer_->Close());
275 
276   FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
277   // When the width and height are zero, we should expect the width and height
278   // in IvfHeader to be kDefaultWidth and kDefaultHeight instead of kWidth and
279   // kHeight.
280   VerifyIvfHeader(&out_file, fourcc, kDefaultWidth, kDefaultHeight,
281                   kNumFramesToFit, true);
282   VerifyDummyTestFrames(&out_file, kNumFramesToFit);
283 
284   out_file.Close();
285 }
286 
TEST_F(IvfFileWriterTest,UseDefaultValueWhenHeightAndWidthAreNotZero)287 TEST_F(IvfFileWriterTest, UseDefaultValueWhenHeightAndWidthAreNotZero) {
288   const uint8_t fourcc[4] = {'V', 'P', '8', '0'};
289   const int kWidth = 360;
290   const int kHeight = 240;
291   const int kNumFramesToWrite = 2;
292   const int kNumFramesToFit = 1;
293 
294   file_writer_ = IvfFileWriter::Wrap(
295       FileWrapper::OpenWriteOnly(file_name_),
296       kHeaderSize +
297           kNumFramesToFit * (kFrameHeaderSize + sizeof(dummy_payload)));
298   ASSERT_TRUE(file_writer_.get());
299 
300   ASSERT_FALSE(WriteDummyTestFrames(kVideoCodecVP8, kWidth, kHeight,
301                                     kNumFramesToWrite, true));
302   ASSERT_FALSE(file_writer_->Close());
303 
304   FileWrapper out_file = FileWrapper::OpenReadOnly(file_name_);
305   VerifyIvfHeader(&out_file, fourcc, kWidth, kHeight, kNumFramesToFit, true);
306   VerifyDummyTestFrames(&out_file, kNumFramesToFit);
307 
308   out_file.Close();
309 }
310 
311 }  // namespace webrtc
312