Implement RTCRtpTransceiver.setCodecPreferences

SetCodecPreferences allows clients to filter and reorder codecs in their
SDP offer and answer.

Bug: webrtc:9777
Change-Id: I716bed9b06496629b45210883b286f599c875239
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/129727
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Seth Hampson <shampson@webrtc.org>
Commit-Queue: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27817}
This commit is contained in:
Florent Castelli 2019-04-23 19:25:51 +02:00 committed by Commit Bot
parent 2ad2fabdcf
commit 2d9d82ecef
12 changed files with 892 additions and 121 deletions

View file

@ -25,9 +25,15 @@ RtpTransceiverInterface::fired_direction() const {
return absl::nullopt;
}
void RtpTransceiverInterface::SetCodecPreferences(
RTCError RtpTransceiverInterface::SetCodecPreferences(
rtc::ArrayView<RtpCodecCapability>) {
RTC_NOTREACHED() << "Not implemented";
return {};
}
std::vector<RtpCodecCapability> RtpTransceiverInterface::codec_preferences()
const {
return {};
}
} // namespace webrtc

View file

@ -129,8 +129,9 @@ class RtpTransceiverInterface : public rtc::RefCountInterface {
// The SetCodecPreferences method overrides the default codec preferences used
// by WebRTC for this transceiver.
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-setcodecpreferences
// TODO(steveanton): Not implemented.
virtual void SetCodecPreferences(rtc::ArrayView<RtpCodecCapability> codecs);
virtual RTCError SetCodecPreferences(
rtc::ArrayView<RtpCodecCapability> codecs);
virtual std::vector<RtpCodecCapability> codec_preferences() const;
protected:
~RtpTransceiverInterface() override = default;

View file

@ -99,6 +99,18 @@ bool Codec::Matches(const Codec& codec) const {
: (absl::EqualsIgnoreCase(name, codec.name));
}
bool Codec::MatchesCapability(
const webrtc::RtpCodecCapability& codec_capability) const {
webrtc::RtpCodecParameters codec_parameters = ToCodecParameters();
return codec_parameters.name == codec_capability.name &&
codec_parameters.kind == codec_capability.kind &&
(codec_parameters.name == cricket::kRtxCodecName ||
(codec_parameters.num_channels == codec_capability.num_channels &&
codec_parameters.clock_rate == codec_capability.clock_rate &&
codec_parameters.parameters == codec_capability.parameters));
}
bool Codec::GetParam(const std::string& name, std::string* out) const {
CodecParameterMap::const_iterator iter = params.find(name);
if (iter == params.end())

View file

@ -73,6 +73,7 @@ struct RTC_EXPORT Codec {
// Indicates if this codec is compatible with the specified codec.
bool Matches(const Codec& codec) const;
bool MatchesCapability(const webrtc::RtpCodecCapability& capability) const;
// Find the parameter for |name| and write the value to |out|.
bool GetParam(const std::string& name, std::string* out) const;

View file

@ -495,7 +495,7 @@ bool FakeDataMediaChannel::SetMaxSendBandwidth(int bps) {
FakeVoiceEngine::FakeVoiceEngine() : fail_create_channel_(false) {
// Add a fake audio codec. Note that the name must not be "" as there are
// sanity checks against that.
codecs_.push_back(AudioCodec(101, "fake_audio_codec", 0, 0, 1));
SetCodecs({AudioCodec(101, "fake_audio_codec", 0, 0, 1)});
}
RtpCapabilities FakeVoiceEngine::GetCapabilities() const {
return RtpCapabilities();
@ -524,13 +524,20 @@ void FakeVoiceEngine::UnregisterChannel(VoiceMediaChannel* channel) {
channels_.erase(absl::c_find(channels_, channel));
}
const std::vector<AudioCodec>& FakeVoiceEngine::send_codecs() const {
return codecs_;
return send_codecs_;
}
const std::vector<AudioCodec>& FakeVoiceEngine::recv_codecs() const {
return codecs_;
return recv_codecs_;
}
void FakeVoiceEngine::SetCodecs(const std::vector<AudioCodec>& codecs) {
codecs_ = codecs;
send_codecs_ = codecs;
recv_codecs_ = codecs;
}
void FakeVoiceEngine::SetRecvCodecs(const std::vector<AudioCodec>& codecs) {
recv_codecs_ = codecs;
}
void FakeVoiceEngine::SetSendCodecs(const std::vector<AudioCodec>& codecs) {
send_codecs_ = codecs;
}
int FakeVoiceEngine::GetInputLevel() {
return 0;
@ -601,6 +608,14 @@ FakeMediaEngine::~FakeMediaEngine() {}
void FakeMediaEngine::SetAudioCodecs(const std::vector<AudioCodec>& codecs) {
voice_->SetCodecs(codecs);
}
void FakeMediaEngine::SetAudioRecvCodecs(
const std::vector<AudioCodec>& codecs) {
voice_->SetRecvCodecs(codecs);
}
void FakeMediaEngine::SetAudioSendCodecs(
const std::vector<AudioCodec>& codecs) {
voice_->SetSendCodecs(codecs);
}
void FakeMediaEngine::SetVideoCodecs(const std::vector<VideoCodec>& codecs) {
video_->SetCodecs(codecs);
}

View file

@ -523,6 +523,8 @@ class FakeVoiceEngine : public VoiceEngineInterface {
const std::vector<AudioCodec>& send_codecs() const override;
const std::vector<AudioCodec>& recv_codecs() const override;
void SetCodecs(const std::vector<AudioCodec>& codecs);
void SetRecvCodecs(const std::vector<AudioCodec>& codecs);
void SetSendCodecs(const std::vector<AudioCodec>& codecs);
int GetInputLevel();
bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) override;
void StopAecDump() override;
@ -531,7 +533,8 @@ class FakeVoiceEngine : public VoiceEngineInterface {
private:
std::vector<FakeVoiceMediaChannel*> channels_;
std::vector<AudioCodec> codecs_;
std::vector<AudioCodec> recv_codecs_;
std::vector<AudioCodec> send_codecs_;
bool fail_create_channel_;
friend class FakeMediaEngine;
@ -572,6 +575,8 @@ class FakeMediaEngine : public CompositeMediaEngine {
~FakeMediaEngine() override;
void SetAudioCodecs(const std::vector<AudioCodec>& codecs);
void SetAudioRecvCodecs(const std::vector<AudioCodec>& codecs);
void SetAudioSendCodecs(const std::vector<AudioCodec>& codecs);
void SetVideoCodecs(const std::vector<VideoCodec>& codecs);
FakeVoiceMediaChannel* GetVoiceChannel(size_t index);

View file

@ -799,7 +799,8 @@ static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
template <class C>
static void NegotiateCodecs(const std::vector<C>& local_codecs,
const std::vector<C>& offered_codecs,
std::vector<C>* negotiated_codecs) {
std::vector<C>* negotiated_codecs,
bool keep_offer_order) {
for (const C& ours : local_codecs) {
C theirs;
// Note that we intentionally only find one matching codec for each of our
@ -823,19 +824,22 @@ static void NegotiateCodecs(const std::vector<C>& local_codecs,
negotiated_codecs->push_back(std::move(negotiated));
}
}
// RFC3264: Although the answerer MAY list the formats in their desired
// order of preference, it is RECOMMENDED that unless there is a
// specific reason, the answerer list formats in the same relative order
// they were present in the offer.
std::unordered_map<int, int> payload_type_preferences;
int preference = static_cast<int>(offered_codecs.size() + 1);
for (const C& codec : offered_codecs) {
payload_type_preferences[codec.id] = preference--;
if (keep_offer_order) {
// RFC3264: Although the answerer MAY list the formats in their desired
// order of preference, it is RECOMMENDED that unless there is a
// specific reason, the answerer list formats in the same relative order
// they were present in the offer.
// This can be skipped when the transceiver has any codec preferences.
std::unordered_map<int, int> payload_type_preferences;
int preference = static_cast<int>(offered_codecs.size() + 1);
for (const C& codec : offered_codecs) {
payload_type_preferences[codec.id] = preference--;
}
absl::c_sort(*negotiated_codecs, [&payload_type_preferences](const C& a,
const C& b) {
return payload_type_preferences[a.id] > payload_type_preferences[b.id];
});
}
absl::c_sort(
*negotiated_codecs, [&payload_type_preferences](const C& a, const C& b) {
return payload_type_preferences[a.id] > payload_type_preferences[b.id];
});
}
// Finds a codec in |codecs2| that matches |codec_to_match|, which is
@ -954,6 +958,51 @@ static void MergeCodecs(const std::vector<C>& reference_codecs,
}
}
template <typename Codecs>
static Codecs MatchCodecPreference(
const std::vector<webrtc::RtpCodecCapability>& codec_preferences,
const Codecs& codecs) {
Codecs filtered_codecs;
std::set<std::string> kept_codecs_ids;
bool want_rtx = false;
for (const auto& codec_preference : codec_preferences) {
auto found_codec = absl::c_find_if(
codecs, [&codec_preference](const typename Codecs::value_type& codec) {
webrtc::RtpCodecParameters codec_parameters =
codec.ToCodecParameters();
return codec_parameters.name == codec_preference.name &&
codec_parameters.kind == codec_preference.kind &&
codec_parameters.num_channels ==
codec_preference.num_channels &&
codec_parameters.clock_rate == codec_preference.clock_rate &&
codec_parameters.parameters == codec_preference.parameters;
});
if (found_codec != codecs.end()) {
filtered_codecs.push_back(*found_codec);
kept_codecs_ids.insert(std::to_string(found_codec->id));
} else if (IsRtxCodec(codec_preference)) {
want_rtx = true;
}
}
if (want_rtx) {
for (const auto& codec : codecs) {
if (IsRtxCodec(codec)) {
const auto apt =
codec.params.find(cricket::kCodecParamAssociatedPayloadType);
if (apt != codec.params.end() &&
kept_codecs_ids.count(apt->second) > 0) {
filtered_codecs.push_back(codec);
}
}
}
}
return filtered_codecs;
}
static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
const webrtc::RtpExtension& ext_to_match,
webrtc::RtpExtension* found_extension) {
@ -1159,7 +1208,8 @@ static bool CreateMediaContentAnswer(
bool bundle_enabled,
MediaContentDescriptionImpl<C>* answer) {
std::vector<C> negotiated_codecs;
NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs);
NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
media_description_options.codec_preferences.empty());
answer->AddCodecs(negotiated_codecs);
answer->set_protocol(offer->protocol());
@ -2009,30 +2059,38 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
GetAudioCodecsForOffer(media_description_options.direction);
AudioCodecs filtered_codecs;
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
const AudioContentDescription* acd =
current_content->media_description()->as_audio();
for (const AudioCodec& codec : acd->codecs()) {
if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
nullptr)) {
filtered_codecs.push_back(codec);
if (!media_description_options.codec_preferences.empty()) {
// Add the codecs from the current transceiver's codec preferences.
// They override any existing codecs from previous negotiations.
filtered_codecs = MatchCodecPreference(
media_description_options.codec_preferences, supported_audio_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
const AudioContentDescription* acd =
current_content->media_description()->as_audio();
for (const AudioCodec& codec : acd->codecs()) {
if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
nullptr)) {
filtered_codecs.push_back(codec);
}
}
}
}
// Add other supported audio codecs.
AudioCodec found_codec;
for (const AudioCodec& codec : supported_audio_codecs) {
if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
codec, &found_codec) &&
!FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
codec, nullptr)) {
// Use the |found_codec| from |audio_codecs| because it has the correctly
// mapped payload type.
filtered_codecs.push_back(found_codec);
// Add other supported audio codecs.
AudioCodec found_codec;
for (const AudioCodec& codec : supported_audio_codecs) {
if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
codec, &found_codec) &&
!FindMatchingCodec<AudioCodec>(supported_audio_codecs,
filtered_codecs, codec, nullptr)) {
// Use the |found_codec| from |audio_codecs| because it has the
// correctly mapped payload type.
filtered_codecs.push_back(found_codec);
}
}
}
@ -2088,30 +2146,38 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
&crypto_suites);
VideoCodecs filtered_codecs;
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
const VideoContentDescription* vcd =
current_content->media_description()->as_video();
for (const VideoCodec& codec : vcd->codecs()) {
if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
nullptr)) {
filtered_codecs.push_back(codec);
if (!media_description_options.codec_preferences.empty()) {
// Add the codecs from the current transceiver's codec preferences.
// They override any existing codecs from previous negotiations.
filtered_codecs = MatchCodecPreference(
media_description_options.codec_preferences, video_codecs_);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
const VideoContentDescription* vcd =
current_content->media_description()->as_video();
for (const VideoCodec& codec : vcd->codecs()) {
if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
nullptr)) {
filtered_codecs.push_back(codec);
}
}
}
}
// Add other supported video codecs.
VideoCodec found_codec;
for (const VideoCodec& codec : video_codecs_) {
if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
&found_codec) &&
!FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
nullptr)) {
// Use the |found_codec| from |video_codecs| because it has the correctly
// mapped payload type.
filtered_codecs.push_back(found_codec);
// Add other supported video codecs.
VideoCodec found_codec;
for (const VideoCodec& codec : video_codecs_) {
if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
&found_codec) &&
!FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
nullptr)) {
// Use the |found_codec| from |video_codecs| because it has the
// correctly mapped payload type.
filtered_codecs.push_back(found_codec);
}
}
}
@ -2254,29 +2320,35 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
AudioCodecs filtered_codecs;
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
const AudioContentDescription* acd =
current_content->media_description()->as_audio();
for (const AudioCodec& codec : acd->codecs()) {
if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
nullptr)) {
filtered_codecs.push_back(codec);
if (!media_description_options.codec_preferences.empty()) {
filtered_codecs = MatchCodecPreference(
media_description_options.codec_preferences, supported_audio_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
const AudioContentDescription* acd =
current_content->media_description()->as_audio();
for (const AudioCodec& codec : acd->codecs()) {
if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
nullptr)) {
filtered_codecs.push_back(codec);
}
}
}
}
// Add other supported audio codecs.
for (const AudioCodec& codec : supported_audio_codecs) {
if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
codec, nullptr) &&
!FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
codec, nullptr)) {
// We should use the local codec with local parameters and the codec id
// would be correctly mapped in |NegotiateCodecs|.
filtered_codecs.push_back(codec);
// Add other supported audio codecs.
for (const AudioCodec& codec : supported_audio_codecs) {
if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
codec, nullptr) &&
!FindMatchingCodec<AudioCodec>(supported_audio_codecs,
filtered_codecs, codec, nullptr)) {
// We should use the local codec with local parameters and the codec id
// would be correctly mapped in |NegotiateCodecs|.
filtered_codecs.push_back(codec);
}
}
}
@ -2342,29 +2414,35 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
}
VideoCodecs filtered_codecs;
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
const VideoContentDescription* vcd =
current_content->media_description()->as_video();
for (const VideoCodec& codec : vcd->codecs()) {
if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
nullptr)) {
filtered_codecs.push_back(codec);
if (!media_description_options.codec_preferences.empty()) {
filtered_codecs = MatchCodecPreference(
media_description_options.codec_preferences, video_codecs_);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
const VideoContentDescription* vcd =
current_content->media_description()->as_video();
for (const VideoCodec& codec : vcd->codecs()) {
if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
nullptr)) {
filtered_codecs.push_back(codec);
}
}
}
}
// Add other supported video codecs.
for (const VideoCodec& codec : video_codecs_) {
if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
nullptr) &&
!FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
nullptr)) {
// We should use the local codec with local parameters and the codec id
// would be correctly mapped in |NegotiateCodecs|.
filtered_codecs.push_back(codec);
// Add other supported video codecs.
for (const VideoCodec& codec : video_codecs_) {
if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
nullptr) &&
!FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
nullptr)) {
// We should use the local codec with local parameters and the codec id
// would be correctly mapped in |NegotiateCodecs|.
filtered_codecs.push_back(codec);
}
}
}
@ -2499,7 +2577,7 @@ void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
// expensive than decoding, and prioritizing a codec in the send list probably
// means it's a codec we can handle efficiently.
NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
&audio_sendrecv_codecs_);
&audio_sendrecv_codecs_, true);
}
bool IsMediaContent(const ContentInfo* content) {

View file

@ -76,6 +76,7 @@ struct MediaDescriptionOptions {
// Note: There's no equivalent "RtpReceiverOptions" because only send
// stream information goes in the local descriptions.
std::vector<SenderOptions> sender_options;
std::vector<webrtc::RtpCodecCapability> codec_preferences;
private:
// Doesn't DCHECK on |type|.

View file

@ -1703,7 +1703,8 @@ PeerConnection::CreateAndAddTransceiver(
// could be invalid, but should not cause a crash).
RTC_DCHECK(!FindSenderById(sender->id()));
auto transceiver = RtpTransceiverProxyWithInternal<RtpTransceiver>::Create(
signaling_thread(), new RtpTransceiver(sender, receiver));
signaling_thread(),
new RtpTransceiver(sender, receiver, channel_manager()));
transceivers_.push_back(transceiver);
transceiver->internal()->SignalNegotiationNeeded.connect(
this, &PeerConnection::OnNegotiationNeeded);
@ -4397,6 +4398,8 @@ GetMediaDescriptionOptionsForTransceiver(
cricket::MediaDescriptionOptions media_description_options(
transceiver->media_type(), mid, transceiver->direction(),
transceiver->stopped());
media_description_options.codec_preferences =
transceiver->codec_preferences();
// This behavior is specified in JSEP. The gist is that:
// 1. The MSID is included if the RtpTransceiver's direction is sendonly or
// sendrecv.

View file

@ -73,11 +73,21 @@ class PeerConnectionMediaBaseTest : public ::testing::Test {
return CreatePeerConnection(RTCConfiguration());
}
WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
return CreatePeerConnection(config, absl::make_unique<FakeMediaEngine>());
}
WrapperPtr CreatePeerConnection(
std::unique_ptr<FakeMediaEngine> media_engine) {
return CreatePeerConnection(RTCConfiguration(), std::move(media_engine));
}
// Creates PeerConnectionFactory and PeerConnection for given configuration.
// Note that PeerConnectionFactory is created with MediaTransportFactory,
// because some tests pass config.use_media_transport = true.
WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
auto media_engine = absl::make_unique<FakeMediaEngine>();
WrapperPtr CreatePeerConnection(
const RTCConfiguration& config,
std::unique_ptr<FakeMediaEngine> media_engine) {
auto* media_engine_ptr = media_engine.get();
PeerConnectionFactoryDependencies factory_dependencies;
@ -125,6 +135,18 @@ class PeerConnectionMediaBaseTest : public ::testing::Test {
return wrapper;
}
// Accepts the same arguments as CreatePeerConnection and adds default video
// track (but no audio).
template <typename... Args>
WrapperPtr CreatePeerConnectionWithVideo(Args&&... args) {
auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
if (!wrapper) {
return nullptr;
}
wrapper->AddVideoTrack("v");
return wrapper;
}
// Accepts the same arguments as CreatePeerConnection and adds default audio
// and video tracks.
template <typename... Args>
@ -1239,6 +1261,498 @@ TEST_P(PeerConnectionMediaTest, MediaTransportNotPropagatedToVoiceEngine) {
ASSERT_EQ(nullptr, callee_video->media_transport());
}
template <typename C>
bool CompareCodecs(const std::vector<webrtc::RtpCodecCapability>& capabilities,
const std::vector<C>& codecs) {
bool capability_has_rtx =
absl::c_any_of(capabilities, [](const webrtc::RtpCodecCapability& codec) {
return codec.name == cricket::kRtxCodecName;
});
bool codecs_has_rtx = absl::c_any_of(codecs, [](const C& codec) {
return codec.name == cricket::kRtxCodecName;
});
std::vector<C> codecs_no_rtx;
absl::c_copy_if(
codecs, std::back_inserter(codecs_no_rtx),
[](const C& codec) { return codec.name != cricket::kRtxCodecName; });
std::vector<webrtc::RtpCodecCapability> capabilities_no_rtx;
absl::c_copy_if(capabilities, std::back_inserter(capabilities_no_rtx),
[](const webrtc::RtpCodecCapability& codec) {
return codec.name != cricket::kRtxCodecName;
});
return capability_has_rtx == codecs_has_rtx &&
absl::c_equal(
capabilities_no_rtx, codecs_no_rtx,
[](const webrtc::RtpCodecCapability& capability, const C& codec) {
return codec.MatchesCapability(capability);
});
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesAudioMissingRecvCodec) {
auto fake_engine = absl::make_unique<FakeMediaEngine>();
auto send_codecs = fake_engine->voice().send_codecs();
send_codecs.push_back(cricket::AudioCodec(send_codecs.back().id + 1,
"send_only_codec", 0, 0, 1));
fake_engine->SetAudioSendCodecs(send_codecs);
auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
auto transceiver = caller->pc()->GetTransceivers().front();
auto capabilities = caller->pc_factory()->GetRtpSenderCapabilities(
cricket::MediaType::MEDIA_TYPE_AUDIO);
std::vector<webrtc::RtpCodecCapability> codecs;
absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
[](const webrtc::RtpCodecCapability& codec) {
return codec.name.find("_only_") != std::string::npos;
});
auto result = transceiver->SetCodecPreferences(codecs);
EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesAudioMissingSendCodec) {
auto fake_engine = absl::make_unique<FakeMediaEngine>();
auto recv_codecs = fake_engine->voice().recv_codecs();
recv_codecs.push_back(cricket::AudioCodec(recv_codecs.back().id + 1,
"recv_only_codec", 0, 0, 1));
fake_engine->SetAudioRecvCodecs(recv_codecs);
auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
auto transceiver = caller->pc()->GetTransceivers().front();
auto capabilities = caller->pc_factory()->GetRtpReceiverCapabilities(
cricket::MediaType::MEDIA_TYPE_AUDIO);
std::vector<webrtc::RtpCodecCapability> codecs;
absl::c_copy_if(capabilities.codecs, std::back_inserter(codecs),
[](const webrtc::RtpCodecCapability& codec) {
return codec.name.find("_only_") != std::string::npos;
});
auto result = transceiver->SetCodecPreferences(codecs);
EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesAudioRejectsVideoCodec) {
auto caller = CreatePeerConnectionWithAudio();
auto transceiver = caller->pc()->GetTransceivers().front();
auto video_codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
.codecs;
auto codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
.codecs;
codecs.insert(codecs.end(), video_codecs.begin(), video_codecs.end());
auto result = transceiver->SetCodecPreferences(codecs);
EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesAudioRejectsOnlyRtxRedFec) {
auto fake_engine = absl::make_unique<FakeMediaEngine>();
auto audio_codecs = fake_engine->voice().send_codecs();
audio_codecs.push_back(cricket::AudioCodec(audio_codecs.back().id + 1,
cricket::kRtxCodecName, 0, 0, 1));
audio_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
std::to_string(audio_codecs.back().id - 1);
audio_codecs.push_back(cricket::AudioCodec(audio_codecs.back().id + 1,
cricket::kRedCodecName, 0, 0, 1));
audio_codecs.push_back(cricket::AudioCodec(
audio_codecs.back().id + 1, cricket::kUlpfecCodecName, 0, 0, 1));
fake_engine->SetAudioCodecs(audio_codecs);
auto caller = CreatePeerConnectionWithAudio(std::move(fake_engine));
auto transceiver = caller->pc()->GetTransceivers().front();
auto codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
.codecs;
auto codecs_only_rtx_red_fec = codecs;
auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
codecs_only_rtx_red_fec.end(),
[](const webrtc::RtpCodecCapability& codec) {
return !(codec.name == cricket::kRtxCodecName ||
codec.name == cricket::kRedCodecName ||
codec.name == cricket::kUlpfecCodecName);
});
codecs_only_rtx_red_fec.erase(it, codecs_only_rtx_red_fec.end());
auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec);
EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
}
TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllAudioCodecs) {
auto caller = CreatePeerConnectionWithAudio();
auto sender_audio_codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
.codecs;
auto audio_transceiver = caller->pc()->GetTransceivers().front();
// Normal case, set all capabilities as preferences
EXPECT_TRUE(audio_transceiver->SetCodecPreferences(sender_audio_codecs).ok());
auto offer = caller->CreateOffer();
auto codecs = offer->description()
->contents()[0]
.media_description()
->as_audio()
->codecs();
EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesResetAudioCodecs) {
auto caller = CreatePeerConnectionWithAudio();
auto sender_audio_codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_AUDIO)
.codecs;
std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
auto audio_transceiver = caller->pc()->GetTransceivers().front();
// Normal case, reset codec preferences
EXPECT_TRUE(audio_transceiver->SetCodecPreferences(empty_codecs).ok());
auto offer = caller->CreateOffer();
auto codecs = offer->description()
->contents()[0]
.media_description()
->as_audio()
->codecs();
EXPECT_TRUE(CompareCodecs(sender_audio_codecs, codecs));
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesVideoRejectsAudioCodec) {
auto caller = CreatePeerConnectionWithVideo();
auto transceiver = caller->pc()->GetTransceivers().front();
auto audio_codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_AUDIO)
.codecs;
auto codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
.codecs;
codecs.insert(codecs.end(), audio_codecs.begin(), audio_codecs.end());
auto result = transceiver->SetCodecPreferences(codecs);
EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesVideoRejectsOnlyRtxRedFec) {
auto fake_engine = absl::make_unique<FakeMediaEngine>();
auto video_codecs = fake_engine->video().codecs();
video_codecs.push_back(
cricket::VideoCodec(video_codecs.back().id + 1, cricket::kRtxCodecName));
video_codecs.push_back(
cricket::VideoCodec(video_codecs.back().id + 1, cricket::kRedCodecName));
video_codecs.push_back(cricket::VideoCodec(video_codecs.back().id + 1,
cricket::kUlpfecCodecName));
fake_engine->SetVideoCodecs(video_codecs);
auto caller = CreatePeerConnectionWithVideo(std::move(fake_engine));
auto transceiver = caller->pc()->GetTransceivers().front();
auto codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MediaType::MEDIA_TYPE_VIDEO)
.codecs;
auto codecs_only_rtx_red_fec = codecs;
auto it = std::remove_if(codecs_only_rtx_red_fec.begin(),
codecs_only_rtx_red_fec.end(),
[](const webrtc::RtpCodecCapability& codec) {
return !(codec.name == cricket::kRtxCodecName ||
codec.name == cricket::kRedCodecName ||
codec.name == cricket::kUlpfecCodecName);
});
codecs_only_rtx_red_fec.erase(it, codecs_only_rtx_red_fec.end());
auto result = transceiver->SetCodecPreferences(codecs_only_rtx_red_fec);
EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION, result.type());
}
TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesAllVideoCodecs) {
auto caller = CreatePeerConnectionWithVideo();
auto sender_video_codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
auto video_transceiver = caller->pc()->GetTransceivers().front();
// Normal case, setting preferences to normal capabilities
EXPECT_TRUE(video_transceiver->SetCodecPreferences(sender_video_codecs).ok());
auto offer = caller->CreateOffer();
auto codecs = offer->description()
->contents()[0]
.media_description()
->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesResetVideoCodecs) {
auto caller = CreatePeerConnectionWithVideo();
auto sender_video_codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
std::vector<webrtc::RtpCodecCapability> empty_codecs = {};
auto video_transceiver = caller->pc()->GetTransceivers().front();
// Normal case, resetting preferences with empty list of codecs
EXPECT_TRUE(video_transceiver->SetCodecPreferences(empty_codecs).ok());
auto offer = caller->CreateOffer();
auto codecs = offer->description()
->contents()[0]
.media_description()
->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(sender_video_codecs, codecs));
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesVideoCodecDuplicatesRemoved) {
auto caller = CreatePeerConnectionWithVideo();
auto sender_video_codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
auto video_transceiver = caller->pc()->GetTransceivers().front();
// Check duplicates are removed
auto single_codec = sender_video_codecs;
single_codec.resize(1);
auto duplicate_codec = single_codec;
duplicate_codec.push_back(duplicate_codec.front());
duplicate_codec.push_back(duplicate_codec.front());
duplicate_codec.push_back(duplicate_codec.front());
EXPECT_TRUE(video_transceiver->SetCodecPreferences(duplicate_codec).ok());
auto offer = caller->CreateOffer();
auto codecs = offer->description()
->contents()[0]
.media_description()
->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(single_codec, codecs));
}
TEST_F(PeerConnectionMediaTestUnifiedPlan, SetCodecPreferencesVideoWithRtx) {
auto caller_fake_engine = absl::make_unique<FakeMediaEngine>();
auto caller_video_codecs = caller_fake_engine->video().codecs();
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
std::to_string(caller_video_codecs.back().id - 1);
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
std::to_string(caller_video_codecs.back().id - 1);
caller_fake_engine->SetVideoCodecs(caller_video_codecs);
auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
auto sender_video_codecs =
caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
auto video_transceiver = caller->pc()->GetTransceivers().front();
// Check that RTX codec is properly added
auto video_codecs_vpx_rtx = sender_video_codecs;
auto it =
std::remove_if(video_codecs_vpx_rtx.begin(), video_codecs_vpx_rtx.end(),
[](const webrtc::RtpCodecCapability& codec) {
return codec.name != cricket::kRtxCodecName &&
codec.name != cricket::kVp8CodecName &&
codec.name != cricket::kVp9CodecName;
});
video_codecs_vpx_rtx.erase(it, video_codecs_vpx_rtx.end());
absl::c_reverse(video_codecs_vpx_rtx);
EXPECT_EQ(video_codecs_vpx_rtx.size(), 3u); // VP8, VP9, RTX
EXPECT_TRUE(
video_transceiver->SetCodecPreferences(video_codecs_vpx_rtx).ok());
auto offer = caller->CreateOffer();
auto codecs = offer->description()
->contents()[0]
.media_description()
->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(video_codecs_vpx_rtx, codecs));
EXPECT_EQ(codecs.size(), 4u);
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesVideoCodecsNegotiation) {
auto caller_fake_engine = absl::make_unique<FakeMediaEngine>();
auto caller_video_codecs = caller_fake_engine->video().codecs();
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
std::to_string(caller_video_codecs.back().id - 1);
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
std::to_string(caller_video_codecs.back().id - 1);
caller_fake_engine->SetVideoCodecs(caller_video_codecs);
auto callee_fake_engine = absl::make_unique<FakeMediaEngine>();
callee_fake_engine->SetVideoCodecs(caller_video_codecs);
auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
auto callee = CreatePeerConnection(std::move(callee_fake_engine));
auto video_codecs = caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
auto send_transceiver = caller->pc()->GetTransceivers().front();
auto video_codecs_vpx = video_codecs;
auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
[](const webrtc::RtpCodecCapability& codec) {
return codec.name != cricket::kVp8CodecName &&
codec.name != cricket::kVp9CodecName;
});
video_codecs_vpx.erase(it, video_codecs_vpx.end());
EXPECT_EQ(video_codecs_vpx.size(), 2u); // VP8, VP9
EXPECT_TRUE(send_transceiver->SetCodecPreferences(video_codecs_vpx).ok());
auto offer = caller->CreateOfferAndSetAsLocal();
auto codecs = offer->description()
->contents()[0]
.media_description()
->as_video()
->codecs();
EXPECT_EQ(codecs.size(), 2u); // VP8, VP9
EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
callee->SetRemoteDescription(std::move(offer));
auto recv_transceiver = callee->pc()->GetTransceivers().front();
auto video_codecs_vp8_rtx = video_codecs;
it = std::remove_if(video_codecs_vp8_rtx.begin(), video_codecs_vp8_rtx.end(),
[](const webrtc::RtpCodecCapability& codec) {
bool r = codec.name != cricket::kVp8CodecName &&
codec.name != cricket::kRtxCodecName;
return r;
});
video_codecs_vp8_rtx.erase(it, video_codecs_vp8_rtx.end());
EXPECT_EQ(video_codecs_vp8_rtx.size(), 2u); // VP8, RTX
recv_transceiver->SetCodecPreferences(video_codecs_vp8_rtx);
auto answer = callee->CreateAnswerAndSetAsLocal();
auto recv_codecs = answer->description()
->contents()[0]
.media_description()
->as_video()
->codecs();
EXPECT_EQ(recv_codecs.size(), 1u); // VP8
}
TEST_F(PeerConnectionMediaTestUnifiedPlan,
SetCodecPreferencesVideoCodecsNegotiationReverseOrder) {
auto caller_fake_engine = absl::make_unique<FakeMediaEngine>();
auto caller_video_codecs = caller_fake_engine->video().codecs();
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kVp8CodecName));
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
std::to_string(caller_video_codecs.back().id - 1);
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kVp9CodecName));
caller_video_codecs.push_back(cricket::VideoCodec(
caller_video_codecs.back().id + 1, cricket::kRtxCodecName));
caller_video_codecs.back().params[cricket::kCodecParamAssociatedPayloadType] =
std::to_string(caller_video_codecs.back().id - 1);
caller_fake_engine->SetVideoCodecs(caller_video_codecs);
auto callee_fake_engine = absl::make_unique<FakeMediaEngine>();
callee_fake_engine->SetVideoCodecs(caller_video_codecs);
auto caller = CreatePeerConnectionWithVideo(std::move(caller_fake_engine));
auto callee = CreatePeerConnection(std::move(callee_fake_engine));
auto video_codecs = caller->pc_factory()
->GetRtpSenderCapabilities(cricket::MEDIA_TYPE_VIDEO)
.codecs;
auto send_transceiver = caller->pc()->GetTransceivers().front();
auto video_codecs_vpx = video_codecs;
auto it = std::remove_if(video_codecs_vpx.begin(), video_codecs_vpx.end(),
[](const webrtc::RtpCodecCapability& codec) {
return codec.name != cricket::kVp8CodecName &&
codec.name != cricket::kVp9CodecName;
});
video_codecs_vpx.erase(it, video_codecs_vpx.end());
EXPECT_EQ(video_codecs_vpx.size(), 2u); // VP8, VP9
EXPECT_TRUE(send_transceiver->SetCodecPreferences(video_codecs_vpx).ok());
auto video_codecs_vpx_reverse = video_codecs_vpx;
absl::c_reverse(video_codecs_vpx_reverse);
auto offer = caller->CreateOfferAndSetAsLocal();
auto codecs = offer->description()
->contents()[0]
.media_description()
->as_video()
->codecs();
EXPECT_EQ(codecs.size(), 2u); // VP9, VP8
EXPECT_TRUE(CompareCodecs(video_codecs_vpx, codecs));
callee->SetRemoteDescription(std::move(offer));
auto recv_transceiver = callee->pc()->GetTransceivers().front();
recv_transceiver->SetCodecPreferences(video_codecs_vpx_reverse);
auto answer = callee->CreateAnswerAndSetAsLocal();
auto recv_codecs = answer->description()
->contents()[0]
.media_description()
->as_video()
->codecs();
EXPECT_TRUE(CompareCodecs(video_codecs_vpx_reverse, recv_codecs));
}
INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest,
PeerConnectionMediaTest,
Values(SdpSemantics::kPlanB,

View file

@ -13,7 +13,9 @@
#include <string>
#include "absl/algorithm/container.h"
#include "pc/channel_manager.h"
#include "pc/rtp_media_utils.h"
#include "pc/rtp_parameters_conversion.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
@ -28,8 +30,11 @@ RtpTransceiver::RtpTransceiver(cricket::MediaType media_type)
RtpTransceiver::RtpTransceiver(
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
receiver)
: unified_plan_(true), media_type_(sender->media_type()) {
receiver,
cricket::ChannelManager* channel_manager)
: unified_plan_(true),
media_type_(sender->media_type()),
channel_manager_(channel_manager) {
RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
media_type_ == cricket::MEDIA_TYPE_VIDEO);
RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
@ -223,10 +228,129 @@ void RtpTransceiver::Stop() {
current_direction_ = absl::nullopt;
}
void RtpTransceiver::SetCodecPreferences(
rtc::ArrayView<RtpCodecCapability> codecs) {
// TODO(steveanton): Implement this.
RTC_NOTREACHED() << "Not implemented";
RTCError RtpTransceiver::SetCodecPreferences(
rtc::ArrayView<RtpCodecCapability> codec_capabilities) {
RTC_DCHECK(unified_plan_);
// 3. If codecs is an empty list, set transceiver's [[PreferredCodecs]] slot
// to codecs and abort these steps.
if (codec_capabilities.empty()) {
codec_preferences_.clear();
return RTCError::OK();
}
// 4. Remove any duplicate values in codecs.
std::vector<RtpCodecCapability> codecs;
absl::c_remove_copy_if(codec_capabilities, std::back_inserter(codecs),
[&codecs](const RtpCodecCapability& codec) {
return absl::c_linear_search(codecs, codec);
});
if (media_type_ == cricket::MEDIA_TYPE_AUDIO) {
std::vector<cricket::AudioCodec> audio_codecs;
std::vector<cricket::AudioCodec> recv_codecs, send_codecs;
channel_manager_->GetSupportedAudioReceiveCodecs(&recv_codecs);
channel_manager_->GetSupportedAudioSendCodecs(&send_codecs);
// 6. If the intersection between codecs and
// RTCRtpSender.getCapabilities(kind).codecs or the intersection between
// codecs and RTCRtpReceiver.getCapabilities(kind).codecs only contains RTX,
// RED or FEC codecs or is an empty set, throw InvalidModificationError.
// This ensures that we always have something to offer, regardless of
// transceiver.direction.
if (!absl::c_any_of(
codecs, [&recv_codecs](const RtpCodecCapability& codec) {
return codec.name != cricket::kRtxCodecName &&
codec.name != cricket::kRedCodecName &&
codec.name != cricket::kFlexfecCodecName &&
absl::c_any_of(
recv_codecs,
[&codec](const cricket::AudioCodec& recv_codec) {
return recv_codec.MatchesCapability(codec);
});
})) {
return RTCError(RTCErrorType::INVALID_MODIFICATION,
"Invalid codec preferences: Missing codec from recv "
"codec capabilities.");
}
if (!absl::c_any_of(
codecs, [&send_codecs](const RtpCodecCapability& codec) {
return codec.name != cricket::kRtxCodecName &&
codec.name != cricket::kRedCodecName &&
codec.name != cricket::kFlexfecCodecName &&
absl::c_any_of(
send_codecs,
[&codec](const cricket::AudioCodec& send_codec) {
return send_codec.MatchesCapability(codec);
});
})) {
return RTCError(RTCErrorType::INVALID_MODIFICATION,
"Invalid codec preferences: Missing codec from send "
"codec capabilities.");
}
// 7. Let codecCapabilities be the union of
// RTCRtpSender.getCapabilities(kind).codecs and
// RTCRtpReceiver.getCapabilities(kind).codecs. 8.1 For each codec in
// codecs, If codec is not in codecCapabilities, throw
// InvalidModificationError.
for (const auto& codec_preference : codecs) {
bool is_recv_codec = absl::c_any_of(
recv_codecs, [&codec_preference](const cricket::AudioCodec& codec) {
return codec.MatchesCapability(codec_preference);
});
bool is_send_codec = absl::c_any_of(
send_codecs, [&codec_preference](const cricket::AudioCodec& codec) {
return codec.MatchesCapability(codec_preference);
});
if (!is_recv_codec && !is_send_codec) {
return RTCError(
RTCErrorType::INVALID_MODIFICATION,
std::string(
"Invalid codec preferences: invalid codec with name \"") +
codec_preference.name + "\".");
}
}
} else if (media_type_ == cricket::MEDIA_TYPE_VIDEO) {
std::vector<cricket::VideoCodec> video_codecs;
// Video codecs are both for the receive and send side, so the checks are
// simpler than the audio ones.
channel_manager_->GetSupportedVideoCodecs(&video_codecs);
// Validate codecs
for (const auto& codec_preference : codecs) {
if (!absl::c_any_of(video_codecs, [&codec_preference](
const cricket::VideoCodec& codec) {
return codec.MatchesCapability(codec_preference);
})) {
return RTCError(
RTCErrorType::INVALID_MODIFICATION,
std::string(
"Invalid codec preferences: invalid codec with name \"") +
codec_preference.name + "\".");
}
}
}
// Check we have a real codec (not just rtx, red or fec)
if (absl::c_all_of(codecs, [](const RtpCodecCapability& codec) {
return codec.name == cricket::kRtxCodecName ||
codec.name == cricket::kRedCodecName ||
codec.name == cricket::kUlpfecCodecName;
})) {
return RTCError(RTCErrorType::INVALID_MODIFICATION,
"Invalid codec preferences: codec list must have a non "
"RTX, RED or FEC entry.");
}
codec_preferences_ = codecs;
return RTCError::OK();
}
} // namespace webrtc

View file

@ -16,6 +16,7 @@
#include "api/rtp_transceiver_interface.h"
#include "pc/channel_interface.h"
#include "pc/channel_manager.h"
#include "pc/rtp_receiver.h"
#include "pc/rtp_sender.h"
@ -66,7 +67,8 @@ class RtpTransceiver final
RtpTransceiver(
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
receiver);
receiver,
cricket::ChannelManager* channel_manager);
~RtpTransceiver() override;
// Returns the Voice/VideoChannel set for this transceiver. May be null if
@ -175,7 +177,11 @@ class RtpTransceiver final
absl::optional<RtpTransceiverDirection> current_direction() const override;
absl::optional<RtpTransceiverDirection> fired_direction() const override;
void Stop() override;
void SetCodecPreferences(rtc::ArrayView<RtpCodecCapability> codecs) override;
RTCError SetCodecPreferences(
rtc::ArrayView<RtpCodecCapability> codecs) override;
std::vector<RtpCodecCapability> codec_preferences() const override {
return codec_preferences_;
}
private:
void OnFirstPacketReceived(cricket::ChannelInterface* channel);
@ -198,6 +204,8 @@ class RtpTransceiver final
bool has_ever_been_used_to_send_ = false;
cricket::ChannelInterface* channel_ = nullptr;
cricket::ChannelManager* channel_manager_ = nullptr;
std::vector<RtpCodecCapability> codec_preferences_;
};
BEGIN_SIGNALING_PROXY_MAP(RtpTransceiver)
@ -212,7 +220,10 @@ PROXY_METHOD1(void, SetDirection, RtpTransceiverDirection)
PROXY_CONSTMETHOD0(absl::optional<RtpTransceiverDirection>, current_direction)
PROXY_CONSTMETHOD0(absl::optional<RtpTransceiverDirection>, fired_direction)
PROXY_METHOD0(void, Stop)
PROXY_METHOD1(void, SetCodecPreferences, rtc::ArrayView<RtpCodecCapability>)
PROXY_METHOD1(webrtc::RTCError,
SetCodecPreferences,
rtc::ArrayView<RtpCodecCapability>)
PROXY_CONSTMETHOD0(std::vector<RtpCodecCapability>, codec_preferences)
END_PROXY_MAP()
} // namespace webrtc