1 /*
2 * Copyright (c) 2015 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 "test/frame_generator.h"
12
13 #include <stdio.h>
14 #include <string.h>
15
16 #include <cstdint>
17 #include <memory>
18 #include <string>
19
20 #include "api/scoped_refptr.h"
21 #include "api/test/create_frame_generator.h"
22 #include "api/test/frame_generator_interface.h"
23 #include "api/video/video_frame_buffer.h"
24 #include "test/gtest.h"
25 #include "test/testsupport/file_utils.h"
26
27 namespace webrtc {
28 namespace test {
29
30 constexpr int kFrameWidth = 4;
31 constexpr int kFrameHeight = 4;
32 constexpr int y_size = kFrameWidth * kFrameHeight;
33 constexpr int uv_size = ((kFrameHeight + 1) / 2) * ((kFrameWidth + 1) / 2);
34
35 class FrameGeneratorTest : public ::testing::Test {
36 public:
SetUp()37 void SetUp() override {
38 two_frame_yuv_filename_ =
39 test::TempFilename(test::OutputPath(), "2_frame_yuv_file");
40 one_frame_yuv_filename_ =
41 test::TempFilename(test::OutputPath(), "1_frame_yuv_file");
42 two_frame_nv12_filename_ =
43 test::TempFilename(test::OutputPath(), "2_frame_nv12_file");
44 one_frame_nv12_filename_ =
45 test::TempFilename(test::OutputPath(), "1_frame_nv12_file");
46
47 FILE* file = fopen(two_frame_yuv_filename_.c_str(), "wb");
48 WriteYuvFile(file, 0, 0, 0);
49 WriteYuvFile(file, 127, 128, 129);
50 fclose(file);
51 file = fopen(one_frame_yuv_filename_.c_str(), "wb");
52 WriteYuvFile(file, 255, 255, 255);
53 fclose(file);
54 file = fopen(two_frame_nv12_filename_.c_str(), "wb");
55 WriteNV12File(file, 0, 0, 0);
56 WriteNV12File(file, 127, 128, 129);
57 fclose(file);
58 file = fopen(one_frame_nv12_filename_.c_str(), "wb");
59 WriteNV12File(file, 255, 255, 255);
60 fclose(file);
61 }
62
TearDown()63 void TearDown() override {
64 remove(one_frame_yuv_filename_.c_str());
65 remove(two_frame_yuv_filename_.c_str());
66 remove(one_frame_nv12_filename_.c_str());
67 remove(two_frame_nv12_filename_.c_str());
68 }
69
70 protected:
WriteYuvFile(FILE * file,uint8_t y,uint8_t u,uint8_t v)71 void WriteYuvFile(FILE* file, uint8_t y, uint8_t u, uint8_t v) {
72 RTC_DCHECK(file);
73 std::unique_ptr<uint8_t[]> plane_buffer(new uint8_t[y_size]);
74 memset(plane_buffer.get(), y, y_size);
75 fwrite(plane_buffer.get(), 1, y_size, file);
76 memset(plane_buffer.get(), u, uv_size);
77 fwrite(plane_buffer.get(), 1, uv_size, file);
78 memset(plane_buffer.get(), v, uv_size);
79 fwrite(plane_buffer.get(), 1, uv_size, file);
80 }
81
WriteNV12File(FILE * file,uint8_t y,uint8_t u,uint8_t v)82 void WriteNV12File(FILE* file, uint8_t y, uint8_t u, uint8_t v) {
83 RTC_DCHECK(file);
84 uint8_t plane_buffer[y_size];
85
86 memset(&plane_buffer, y, y_size);
87 fwrite(&plane_buffer, 1, y_size, file);
88 for (size_t i = 0; i < uv_size; ++i) {
89 plane_buffer[2 * i] = u;
90 plane_buffer[2 * i + 1] = v;
91 }
92 fwrite(&plane_buffer, 1, 2 * uv_size, file);
93 }
94
CheckFrameAndMutate(const FrameGeneratorInterface::VideoFrameData & frame,uint8_t y,uint8_t u,uint8_t v)95 void CheckFrameAndMutate(const FrameGeneratorInterface::VideoFrameData& frame,
96 uint8_t y,
97 uint8_t u,
98 uint8_t v) {
99 // Check that frame is valid, has the correct color and timestamp are clean.
100 rtc::scoped_refptr<I420BufferInterface> i420_buffer =
101 frame.buffer->ToI420();
102 const uint8_t* buffer;
103 buffer = i420_buffer->DataY();
104 for (int i = 0; i < y_size; ++i)
105 ASSERT_EQ(y, buffer[i]);
106 buffer = i420_buffer->DataU();
107 for (int i = 0; i < uv_size; ++i)
108 ASSERT_EQ(u, buffer[i]);
109 buffer = i420_buffer->DataV();
110 for (int i = 0; i < uv_size; ++i)
111 ASSERT_EQ(v, buffer[i]);
112 }
113
Hash(const FrameGeneratorInterface::VideoFrameData & frame)114 uint64_t Hash(const FrameGeneratorInterface::VideoFrameData& frame) {
115 // Generate a 64-bit hash from the frame's buffer.
116 uint64_t hash = 19;
117 rtc::scoped_refptr<I420BufferInterface> i420_buffer =
118 frame.buffer->ToI420();
119 const uint8_t* buffer = i420_buffer->DataY();
120 for (int i = 0; i < y_size; ++i) {
121 hash = (37 * hash) + buffer[i];
122 }
123 buffer = i420_buffer->DataU();
124 for (int i = 0; i < uv_size; ++i) {
125 hash = (37 * hash) + buffer[i];
126 }
127 buffer = i420_buffer->DataV();
128 for (int i = 0; i < uv_size; ++i) {
129 hash = (37 * hash) + buffer[i];
130 }
131 return hash;
132 }
133
134 std::string two_frame_yuv_filename_;
135 std::string one_frame_yuv_filename_;
136 std::string two_frame_nv12_filename_;
137 std::string one_frame_nv12_filename_;
138 };
139
TEST_F(FrameGeneratorTest,SingleFrameYuvFile)140 TEST_F(FrameGeneratorTest, SingleFrameYuvFile) {
141 std::unique_ptr<FrameGeneratorInterface> generator(
142 CreateFromYuvFileFrameGenerator(
143 std::vector<std::string>(1, one_frame_yuv_filename_), kFrameWidth,
144 kFrameHeight, 1));
145 CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
146 CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
147 }
148
TEST_F(FrameGeneratorTest,TwoFrameYuvFile)149 TEST_F(FrameGeneratorTest, TwoFrameYuvFile) {
150 std::unique_ptr<FrameGeneratorInterface> generator(
151 CreateFromYuvFileFrameGenerator(
152 std::vector<std::string>(1, two_frame_yuv_filename_), kFrameWidth,
153 kFrameHeight, 1));
154 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
155 CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129);
156 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
157 }
158
TEST_F(FrameGeneratorTest,MultipleFrameYuvFiles)159 TEST_F(FrameGeneratorTest, MultipleFrameYuvFiles) {
160 std::vector<std::string> files;
161 files.push_back(two_frame_yuv_filename_);
162 files.push_back(one_frame_yuv_filename_);
163
164 std::unique_ptr<FrameGeneratorInterface> generator(
165 CreateFromYuvFileFrameGenerator(files, kFrameWidth, kFrameHeight, 1));
166 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
167 CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129);
168 CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
169 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
170 }
171
TEST_F(FrameGeneratorTest,TwoFrameYuvFileWithRepeat)172 TEST_F(FrameGeneratorTest, TwoFrameYuvFileWithRepeat) {
173 const int kRepeatCount = 3;
174 std::unique_ptr<FrameGeneratorInterface> generator(
175 CreateFromYuvFileFrameGenerator(
176 std::vector<std::string>(1, two_frame_yuv_filename_), kFrameWidth,
177 kFrameHeight, kRepeatCount));
178 for (int i = 0; i < kRepeatCount; ++i)
179 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
180 for (int i = 0; i < kRepeatCount; ++i)
181 CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129);
182 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
183 }
184
TEST_F(FrameGeneratorTest,MultipleFrameYuvFilesWithRepeat)185 TEST_F(FrameGeneratorTest, MultipleFrameYuvFilesWithRepeat) {
186 const int kRepeatCount = 3;
187 std::vector<std::string> files;
188 files.push_back(two_frame_yuv_filename_);
189 files.push_back(one_frame_yuv_filename_);
190 std::unique_ptr<FrameGeneratorInterface> generator(
191 CreateFromYuvFileFrameGenerator(files, kFrameWidth, kFrameHeight,
192 kRepeatCount));
193 for (int i = 0; i < kRepeatCount; ++i)
194 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
195 for (int i = 0; i < kRepeatCount; ++i)
196 CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129);
197 for (int i = 0; i < kRepeatCount; ++i)
198 CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
199 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
200 }
201
TEST_F(FrameGeneratorTest,SingleFrameNV12File)202 TEST_F(FrameGeneratorTest, SingleFrameNV12File) {
203 std::unique_ptr<FrameGeneratorInterface> generator(
204 CreateFromNV12FileFrameGenerator(
205 std::vector<std::string>(1, one_frame_nv12_filename_), kFrameWidth,
206 kFrameHeight, 1));
207 CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
208 CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
209 }
210
TEST_F(FrameGeneratorTest,TwoFrameNV12File)211 TEST_F(FrameGeneratorTest, TwoFrameNV12File) {
212 std::unique_ptr<FrameGeneratorInterface> generator(
213 CreateFromNV12FileFrameGenerator(
214 std::vector<std::string>(1, two_frame_nv12_filename_), kFrameWidth,
215 kFrameHeight, 1));
216 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
217 CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129);
218 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
219 }
220
TEST_F(FrameGeneratorTest,MultipleFrameNV12Files)221 TEST_F(FrameGeneratorTest, MultipleFrameNV12Files) {
222 std::vector<std::string> files;
223 files.push_back(two_frame_nv12_filename_);
224 files.push_back(one_frame_nv12_filename_);
225
226 std::unique_ptr<FrameGeneratorInterface> generator(
227 CreateFromNV12FileFrameGenerator(files, kFrameWidth, kFrameHeight, 1));
228 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
229 CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129);
230 CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
231 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
232 }
233
TEST_F(FrameGeneratorTest,TwoFrameNV12FileWithRepeat)234 TEST_F(FrameGeneratorTest, TwoFrameNV12FileWithRepeat) {
235 const int kRepeatCount = 3;
236 std::unique_ptr<FrameGeneratorInterface> generator(
237 CreateFromNV12FileFrameGenerator(
238 std::vector<std::string>(1, two_frame_nv12_filename_), kFrameWidth,
239 kFrameHeight, kRepeatCount));
240 for (int i = 0; i < kRepeatCount; ++i)
241 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
242 for (int i = 0; i < kRepeatCount; ++i)
243 CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129);
244 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
245 }
246
TEST_F(FrameGeneratorTest,MultipleFrameNV12FilesWithRepeat)247 TEST_F(FrameGeneratorTest, MultipleFrameNV12FilesWithRepeat) {
248 const int kRepeatCount = 3;
249 std::vector<std::string> files;
250 files.push_back(two_frame_nv12_filename_);
251 files.push_back(one_frame_nv12_filename_);
252 std::unique_ptr<FrameGeneratorInterface> generator(
253 CreateFromNV12FileFrameGenerator(files, kFrameWidth, kFrameHeight,
254 kRepeatCount));
255 for (int i = 0; i < kRepeatCount; ++i)
256 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
257 for (int i = 0; i < kRepeatCount; ++i)
258 CheckFrameAndMutate(generator->NextFrame(), 127, 128, 129);
259 for (int i = 0; i < kRepeatCount; ++i)
260 CheckFrameAndMutate(generator->NextFrame(), 255, 255, 255);
261 CheckFrameAndMutate(generator->NextFrame(), 0, 0, 0);
262 }
263
TEST_F(FrameGeneratorTest,SlideGenerator)264 TEST_F(FrameGeneratorTest, SlideGenerator) {
265 const int kGenCount = 9;
266 const int kRepeatCount = 3;
267 std::unique_ptr<FrameGeneratorInterface> generator(
268 CreateSlideFrameGenerator(kFrameWidth, kFrameHeight, kRepeatCount));
269 uint64_t hashes[kGenCount];
270 for (int i = 0; i < kGenCount; ++i) {
271 hashes[i] = Hash(generator->NextFrame());
272 }
273 // Check that the buffer changes only every `kRepeatCount` frames.
274 for (int i = 1; i < kGenCount; ++i) {
275 if (i % kRepeatCount == 0) {
276 EXPECT_NE(hashes[i - 1], hashes[i]);
277 } else {
278 EXPECT_EQ(hashes[i - 1], hashes[i]);
279 }
280 }
281 }
282
283 } // namespace test
284 } // namespace webrtc
285