mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00
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:
parent
2ad2fabdcf
commit
2d9d82ecef
12 changed files with 892 additions and 121 deletions
|
@ -25,9 +25,15 @@ RtpTransceiverInterface::fired_direction() const {
|
||||||
return absl::nullopt;
|
return absl::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpTransceiverInterface::SetCodecPreferences(
|
RTCError RtpTransceiverInterface::SetCodecPreferences(
|
||||||
rtc::ArrayView<RtpCodecCapability>) {
|
rtc::ArrayView<RtpCodecCapability>) {
|
||||||
RTC_NOTREACHED() << "Not implemented";
|
RTC_NOTREACHED() << "Not implemented";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<RtpCodecCapability> RtpTransceiverInterface::codec_preferences()
|
||||||
|
const {
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
|
@ -129,8 +129,9 @@ class RtpTransceiverInterface : public rtc::RefCountInterface {
|
||||||
// The SetCodecPreferences method overrides the default codec preferences used
|
// The SetCodecPreferences method overrides the default codec preferences used
|
||||||
// by WebRTC for this transceiver.
|
// by WebRTC for this transceiver.
|
||||||
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-setcodecpreferences
|
// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver-setcodecpreferences
|
||||||
// TODO(steveanton): Not implemented.
|
virtual RTCError SetCodecPreferences(
|
||||||
virtual void SetCodecPreferences(rtc::ArrayView<RtpCodecCapability> codecs);
|
rtc::ArrayView<RtpCodecCapability> codecs);
|
||||||
|
virtual std::vector<RtpCodecCapability> codec_preferences() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~RtpTransceiverInterface() override = default;
|
~RtpTransceiverInterface() override = default;
|
||||||
|
|
|
@ -99,6 +99,18 @@ bool Codec::Matches(const Codec& codec) const {
|
||||||
: (absl::EqualsIgnoreCase(name, codec.name));
|
: (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 {
|
bool Codec::GetParam(const std::string& name, std::string* out) const {
|
||||||
CodecParameterMap::const_iterator iter = params.find(name);
|
CodecParameterMap::const_iterator iter = params.find(name);
|
||||||
if (iter == params.end())
|
if (iter == params.end())
|
||||||
|
|
|
@ -73,6 +73,7 @@ struct RTC_EXPORT Codec {
|
||||||
|
|
||||||
// Indicates if this codec is compatible with the specified codec.
|
// Indicates if this codec is compatible with the specified codec.
|
||||||
bool Matches(const Codec& codec) const;
|
bool Matches(const Codec& codec) const;
|
||||||
|
bool MatchesCapability(const webrtc::RtpCodecCapability& capability) const;
|
||||||
|
|
||||||
// Find the parameter for |name| and write the value to |out|.
|
// Find the parameter for |name| and write the value to |out|.
|
||||||
bool GetParam(const std::string& name, std::string* out) const;
|
bool GetParam(const std::string& name, std::string* out) const;
|
||||||
|
|
|
@ -495,7 +495,7 @@ bool FakeDataMediaChannel::SetMaxSendBandwidth(int bps) {
|
||||||
FakeVoiceEngine::FakeVoiceEngine() : fail_create_channel_(false) {
|
FakeVoiceEngine::FakeVoiceEngine() : fail_create_channel_(false) {
|
||||||
// Add a fake audio codec. Note that the name must not be "" as there are
|
// Add a fake audio codec. Note that the name must not be "" as there are
|
||||||
// sanity checks against that.
|
// 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 {
|
RtpCapabilities FakeVoiceEngine::GetCapabilities() const {
|
||||||
return RtpCapabilities();
|
return RtpCapabilities();
|
||||||
|
@ -524,13 +524,20 @@ void FakeVoiceEngine::UnregisterChannel(VoiceMediaChannel* channel) {
|
||||||
channels_.erase(absl::c_find(channels_, channel));
|
channels_.erase(absl::c_find(channels_, channel));
|
||||||
}
|
}
|
||||||
const std::vector<AudioCodec>& FakeVoiceEngine::send_codecs() const {
|
const std::vector<AudioCodec>& FakeVoiceEngine::send_codecs() const {
|
||||||
return codecs_;
|
return send_codecs_;
|
||||||
}
|
}
|
||||||
const std::vector<AudioCodec>& FakeVoiceEngine::recv_codecs() const {
|
const std::vector<AudioCodec>& FakeVoiceEngine::recv_codecs() const {
|
||||||
return codecs_;
|
return recv_codecs_;
|
||||||
}
|
}
|
||||||
void FakeVoiceEngine::SetCodecs(const std::vector<AudioCodec>& 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() {
|
int FakeVoiceEngine::GetInputLevel() {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -601,6 +608,14 @@ FakeMediaEngine::~FakeMediaEngine() {}
|
||||||
void FakeMediaEngine::SetAudioCodecs(const std::vector<AudioCodec>& codecs) {
|
void FakeMediaEngine::SetAudioCodecs(const std::vector<AudioCodec>& codecs) {
|
||||||
voice_->SetCodecs(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) {
|
void FakeMediaEngine::SetVideoCodecs(const std::vector<VideoCodec>& codecs) {
|
||||||
video_->SetCodecs(codecs);
|
video_->SetCodecs(codecs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -523,6 +523,8 @@ class FakeVoiceEngine : public VoiceEngineInterface {
|
||||||
const std::vector<AudioCodec>& send_codecs() const override;
|
const std::vector<AudioCodec>& send_codecs() const override;
|
||||||
const std::vector<AudioCodec>& recv_codecs() const override;
|
const std::vector<AudioCodec>& recv_codecs() const override;
|
||||||
void SetCodecs(const std::vector<AudioCodec>& codecs);
|
void SetCodecs(const std::vector<AudioCodec>& codecs);
|
||||||
|
void SetRecvCodecs(const std::vector<AudioCodec>& codecs);
|
||||||
|
void SetSendCodecs(const std::vector<AudioCodec>& codecs);
|
||||||
int GetInputLevel();
|
int GetInputLevel();
|
||||||
bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) override;
|
bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) override;
|
||||||
void StopAecDump() override;
|
void StopAecDump() override;
|
||||||
|
@ -531,7 +533,8 @@ class FakeVoiceEngine : public VoiceEngineInterface {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<FakeVoiceMediaChannel*> channels_;
|
std::vector<FakeVoiceMediaChannel*> channels_;
|
||||||
std::vector<AudioCodec> codecs_;
|
std::vector<AudioCodec> recv_codecs_;
|
||||||
|
std::vector<AudioCodec> send_codecs_;
|
||||||
bool fail_create_channel_;
|
bool fail_create_channel_;
|
||||||
|
|
||||||
friend class FakeMediaEngine;
|
friend class FakeMediaEngine;
|
||||||
|
@ -572,6 +575,8 @@ class FakeMediaEngine : public CompositeMediaEngine {
|
||||||
~FakeMediaEngine() override;
|
~FakeMediaEngine() override;
|
||||||
|
|
||||||
void SetAudioCodecs(const std::vector<AudioCodec>& codecs);
|
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);
|
void SetVideoCodecs(const std::vector<VideoCodec>& codecs);
|
||||||
|
|
||||||
FakeVoiceMediaChannel* GetVoiceChannel(size_t index);
|
FakeVoiceMediaChannel* GetVoiceChannel(size_t index);
|
||||||
|
|
|
@ -799,7 +799,8 @@ static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
|
||||||
template <class C>
|
template <class C>
|
||||||
static void NegotiateCodecs(const std::vector<C>& local_codecs,
|
static void NegotiateCodecs(const std::vector<C>& local_codecs,
|
||||||
const std::vector<C>& offered_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) {
|
for (const C& ours : local_codecs) {
|
||||||
C theirs;
|
C theirs;
|
||||||
// Note that we intentionally only find one matching codec for each of our
|
// 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));
|
negotiated_codecs->push_back(std::move(negotiated));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// RFC3264: Although the answerer MAY list the formats in their desired
|
if (keep_offer_order) {
|
||||||
// order of preference, it is RECOMMENDED that unless there is a
|
// RFC3264: Although the answerer MAY list the formats in their desired
|
||||||
// specific reason, the answerer list formats in the same relative order
|
// order of preference, it is RECOMMENDED that unless there is a
|
||||||
// they were present in the offer.
|
// specific reason, the answerer list formats in the same relative order
|
||||||
std::unordered_map<int, int> payload_type_preferences;
|
// they were present in the offer.
|
||||||
int preference = static_cast<int>(offered_codecs.size() + 1);
|
// This can be skipped when the transceiver has any codec preferences.
|
||||||
for (const C& codec : offered_codecs) {
|
std::unordered_map<int, int> payload_type_preferences;
|
||||||
payload_type_preferences[codec.id] = preference--;
|
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
|
// 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,
|
static bool FindByUriAndEncryption(const RtpHeaderExtensions& extensions,
|
||||||
const webrtc::RtpExtension& ext_to_match,
|
const webrtc::RtpExtension& ext_to_match,
|
||||||
webrtc::RtpExtension* found_extension) {
|
webrtc::RtpExtension* found_extension) {
|
||||||
|
@ -1159,7 +1208,8 @@ static bool CreateMediaContentAnswer(
|
||||||
bool bundle_enabled,
|
bool bundle_enabled,
|
||||||
MediaContentDescriptionImpl<C>* answer) {
|
MediaContentDescriptionImpl<C>* answer) {
|
||||||
std::vector<C> negotiated_codecs;
|
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->AddCodecs(negotiated_codecs);
|
||||||
answer->set_protocol(offer->protocol());
|
answer->set_protocol(offer->protocol());
|
||||||
|
|
||||||
|
@ -2009,30 +2059,38 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
|
||||||
GetAudioCodecsForOffer(media_description_options.direction);
|
GetAudioCodecsForOffer(media_description_options.direction);
|
||||||
|
|
||||||
AudioCodecs filtered_codecs;
|
AudioCodecs filtered_codecs;
|
||||||
// Add the codecs from current content if it exists and is not rejected nor
|
|
||||||
// recycled.
|
if (!media_description_options.codec_preferences.empty()) {
|
||||||
if (current_content && !current_content->rejected &&
|
// Add the codecs from the current transceiver's codec preferences.
|
||||||
current_content->name == media_description_options.mid) {
|
// They override any existing codecs from previous negotiations.
|
||||||
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
|
filtered_codecs = MatchCodecPreference(
|
||||||
const AudioContentDescription* acd =
|
media_description_options.codec_preferences, supported_audio_codecs);
|
||||||
current_content->media_description()->as_audio();
|
} else {
|
||||||
for (const AudioCodec& codec : acd->codecs()) {
|
// Add the codecs from current content if it exists and is not rejected nor
|
||||||
if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
|
// recycled.
|
||||||
nullptr)) {
|
if (current_content && !current_content->rejected &&
|
||||||
filtered_codecs.push_back(codec);
|
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.
|
||||||
// Add other supported audio codecs.
|
AudioCodec found_codec;
|
||||||
AudioCodec found_codec;
|
for (const AudioCodec& codec : supported_audio_codecs) {
|
||||||
for (const AudioCodec& codec : supported_audio_codecs) {
|
if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
|
||||||
if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
|
codec, &found_codec) &&
|
||||||
codec, &found_codec) &&
|
!FindMatchingCodec<AudioCodec>(supported_audio_codecs,
|
||||||
!FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
|
filtered_codecs, codec, nullptr)) {
|
||||||
codec, nullptr)) {
|
// Use the |found_codec| from |audio_codecs| because it has the
|
||||||
// Use the |found_codec| from |audio_codecs| because it has the correctly
|
// correctly mapped payload type.
|
||||||
// mapped payload type.
|
filtered_codecs.push_back(found_codec);
|
||||||
filtered_codecs.push_back(found_codec);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2088,30 +2146,38 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
|
||||||
&crypto_suites);
|
&crypto_suites);
|
||||||
|
|
||||||
VideoCodecs filtered_codecs;
|
VideoCodecs filtered_codecs;
|
||||||
// Add the codecs from current content if it exists and is not rejected nor
|
|
||||||
// recycled.
|
if (!media_description_options.codec_preferences.empty()) {
|
||||||
if (current_content && !current_content->rejected &&
|
// Add the codecs from the current transceiver's codec preferences.
|
||||||
current_content->name == media_description_options.mid) {
|
// They override any existing codecs from previous negotiations.
|
||||||
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
|
filtered_codecs = MatchCodecPreference(
|
||||||
const VideoContentDescription* vcd =
|
media_description_options.codec_preferences, video_codecs_);
|
||||||
current_content->media_description()->as_video();
|
} else {
|
||||||
for (const VideoCodec& codec : vcd->codecs()) {
|
// Add the codecs from current content if it exists and is not rejected nor
|
||||||
if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
|
// recycled.
|
||||||
nullptr)) {
|
if (current_content && !current_content->rejected &&
|
||||||
filtered_codecs.push_back(codec);
|
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.
|
||||||
// Add other supported video codecs.
|
VideoCodec found_codec;
|
||||||
VideoCodec found_codec;
|
for (const VideoCodec& codec : video_codecs_) {
|
||||||
for (const VideoCodec& codec : video_codecs_) {
|
if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
|
||||||
if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
|
&found_codec) &&
|
||||||
&found_codec) &&
|
!FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
|
||||||
!FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
|
nullptr)) {
|
||||||
nullptr)) {
|
// Use the |found_codec| from |video_codecs| because it has the
|
||||||
// Use the |found_codec| from |video_codecs| because it has the correctly
|
// correctly mapped payload type.
|
||||||
// mapped payload type.
|
filtered_codecs.push_back(found_codec);
|
||||||
filtered_codecs.push_back(found_codec);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2254,29 +2320,35 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
|
||||||
GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
|
GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
|
||||||
|
|
||||||
AudioCodecs filtered_codecs;
|
AudioCodecs filtered_codecs;
|
||||||
// Add the codecs from current content if it exists and is not rejected nor
|
|
||||||
// recycled.
|
if (!media_description_options.codec_preferences.empty()) {
|
||||||
if (current_content && !current_content->rejected &&
|
filtered_codecs = MatchCodecPreference(
|
||||||
current_content->name == media_description_options.mid) {
|
media_description_options.codec_preferences, supported_audio_codecs);
|
||||||
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
|
} else {
|
||||||
const AudioContentDescription* acd =
|
// Add the codecs from current content if it exists and is not rejected nor
|
||||||
current_content->media_description()->as_audio();
|
// recycled.
|
||||||
for (const AudioCodec& codec : acd->codecs()) {
|
if (current_content && !current_content->rejected &&
|
||||||
if (FindMatchingCodec<AudioCodec>(acd->codecs(), audio_codecs, codec,
|
current_content->name == media_description_options.mid) {
|
||||||
nullptr)) {
|
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO));
|
||||||
filtered_codecs.push_back(codec);
|
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.
|
||||||
// Add other supported audio codecs.
|
for (const AudioCodec& codec : supported_audio_codecs) {
|
||||||
for (const AudioCodec& codec : supported_audio_codecs) {
|
if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
|
||||||
if (FindMatchingCodec<AudioCodec>(supported_audio_codecs, audio_codecs,
|
codec, nullptr) &&
|
||||||
codec, nullptr) &&
|
!FindMatchingCodec<AudioCodec>(supported_audio_codecs,
|
||||||
!FindMatchingCodec<AudioCodec>(supported_audio_codecs, filtered_codecs,
|
filtered_codecs, codec, nullptr)) {
|
||||||
codec, nullptr)) {
|
// We should use the local codec with local parameters and the codec id
|
||||||
// We should use the local codec with local parameters and the codec id
|
// would be correctly mapped in |NegotiateCodecs|.
|
||||||
// would be correctly mapped in |NegotiateCodecs|.
|
filtered_codecs.push_back(codec);
|
||||||
filtered_codecs.push_back(codec);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2342,29 +2414,35 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCodecs filtered_codecs;
|
VideoCodecs filtered_codecs;
|
||||||
// Add the codecs from current content if it exists and is not rejected nor
|
|
||||||
// recycled.
|
if (!media_description_options.codec_preferences.empty()) {
|
||||||
if (current_content && !current_content->rejected &&
|
filtered_codecs = MatchCodecPreference(
|
||||||
current_content->name == media_description_options.mid) {
|
media_description_options.codec_preferences, video_codecs_);
|
||||||
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
|
} else {
|
||||||
const VideoContentDescription* vcd =
|
// Add the codecs from current content if it exists and is not rejected nor
|
||||||
current_content->media_description()->as_video();
|
// recycled.
|
||||||
for (const VideoCodec& codec : vcd->codecs()) {
|
if (current_content && !current_content->rejected &&
|
||||||
if (FindMatchingCodec<VideoCodec>(vcd->codecs(), video_codecs, codec,
|
current_content->name == media_description_options.mid) {
|
||||||
nullptr)) {
|
RTC_CHECK(IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO));
|
||||||
filtered_codecs.push_back(codec);
|
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.
|
||||||
// Add other supported video codecs.
|
for (const VideoCodec& codec : video_codecs_) {
|
||||||
for (const VideoCodec& codec : video_codecs_) {
|
if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
|
||||||
if (FindMatchingCodec<VideoCodec>(video_codecs_, video_codecs, codec,
|
nullptr) &&
|
||||||
nullptr) &&
|
!FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
|
||||||
!FindMatchingCodec<VideoCodec>(video_codecs_, filtered_codecs, codec,
|
nullptr)) {
|
||||||
nullptr)) {
|
// We should use the local codec with local parameters and the codec id
|
||||||
// We should use the local codec with local parameters and the codec id
|
// would be correctly mapped in |NegotiateCodecs|.
|
||||||
// would be correctly mapped in |NegotiateCodecs|.
|
filtered_codecs.push_back(codec);
|
||||||
filtered_codecs.push_back(codec);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2499,7 +2577,7 @@ void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
|
||||||
// expensive than decoding, and prioritizing a codec in the send list probably
|
// expensive than decoding, and prioritizing a codec in the send list probably
|
||||||
// means it's a codec we can handle efficiently.
|
// means it's a codec we can handle efficiently.
|
||||||
NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
|
NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_,
|
||||||
&audio_sendrecv_codecs_);
|
&audio_sendrecv_codecs_, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsMediaContent(const ContentInfo* content) {
|
bool IsMediaContent(const ContentInfo* content) {
|
||||||
|
|
|
@ -76,6 +76,7 @@ struct MediaDescriptionOptions {
|
||||||
// Note: There's no equivalent "RtpReceiverOptions" because only send
|
// Note: There's no equivalent "RtpReceiverOptions" because only send
|
||||||
// stream information goes in the local descriptions.
|
// stream information goes in the local descriptions.
|
||||||
std::vector<SenderOptions> sender_options;
|
std::vector<SenderOptions> sender_options;
|
||||||
|
std::vector<webrtc::RtpCodecCapability> codec_preferences;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Doesn't DCHECK on |type|.
|
// Doesn't DCHECK on |type|.
|
||||||
|
|
|
@ -1703,7 +1703,8 @@ PeerConnection::CreateAndAddTransceiver(
|
||||||
// could be invalid, but should not cause a crash).
|
// could be invalid, but should not cause a crash).
|
||||||
RTC_DCHECK(!FindSenderById(sender->id()));
|
RTC_DCHECK(!FindSenderById(sender->id()));
|
||||||
auto transceiver = RtpTransceiverProxyWithInternal<RtpTransceiver>::Create(
|
auto transceiver = RtpTransceiverProxyWithInternal<RtpTransceiver>::Create(
|
||||||
signaling_thread(), new RtpTransceiver(sender, receiver));
|
signaling_thread(),
|
||||||
|
new RtpTransceiver(sender, receiver, channel_manager()));
|
||||||
transceivers_.push_back(transceiver);
|
transceivers_.push_back(transceiver);
|
||||||
transceiver->internal()->SignalNegotiationNeeded.connect(
|
transceiver->internal()->SignalNegotiationNeeded.connect(
|
||||||
this, &PeerConnection::OnNegotiationNeeded);
|
this, &PeerConnection::OnNegotiationNeeded);
|
||||||
|
@ -4397,6 +4398,8 @@ GetMediaDescriptionOptionsForTransceiver(
|
||||||
cricket::MediaDescriptionOptions media_description_options(
|
cricket::MediaDescriptionOptions media_description_options(
|
||||||
transceiver->media_type(), mid, transceiver->direction(),
|
transceiver->media_type(), mid, transceiver->direction(),
|
||||||
transceiver->stopped());
|
transceiver->stopped());
|
||||||
|
media_description_options.codec_preferences =
|
||||||
|
transceiver->codec_preferences();
|
||||||
// This behavior is specified in JSEP. The gist is that:
|
// This behavior is specified in JSEP. The gist is that:
|
||||||
// 1. The MSID is included if the RtpTransceiver's direction is sendonly or
|
// 1. The MSID is included if the RtpTransceiver's direction is sendonly or
|
||||||
// sendrecv.
|
// sendrecv.
|
||||||
|
|
|
@ -73,11 +73,21 @@ class PeerConnectionMediaBaseTest : public ::testing::Test {
|
||||||
return CreatePeerConnection(RTCConfiguration());
|
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.
|
// Creates PeerConnectionFactory and PeerConnection for given configuration.
|
||||||
// Note that PeerConnectionFactory is created with MediaTransportFactory,
|
// Note that PeerConnectionFactory is created with MediaTransportFactory,
|
||||||
// because some tests pass config.use_media_transport = true.
|
// because some tests pass config.use_media_transport = true.
|
||||||
WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
|
WrapperPtr CreatePeerConnection(
|
||||||
auto media_engine = absl::make_unique<FakeMediaEngine>();
|
const RTCConfiguration& config,
|
||||||
|
std::unique_ptr<FakeMediaEngine> media_engine) {
|
||||||
auto* media_engine_ptr = media_engine.get();
|
auto* media_engine_ptr = media_engine.get();
|
||||||
|
|
||||||
PeerConnectionFactoryDependencies factory_dependencies;
|
PeerConnectionFactoryDependencies factory_dependencies;
|
||||||
|
@ -125,6 +135,18 @@ class PeerConnectionMediaBaseTest : public ::testing::Test {
|
||||||
return wrapper;
|
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
|
// Accepts the same arguments as CreatePeerConnection and adds default audio
|
||||||
// and video tracks.
|
// and video tracks.
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
|
@ -1239,6 +1261,498 @@ TEST_P(PeerConnectionMediaTest, MediaTransportNotPropagatedToVoiceEngine) {
|
||||||
ASSERT_EQ(nullptr, callee_video->media_transport());
|
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,
|
INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest,
|
||||||
PeerConnectionMediaTest,
|
PeerConnectionMediaTest,
|
||||||
Values(SdpSemantics::kPlanB,
|
Values(SdpSemantics::kPlanB,
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "absl/algorithm/container.h"
|
#include "absl/algorithm/container.h"
|
||||||
|
#include "pc/channel_manager.h"
|
||||||
#include "pc/rtp_media_utils.h"
|
#include "pc/rtp_media_utils.h"
|
||||||
|
#include "pc/rtp_parameters_conversion.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
|
|
||||||
|
@ -28,8 +30,11 @@ RtpTransceiver::RtpTransceiver(cricket::MediaType media_type)
|
||||||
RtpTransceiver::RtpTransceiver(
|
RtpTransceiver::RtpTransceiver(
|
||||||
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
|
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
|
||||||
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
|
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
|
||||||
receiver)
|
receiver,
|
||||||
: unified_plan_(true), media_type_(sender->media_type()) {
|
cricket::ChannelManager* channel_manager)
|
||||||
|
: unified_plan_(true),
|
||||||
|
media_type_(sender->media_type()),
|
||||||
|
channel_manager_(channel_manager) {
|
||||||
RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
|
RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
|
||||||
media_type_ == cricket::MEDIA_TYPE_VIDEO);
|
media_type_ == cricket::MEDIA_TYPE_VIDEO);
|
||||||
RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
|
RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
|
||||||
|
@ -223,10 +228,129 @@ void RtpTransceiver::Stop() {
|
||||||
current_direction_ = absl::nullopt;
|
current_direction_ = absl::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpTransceiver::SetCodecPreferences(
|
RTCError RtpTransceiver::SetCodecPreferences(
|
||||||
rtc::ArrayView<RtpCodecCapability> codecs) {
|
rtc::ArrayView<RtpCodecCapability> codec_capabilities) {
|
||||||
// TODO(steveanton): Implement this.
|
RTC_DCHECK(unified_plan_);
|
||||||
RTC_NOTREACHED() << "Not implemented";
|
|
||||||
|
// 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
|
} // namespace webrtc
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "api/rtp_transceiver_interface.h"
|
#include "api/rtp_transceiver_interface.h"
|
||||||
#include "pc/channel_interface.h"
|
#include "pc/channel_interface.h"
|
||||||
|
#include "pc/channel_manager.h"
|
||||||
#include "pc/rtp_receiver.h"
|
#include "pc/rtp_receiver.h"
|
||||||
#include "pc/rtp_sender.h"
|
#include "pc/rtp_sender.h"
|
||||||
|
|
||||||
|
@ -66,7 +67,8 @@ class RtpTransceiver final
|
||||||
RtpTransceiver(
|
RtpTransceiver(
|
||||||
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
|
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
|
||||||
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
|
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
|
||||||
receiver);
|
receiver,
|
||||||
|
cricket::ChannelManager* channel_manager);
|
||||||
~RtpTransceiver() override;
|
~RtpTransceiver() override;
|
||||||
|
|
||||||
// Returns the Voice/VideoChannel set for this transceiver. May be null if
|
// 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> current_direction() const override;
|
||||||
absl::optional<RtpTransceiverDirection> fired_direction() const override;
|
absl::optional<RtpTransceiverDirection> fired_direction() const override;
|
||||||
void Stop() 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:
|
private:
|
||||||
void OnFirstPacketReceived(cricket::ChannelInterface* channel);
|
void OnFirstPacketReceived(cricket::ChannelInterface* channel);
|
||||||
|
@ -198,6 +204,8 @@ class RtpTransceiver final
|
||||||
bool has_ever_been_used_to_send_ = false;
|
bool has_ever_been_used_to_send_ = false;
|
||||||
|
|
||||||
cricket::ChannelInterface* channel_ = nullptr;
|
cricket::ChannelInterface* channel_ = nullptr;
|
||||||
|
cricket::ChannelManager* channel_manager_ = nullptr;
|
||||||
|
std::vector<RtpCodecCapability> codec_preferences_;
|
||||||
};
|
};
|
||||||
|
|
||||||
BEGIN_SIGNALING_PROXY_MAP(RtpTransceiver)
|
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>, current_direction)
|
||||||
PROXY_CONSTMETHOD0(absl::optional<RtpTransceiverDirection>, fired_direction)
|
PROXY_CONSTMETHOD0(absl::optional<RtpTransceiverDirection>, fired_direction)
|
||||||
PROXY_METHOD0(void, Stop)
|
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()
|
END_PROXY_MAP()
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
Loading…
Reference in a new issue