1 /*
2 * Copyright (c) 2022 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 #include "test/pc/e2e/analyzer/video/video_dumping.h"
11
12 #include <stdio.h>
13
14 #include <memory>
15 #include <string>
16 #include <vector>
17
18 #include "absl/types/optional.h"
19 #include "api/scoped_refptr.h"
20 #include "api/video/i420_buffer.h"
21 #include "api/video/video_frame.h"
22 #include "api/video/video_frame_buffer.h"
23 #include "rtc_base/random.h"
24 #include "test/gmock.h"
25 #include "test/gtest.h"
26 #include "test/testsupport/file_utils.h"
27 #include "test/testsupport/frame_reader.h"
28 #include "test/testsupport/video_frame_writer.h"
29
30 namespace webrtc {
31 namespace webrtc_pc_e2e {
32 namespace {
33
34 using ::testing::ElementsAreArray;
35 using ::testing::Eq;
36 using ::testing::Test;
37
RandByte(Random & random)38 uint8_t RandByte(Random& random) {
39 return random.Rand(255);
40 }
41
CreateRandom2x2VideoFrame(uint16_t id,Random & random)42 VideoFrame CreateRandom2x2VideoFrame(uint16_t id, Random& random) {
43 rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(2, 2);
44
45 uint8_t data[6] = {RandByte(random), RandByte(random), RandByte(random),
46 RandByte(random), RandByte(random), RandByte(random)};
47
48 memcpy(buffer->MutableDataY(), data, 2);
49 memcpy(buffer->MutableDataY() + buffer->StrideY(), data + 2, 2);
50 memcpy(buffer->MutableDataU(), data + 4, 1);
51 memcpy(buffer->MutableDataV(), data + 5, 1);
52
53 return VideoFrame::Builder()
54 .set_id(id)
55 .set_video_frame_buffer(buffer)
56 .set_timestamp_us(1)
57 .build();
58 }
59
AsVector(const uint8_t * data,size_t size)60 std::vector<uint8_t> AsVector(const uint8_t* data, size_t size) {
61 std::vector<uint8_t> out;
62 out.assign(data, data + size);
63 return out;
64 }
65
AssertFramesEqual(rtc::scoped_refptr<webrtc::I420BufferInterface> actual,rtc::scoped_refptr<VideoFrameBuffer> expected)66 void AssertFramesEqual(rtc::scoped_refptr<webrtc::I420BufferInterface> actual,
67 rtc::scoped_refptr<VideoFrameBuffer> expected) {
68 ASSERT_THAT(actual->width(), Eq(expected->width()));
69 ASSERT_THAT(actual->height(), Eq(expected->height()));
70 rtc::scoped_refptr<webrtc::I420BufferInterface> expected_i420 =
71 expected->ToI420();
72
73 int height = actual->height();
74
75 EXPECT_THAT(AsVector(actual->DataY(), actual->StrideY() * height),
76 ElementsAreArray(expected_i420->DataY(),
77 expected_i420->StrideY() * height));
78 EXPECT_THAT(AsVector(actual->DataU(), actual->StrideU() * (height + 1) / 2),
79 ElementsAreArray(expected_i420->DataU(),
80 expected_i420->StrideU() * (height + 1) / 2));
81 EXPECT_THAT(AsVector(actual->DataV(), actual->StrideV() * (height + 1) / 2),
82 ElementsAreArray(expected_i420->DataV(),
83 expected_i420->StrideV() * (height + 1) / 2));
84 }
85
AssertFrameIdsAre(const std::string & filename,std::vector<std::string> expected_ids)86 void AssertFrameIdsAre(const std::string& filename,
87 std::vector<std::string> expected_ids) {
88 FILE* file = fopen(filename.c_str(), "r");
89 ASSERT_TRUE(file != nullptr);
90 std::vector<std::string> actual_ids;
91 char buffer[8];
92 while (fgets(buffer, sizeof buffer, file) != nullptr) {
93 std::string current_id(buffer);
94 ASSERT_GE(current_id.size(), 2lu);
95 // Trim "\n" at the end.
96 actual_ids.push_back(current_id.substr(0, current_id.size() - 1));
97 }
98 EXPECT_THAT(actual_ids, ElementsAreArray(expected_ids));
99 }
100
101 class VideoDumpingTest : public Test {
102 protected:
103 ~VideoDumpingTest() override = default;
104
SetUp()105 void SetUp() override {
106 video_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
107 "video_dumping_test");
108 ids_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
109 "video_dumping_test");
110 }
111
TearDown()112 void TearDown() override {
113 remove(video_filename_.c_str());
114 remove(ids_filename_.c_str());
115 }
116
117 std::string video_filename_;
118 std::string ids_filename_;
119 };
120
121 using CreateVideoFrameWithIdsWriterTest = VideoDumpingTest;
122
TEST_F(CreateVideoFrameWithIdsWriterTest,VideoIsWritenWithFrameIds)123 TEST_F(CreateVideoFrameWithIdsWriterTest, VideoIsWritenWithFrameIds) {
124 Random random(/*seed=*/100);
125 VideoFrame frame1 = CreateRandom2x2VideoFrame(1, random);
126 VideoFrame frame2 = CreateRandom2x2VideoFrame(2, random);
127
128 std::unique_ptr<test::VideoFrameWriter> writer =
129 CreateVideoFrameWithIdsWriter(
130 std::make_unique<test::Y4mVideoFrameWriterImpl>(
131 std::string(video_filename_),
132 /*width=*/2, /*height=*/2, /*fps=*/2),
133 ids_filename_);
134
135 ASSERT_TRUE(writer->WriteFrame(frame1));
136 ASSERT_TRUE(writer->WriteFrame(frame2));
137 writer->Close();
138
139 test::Y4mFrameReaderImpl frame_reader(video_filename_, /*width=*/2,
140 /*height=*/2);
141 ASSERT_TRUE(frame_reader.Init());
142 EXPECT_THAT(frame_reader.NumberOfFrames(), Eq(2));
143 AssertFramesEqual(frame_reader.ReadFrame(), frame1.video_frame_buffer());
144 AssertFramesEqual(frame_reader.ReadFrame(), frame2.video_frame_buffer());
145 AssertFrameIdsAre(ids_filename_, {"1", "2"});
146 }
147
148 using VideoWriterTest = VideoDumpingTest;
149
TEST_F(VideoWriterTest,AllFramesAreWrittenWithSamplingModulo1)150 TEST_F(VideoWriterTest, AllFramesAreWrittenWithSamplingModulo1) {
151 Random random(/*seed=*/100);
152 VideoFrame frame1 = CreateRandom2x2VideoFrame(1, random);
153 VideoFrame frame2 = CreateRandom2x2VideoFrame(2, random);
154
155 {
156 test::Y4mVideoFrameWriterImpl frame_writer(std::string(video_filename_),
157 /*width=*/2, /*height=*/2,
158 /*fps=*/2);
159 VideoWriter writer(&frame_writer, /*sampling_modulo=*/1);
160
161 writer.OnFrame(frame1);
162 writer.OnFrame(frame2);
163 frame_writer.Close();
164 }
165
166 test::Y4mFrameReaderImpl frame_reader(video_filename_, /*width=*/2,
167 /*height=*/2);
168 ASSERT_TRUE(frame_reader.Init());
169 EXPECT_THAT(frame_reader.NumberOfFrames(), Eq(2));
170 AssertFramesEqual(frame_reader.ReadFrame(), frame1.video_frame_buffer());
171 AssertFramesEqual(frame_reader.ReadFrame(), frame2.video_frame_buffer());
172 }
173
TEST_F(VideoWriterTest,OnlyEvery2ndFramesIsWrittenWithSamplingModulo2)174 TEST_F(VideoWriterTest, OnlyEvery2ndFramesIsWrittenWithSamplingModulo2) {
175 Random random(/*seed=*/100);
176 VideoFrame frame1 = CreateRandom2x2VideoFrame(1, random);
177 VideoFrame frame2 = CreateRandom2x2VideoFrame(2, random);
178 VideoFrame frame3 = CreateRandom2x2VideoFrame(3, random);
179
180 {
181 test::Y4mVideoFrameWriterImpl frame_writer(std::string(video_filename_),
182 /*width=*/2, /*height=*/2,
183 /*fps=*/2);
184 VideoWriter writer(&frame_writer, /*sampling_modulo=*/2);
185
186 writer.OnFrame(frame1);
187 writer.OnFrame(frame2);
188 writer.OnFrame(frame3);
189 frame_writer.Close();
190 }
191
192 test::Y4mFrameReaderImpl frame_reader(video_filename_, /*width=*/2,
193 /*height=*/2);
194 ASSERT_TRUE(frame_reader.Init());
195 EXPECT_THAT(frame_reader.NumberOfFrames(), Eq(2));
196 AssertFramesEqual(frame_reader.ReadFrame(), frame1.video_frame_buffer());
197 AssertFramesEqual(frame_reader.ReadFrame(), frame3.video_frame_buffer());
198 }
199
200 } // namespace
201 } // namespace webrtc_pc_e2e
202 } // namespace webrtc
203