1 // Copyright 2019 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/receiver_session.h"
6
7 #include <algorithm>
8 #include <chrono>
9 #include <string>
10 #include <utility>
11
12 #include "absl/strings/match.h"
13 #include "absl/strings/numbers.h"
14 #include "cast/common/channel/message_util.h"
15 #include "cast/common/public/message_port.h"
16 #include "cast/streaming/answer_messages.h"
17 #include "cast/streaming/environment.h"
18 #include "cast/streaming/message_fields.h"
19 #include "cast/streaming/offer_messages.h"
20 #include "cast/streaming/receiver.h"
21 #include "cast/streaming/sender_message.h"
22 #include "util/json/json_helpers.h"
23 #include "util/osp_logging.h"
24
25 namespace openscreen {
26 namespace cast {
27 namespace {
28
29 template <typename Stream, typename Codec>
SelectStream(const std::vector<Codec> & preferred_codecs,ReceiverSession::Client * client,const std::vector<Stream> & offered_streams)30 std::unique_ptr<Stream> SelectStream(
31 const std::vector<Codec>& preferred_codecs,
32 ReceiverSession::Client* client,
33 const std::vector<Stream>& offered_streams) {
34 for (auto codec : preferred_codecs) {
35 for (const Stream& offered_stream : offered_streams) {
36 if (offered_stream.codec == codec &&
37 (offered_stream.stream.codec_parameter.empty() ||
38 client->SupportsCodecParameter(
39 offered_stream.stream.codec_parameter))) {
40 OSP_VLOG << "Selected " << CodecToString(codec)
41 << " as codec for streaming";
42 return std::make_unique<Stream>(offered_stream);
43 }
44 }
45 }
46 return nullptr;
47 }
48
ToCapability(AudioCodec codec)49 MediaCapability ToCapability(AudioCodec codec) {
50 switch (codec) {
51 case AudioCodec::kAac:
52 return MediaCapability::kAac;
53 case AudioCodec::kOpus:
54 return MediaCapability::kOpus;
55 default:
56 OSP_DLOG_FATAL << "Invalid audio codec: " << static_cast<int>(codec);
57 OSP_NOTREACHED();
58 }
59 }
60
ToCapability(VideoCodec codec)61 MediaCapability ToCapability(VideoCodec codec) {
62 switch (codec) {
63 case VideoCodec::kVp8:
64 return MediaCapability::kVp8;
65 case VideoCodec::kVp9:
66 return MediaCapability::kVp9;
67 case VideoCodec::kH264:
68 return MediaCapability::kH264;
69 case VideoCodec::kHevc:
70 return MediaCapability::kHevc;
71 case VideoCodec::kAv1:
72 return MediaCapability::kAv1;
73 default:
74 OSP_DLOG_FATAL << "Invalid video codec: " << static_cast<int>(codec);
75 OSP_NOTREACHED();
76 }
77 }
78
79 // Calculates whether any codecs present in |second| are not present in |first|.
80 template <typename T>
IsMissingCodecs(const std::vector<T> & first,const std::vector<T> & second)81 bool IsMissingCodecs(const std::vector<T>& first,
82 const std::vector<T>& second) {
83 if (second.size() > first.size()) {
84 return true;
85 }
86
87 for (auto codec : second) {
88 if (std::find(first.begin(), first.end(), codec) == first.end()) {
89 return true;
90 }
91 }
92
93 return false;
94 }
95
96 // Calculates whether the limits defined by |first| are less restrictive than
97 // those defined by |second|.
98 // NOTE: These variables are intentionally passed by copy - the function will
99 // mutate them.
100 template <typename T>
HasLessRestrictiveLimits(std::vector<T> first,std::vector<T> second)101 bool HasLessRestrictiveLimits(std::vector<T> first, std::vector<T> second) {
102 // Sort both vectors to allow for element-by-element comparison between the
103 // two. All elements with |applies_to_all_codecs| set are sorted to the front.
104 std::function<bool(const T&, const T&)> sorter = [](const T& first,
105 const T& second) {
106 if (first.applies_to_all_codecs != second.applies_to_all_codecs) {
107 return first.applies_to_all_codecs;
108 }
109 return static_cast<int>(first.codec) < static_cast<int>(second.codec);
110 };
111 std::sort(first.begin(), first.end(), sorter);
112 std::sort(second.begin(), second.end(), sorter);
113 auto first_it = first.begin();
114 auto second_it = second.begin();
115
116 // |applies_to_all_codecs| is a special case, so handle that first.
117 T fake_applies_to_all_codecs_struct;
118 fake_applies_to_all_codecs_struct.applies_to_all_codecs = true;
119 T* first_applies_to_all_codecs_struct =
120 !first.empty() && first.front().applies_to_all_codecs
121 ? &(*first_it++)
122 : &fake_applies_to_all_codecs_struct;
123 T* second_applies_to_all_codecs_struct =
124 !second.empty() && second.front().applies_to_all_codecs
125 ? &(*second_it++)
126 : &fake_applies_to_all_codecs_struct;
127 if (!first_applies_to_all_codecs_struct->IsSupersetOf(
128 *second_applies_to_all_codecs_struct)) {
129 return false;
130 }
131
132 // Now all elements of the vectors can be assumed to NOT have
133 // |applies_to_all_codecs| set. So iterate through all codecs set in either
134 // vector and check that the first has the less restrictive configuration set.
135 while (first_it != first.end() || second_it != second.end()) {
136 // Calculate the current codec to process, and whether each vector contains
137 // an instance of this codec.
138 decltype(T::codec) current_codec;
139 bool use_first_fake = false;
140 bool use_second_fake = false;
141 if (first_it == first.end()) {
142 current_codec = second_it->codec;
143 use_first_fake = true;
144 } else if (second_it == second.end()) {
145 current_codec = first_it->codec;
146 use_second_fake = true;
147 } else {
148 current_codec = std::min(first_it->codec, second_it->codec);
149 use_first_fake = first_it->codec != current_codec;
150 use_second_fake = second_it->codec != current_codec;
151 }
152
153 // Compare each vector's limit associated with this codec, or compare
154 // against the default limits if no such codec limits are set.
155 T fake_codecs_struct;
156 fake_codecs_struct.codec = current_codec;
157 T* first_codec_struct =
158 use_first_fake ? &fake_codecs_struct : &(*first_it++);
159 T* second_codec_struct =
160 use_second_fake ? &fake_codecs_struct : &(*second_it++);
161 OSP_DCHECK(!first_codec_struct->applies_to_all_codecs);
162 OSP_DCHECK(!second_codec_struct->applies_to_all_codecs);
163 if (!first_codec_struct->IsSupersetOf(*second_codec_struct)) {
164 return false;
165 }
166 }
167
168 return true;
169 }
170
171 } // namespace
172
173 ReceiverSession::Client::~Client() = default;
174
175 using RemotingPreferences = ReceiverSession::RemotingPreferences;
176
177 using Preferences = ReceiverSession::Preferences;
178
179 Preferences::Preferences() = default;
Preferences(std::vector<VideoCodec> video_codecs,std::vector<AudioCodec> audio_codecs)180 Preferences::Preferences(std::vector<VideoCodec> video_codecs,
181 std::vector<AudioCodec> audio_codecs)
182 : video_codecs(std::move(video_codecs)),
183 audio_codecs(std::move(audio_codecs)) {}
184
Preferences(std::vector<VideoCodec> video_codecs,std::vector<AudioCodec> audio_codecs,std::vector<AudioLimits> audio_limits,std::vector<VideoLimits> video_limits,std::unique_ptr<Display> description)185 Preferences::Preferences(std::vector<VideoCodec> video_codecs,
186 std::vector<AudioCodec> audio_codecs,
187 std::vector<AudioLimits> audio_limits,
188 std::vector<VideoLimits> video_limits,
189 std::unique_ptr<Display> description)
190 : video_codecs(std::move(video_codecs)),
191 audio_codecs(std::move(audio_codecs)),
192 audio_limits(std::move(audio_limits)),
193 video_limits(std::move(video_limits)),
194 display_description(std::move(description)) {}
195
196 Preferences::Preferences(Preferences&&) noexcept = default;
197 Preferences& Preferences::operator=(Preferences&&) noexcept = default;
198
Preferences(const Preferences & other)199 Preferences::Preferences(const Preferences& other) {
200 *this = other;
201 }
202
operator =(const Preferences & other)203 Preferences& Preferences::operator=(const Preferences& other) {
204 video_codecs = other.video_codecs;
205 audio_codecs = other.audio_codecs;
206 audio_limits = other.audio_limits;
207 video_limits = other.video_limits;
208 if (other.display_description) {
209 display_description = std::make_unique<Display>(*other.display_description);
210 }
211 if (other.remoting) {
212 remoting = std::make_unique<RemotingPreferences>(*other.remoting);
213 }
214 return *this;
215 }
216
ReceiverSession(Client * const client,Environment * environment,MessagePort * message_port,Preferences preferences)217 ReceiverSession::ReceiverSession(Client* const client,
218 Environment* environment,
219 MessagePort* message_port,
220 Preferences preferences)
221 : client_(client),
222 environment_(environment),
223 preferences_(std::move(preferences)),
224 session_id_(MakeUniqueSessionId("streaming_receiver")),
225 messenger_(message_port,
226 session_id_,
227 [this](Error error) {
228 OSP_DLOG_WARN << "Got a session messenger error: " << error;
229 client_->OnError(this, error);
230 }),
231 packet_router_(environment_) {
232 OSP_DCHECK(client_);
233 OSP_DCHECK(environment_);
234
235 OSP_DCHECK(!std::any_of(
236 preferences_.video_codecs.begin(), preferences_.video_codecs.end(),
__anona65f6d470402(VideoCodec c) 237 [](VideoCodec c) { return c == VideoCodec::kNotSpecified; }));
238 OSP_DCHECK(!std::any_of(
239 preferences_.audio_codecs.begin(), preferences_.audio_codecs.end(),
__anona65f6d470502(AudioCodec c) 240 [](AudioCodec c) { return c == AudioCodec::kNotSpecified; }));
241
242 messenger_.SetHandler(
243 SenderMessage::Type::kOffer,
__anona65f6d470602(SenderMessage message) 244 [this](SenderMessage message) { OnOffer(std::move(message)); });
245 messenger_.SetHandler(SenderMessage::Type::kGetCapabilities,
__anona65f6d470702(SenderMessage message) 246 [this](SenderMessage message) {
247 OnCapabilitiesRequest(std::move(message));
248 });
249 messenger_.SetHandler(SenderMessage::Type::kRpc,
__anona65f6d470802(SenderMessage message) 250 [this](SenderMessage message) {
251 this->OnRpcMessage(std::move(message));
252 });
253 environment_->SetSocketSubscriber(this);
254 }
255
~ReceiverSession()256 ReceiverSession::~ReceiverSession() {
257 ResetReceivers(Client::kEndOfSession);
258 }
259
OnSocketReady()260 void ReceiverSession::OnSocketReady() {
261 if (pending_session_) {
262 InitializeSession(*pending_session_);
263 pending_session_.reset();
264 }
265 }
266
OnSocketInvalid(Error error)267 void ReceiverSession::OnSocketInvalid(Error error) {
268 if (pending_session_) {
269 SendErrorAnswerReply(pending_session_->sequence_number,
270 "Failed to bind UDP socket");
271 pending_session_.reset();
272 }
273
274 client_->OnError(this,
275 Error(Error::Code::kSocketFailure,
276 "The environment is invalid and should be replaced."));
277 }
278
IsValid() const279 bool ReceiverSession::SessionProperties::IsValid() const {
280 return (selected_audio || selected_video) && sequence_number >= 0;
281 }
282
OnOffer(SenderMessage message)283 void ReceiverSession::OnOffer(SenderMessage message) {
284 // We just drop offers we can't respond to. Note that libcast senders will
285 // always send a strictly positive sequence numbers, but zero is permitted
286 // by the spec.
287 if (message.sequence_number < 0) {
288 OSP_DLOG_WARN
289 << "Dropping offer with missing sequence number, can't respond";
290 return;
291 }
292
293 if (!message.valid) {
294 SendErrorAnswerReply(message.sequence_number,
295 "Failed to parse malformed OFFER");
296 client_->OnError(this, Error(Error::Code::kParameterInvalid,
297 "Received invalid OFFER message"));
298 return;
299 }
300
301 auto properties = std::make_unique<SessionProperties>();
302 properties->sequence_number = message.sequence_number;
303
304 const Offer& offer = absl::get<Offer>(message.body);
305 if (offer.cast_mode == CastMode::kRemoting) {
306 if (!preferences_.remoting) {
307 SendErrorAnswerReply(message.sequence_number,
308 "This receiver does not have remoting enabled.");
309 return;
310 }
311 }
312
313 properties->mode = offer.cast_mode;
314 SelectStreams(offer, properties.get());
315 if (!properties->IsValid()) {
316 SendErrorAnswerReply(message.sequence_number,
317 "Failed to select any streams from OFFER");
318 return;
319 }
320
321 switch (environment_->socket_state()) {
322 // If the environment is ready or in a bad state, we can respond
323 // immediately.
324 case Environment::SocketState::kInvalid:
325 SendErrorAnswerReply(message.sequence_number,
326 "UDP socket is closed, likely due to a bind error.");
327 break;
328
329 case Environment::SocketState::kReady:
330 InitializeSession(*properties);
331 break;
332
333 // Else we need to store the properties we just created until we get a
334 // ready or error event.
335 case Environment::SocketState::kStarting:
336 pending_session_ = std::move(properties);
337 break;
338 }
339 }
340
OnCapabilitiesRequest(SenderMessage message)341 void ReceiverSession::OnCapabilitiesRequest(SenderMessage message) {
342 if (message.sequence_number < 0) {
343 OSP_DLOG_WARN
344 << "Dropping offer with missing sequence number, can't respond";
345 return;
346 }
347
348 ReceiverMessage response{
349 ReceiverMessage::Type::kCapabilitiesResponse, message.sequence_number,
350 true /* valid */
351 };
352 if (preferences_.remoting) {
353 response.body = CreateRemotingCapabilityV2();
354 } else {
355 response.valid = false;
356 response.body =
357 ReceiverError{static_cast<int>(Error::Code::kRemotingNotSupported),
358 "Remoting is not supported"};
359 }
360
361 const Error result = messenger_.SendMessage(std::move(response));
362 if (!result.ok()) {
363 client_->OnError(this, std::move(result));
364 }
365 }
366
OnRpcMessage(SenderMessage message)367 void ReceiverSession::OnRpcMessage(SenderMessage message) {
368 if (!message.valid) {
369 OSP_DLOG_WARN
370 << "Bad RPC message. This may or may not represent a serious problem.";
371 return;
372 }
373
374 const auto& body = absl::get<std::vector<uint8_t>>(message.body);
375 if (!rpc_messenger_) {
376 OSP_DLOG_INFO << "Received an RPC message without having a messenger.";
377 return;
378 }
379 rpc_messenger_->ProcessMessageFromRemote(body.data(), body.size());
380 }
381
SelectStreams(const Offer & offer,SessionProperties * properties)382 void ReceiverSession::SelectStreams(const Offer& offer,
383 SessionProperties* properties) {
384 if (offer.cast_mode == CastMode::kMirroring) {
385 if (!offer.audio_streams.empty() && !preferences_.audio_codecs.empty()) {
386 properties->selected_audio =
387 SelectStream(preferences_.audio_codecs, client_, offer.audio_streams);
388 }
389 if (!offer.video_streams.empty() && !preferences_.video_codecs.empty()) {
390 properties->selected_video =
391 SelectStream(preferences_.video_codecs, client_, offer.video_streams);
392 }
393 } else {
394 OSP_DCHECK(offer.cast_mode == CastMode::kRemoting);
395
396 if (offer.audio_streams.size() == 1) {
397 properties->selected_audio =
398 std::make_unique<AudioStream>(offer.audio_streams[0]);
399 }
400 if (offer.video_streams.size() == 1) {
401 properties->selected_video =
402 std::make_unique<VideoStream>(offer.video_streams[0]);
403 }
404 }
405 }
406
InitializeSession(const SessionProperties & properties)407 void ReceiverSession::InitializeSession(const SessionProperties& properties) {
408 Answer answer = ConstructAnswer(properties);
409 if (!answer.IsValid()) {
410 // If the answer message is invalid, there is no point in setting up a
411 // negotiation because the sender won't be able to connect to it.
412 SendErrorAnswerReply(properties.sequence_number,
413 "Failed to construct an ANSWER message");
414 return;
415 }
416
417 // Only spawn receivers if we know we have a valid answer message.
418 ConfiguredReceivers receivers = SpawnReceivers(properties);
419 if (properties.mode == CastMode::kMirroring) {
420 client_->OnNegotiated(this, std::move(receivers));
421 } else {
422 // TODO(jophba): cleanup sequence number usage.
423 rpc_messenger_ = std::make_unique<RpcMessenger>([this](std::vector<uint8_t> message) {
424 Error error = this->messenger_.SendMessage(
425 ReceiverMessage{ReceiverMessage::Type::kRpc, -1, true /* valid */,
426 std::move(message)});
427
428 if (!error.ok()) {
429 OSP_LOG_WARN << "Failed to send RPC message: " << error;
430 }
431 });
432 client_->OnRemotingNegotiated(
433 this, RemotingNegotiation{std::move(receivers), rpc_messenger_.get()});
434 }
435 const Error result = messenger_.SendMessage(ReceiverMessage{
436 ReceiverMessage::Type::kAnswer, properties.sequence_number,
437 true /* valid */, std::move(answer)});
438 if (!result.ok()) {
439 client_->OnError(this, std::move(result));
440 }
441 }
442
ConstructReceiver(const Stream & stream)443 std::unique_ptr<Receiver> ReceiverSession::ConstructReceiver(
444 const Stream& stream) {
445 // Session config is currently only for mirroring.
446 SessionConfig config = {stream.ssrc, stream.ssrc + 1,
447 stream.rtp_timebase, stream.channels,
448 stream.target_delay, stream.aes_key,
449 stream.aes_iv_mask, /* is_pli_enabled */ true};
450 return std::make_unique<Receiver>(environment_, &packet_router_,
451 std::move(config));
452 }
453
SpawnReceivers(const SessionProperties & properties)454 ReceiverSession::ConfiguredReceivers ReceiverSession::SpawnReceivers(
455 const SessionProperties& properties) {
456 OSP_DCHECK(properties.IsValid());
457 ResetReceivers(Client::kRenegotiated);
458
459 AudioCaptureConfig audio_config;
460 if (properties.selected_audio) {
461 current_audio_receiver_ =
462 ConstructReceiver(properties.selected_audio->stream);
463 audio_config =
464 AudioCaptureConfig{properties.selected_audio->codec,
465 properties.selected_audio->stream.channels,
466 properties.selected_audio->bit_rate,
467 properties.selected_audio->stream.rtp_timebase,
468 properties.selected_audio->stream.target_delay,
469 properties.selected_audio->stream.codec_parameter};
470 }
471
472 VideoCaptureConfig video_config;
473 if (properties.selected_video) {
474 current_video_receiver_ =
475 ConstructReceiver(properties.selected_video->stream);
476 video_config =
477 VideoCaptureConfig{properties.selected_video->codec,
478 properties.selected_video->max_frame_rate,
479 properties.selected_video->max_bit_rate,
480 properties.selected_video->resolutions,
481 properties.selected_video->stream.target_delay,
482 properties.selected_video->stream.codec_parameter};
483 }
484
485 return ConfiguredReceivers{
486 current_audio_receiver_.get(), std::move(audio_config),
487 current_video_receiver_.get(), std::move(video_config)};
488 }
489
ResetReceivers(Client::ReceiversDestroyingReason reason)490 void ReceiverSession::ResetReceivers(Client::ReceiversDestroyingReason reason) {
491 if (current_video_receiver_ || current_audio_receiver_) {
492 client_->OnReceiversDestroying(this, reason);
493 current_audio_receiver_.reset();
494 current_video_receiver_.reset();
495 rpc_messenger_.reset();
496 }
497 }
498
ConstructAnswer(const SessionProperties & properties)499 Answer ReceiverSession::ConstructAnswer(const SessionProperties& properties) {
500 OSP_DCHECK(properties.IsValid());
501
502 std::vector<int> stream_indexes;
503 std::vector<Ssrc> stream_ssrcs;
504 Constraints constraints;
505 if (properties.selected_audio) {
506 stream_indexes.push_back(properties.selected_audio->stream.index);
507 stream_ssrcs.push_back(properties.selected_audio->stream.ssrc + 1);
508
509 for (const auto& limit : preferences_.audio_limits) {
510 if (limit.codec == properties.selected_audio->codec ||
511 limit.applies_to_all_codecs) {
512 constraints.audio = AudioConstraints{
513 limit.max_sample_rate, limit.max_channels, limit.min_bit_rate,
514 limit.max_bit_rate, limit.max_delay,
515 };
516 break;
517 }
518 }
519 }
520
521 if (properties.selected_video) {
522 stream_indexes.push_back(properties.selected_video->stream.index);
523 stream_ssrcs.push_back(properties.selected_video->stream.ssrc + 1);
524
525 for (const auto& limit : preferences_.video_limits) {
526 if (limit.codec == properties.selected_video->codec ||
527 limit.applies_to_all_codecs) {
528 constraints.video = VideoConstraints{
529 limit.max_pixels_per_second, absl::nullopt, /* min dimensions */
530 limit.max_dimensions, limit.min_bit_rate,
531 limit.max_bit_rate, limit.max_delay,
532 };
533 break;
534 }
535 }
536 }
537
538 absl::optional<DisplayDescription> display;
539 if (preferences_.display_description) {
540 const auto* d = preferences_.display_description.get();
541 display = DisplayDescription{d->dimensions, absl::nullopt,
542 d->can_scale_content
543 ? AspectRatioConstraint::kVariable
544 : AspectRatioConstraint::kFixed};
545 }
546
547 // Only set the constraints in the answer if they are valid (meaning we
548 // successfully found limits above).
549 absl::optional<Constraints> answer_constraints;
550 if (constraints.IsValid()) {
551 answer_constraints = std::move(constraints);
552 }
553 return Answer{environment_->GetBoundLocalEndpoint().port,
554 std::move(stream_indexes), std::move(stream_ssrcs),
555 answer_constraints, std::move(display)};
556 }
557
CreateRemotingCapabilityV2()558 ReceiverCapability ReceiverSession::CreateRemotingCapabilityV2() {
559 // If we don't support remoting, there is no reason to respond to
560 // capability requests--they are not used for mirroring.
561 OSP_DCHECK(preferences_.remoting);
562 ReceiverCapability capability;
563 capability.remoting_version = kSupportedRemotingVersion;
564
565 for (const AudioCodec& codec : preferences_.audio_codecs) {
566 capability.media_capabilities.push_back(ToCapability(codec));
567 }
568 for (const VideoCodec& codec : preferences_.video_codecs) {
569 capability.media_capabilities.push_back(ToCapability(codec));
570 }
571
572 if (preferences_.remoting->supports_chrome_audio_codecs) {
573 capability.media_capabilities.push_back(MediaCapability::kAudio);
574 }
575 if (preferences_.remoting->supports_4k) {
576 capability.media_capabilities.push_back(MediaCapability::k4k);
577 }
578 return capability;
579 }
580
SendErrorAnswerReply(int sequence_number,const char * message)581 void ReceiverSession::SendErrorAnswerReply(int sequence_number,
582 const char* message) {
583 const Error error(Error::Code::kParseError, message);
584 OSP_DLOG_WARN << message;
585 const Error result = messenger_.SendMessage(ReceiverMessage{
586 ReceiverMessage::Type::kAnswer, sequence_number, false /* valid */,
587 ReceiverError{static_cast<int>(Error::Code::kParseError), message}});
588 if (!result.ok()) {
589 client_->OnError(this, std::move(result));
590 }
591 }
592
IsSupersetOf(const ReceiverSession::VideoLimits & second) const593 bool ReceiverSession::VideoLimits::IsSupersetOf(
594 const ReceiverSession::VideoLimits& second) const {
595 return (applies_to_all_codecs == second.applies_to_all_codecs) &&
596 (applies_to_all_codecs || codec == second.codec) &&
597 (max_pixels_per_second >= second.max_pixels_per_second) &&
598 (min_bit_rate <= second.min_bit_rate) &&
599 (max_bit_rate >= second.max_bit_rate) &&
600 (max_delay >= second.max_delay) &&
601 (max_dimensions.IsSupersetOf(second.max_dimensions));
602 }
603
IsSupersetOf(const ReceiverSession::AudioLimits & second) const604 bool ReceiverSession::AudioLimits::IsSupersetOf(
605 const ReceiverSession::AudioLimits& second) const {
606 return (applies_to_all_codecs == second.applies_to_all_codecs) &&
607 (applies_to_all_codecs || codec == second.codec) &&
608 (max_sample_rate >= second.max_sample_rate) &&
609 (max_channels >= second.max_channels) &&
610 (min_bit_rate <= second.min_bit_rate) &&
611 (max_bit_rate >= second.max_bit_rate) &&
612 (max_delay >= second.max_delay);
613 }
614
IsSupersetOf(const ReceiverSession::Display & other) const615 bool ReceiverSession::Display::IsSupersetOf(
616 const ReceiverSession::Display& other) const {
617 return dimensions.IsSupersetOf(other.dimensions) &&
618 (can_scale_content || !other.can_scale_content);
619 }
620
IsSupersetOf(const ReceiverSession::RemotingPreferences & other) const621 bool ReceiverSession::RemotingPreferences::IsSupersetOf(
622 const ReceiverSession::RemotingPreferences& other) const {
623 return (supports_chrome_audio_codecs ||
624 !other.supports_chrome_audio_codecs) &&
625 (supports_4k || !other.supports_4k);
626 }
627
IsSupersetOf(const ReceiverSession::Preferences & other) const628 bool ReceiverSession::Preferences::IsSupersetOf(
629 const ReceiverSession::Preferences& other) const {
630 // Check simple cases first.
631 if ((!!display_description != !!other.display_description) ||
632 (display_description &&
633 !display_description->IsSupersetOf(*other.display_description))) {
634 return false;
635 } else if (other.remoting &&
636 (!remoting || !remoting->IsSupersetOf(*other.remoting))) {
637 return false;
638 }
639
640 // Then check set codecs.
641 if (IsMissingCodecs(video_codecs, other.video_codecs) ||
642 IsMissingCodecs(audio_codecs, other.audio_codecs)) {
643 return false;
644 }
645
646 // Then check limits. Do this last because it's the most resource intensive to
647 // check.
648 return HasLessRestrictiveLimits(video_limits, other.video_limits) &&
649 HasLessRestrictiveLimits(audio_limits, other.audio_limits);
650 }
651
652 } // namespace cast
653 } // namespace openscreen
654