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 <algorithm>
8 #include <utility>
9
10 #include "cast/streaming/answer_messages.h"
11 #include "util/osp_logging.h"
12
13 namespace openscreen {
14 namespace cast {
15 namespace capture_recommendations {
16 namespace {
17
ApplyDisplay(const DisplayDescription & description,Recommendations * recommendations)18 void ApplyDisplay(const DisplayDescription& description,
19 Recommendations* recommendations) {
20 recommendations->video.supports_scaling =
21 (description.aspect_ratio_constraint &&
22 (description.aspect_ratio_constraint.value() ==
23 AspectRatioConstraint::kVariable));
24
25 // We should never exceed the display's resolution, since it will always
26 // force scaling.
27 if (description.dimensions) {
28 recommendations->video.maximum = description.dimensions.value();
29 recommendations->video.bit_rate_limits.maximum =
30 recommendations->video.maximum.effective_bit_rate();
31
32 if (recommendations->video.maximum.width <
33 recommendations->video.minimum.width) {
34 recommendations->video.minimum =
35 recommendations->video.maximum.ToResolution();
36 }
37 }
38
39 // If the receiver gives us an aspect ratio that doesn't match the display
40 // resolution they give us, the behavior is undefined from the spec.
41 // Here we prioritize the aspect ratio, and the receiver can scale the frame
42 // as they wish.
43 double aspect_ratio = 0.0;
44 if (description.aspect_ratio) {
45 aspect_ratio = static_cast<double>(description.aspect_ratio->width) /
46 description.aspect_ratio->height;
47 recommendations->video.maximum.width =
48 recommendations->video.maximum.height * aspect_ratio;
49 } else if (description.dimensions) {
50 aspect_ratio = static_cast<double>(description.dimensions->width) /
51 description.dimensions->height;
52 } else {
53 return;
54 }
55 recommendations->video.minimum.width =
56 recommendations->video.minimum.height * aspect_ratio;
57 }
58
ApplyConstraints(const Constraints & constraints,Recommendations * recommendations)59 void ApplyConstraints(const Constraints& constraints,
60 Recommendations* recommendations) {
61 // Audio has no fields in the display description, so we can safely
62 // ignore the current recommendations when setting values here.
63 if (constraints.audio.max_delay.has_value()) {
64 recommendations->audio.max_delay = constraints.audio.max_delay.value();
65 }
66 recommendations->audio.max_channels = constraints.audio.max_channels;
67 recommendations->audio.max_sample_rate = constraints.audio.max_sample_rate;
68
69 recommendations->audio.bit_rate_limits = BitRateLimits{
70 std::max(constraints.audio.min_bit_rate, kDefaultAudioMinBitRate),
71 std::max(constraints.audio.max_bit_rate, kDefaultAudioMinBitRate)};
72
73 // With video, we take the intersection of values of the constraints and
74 // the display description.
75 if (constraints.video.max_delay.has_value()) {
76 recommendations->video.max_delay = constraints.video.max_delay.value();
77 }
78
79 if (constraints.video.max_pixels_per_second.has_value()) {
80 recommendations->video.max_pixels_per_second =
81 constraints.video.max_pixels_per_second.value();
82 }
83
84 recommendations->video.bit_rate_limits =
85 BitRateLimits{std::max(constraints.video.min_bit_rate,
86 recommendations->video.bit_rate_limits.minimum),
87 std::min(constraints.video.max_bit_rate,
88 recommendations->video.bit_rate_limits.maximum)};
89 Dimensions dimensions = constraints.video.max_dimensions;
90 if (dimensions.width <= kDefaultMinResolution.width) {
91 recommendations->video.maximum = {kDefaultMinResolution.width,
92 kDefaultMinResolution.height,
93 kDefaultFrameRate};
94 } else if (dimensions.width < recommendations->video.maximum.width) {
95 recommendations->video.maximum = std::move(dimensions);
96 }
97
98 if (constraints.video.min_resolution) {
99 const Resolution& min = constraints.video.min_resolution->ToResolution();
100 if (kDefaultMinResolution.width < min.width) {
101 recommendations->video.minimum = std::move(min);
102 }
103 }
104 }
105
106 } // namespace
107
operator ==(const BitRateLimits & other) const108 bool BitRateLimits::operator==(const BitRateLimits& other) const {
109 return std::tie(minimum, maximum) == std::tie(other.minimum, other.maximum);
110 }
111
operator ==(const Audio & other) const112 bool Audio::operator==(const Audio& other) const {
113 return std::tie(bit_rate_limits, max_delay, max_channels, max_sample_rate) ==
114 std::tie(other.bit_rate_limits, other.max_delay, other.max_channels,
115 other.max_sample_rate);
116 }
117
operator ==(const Video & other) const118 bool Video::operator==(const Video& other) const {
119 return std::tie(bit_rate_limits, minimum, maximum, supports_scaling,
120 max_delay, max_pixels_per_second) ==
121 std::tie(other.bit_rate_limits, other.minimum, other.maximum,
122 other.supports_scaling, other.max_delay,
123 other.max_pixels_per_second);
124 }
125
operator ==(const Recommendations & other) const126 bool Recommendations::operator==(const Recommendations& other) const {
127 return std::tie(audio, video) == std::tie(other.audio, other.video);
128 }
129
GetRecommendations(const Answer & answer)130 Recommendations GetRecommendations(const Answer& answer) {
131 Recommendations recommendations;
132 if (answer.display.has_value() && answer.display->IsValid()) {
133 ApplyDisplay(answer.display.value(), &recommendations);
134 }
135 if (answer.constraints.has_value() && answer.constraints->IsValid()) {
136 ApplyConstraints(answer.constraints.value(), &recommendations);
137 }
138 return recommendations;
139 }
140
141 } // namespace capture_recommendations
142 } // namespace cast
143 } // namespace openscreen
144