| /* | |
| * Copyright 2017 The WebRTC project authors. All Rights Reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #include "pc/rtp_parameters_conversion.h" | |
| #include <optional> | |
| #include <string> | |
| #include <vector> | |
| #include "api/media_types.h" | |
| #include "api/rtp_parameters.h" | |
| #include "media/base/codec.h" | |
| #include "media/base/media_constants.h" | |
| #include "pc/session_description.h" | |
| #include "rtc_base/logging.h" | |
| namespace webrtc { | |
| std::optional<RtcpFeedback> ToRtcpFeedback( | |
| const FeedbackParam& cricket_feedback) { | |
| if (cricket_feedback.id() == kRtcpFbParamCcm) { | |
| if (cricket_feedback.param() == kRtcpFbCcmParamFir) { | |
| return RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR); | |
| } else { | |
| RTC_LOG(LS_WARNING) << "Unsupported parameter for CCM RTCP feedback: " | |
| << cricket_feedback.param(); | |
| return std::nullopt; | |
| } | |
| } else if (cricket_feedback.id() == kRtcpFbParamLntf) { | |
| if (cricket_feedback.param().empty()) { | |
| return RtcpFeedback(RtcpFeedbackType::LNTF); | |
| } else { | |
| RTC_LOG(LS_WARNING) << "Unsupported parameter for LNTF RTCP feedback: " | |
| << cricket_feedback.param(); | |
| return std::nullopt; | |
| } | |
| } else if (cricket_feedback.id() == kRtcpFbParamNack) { | |
| if (cricket_feedback.param().empty()) { | |
| return RtcpFeedback(RtcpFeedbackType::NACK, | |
| RtcpFeedbackMessageType::GENERIC_NACK); | |
| } else if (cricket_feedback.param() == kRtcpFbNackParamPli) { | |
| return RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI); | |
| } else { | |
| RTC_LOG(LS_WARNING) << "Unsupported parameter for NACK RTCP feedback: " | |
| << cricket_feedback.param(); | |
| return std::nullopt; | |
| } | |
| } else if (cricket_feedback.id() == kRtcpFbParamRemb) { | |
| if (!cricket_feedback.param().empty()) { | |
| RTC_LOG(LS_WARNING) << "Unsupported parameter for REMB RTCP feedback: " | |
| << cricket_feedback.param(); | |
| return std::nullopt; | |
| } else { | |
| return RtcpFeedback(RtcpFeedbackType::REMB); | |
| } | |
| } else if (cricket_feedback.id() == kRtcpFbParamTransportCc) { | |
| if (!cricket_feedback.param().empty()) { | |
| RTC_LOG(LS_WARNING) | |
| << "Unsupported parameter for transport-cc RTCP feedback: " | |
| << cricket_feedback.param(); | |
| return std::nullopt; | |
| } else { | |
| return RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC); | |
| } | |
| } | |
| RTC_LOG(LS_WARNING) << "Unsupported RTCP feedback type: " | |
| << cricket_feedback.id(); | |
| return std::nullopt; | |
| } | |
| RtpCodecCapability ToRtpCodecCapability(const Codec& cricket_codec) { | |
| RtpCodecCapability codec; | |
| codec.name = cricket_codec.name; | |
| codec.kind = cricket_codec.type == Codec::Type::kAudio ? MediaType::AUDIO | |
| : MediaType::VIDEO; | |
| codec.clock_rate.emplace(cricket_codec.clockrate); | |
| codec.preferred_payload_type.emplace(cricket_codec.id); | |
| for (const FeedbackParam& cricket_feedback : | |
| cricket_codec.feedback_params.params()) { | |
| std::optional<RtcpFeedback> feedback = ToRtcpFeedback(cricket_feedback); | |
| if (feedback) { | |
| codec.rtcp_feedback.push_back(feedback.value()); | |
| } | |
| } | |
| switch (cricket_codec.type) { | |
| case Codec::Type::kAudio: | |
| codec.num_channels = static_cast<int>(cricket_codec.channels); | |
| break; | |
| case Codec::Type::kVideo: | |
| codec.scalability_modes = cricket_codec.scalability_modes; | |
| break; | |
| } | |
| codec.parameters.insert(cricket_codec.params.begin(), | |
| cricket_codec.params.end()); | |
| return codec; | |
| } | |
| RtpCapabilities ToRtpCapabilities( | |
| const std::vector<Codec>& cricket_codecs, | |
| const RtpHeaderExtensions& cricket_extensions) { | |
| RtpCapabilities capabilities; | |
| bool have_red = false; | |
| bool have_ulpfec = false; | |
| bool have_flexfec = false; | |
| bool have_rtx = false; | |
| for (const Codec& cricket_codec : cricket_codecs) { | |
| if (cricket_codec.name == kRedCodecName) { | |
| if (have_red) { | |
| // There should only be one RED codec entry in caps. | |
| continue; | |
| } | |
| have_red = true; | |
| } else if (cricket_codec.name == kUlpfecCodecName) { | |
| have_ulpfec = true; | |
| } else if (cricket_codec.name == kFlexfecCodecName) { | |
| have_flexfec = true; | |
| } else if (cricket_codec.name == kRtxCodecName) { | |
| if (have_rtx) { | |
| // There should only be one RTX codec entry in caps. | |
| continue; | |
| } | |
| have_rtx = true; | |
| } | |
| auto codec_capability = ToRtpCodecCapability(cricket_codec); | |
| if (cricket_codec.name == kRtxCodecName || | |
| cricket_codec.name == kRedCodecName) { | |
| // For RTX this removes the APT which points to a payload type. | |
| // For RED this removes the redundancy spec which points to a payload | |
| // type. | |
| codec_capability.parameters.clear(); | |
| } | |
| capabilities.codecs.push_back(codec_capability); | |
| } | |
| for (const RtpExtension& cricket_extension : cricket_extensions) { | |
| capabilities.header_extensions.emplace_back(cricket_extension.uri, | |
| cricket_extension.id); | |
| } | |
| if (have_red) { | |
| capabilities.fec.push_back(FecMechanism::RED); | |
| } | |
| if (have_red && have_ulpfec) { | |
| capabilities.fec.push_back(FecMechanism::RED_AND_ULPFEC); | |
| } | |
| if (have_flexfec) { | |
| capabilities.fec.push_back(FecMechanism::FLEXFEC); | |
| } | |
| return capabilities; | |
| } | |
| } // namespace webrtc |