diff options
Diffstat (limited to 'cast/streaming/receiver_session.cc')
-rw-r--r-- | cast/streaming/receiver_session.cc | 175 |
1 files changed, 169 insertions, 6 deletions
diff --git a/cast/streaming/receiver_session.cc b/cast/streaming/receiver_session.cc index 02c57e60..36a47bca 100644 --- a/cast/streaming/receiver_session.cc +++ b/cast/streaming/receiver_session.cc @@ -70,18 +70,104 @@ MediaCapability ToCapability(VideoCodec codec) { } } +// Calculates whether any codecs present in |second| are not present in |first|. +template <typename T> +bool IsMissingCodecs(const std::vector<T>& first, + const std::vector<T>& second) { + if (second.size() > first.size()) { + return true; + } + + for (auto codec : second) { + if (std::find(first.begin(), first.end(), codec) == first.end()) { + return true; + } + } + + return false; +} + +// Calculates whether the limits defined by |first| are less restrictive than +// those defined by |second|. +// NOTE: These variables are intentionally passed by copy - the function will +// mutate them. +template <typename T> +bool HasLessRestrictiveLimits(std::vector<T> first, std::vector<T> second) { + // Sort both vectors to allow for element-by-element comparison between the + // two. All elements with |applies_to_all_codecs| set are sorted to the front. + std::function<bool(const T&, const T&)> sorter = [](const T& first, + const T& second) { + if (first.applies_to_all_codecs != second.applies_to_all_codecs) { + return first.applies_to_all_codecs; + } + return static_cast<int>(first.codec) < static_cast<int>(second.codec); + }; + std::sort(first.begin(), first.end(), sorter); + std::sort(second.begin(), second.end(), sorter); + auto first_it = first.begin(); + auto second_it = second.begin(); + + // |applies_to_all_codecs| is a special case, so handle that first. + T fake_applies_to_all_codecs_struct; + fake_applies_to_all_codecs_struct.applies_to_all_codecs = true; + T* first_applies_to_all_codecs_struct = + !first.empty() && first.front().applies_to_all_codecs + ? &(*first_it++) + : &fake_applies_to_all_codecs_struct; + T* second_applies_to_all_codecs_struct = + !second.empty() && second.front().applies_to_all_codecs + ? &(*second_it++) + : &fake_applies_to_all_codecs_struct; + if (!first_applies_to_all_codecs_struct->IsSupersetOf( + *second_applies_to_all_codecs_struct)) { + return false; + } + + // Now all elements of the vectors can be assumed to NOT have + // |applies_to_all_codecs| set. So iterate through all codecs set in either + // vector and check that the first has the less restrictive configuration set. + while (first_it != first.end() || second_it != second.end()) { + // Calculate the current codec to process, and whether each vector contains + // an instance of this codec. + decltype(T::codec) current_codec; + bool use_first_fake = false; + bool use_second_fake = false; + if (first_it == first.end()) { + current_codec = second_it->codec; + use_first_fake = true; + } else if (second_it == second.end()) { + current_codec = first_it->codec; + use_second_fake = true; + } else { + current_codec = std::min(first_it->codec, second_it->codec); + use_first_fake = first_it->codec != current_codec; + use_second_fake = second_it->codec != current_codec; + } + + // Compare each vector's limit associated with this codec, or compare + // against the default limits if no such codec limits are set. + T fake_codecs_struct; + fake_codecs_struct.codec = current_codec; + T* first_codec_struct = + use_first_fake ? &fake_codecs_struct : &(*first_it++); + T* second_codec_struct = + use_second_fake ? &fake_codecs_struct : &(*second_it++); + OSP_DCHECK(!first_codec_struct->applies_to_all_codecs); + OSP_DCHECK(!second_codec_struct->applies_to_all_codecs); + if (!first_codec_struct->IsSupersetOf(*second_codec_struct)) { + return false; + } + } + + return true; +} + } // namespace ReceiverSession::Client::~Client() = default; using RemotingPreferences = ReceiverSession::RemotingPreferences; -RemotingPreferences::RemotingPreferences() = default; -RemotingPreferences::RemotingPreferences(RemotingPreferences&&) noexcept = - default; -RemotingPreferences& RemotingPreferences::operator=( - RemotingPreferences&&) noexcept = default; - using Preferences = ReceiverSession::Preferences; Preferences::Preferences() = default; @@ -104,6 +190,24 @@ Preferences::Preferences(std::vector<VideoCodec> video_codecs, Preferences::Preferences(Preferences&&) noexcept = default; Preferences& Preferences::operator=(Preferences&&) noexcept = default; +Preferences::Preferences(const Preferences& other) { + *this = other; +} + +Preferences& Preferences::operator=(const Preferences& other) { + video_codecs = other.video_codecs; + audio_codecs = other.audio_codecs; + audio_limits = other.audio_limits; + video_limits = other.video_limits; + if (other.display_description) { + display_description = std::make_unique<Display>(*other.display_description); + } + if (other.remoting) { + remoting = std::make_unique<RemotingPreferences>(*other.remoting); + } + return *this; +} + ReceiverSession::ReceiverSession(Client* const client, Environment* environment, MessagePort* message_port, @@ -458,5 +562,64 @@ void ReceiverSession::SendErrorAnswerReply(int sequence_number, } } +bool ReceiverSession::VideoLimits::IsSupersetOf( + const ReceiverSession::VideoLimits& second) const { + return (applies_to_all_codecs == second.applies_to_all_codecs) && + (applies_to_all_codecs || codec == second.codec) && + (max_pixels_per_second >= second.max_pixels_per_second) && + (min_bit_rate <= second.min_bit_rate) && + (max_bit_rate >= second.max_bit_rate) && + (max_delay >= second.max_delay) && + (max_dimensions.IsSupersetOf(second.max_dimensions)); +} + +bool ReceiverSession::AudioLimits::IsSupersetOf( + const ReceiverSession::AudioLimits& second) const { + return (applies_to_all_codecs == second.applies_to_all_codecs) && + (applies_to_all_codecs || codec == second.codec) && + (max_sample_rate >= second.max_sample_rate) && + (max_channels >= second.max_channels) && + (min_bit_rate <= second.min_bit_rate) && + (max_bit_rate >= second.max_bit_rate) && + (max_delay >= second.max_delay); +} + +bool ReceiverSession::Display::IsSupersetOf( + const ReceiverSession::Display& other) const { + return dimensions.IsSupersetOf(other.dimensions) && + (can_scale_content || !other.can_scale_content); +} + +bool ReceiverSession::RemotingPreferences::IsSupersetOf( + const ReceiverSession::RemotingPreferences& other) const { + return (supports_chrome_audio_codecs || + !other.supports_chrome_audio_codecs) && + (supports_4k || !other.supports_4k); +} + +bool ReceiverSession::Preferences::IsSupersetOf( + const ReceiverSession::Preferences& other) const { + // Check simple cases first. + if ((!!display_description != !!other.display_description) || + (display_description && + !display_description->IsSupersetOf(*other.display_description))) { + return false; + } else if (other.remoting && + (!remoting || !remoting->IsSupersetOf(*other.remoting))) { + return false; + } + + // Then check set codecs. + if (IsMissingCodecs(video_codecs, other.video_codecs) || + IsMissingCodecs(audio_codecs, other.audio_codecs)) { + return false; + } + + // Then check limits. Do this last because it's the most resource intensive to + // check. + return HasLessRestrictiveLimits(video_limits, other.video_limits) && + HasLessRestrictiveLimits(audio_limits, other.audio_limits); +} + } // namespace cast } // namespace openscreen |