| /* | |
| * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #include <stdio.h> | |
| #include <cstdio> | |
| #include <memory> | |
| #include <string> | |
| #include <tuple> | |
| #include <vector> | |
| #include "absl/strings/string_view.h" | |
| #include "api/scoped_refptr.h" | |
| #include "api/video/resolution.h" | |
| #include "api/video/video_frame_buffer.h" | |
| #include "test/gtest.h" | |
| #include "test/testsupport/file_utils.h" | |
| #include "test/testsupport/frame_reader.h" | |
| namespace webrtc { | |
| namespace test { | |
| namespace { | |
| using Ratio = FrameReader::Ratio; | |
| using RepeatMode = YuvFrameReaderImpl::RepeatMode; | |
| constexpr Resolution kResolution({.width = 1, .height = 1}); | |
| constexpr char kFileHeader[] = "YUV4MPEG2 W1 H1 F30:1 C420\n"; | |
| constexpr char kFrameHeader[] = "FRAME\n"; | |
| constexpr char kFrameContent[3][3] = {{0, 1, 2}, {1, 2, 3}, {2, 3, 4}}; | |
| constexpr int kNumFrames = sizeof(kFrameContent) / sizeof(kFrameContent[0]); | |
| } // namespace | |
| class Y4mFrameReaderTest : public ::testing::Test { | |
| protected: | |
| Y4mFrameReaderTest() = default; | |
| ~Y4mFrameReaderTest() override = default; | |
| void SetUp() override { | |
| filepath_ = | |
| test::TempFilename(test::OutputPath(), "y4m_frame_reader_unittest"); | |
| FILE* file = fopen(filepath_.c_str(), "wb"); | |
| fwrite(kFileHeader, 1, sizeof(kFileHeader) - 1, file); | |
| for (int n = 0; n < kNumFrames; ++n) { | |
| fwrite(kFrameHeader, 1, sizeof(kFrameHeader) - 1, file); | |
| fwrite(kFrameContent[n], 1, sizeof(kFrameContent[n]), file); | |
| } | |
| fclose(file); | |
| reader_ = CreateY4mFrameReader(filepath_); | |
| } | |
| void TearDown() override { remove(filepath_.c_str()); } | |
| std::string filepath_; | |
| std::unique_ptr<FrameReader> reader_; | |
| }; | |
| TEST_F(Y4mFrameReaderTest, num_frames) { | |
| EXPECT_EQ(kNumFrames, reader_->num_frames()); | |
| } | |
| TEST_F(Y4mFrameReaderTest, PullFrame_frameResolution) { | |
| scoped_refptr<I420BufferInterface> buffer = reader_->PullFrame(); | |
| EXPECT_EQ(kResolution.width, buffer->width()); | |
| EXPECT_EQ(kResolution.height, buffer->height()); | |
| } | |
| TEST_F(Y4mFrameReaderTest, PullFrame_frameContent) { | |
| scoped_refptr<I420BufferInterface> buffer = reader_->PullFrame(); | |
| EXPECT_EQ(kFrameContent[0][0], *buffer->DataY()); | |
| EXPECT_EQ(kFrameContent[0][1], *buffer->DataU()); | |
| EXPECT_EQ(kFrameContent[0][2], *buffer->DataV()); | |
| } | |
| TEST_F(Y4mFrameReaderTest, ReadFrame_randomOrder) { | |
| std::vector<int> expected_frames = {2, 0, 1}; | |
| std::vector<int> actual_frames; | |
| for (int frame_num : expected_frames) { | |
| scoped_refptr<I420BufferInterface> buffer = reader_->ReadFrame(frame_num); | |
| actual_frames.push_back(*buffer->DataY()); | |
| } | |
| EXPECT_EQ(expected_frames, actual_frames); | |
| } | |
| TEST_F(Y4mFrameReaderTest, PullFrame_scale) { | |
| scoped_refptr<I420BufferInterface> buffer = reader_->PullFrame( | |
| /*pulled_frame_num=*/nullptr, Resolution({.width = 2, .height = 2}), | |
| FrameReader::kNoScale); | |
| EXPECT_EQ(2, buffer->width()); | |
| EXPECT_EQ(2, buffer->height()); | |
| } | |
| class Y4mFrameReaderRepeatModeTest | |
| : public Y4mFrameReaderTest, | |
| public ::testing::WithParamInterface< | |
| std::tuple<RepeatMode, std::vector<int>>> {}; | |
| TEST_P(Y4mFrameReaderRepeatModeTest, PullFrame) { | |
| RepeatMode mode = std::get<0>(GetParam()); | |
| std::vector<int> expected_frames = std::get<1>(GetParam()); | |
| reader_ = CreateY4mFrameReader(filepath_, mode); | |
| std::vector<int> read_frames; | |
| for (size_t i = 0; i < expected_frames.size(); ++i) { | |
| scoped_refptr<I420BufferInterface> buffer = reader_->PullFrame(); | |
| read_frames.push_back(*buffer->DataY()); | |
| } | |
| EXPECT_EQ(expected_frames, read_frames); | |
| } | |
| INSTANTIATE_TEST_SUITE_P( | |
| Y4mFrameReaderTest, | |
| Y4mFrameReaderRepeatModeTest, | |
| ::testing::ValuesIn( | |
| {std::make_tuple(RepeatMode::kSingle, std::vector<int>{0, 1, 2}), | |
| std::make_tuple(RepeatMode::kRepeat, | |
| std::vector<int>{0, 1, 2, 0, 1, 2}), | |
| std::make_tuple(RepeatMode::kPingPong, | |
| std::vector<int>{0, 1, 2, 1, 0, 1, 2})})); | |
| class Y4mFrameReaderFramerateScaleTest | |
| : public Y4mFrameReaderTest, | |
| public ::testing::WithParamInterface< | |
| std::tuple<Ratio, std::vector<int>>> {}; | |
| TEST_P(Y4mFrameReaderFramerateScaleTest, PullFrame) { | |
| Ratio framerate_scale = std::get<0>(GetParam()); | |
| std::vector<int> expected_frames = std::get<1>(GetParam()); | |
| std::vector<int> actual_frames; | |
| for (size_t i = 0; i < expected_frames.size(); ++i) { | |
| int pulled_frame; | |
| scoped_refptr<I420BufferInterface> buffer = | |
| reader_->PullFrame(&pulled_frame, kResolution, framerate_scale); | |
| actual_frames.push_back(pulled_frame); | |
| } | |
| EXPECT_EQ(expected_frames, actual_frames); | |
| } | |
| INSTANTIATE_TEST_SUITE_P(Y4mFrameReaderTest, | |
| Y4mFrameReaderFramerateScaleTest, | |
| ::testing::ValuesIn({ | |
| std::make_tuple(Ratio({.num = 1, .den = 2}), | |
| std::vector<int>{0, 2, 4}), | |
| std::make_tuple(Ratio({.num = 2, .den = 3}), | |
| std::vector<int>{0, 1, 3, 4, 6}), | |
| std::make_tuple(Ratio({.num = 2, .den = 1}), | |
| std::vector<int>{0, 0, 1, 1}), | |
| })); | |
| } // namespace test | |
| } // namespace webrtc |