1 // Copyright 2021 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "examples/file_writer.h"
16
17 #include <cstddef>
18 #include <cstdint>
19 #include <cstring>
20 #include <memory>
21 #include <new>
22 #include <ostream>
23 #include <string>
24 #include <utility>
25
26 #include "absl/memory/memory.h"
27 #include "gav1/decoder_buffer.h"
28 #include "gtest/gtest.h"
29 #include "tests/utils.h"
30
31 namespace libgav1 {
32 namespace {
33
34 const char kExpectedY4mHeader8bit[] = "YUV4MPEG2 W352 H288 F30:1 Ip C420jpeg\n";
35 const char kExpectedY4mHeader10bit[] = "YUV4MPEG2 W352 H288 F30:1 Ip C420p10\n";
36 const char kExpectedY4mHeader8bitMonochrome[] =
37 "YUV4MPEG2 W352 H288 F30:1 Ip Cmono\n";
38 const char kExpectedY4mHeader10bitMonochrome[] =
39 "YUV4MPEG2 W352 H288 F30:1 Ip Cmono10\n";
40
41 // Note: These are non-const because DecoderBuffer.plane is non-const.
42 char fake_plane0[] = "PLANE0\n";
43 char fake_plane1[] = "PLANE1\n";
44 char fake_plane2[] = "PLANE2\n";
45
46 constexpr size_t kExpectedRawDataBufferCount = 3;
47 const char* kExpectedRawData[kExpectedRawDataBufferCount] = {
48 fake_plane0, fake_plane1, fake_plane2};
49
50 const char* const kExpectedRawDataMonochrome = fake_plane0;
51
52 constexpr size_t kExpectedY4mDataBufferCount = 5;
53 const char* const kExpectedY4mFileData8bit[kExpectedY4mDataBufferCount] = {
54 kExpectedY4mHeader8bit, "FRAME\n", fake_plane0, fake_plane1, fake_plane2};
55 const char* const kExpectedY4mFileData10bit[kExpectedY4mDataBufferCount] = {
56 kExpectedY4mHeader10bit, "FRAME\n", fake_plane0, fake_plane1, fake_plane2};
57
58 constexpr size_t kExpectedY4mDataBufferCountMonochrome = 3;
59 const char* const
60 kExpectedY4mFileData8bitMonochrome[kExpectedY4mDataBufferCountMonochrome] =
61 {kExpectedY4mHeader8bitMonochrome, "FRAME\n", fake_plane0};
62 const char* const
63 kExpectedY4mFileData10bitMonochrome[kExpectedY4mDataBufferCountMonochrome] =
64 {kExpectedY4mHeader10bitMonochrome, "FRAME\n", fake_plane0};
65
66 // TODO(tomfinegan): Add a bitdepth arg, and test writing 10 bit frame buffers.
GetFakeDecoderBuffer(ImageFormat image_format)67 std::unique_ptr<DecoderBuffer> GetFakeDecoderBuffer(ImageFormat image_format) {
68 auto buffer = absl::WrapUnique(new (std::nothrow) DecoderBuffer);
69 if (buffer == nullptr) return nullptr;
70 buffer->chroma_sample_position = kChromaSamplePositionUnknown;
71 buffer->image_format = image_format;
72 buffer->bitdepth = 8;
73 buffer->displayed_width[0] = static_cast<int>(strlen(fake_plane0));
74 buffer->displayed_width[1] = static_cast<int>(strlen(fake_plane1));
75 buffer->displayed_width[2] = static_cast<int>(strlen(fake_plane2));
76 buffer->displayed_height[0] = 1;
77 buffer->displayed_height[1] = 1;
78 buffer->displayed_height[2] = 1;
79 buffer->stride[0] = static_cast<int>(strlen(fake_plane0));
80 buffer->stride[1] = static_cast<int>(strlen(fake_plane1));
81 buffer->stride[2] = static_cast<int>(strlen(fake_plane2));
82 buffer->plane[0] = reinterpret_cast<uint8_t*>(fake_plane0);
83 buffer->plane[1] = reinterpret_cast<uint8_t*>(fake_plane1);
84 buffer->plane[2] = reinterpret_cast<uint8_t*>(fake_plane2);
85 buffer->user_private_data = 0;
86 buffer->buffer_private_data = nullptr;
87 return buffer;
88 }
89
TEST(FileWriterTest,FailOpen)90 TEST(FileWriterTest, FailOpen) {
91 EXPECT_EQ(FileWriter::Open(test_utils::GetTestOutputFilePath("fail_open"),
92 static_cast<FileWriter::FileType>(3), nullptr),
93 nullptr);
94 EXPECT_EQ(FileWriter::Open(test_utils::GetTestOutputFilePath("fail_open"),
95 FileWriter::kFileTypeY4m, nullptr),
96 nullptr);
97 }
98
99 struct FileWriterY4mHeaderTestParameters {
100 FileWriterY4mHeaderTestParameters() = default;
101 FileWriterY4mHeaderTestParameters(const FileWriterY4mHeaderTestParameters&) =
102 default;
103 FileWriterY4mHeaderTestParameters& operator=(
104 const FileWriterY4mHeaderTestParameters&) = default;
105 FileWriterY4mHeaderTestParameters(FileWriterY4mHeaderTestParameters&&) =
106 default;
107 FileWriterY4mHeaderTestParameters& operator=(
108 FileWriterY4mHeaderTestParameters&&) = default;
109 ~FileWriterY4mHeaderTestParameters() = default;
110
FileWriterY4mHeaderTestParameterslibgav1::__anon51d469d90111::FileWriterY4mHeaderTestParameters111 FileWriterY4mHeaderTestParameters(std::string file_name,
112 ChromaSamplePosition chroma_sample_position,
113 ImageFormat image_format, int bitdepth,
114 const char* expected_header_string)
115 : file_name(std::move(file_name)),
116 chroma_sample_position(chroma_sample_position),
117 image_format(image_format),
118 bitdepth(bitdepth),
119 expected_header_string(expected_header_string) {}
120 std::string file_name;
121 ChromaSamplePosition chroma_sample_position = kChromaSamplePositionUnknown;
122 ImageFormat image_format = kImageFormatMonochrome400;
123 int bitdepth = 8;
124 const char* expected_header_string = nullptr;
125 };
126
operator <<(std::ostream & stream,const FileWriterY4mHeaderTestParameters & parameters)127 std::ostream& operator<<(std::ostream& stream,
128 const FileWriterY4mHeaderTestParameters& parameters) {
129 stream << "file_name=" << parameters.file_name << "\n"
130 << "chroma_sample_position=" << parameters.chroma_sample_position
131 << "\n"
132 << "image_format=" << parameters.image_format << "\n"
133 << "bitdepth=" << parameters.bitdepth << "\n"
134 << "expected_header_string=" << parameters.expected_header_string
135 << "\n";
136 return stream;
137 }
138
139 class FileWriterY4mHeaderTest
140 : public testing::TestWithParam<FileWriterY4mHeaderTestParameters> {
141 public:
FileWriterY4mHeaderTest()142 FileWriterY4mHeaderTest() {
143 test_parameters_ = GetParam();
144 y4m_parameters_.width = 352;
145 y4m_parameters_.height = 288;
146 y4m_parameters_.frame_rate_numerator = 30;
147 y4m_parameters_.frame_rate_denominator = 1;
148 y4m_parameters_.chroma_sample_position =
149 test_parameters_.chroma_sample_position;
150 y4m_parameters_.image_format = test_parameters_.image_format;
151 y4m_parameters_.bitdepth = test_parameters_.bitdepth;
152 }
153 FileWriterY4mHeaderTest(const FileWriterY4mHeaderTest&) = delete;
154 FileWriterY4mHeaderTest& operator=(const FileWriterY4mHeaderTest&) = delete;
155 ~FileWriterY4mHeaderTest() override = default;
156
157 protected:
158 FileWriterY4mHeaderTestParameters test_parameters_;
159 FileWriter::Y4mParameters y4m_parameters_;
160 };
161
TEST_P(FileWriterY4mHeaderTest,WriteY4mHeader)162 TEST_P(FileWriterY4mHeaderTest, WriteY4mHeader) {
163 const std::string file_name =
164 test_utils::GetTestOutputFilePath(test_parameters_.file_name);
165 EXPECT_NE(
166 FileWriter::Open(file_name, FileWriter::kFileTypeY4m, &y4m_parameters_),
167 nullptr);
168 std::string y4m_header_string;
169 test_utils::GetTestData(test_parameters_.file_name, true, &y4m_header_string);
170 EXPECT_STREQ(y4m_header_string.c_str(),
171 test_parameters_.expected_header_string);
172 }
173
174 INSTANTIATE_TEST_SUITE_P(
175 WriteY4mHeader, FileWriterY4mHeaderTest,
176 testing::Values(
177 FileWriterY4mHeaderTestParameters(
178 "y4m_header_8bit", kChromaSamplePositionUnknown, kImageFormatYuv420,
179 /*bitdepth=*/8, kExpectedY4mHeader8bit),
180 FileWriterY4mHeaderTestParameters("y4m_header_10bit",
181 kChromaSamplePositionUnknown,
182 kImageFormatYuv420, /*bitdepth=*/10,
183 kExpectedY4mHeader10bit),
184 FileWriterY4mHeaderTestParameters("y4m_header_8bit_monochrome",
185 kChromaSamplePositionUnknown,
186 kImageFormatMonochrome400,
187 /*bitdepth=*/8,
188 kExpectedY4mHeader8bitMonochrome),
189 FileWriterY4mHeaderTestParameters("y4m_header_10bit_monochrome",
190 kChromaSamplePositionUnknown,
191 kImageFormatMonochrome400,
192 /*bitdepth=*/10,
193 kExpectedY4mHeader10bitMonochrome)));
194
195 struct FileWriterTestParameters {
196 FileWriterTestParameters() = default;
197 FileWriterTestParameters(const FileWriterTestParameters&) = default;
198 FileWriterTestParameters& operator=(const FileWriterTestParameters&) =
199 default;
200 FileWriterTestParameters(FileWriterTestParameters&&) = default;
201 FileWriterTestParameters& operator=(FileWriterTestParameters&&) = default;
202 ~FileWriterTestParameters() = default;
203
FileWriterTestParameterslibgav1::__anon51d469d90111::FileWriterTestParameters204 FileWriterTestParameters(std::string file_name,
205 FileWriter::FileType file_type,
206 const FileWriter::Y4mParameters* y4m_parameters,
207 size_t num_frames)
208 : file_name(std::move(file_name)),
209 file_type(file_type),
210 y4m_parameters(y4m_parameters),
211 num_frames(num_frames) {}
212 std::string file_name;
213 FileWriter::FileType file_type = FileWriter::kFileTypeRaw;
214 const FileWriter::Y4mParameters* y4m_parameters = nullptr;
215 size_t num_frames = 1;
216 };
217
operator <<(std::ostream & stream,const ChromaSamplePosition & position)218 std::ostream& operator<<(std::ostream& stream,
219 const ChromaSamplePosition& position) {
220 switch (position) {
221 case kChromaSamplePositionUnknown:
222 stream << "kCromaSamplePositionUnknown";
223 break;
224 case kChromaSamplePositionVertical:
225 stream << "kChromaSamplePositionVertical";
226 break;
227 case kChromaSamplePositionColocated:
228 stream << "kChromaSamplePositionColocated";
229 break;
230 case kChromaSamplePositionReserved:
231 stream << "kChromaSamplePositionReserved";
232 break;
233 }
234 return stream;
235 }
236
operator <<(std::ostream & stream,const ImageFormat & image_format)237 std::ostream& operator<<(std::ostream& stream,
238 const ImageFormat& image_format) {
239 switch (image_format) {
240 case kImageFormatMonochrome400:
241 stream << "kImageFormatMonochrome400";
242 break;
243 case kImageFormatYuv420:
244 stream << "kImageFormatYuv420";
245 break;
246 case kImageFormatYuv422:
247 stream << "kImageFormatYuv422";
248 break;
249 case kImageFormatYuv444:
250 stream << "kImageFormatYuv444";
251 break;
252 }
253 return stream;
254 }
255
operator <<(std::ostream & stream,const FileWriter::Y4mParameters & parameters)256 std::ostream& operator<<(std::ostream& stream,
257 const FileWriter::Y4mParameters& parameters) {
258 stream << "y4m_parameters:\n"
259 << " width=" << parameters.width << "\n"
260 << " height=" << parameters.height << "\n"
261 << " frame_rate_numerator=" << parameters.frame_rate_numerator << "\n"
262 << " frame_rate_denominator=" << parameters.frame_rate_denominator
263 << "\n"
264 << " chroma_sample_position=" << parameters.chroma_sample_position
265 << "\n"
266 << " image_format=" << parameters.image_format << "\n"
267 << " bitdepth=" << parameters.bitdepth << "\n";
268
269 return stream;
270 }
271
operator <<(std::ostream & stream,const FileWriterTestParameters & parameters)272 std::ostream& operator<<(std::ostream& stream,
273 const FileWriterTestParameters& parameters) {
274 stream << "file_name=" << parameters.file_name << "\n"
275 << "file_type="
276 << (parameters.file_type == FileWriter::kFileTypeRaw ? "kFileTypeRaw"
277 : "kFileTypeY4m")
278 << "\n";
279 if (parameters.y4m_parameters != nullptr) {
280 stream << *parameters.y4m_parameters;
281 } else {
282 stream << "y4m_parameters: <nullptr>\n";
283 }
284 stream << "num_frames=" << parameters.num_frames << "\n";
285 return stream;
286 }
287
288 class FileWriterTestBase
289 : public testing::TestWithParam<FileWriterTestParameters> {
290 public:
291 FileWriterTestBase() = default;
292 FileWriterTestBase(const FileWriterTestBase&) = delete;
293 FileWriterTestBase& operator=(const FileWriterTestBase&) = delete;
294 ~FileWriterTestBase() override = default;
295
296 protected:
SetUp()297 void SetUp() override { OpenWriter(GetParam()); }
298
OpenWriter(const FileWriterTestParameters & parameters)299 void OpenWriter(const FileWriterTestParameters& parameters) {
300 parameters_ = parameters;
301 parameters_.file_name = parameters.file_name;
302 file_writer_ = FileWriter::Open(
303 test_utils::GetTestOutputFilePath(parameters.file_name),
304 parameters_.file_type, parameters_.y4m_parameters);
305 ASSERT_NE(file_writer_, nullptr);
306 }
307
WriteFramesAndCloseFile()308 void WriteFramesAndCloseFile() {
309 if (parameters_.y4m_parameters != nullptr) {
310 image_format_ = parameters_.y4m_parameters->image_format;
311 }
312 decoder_buffer_ = GetFakeDecoderBuffer(image_format_);
313 for (size_t frame_num = 0; frame_num < parameters_.num_frames;
314 ++frame_num) {
315 ASSERT_TRUE(file_writer_->WriteFrame(*decoder_buffer_));
316 }
317 file_writer_ = nullptr;
318 }
319
320 ImageFormat image_format_ = kImageFormatYuv420;
321 FileWriterTestParameters parameters_;
322 std::unique_ptr<FileWriter> file_writer_;
323 std::unique_ptr<DecoderBuffer> decoder_buffer_;
324 };
325
326 class FileWriterTestRaw : public FileWriterTestBase {
327 public:
328 FileWriterTestRaw() = default;
329 FileWriterTestRaw(const FileWriterTestRaw&) = delete;
330 FileWriterTestRaw& operator=(const FileWriterTestRaw&) = delete;
331 ~FileWriterTestRaw() override = default;
332
333 protected:
SetUp()334 void SetUp() override { FileWriterTestBase::SetUp(); }
335 };
336
337 class FileWriterTestY4m : public FileWriterTestBase {
338 public:
339 FileWriterTestY4m() = default;
340 FileWriterTestY4m(const FileWriterTestY4m&) = delete;
341 FileWriterTestY4m& operator=(const FileWriterTestY4m&) = delete;
342 ~FileWriterTestY4m() override = default;
343
344 protected:
SetUp()345 void SetUp() override { FileWriterTestBase::SetUp(); }
346 };
347
TEST_P(FileWriterTestRaw,WriteRawFrames)348 TEST_P(FileWriterTestRaw, WriteRawFrames) {
349 WriteFramesAndCloseFile();
350
351 std::string actual_file_data;
352 test_utils::GetTestData(parameters_.file_name, true, &actual_file_data);
353
354 std::string expected_file_data;
355 for (size_t frame_num = 0; frame_num < parameters_.num_frames; ++frame_num) {
356 if (image_format_ == kImageFormatMonochrome400) {
357 expected_file_data += kExpectedRawDataMonochrome;
358 } else {
359 for (const auto& buffer : kExpectedRawData) {
360 expected_file_data += buffer;
361 }
362 }
363 }
364
365 ASSERT_EQ(actual_file_data, expected_file_data);
366 }
367
TEST_P(FileWriterTestY4m,WriteY4mFrames)368 TEST_P(FileWriterTestY4m, WriteY4mFrames) {
369 WriteFramesAndCloseFile();
370
371 std::string actual_file_data;
372 test_utils::GetTestData(parameters_.file_name, true, &actual_file_data);
373
374 std::string expected_file_data;
375 for (size_t frame_num = 0; frame_num < parameters_.num_frames; ++frame_num) {
376 if (image_format_ == kImageFormatMonochrome400) {
377 const char* const* expected_data_planes =
378 (parameters_.y4m_parameters->bitdepth == 8)
379 ? kExpectedY4mFileData8bitMonochrome
380 : kExpectedY4mFileData10bitMonochrome;
381 // Skip the Y4M file header "plane" after frame 0.
382 for (size_t buffer_num = (frame_num == 0) ? 0 : 1;
383 buffer_num < kExpectedY4mDataBufferCountMonochrome; ++buffer_num) {
384 expected_file_data += expected_data_planes[buffer_num];
385 }
386 } else {
387 const char* const* expected_data_planes =
388 (parameters_.y4m_parameters->bitdepth == 8)
389 ? kExpectedY4mFileData8bit
390 : kExpectedY4mFileData10bit;
391
392 // Skip the Y4M file header "plane" after frame 0.
393 for (size_t buffer_num = (frame_num == 0) ? 0 : 1;
394 buffer_num < kExpectedY4mDataBufferCount; ++buffer_num) {
395 expected_file_data += expected_data_planes[buffer_num];
396 }
397 }
398 }
399
400 ASSERT_EQ(actual_file_data, expected_file_data);
401 }
402
403 INSTANTIATE_TEST_SUITE_P(
404 WriteRawFrames, FileWriterTestRaw,
405 testing::Values(
406 FileWriterTestParameters("raw_frames_test_1frame",
407 FileWriter::kFileTypeRaw,
408 /*y4m_parameters=*/nullptr,
409 /*num_frames=*/1),
410 FileWriterTestParameters("raw_frames_test_5frames",
411 FileWriter::kFileTypeRaw,
412 /*y4m_parameters=*/nullptr,
413 /*num_frames=*/5),
414 FileWriterTestParameters("raw_frames_test_1frame_monochrome",
415 FileWriter::kFileTypeRaw,
416 /*y4m_parameters=*/nullptr,
417 /*num_frames=*/1),
418 FileWriterTestParameters("raw_frames_test_5frames_monochrome",
419 FileWriter::kFileTypeRaw,
420 /*y4m_parameters=*/nullptr,
421 /*num_frames=*/5)));
422
423 const FileWriter::Y4mParameters kY4mParameters8Bit = {
424 352, // width
425 288, // height
426 30, // frame_rate_numerator
427 1, // frame_rate_denominator
428 kChromaSamplePositionUnknown,
429 kImageFormatYuv420,
430 8 // bitdepth
431 };
432
433 const FileWriter::Y4mParameters kY4mParameters10Bit = {
434 352, // width
435 288, // height
436 30, // frame_rate_numerator
437 1, // frame_rate_denominator
438 kChromaSamplePositionUnknown,
439 kImageFormatYuv420,
440 10 // bitdepth
441 };
442
443 const FileWriter::Y4mParameters kY4mParameters8BitMonochrome = {
444 352, // width
445 288, // height
446 30, // frame_rate_numerator
447 1, // frame_rate_denominator
448 kChromaSamplePositionUnknown,
449 kImageFormatMonochrome400,
450 8 // bitdepth
451 };
452
453 const FileWriter::Y4mParameters kY4mParameters10BitMonochrome = {
454 352, // width
455 288, // height
456 30, // frame_rate_numerator
457 1, // frame_rate_denominator
458 kChromaSamplePositionUnknown,
459 kImageFormatMonochrome400,
460 10 // bitdepth
461 };
462
463 INSTANTIATE_TEST_SUITE_P(
464 WriteY4mFrames, FileWriterTestY4m,
465 testing::Values(
466 FileWriterTestParameters("y4m_frames_test_8bit_1frame",
467 FileWriter::kFileTypeY4m, &kY4mParameters8Bit,
468 /*num_frames=*/1),
469 FileWriterTestParameters("y4m_frames_test_8bit_5frames",
470 FileWriter::kFileTypeY4m, &kY4mParameters8Bit,
471 /*num_frames=*/5),
472 FileWriterTestParameters("y4m_frames_test_10bit_1frame",
473 FileWriter::kFileTypeY4m, &kY4mParameters10Bit,
474 /*num_frames=*/1),
475 FileWriterTestParameters("y4m_frames_test_10bit_5frames",
476 FileWriter::kFileTypeY4m, &kY4mParameters10Bit,
477 /*num_frames=*/5),
478 FileWriterTestParameters("y4m_frames_test_8bit_1frame_monochrome",
479 FileWriter::kFileTypeY4m,
480 &kY4mParameters8BitMonochrome,
481 /*num_frames=*/1),
482 FileWriterTestParameters("y4m_frames_test_8bit_5frames_monochrome",
483 FileWriter::kFileTypeY4m,
484 &kY4mParameters8BitMonochrome,
485 /*num_frames=*/5),
486 FileWriterTestParameters("y4m_frames_test_10bit_1frame_monochrome",
487 FileWriter::kFileTypeY4m,
488 &kY4mParameters10BitMonochrome,
489 /*num_frames=*/1),
490 FileWriterTestParameters("y4m_frames_test_10bit_5frames_monochrome",
491 FileWriter::kFileTypeY4m,
492 &kY4mParameters10BitMonochrome,
493 /*num_frames=*/5)));
494
495 } // namespace
496 } // namespace libgav1
497