blob: 8e9765af99e70c299e62b98f986da7722622e9a8 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cast/streaming/capture_recommendations.h"
#include "absl/types/optional.h"
#include "cast/streaming/answer_messages.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "util/chrono_helpers.h"
namespace openscreen {
namespace cast {
namespace capture_recommendations {
namespace {
constexpr Recommendations kDefaultRecommendations{
Audio{BitRateLimits{32000, 256000}, milliseconds(4000), 2, 48000, 16000},
Video{BitRateLimits{300000, 1920 * 1080 * 30}, Resolution{320, 240, 30},
Resolution{1920, 1080, 30}, false, milliseconds(4000),
1920 * 1080 * 30 / 8}};
constexpr DisplayDescription kEmptyDescription{};
constexpr DisplayDescription kValidOnlyResolution{
Dimensions{1024, 768, SimpleFraction{60, 1}}, absl::nullopt, absl::nullopt};
constexpr DisplayDescription kValidOnlyAspectRatio{
absl::nullopt, AspectRatio{4, 3}, absl::nullopt};
constexpr DisplayDescription kValidOnlyAspectRatioSixteenNine{
absl::nullopt, AspectRatio{16, 9}, absl::nullopt};
constexpr DisplayDescription kValidOnlyVariable{
absl::nullopt, absl::nullopt, AspectRatioConstraint::kVariable};
constexpr DisplayDescription kInvalidOnlyFixed{absl::nullopt, absl::nullopt,
AspectRatioConstraint::kFixed};
constexpr DisplayDescription kValidFixedAspectRatio{
absl::nullopt, AspectRatio{4, 3}, AspectRatioConstraint::kFixed};
constexpr DisplayDescription kValidVariableAspectRatio{
absl::nullopt, AspectRatio{4, 3}, AspectRatioConstraint::kVariable};
constexpr DisplayDescription kValidFixedMissingAspectRatio{
Dimensions{1024, 768, SimpleFraction{60, 1}}, absl::nullopt,
AspectRatioConstraint::kFixed};
constexpr DisplayDescription kValidDisplayFhd{
Dimensions{1920, 1080, SimpleFraction{30, 1}}, AspectRatio{16, 9},
AspectRatioConstraint::kVariable};
constexpr DisplayDescription kValidDisplayXga{
Dimensions{1024, 768, SimpleFraction{60, 1}}, AspectRatio{4, 3},
AspectRatioConstraint::kFixed};
constexpr DisplayDescription kValidDisplayTiny{
Dimensions{300, 200, SimpleFraction{30, 1}}, AspectRatio{3, 2},
AspectRatioConstraint::kFixed};
constexpr DisplayDescription kValidDisplayMismatched{
Dimensions{300, 200, SimpleFraction{30, 1}}, AspectRatio{3, 4},
AspectRatioConstraint::kFixed};
constexpr Constraints kEmptyConstraints{};
constexpr Constraints kValidConstraintsHighEnd{
{96100, 5, 96000, 500000, std::chrono::seconds(6)},
{6000000, Dimensions{640, 480, SimpleFraction{30, 1}},
Dimensions{3840, 2160, SimpleFraction{144, 1}}, 600000, 6000000,
std::chrono::seconds(6)}};
constexpr Constraints kValidConstraintsLowEnd{
{22000, 2, 24000, 50000, std::chrono::seconds(1)},
{60000, Dimensions{120, 80, SimpleFraction{10, 1}},
Dimensions{1200, 800, SimpleFraction{30, 1}}, 100000, 1000000,
std::chrono::seconds(1)}};
} // namespace
TEST(CaptureRecommendationsTest, UsesDefaultsIfNoReceiverInformationAvailable) {
EXPECT_EQ(kDefaultRecommendations, GetRecommendations(Answer{}));
}
TEST(CaptureRecommendationsTest, EmptyDisplayDescription) {
Answer answer;
answer.display = kEmptyDescription;
EXPECT_EQ(kDefaultRecommendations, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, OnlyResolution) {
Recommendations expected = kDefaultRecommendations;
expected.video.maximum = Resolution{1024, 768, 60.0};
expected.video.bit_rate_limits.maximum = 47185920;
Answer answer;
answer.display = kValidOnlyResolution;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, OnlyAspectRatioFourThirds) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{320, 240, 30.0};
expected.video.maximum = Resolution{1440, 1080, 30.0};
Answer answer;
answer.display = kValidOnlyAspectRatio;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, OnlyAspectRatioSixteenNine) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{426, 240, 30.0};
expected.video.maximum = Resolution{1920, 1080, 30.0};
Answer answer;
answer.display = kValidOnlyAspectRatioSixteenNine;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, OnlyAspectRatioConstraint) {
Recommendations expected = kDefaultRecommendations;
expected.video.supports_scaling = true;
Answer answer;
answer.display = kValidOnlyVariable;
EXPECT_EQ(expected, GetRecommendations(answer));
}
// It doesn't make sense to just provide a "fixed" aspect ratio with no
// other dimension information, so we just return default recommendations
// in this case and assume the sender will handle it elsewhere, e.g. on
// ANSWER message parsing.
TEST(CaptureRecommendationsTest, OnlyInvalidAspectRatioConstraint) {
Answer answer;
answer.display = kInvalidOnlyFixed;
EXPECT_EQ(kDefaultRecommendations, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, FixedAspectRatioConstraint) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{320, 240, 30.0};
expected.video.maximum = Resolution{1440, 1080, 30.0};
expected.video.supports_scaling = false;
Answer answer;
answer.display = kValidFixedAspectRatio;
EXPECT_EQ(expected, GetRecommendations(answer));
}
// Our behavior is actually the same whether the constraint is passed, we
// just percolate the constraint up to the capture devices so that intermediate
// frame sizes between minimum and maximum can be properly scaled.
TEST(CaptureRecommendationsTest, VariableAspectRatioConstraint) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{320, 240, 30.0};
expected.video.maximum = Resolution{1440, 1080, 30.0};
expected.video.supports_scaling = true;
Answer answer;
answer.display = kValidVariableAspectRatio;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, ResolutionWithFixedConstraint) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{320, 240, 30.0};
expected.video.maximum = Resolution{1024, 768, 60.0};
expected.video.supports_scaling = false;
expected.video.bit_rate_limits.maximum = 47185920;
Answer answer;
answer.display = kValidFixedMissingAspectRatio;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, ExplicitFhdChangesMinimum) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{426, 240, 30.0};
expected.video.supports_scaling = true;
Answer answer;
answer.display = kValidDisplayFhd;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, XgaResolution) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{320, 240, 30.0};
expected.video.maximum = Resolution{1024, 768, 60.0};
expected.video.supports_scaling = false;
expected.video.bit_rate_limits.maximum = 47185920;
Answer answer;
answer.display = kValidDisplayXga;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, MismatchedDisplayAndAspectRatio) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{150, 200, 30.0};
expected.video.maximum = Resolution{150, 200, 30.0};
expected.video.supports_scaling = false;
expected.video.bit_rate_limits.maximum = 300 * 200 * 30;
Answer answer;
answer.display = kValidDisplayMismatched;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, TinyDisplay) {
Recommendations expected = kDefaultRecommendations;
expected.video.minimum = Resolution{300, 200, 30.0};
expected.video.maximum = Resolution{300, 200, 30.0};
expected.video.supports_scaling = false;
expected.video.bit_rate_limits.maximum = 300 * 200 * 30;
Answer answer;
answer.display = kValidDisplayTiny;
EXPECT_EQ(expected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, EmptyConstraints) {
Answer answer;
answer.constraints = kEmptyConstraints;
EXPECT_EQ(kDefaultRecommendations, GetRecommendations(answer));
}
// Generally speaking, if the receiver gives us constraints higher than our
// defaults we will accept them, with the exception of maximum resolutions
// exceeding 1080P.
TEST(CaptureRecommendationsTest, HandlesHighEnd) {
const Recommendations kExpected{
Audio{BitRateLimits{96000, 500000}, milliseconds(6000), 5, 96100, 16000},
Video{BitRateLimits{600000, 6000000}, Resolution{640, 480, 30},
Resolution{1920, 1080, 30}, false, milliseconds(6000), 6000000}};
Answer answer;
answer.constraints = kValidConstraintsHighEnd;
EXPECT_EQ(kExpected, GetRecommendations(answer));
}
// However, if the receiver gives us constraints lower than our minimum
// defaults, we will ignore them--they would result in an unacceptable cast
// experience.
TEST(CaptureRecommendationsTest, HandlesLowEnd) {
const Recommendations kExpected{
Audio{BitRateLimits{32000, 50000}, milliseconds(1000), 2, 22000, 16000},
Video{BitRateLimits{300000, 1000000}, Resolution{320, 240, 30},
Resolution{1200, 800, 30}, false, milliseconds(1000), 60000}};
Answer answer;
answer.constraints = kValidConstraintsLowEnd;
EXPECT_EQ(kExpected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, HandlesTooSmallScreen) {
const Recommendations kExpected{
Audio{BitRateLimits{32000, 50000}, milliseconds(1000), 2, 22000, 16000},
Video{BitRateLimits{300000, 1000000}, Resolution{320, 240, 30},
Resolution{320, 240, 30}, false, milliseconds(1000), 60000}};
Answer answer;
answer.constraints = kValidConstraintsLowEnd;
answer.constraints->video.max_dimensions =
answer.constraints->video.min_dimensions.value();
EXPECT_EQ(kExpected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, HandlesMinimumSizeScreen) {
const Recommendations kExpected{
Audio{BitRateLimits{32000, 50000}, milliseconds(1000), 2, 22000, 16000},
Video{BitRateLimits{300000, 1000000}, Resolution{320, 240, 30},
Resolution{320, 240, 30}, false, milliseconds(1000), 60000}};
Answer answer;
answer.constraints = kValidConstraintsLowEnd;
answer.constraints->video.max_dimensions =
Dimensions{320, 240, SimpleFraction{30, 1}};
EXPECT_EQ(kExpected, GetRecommendations(answer));
}
TEST(CaptureRecommendationsTest, UsesIntersectionOfDisplayAndConstraints) {
const Recommendations kExpected{
Audio{BitRateLimits{96000, 500000}, milliseconds(6000), 5, 96100, 16000},
Video{BitRateLimits{600000, 6000000}, Resolution{640, 480, 30},
// Max resolution should be 1080P, since that's the display
// resolution. No reason to capture at 4K, even though the
// receiver supports it.
Resolution{1920, 1080, 30}, true, milliseconds(6000), 6000000}};
Answer answer;
answer.display = kValidDisplayFhd;
answer.constraints = kValidConstraintsHighEnd;
EXPECT_EQ(kExpected, GetRecommendations(answer));
}
} // namespace capture_recommendations
} // namespace cast
} // namespace openscreen