xref: /aosp_15_r20/external/openscreen/cast/streaming/capture_recommendations.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 <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