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