xref: /aosp_15_r20/external/openscreen/cast/streaming/capture_recommendations_unittest.cc (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/streaming/capture_recommendations.h"
6 
7 #include "absl/types/optional.h"
8 #include "cast/streaming/answer_messages.h"
9 #include "cast/streaming/resolution.h"
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
12 #include "util/chrono_helpers.h"
13 
14 namespace openscreen {
15 namespace cast {
16 namespace capture_recommendations {
17 namespace {
18 
19 const Recommendations kDefaultRecommendations{
20     Audio{BitRateLimits{32000, 256000}, milliseconds(400), 2, 48000, 16000},
21     Video{BitRateLimits{300000, 1920 * 1080 * 30}, Resolution{320, 240},
22           Dimensions{1920, 1080, 30}, false, milliseconds(400),
23           1920 * 1080 * 30 / 8}};
24 
25 const DisplayDescription kEmptyDescription{};
26 
27 const DisplayDescription kValidOnlyResolution{
28     Dimensions{1024, 768, SimpleFraction{60, 1}}, absl::nullopt, absl::nullopt};
29 
30 const DisplayDescription kValidOnlyAspectRatio{absl::nullopt, AspectRatio{4, 3},
31                                                absl::nullopt};
32 
33 const DisplayDescription kValidOnlyAspectRatioSixteenNine{
34     absl::nullopt, AspectRatio{16, 9}, absl::nullopt};
35 
36 const DisplayDescription kValidOnlyVariable{absl::nullopt, absl::nullopt,
37                                             AspectRatioConstraint::kVariable};
38 
39 const DisplayDescription kInvalidOnlyFixed{absl::nullopt, absl::nullopt,
40                                            AspectRatioConstraint::kFixed};
41 
42 const DisplayDescription kValidFixedAspectRatio{
43     absl::nullopt, AspectRatio{4, 3}, AspectRatioConstraint::kFixed};
44 
45 const DisplayDescription kValidVariableAspectRatio{
46     absl::nullopt, AspectRatio{4, 3}, AspectRatioConstraint::kVariable};
47 
48 const DisplayDescription kValidFixedMissingAspectRatio{
49     Dimensions{1024, 768, SimpleFraction{60, 1}}, absl::nullopt,
50     AspectRatioConstraint::kFixed};
51 
52 const DisplayDescription kValidDisplayFhd{
53     Dimensions{1920, 1080, SimpleFraction{30, 1}}, AspectRatio{16, 9},
54     AspectRatioConstraint::kVariable};
55 
56 const DisplayDescription kValidDisplayXga{
57     Dimensions{1024, 768, SimpleFraction{60, 1}}, AspectRatio{4, 3},
58     AspectRatioConstraint::kFixed};
59 
60 const DisplayDescription kValidDisplayTiny{
61     Dimensions{300, 200, SimpleFraction{30, 1}}, AspectRatio{3, 2},
62     AspectRatioConstraint::kFixed};
63 
64 const DisplayDescription kValidDisplayMismatched{
65     Dimensions{300, 200, SimpleFraction{30, 1}}, AspectRatio{3, 4},
66     AspectRatioConstraint::kFixed};
67 
68 const Constraints kEmptyConstraints{};
69 
70 const Constraints kValidConstraintsHighEnd{
71     {96100, 5, 96000, 500000, std::chrono::seconds(6)},
72     {6000000, Dimensions{640, 480, SimpleFraction{30, 1}},
73      Dimensions{3840, 2160, SimpleFraction{144, 1}}, 600000, 6000000,
74      std::chrono::seconds(6)}};
75 
76 const Constraints kValidConstraintsLowEnd{
77     {22000, 2, 24000, 50000, std::chrono::seconds(1)},
78     {60000, Dimensions{120, 80, SimpleFraction{10, 1}},
79      Dimensions{1200, 800, SimpleFraction{30, 1}}, 100000, 1000000,
80      std::chrono::seconds(1)}};
81 
82 }  // namespace
83 
TEST(CaptureRecommendationsTest,UsesDefaultsIfNoReceiverInformationAvailable)84 TEST(CaptureRecommendationsTest, UsesDefaultsIfNoReceiverInformationAvailable) {
85   EXPECT_EQ(kDefaultRecommendations, GetRecommendations(Answer{}));
86 }
87 
TEST(CaptureRecommendationsTest,EmptyDisplayDescription)88 TEST(CaptureRecommendationsTest, EmptyDisplayDescription) {
89   Answer answer;
90   answer.display = kEmptyDescription;
91   EXPECT_EQ(kDefaultRecommendations, GetRecommendations(answer));
92 }
93 
TEST(CaptureRecommendationsTest,OnlyResolution)94 TEST(CaptureRecommendationsTest, OnlyResolution) {
95   Recommendations expected = kDefaultRecommendations;
96   expected.video.maximum = Dimensions{1024, 768, 60.0};
97   expected.video.bit_rate_limits.maximum = 47185920;
98   Answer answer;
99   answer.display = kValidOnlyResolution;
100   EXPECT_EQ(expected, GetRecommendations(answer));
101 }
102 
TEST(CaptureRecommendationsTest,OnlyAspectRatioFourThirds)103 TEST(CaptureRecommendationsTest, OnlyAspectRatioFourThirds) {
104   Recommendations expected = kDefaultRecommendations;
105   expected.video.minimum = Resolution{320, 240};
106   expected.video.maximum = Dimensions{1440, 1080, 30.0};
107   Answer answer;
108   answer.display = kValidOnlyAspectRatio;
109 
110   EXPECT_EQ(expected, GetRecommendations(answer));
111 }
112 
TEST(CaptureRecommendationsTest,OnlyAspectRatioSixteenNine)113 TEST(CaptureRecommendationsTest, OnlyAspectRatioSixteenNine) {
114   Recommendations expected = kDefaultRecommendations;
115   expected.video.minimum = Resolution{426, 240};
116   expected.video.maximum = Dimensions{1920, 1080, 30.0};
117   Answer answer;
118   answer.display = kValidOnlyAspectRatioSixteenNine;
119 
120   EXPECT_EQ(expected, GetRecommendations(answer));
121 }
122 
TEST(CaptureRecommendationsTest,OnlyAspectRatioConstraint)123 TEST(CaptureRecommendationsTest, OnlyAspectRatioConstraint) {
124   Recommendations expected = kDefaultRecommendations;
125   expected.video.supports_scaling = true;
126   Answer answer;
127   answer.display = kValidOnlyVariable;
128   EXPECT_EQ(expected, GetRecommendations(answer));
129 }
130 
131 // It doesn't make sense to just provide a "fixed" aspect ratio with no
132 // other dimension information, so we just return default recommendations
133 // in this case and assume the sender will handle it elsewhere, e.g. on
134 // ANSWER message parsing.
TEST(CaptureRecommendationsTest,OnlyInvalidAspectRatioConstraint)135 TEST(CaptureRecommendationsTest, OnlyInvalidAspectRatioConstraint) {
136   Answer answer;
137   answer.display = kInvalidOnlyFixed;
138   EXPECT_EQ(kDefaultRecommendations, GetRecommendations(answer));
139 }
140 
TEST(CaptureRecommendationsTest,FixedAspectRatioConstraint)141 TEST(CaptureRecommendationsTest, FixedAspectRatioConstraint) {
142   Recommendations expected = kDefaultRecommendations;
143   expected.video.minimum = Resolution{320, 240};
144   expected.video.maximum = Dimensions{1440, 1080, 30.0};
145   expected.video.supports_scaling = false;
146   Answer answer;
147   answer.display = kValidFixedAspectRatio;
148   EXPECT_EQ(expected, GetRecommendations(answer));
149 }
150 
151 // Our behavior is actually the same whether the constraint is passed, we
152 // just percolate the constraint up to the capture devices so that intermediate
153 // frame sizes between minimum and maximum can be properly scaled.
TEST(CaptureRecommendationsTest,VariableAspectRatioConstraint)154 TEST(CaptureRecommendationsTest, VariableAspectRatioConstraint) {
155   Recommendations expected = kDefaultRecommendations;
156   expected.video.minimum = Resolution{320, 240};
157   expected.video.maximum = Dimensions{1440, 1080, 30.0};
158   expected.video.supports_scaling = true;
159   Answer answer;
160   answer.display = kValidVariableAspectRatio;
161   EXPECT_EQ(expected, GetRecommendations(answer));
162 }
163 
TEST(CaptureRecommendationsTest,ResolutionWithFixedConstraint)164 TEST(CaptureRecommendationsTest, ResolutionWithFixedConstraint) {
165   Recommendations expected = kDefaultRecommendations;
166   expected.video.minimum = Resolution{320, 240};
167   expected.video.maximum = Dimensions{1024, 768, 60.0};
168   expected.video.supports_scaling = false;
169   expected.video.bit_rate_limits.maximum = 47185920;
170   Answer answer;
171   answer.display = kValidFixedMissingAspectRatio;
172   EXPECT_EQ(expected, GetRecommendations(answer));
173 }
174 
TEST(CaptureRecommendationsTest,ExplicitFhdChangesMinimum)175 TEST(CaptureRecommendationsTest, ExplicitFhdChangesMinimum) {
176   Recommendations expected = kDefaultRecommendations;
177   expected.video.minimum = Resolution{426, 240};
178   expected.video.supports_scaling = true;
179   Answer answer;
180   answer.display = kValidDisplayFhd;
181   EXPECT_EQ(expected, GetRecommendations(answer));
182 }
183 
TEST(CaptureRecommendationsTest,XgaResolution)184 TEST(CaptureRecommendationsTest, XgaResolution) {
185   Recommendations expected = kDefaultRecommendations;
186   expected.video.minimum = Resolution{320, 240};
187   expected.video.maximum = Dimensions{1024, 768, 60.0};
188   expected.video.supports_scaling = false;
189   expected.video.bit_rate_limits.maximum = 47185920;
190   Answer answer;
191   answer.display = kValidDisplayXga;
192   EXPECT_EQ(expected, GetRecommendations(answer));
193 }
194 
TEST(CaptureRecommendationsTest,MismatchedDisplayAndAspectRatio)195 TEST(CaptureRecommendationsTest, MismatchedDisplayAndAspectRatio) {
196   Recommendations expected = kDefaultRecommendations;
197   expected.video.minimum = Resolution{150, 200};
198   expected.video.maximum = Dimensions{150, 200, 30.0};
199   expected.video.supports_scaling = false;
200   expected.video.bit_rate_limits.maximum = 300 * 200 * 30;
201   Answer answer;
202   answer.display = kValidDisplayMismatched;
203   EXPECT_EQ(expected, GetRecommendations(answer));
204 }
205 
TEST(CaptureRecommendationsTest,TinyDisplay)206 TEST(CaptureRecommendationsTest, TinyDisplay) {
207   Recommendations expected = kDefaultRecommendations;
208   expected.video.minimum = Resolution{300, 200};
209   expected.video.maximum = Dimensions{300, 200, 30.0};
210   expected.video.supports_scaling = false;
211   expected.video.bit_rate_limits.maximum = 300 * 200 * 30;
212   Answer answer;
213   answer.display = kValidDisplayTiny;
214   EXPECT_EQ(expected, GetRecommendations(answer));
215 }
216 
TEST(CaptureRecommendationsTest,EmptyConstraints)217 TEST(CaptureRecommendationsTest, EmptyConstraints) {
218   Answer answer;
219   answer.constraints = kEmptyConstraints;
220   EXPECT_EQ(kDefaultRecommendations, GetRecommendations(answer));
221 }
222 
223 // Generally speaking, if the receiver gives us constraints higher than our
224 // defaults we will accept them, with the exception of maximum resolutions
225 // exceeding 1080P.
TEST(CaptureRecommendationsTest,HandlesHighEnd)226 TEST(CaptureRecommendationsTest, HandlesHighEnd) {
227   const Recommendations kExpected{
228       Audio{BitRateLimits{96000, 500000}, milliseconds(6000), 5, 96100, 16000},
229       Video{BitRateLimits{600000, 6000000}, Resolution{640, 480},
230             Dimensions{1920, 1080, 30}, false, milliseconds(6000), 6000000}};
231   Answer answer;
232   answer.constraints = kValidConstraintsHighEnd;
233   EXPECT_EQ(kExpected, GetRecommendations(answer));
234 }
235 
236 // However, if the receiver gives us constraints lower than our minimum
237 // defaults, we will ignore them--they would result in an unacceptable cast
238 // experience.
TEST(CaptureRecommendationsTest,HandlesLowEnd)239 TEST(CaptureRecommendationsTest, HandlesLowEnd) {
240   const Recommendations kExpected{
241       Audio{BitRateLimits{32000, 50000}, milliseconds(1000), 2, 22000, 16000},
242       Video{BitRateLimits{300000, 1000000}, Resolution{320, 240},
243             Dimensions{1200, 800, 30}, false, milliseconds(1000), 60000}};
244   Answer answer;
245   answer.constraints = kValidConstraintsLowEnd;
246   EXPECT_EQ(kExpected, GetRecommendations(answer));
247 }
248 
TEST(CaptureRecommendationsTest,HandlesTooSmallScreen)249 TEST(CaptureRecommendationsTest, HandlesTooSmallScreen) {
250   const Recommendations kExpected{
251       Audio{BitRateLimits{32000, 50000}, milliseconds(1000), 2, 22000, 16000},
252       Video{BitRateLimits{300000, 1000000}, Resolution{320, 240},
253             Dimensions{320, 240, 30}, false, milliseconds(1000), 60000}};
254   Answer answer;
255   answer.constraints = kValidConstraintsLowEnd;
256   answer.constraints->video.max_dimensions =
257       answer.constraints->video.min_resolution.value();
258   EXPECT_EQ(kExpected, GetRecommendations(answer));
259 }
260 
TEST(CaptureRecommendationsTest,HandlesMinimumSizeScreen)261 TEST(CaptureRecommendationsTest, HandlesMinimumSizeScreen) {
262   const Recommendations kExpected{
263       Audio{BitRateLimits{32000, 50000}, milliseconds(1000), 2, 22000, 16000},
264       Video{BitRateLimits{300000, 1000000}, Resolution{320, 240},
265             Dimensions{320, 240, 30}, false, milliseconds(1000), 60000}};
266   Answer answer;
267   answer.constraints = kValidConstraintsLowEnd;
268   answer.constraints->video.max_dimensions =
269       Dimensions{320, 240, SimpleFraction{30, 1}};
270   EXPECT_EQ(kExpected, GetRecommendations(answer));
271 }
272 
TEST(CaptureRecommendationsTest,UsesIntersectionOfDisplayAndConstraints)273 TEST(CaptureRecommendationsTest, UsesIntersectionOfDisplayAndConstraints) {
274   const Recommendations kExpected{
275       Audio{BitRateLimits{96000, 500000}, milliseconds(6000), 5, 96100, 16000},
276       Video{BitRateLimits{600000, 6000000}, Resolution{640, 480},
277             // Max resolution should be 1080P, since that's the display
278             // resolution. No reason to capture at 4K, even though the
279             // receiver supports it.
280             Dimensions{1920, 1080, 30}, true, milliseconds(6000), 6000000}};
281   Answer answer;
282   answer.display = kValidDisplayFhd;
283   answer.constraints = kValidConstraintsHighEnd;
284   EXPECT_EQ(kExpected, GetRecommendations(answer));
285 }
286 
287 }  // namespace capture_recommendations
288 }  // namespace cast
289 }  // namespace openscreen
290