Provide mechanism to make codec decisions per-transceiver
This provides a way to tell the SDP generator to use a specific list of codecs, rather than trying to compute what list to send. Preparatory to making codec decisions per-transceiver. Bug: webrtc:42226302 Change-Id: I1b7d4e55ed7a0546394b74820b4e51434ef86ad9 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/349620 Commit-Queue: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Tony Herre <herre@google.com> Cr-Commit-Position: refs/heads/main@{#42247}
This commit is contained in:
parent
30e3553229
commit
141f4c153f
@ -1036,10 +1036,7 @@ bool SetCodecsInAnswer(const MediaContentDescription* offer,
|
||||
const webrtc::FieldTrialsView& field_trials) {
|
||||
RTC_DCHECK(offer->type() == MEDIA_TYPE_AUDIO ||
|
||||
offer->type() == MEDIA_TYPE_VIDEO);
|
||||
std::vector<Codec> negotiated_codecs;
|
||||
NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
|
||||
media_description_options.codec_preferences.empty());
|
||||
answer->AddCodecs(negotiated_codecs);
|
||||
answer->AddCodecs(local_codecs);
|
||||
answer->set_protocol(offer->protocol());
|
||||
if (!AddStreamParams(media_description_options.sender_options,
|
||||
session_options.rtcp_cname, ssrc_generator,
|
||||
@ -2013,6 +2010,10 @@ RTCError MediaSessionDescriptionFactory::AddTransportAnswer(
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
// Add the RTP description to the SessionDescription.
|
||||
// If media_description_options.codecs_to_include is set, those codecs are used.
|
||||
//
|
||||
// If it is not set, the codecs used are computed based on:
|
||||
// `codecs` = set of all possible codecs that can be used, with correct
|
||||
// payload type mappings
|
||||
//
|
||||
@ -2039,6 +2040,8 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForOffer(
|
||||
RTC_DCHECK(media_description_options.type == MEDIA_TYPE_AUDIO ||
|
||||
media_description_options.type == MEDIA_TYPE_VIDEO);
|
||||
|
||||
std::vector<Codec> codecs_to_include;
|
||||
if (media_description_options.codecs_to_include.empty()) {
|
||||
const std::vector<Codec>& supported_codecs =
|
||||
media_description_options.type == MEDIA_TYPE_AUDIO
|
||||
? GetAudioCodecsForOffer(media_description_options.direction)
|
||||
@ -2049,7 +2052,11 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForOffer(
|
||||
if (!error_or_filtered_codecs.ok()) {
|
||||
return error_or_filtered_codecs.MoveError();
|
||||
}
|
||||
|
||||
codecs_to_include = error_or_filtered_codecs.MoveValue();
|
||||
} else {
|
||||
// Ignore both the codecs argument and the Get*CodecsForOffer results.
|
||||
codecs_to_include = media_description_options.codecs_to_include;
|
||||
}
|
||||
std::unique_ptr<MediaContentDescription> content_description;
|
||||
if (media_description_options.type == MEDIA_TYPE_AUDIO) {
|
||||
content_description = std::make_unique<AudioContentDescription>();
|
||||
@ -2058,10 +2065,9 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForOffer(
|
||||
}
|
||||
|
||||
auto error = CreateMediaContentOffer(
|
||||
media_description_options, session_options,
|
||||
error_or_filtered_codecs.MoveValue(), header_extensions, ssrc_generator(),
|
||||
current_streams, content_description.get(),
|
||||
transport_desc_factory_->trials());
|
||||
media_description_options, session_options, codecs_to_include,
|
||||
header_extensions, ssrc_generator(), current_streams,
|
||||
content_description.get(), transport_desc_factory_->trials());
|
||||
if (!error.ok()) {
|
||||
return error;
|
||||
}
|
||||
@ -2197,6 +2203,9 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer(
|
||||
auto offer_rtd = offer_content_description->direction();
|
||||
auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
|
||||
|
||||
std::vector<Codec> codecs_to_include;
|
||||
bool negotiate;
|
||||
if (media_description_options.codecs_to_include.empty()) {
|
||||
const std::vector<Codec>& supported_codecs =
|
||||
media_description_options.type == MEDIA_TYPE_AUDIO
|
||||
? GetAudioCodecsForAnswer(offer_rtd, answer_rtd)
|
||||
@ -2207,14 +2216,18 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer(
|
||||
if (!error_or_filtered_codecs.ok()) {
|
||||
return error_or_filtered_codecs.MoveError();
|
||||
}
|
||||
auto filtered_codecs = error_or_filtered_codecs.MoveValue();
|
||||
|
||||
codecs_to_include = error_or_filtered_codecs.MoveValue();
|
||||
negotiate = true;
|
||||
} else {
|
||||
codecs_to_include = media_description_options.codecs_to_include;
|
||||
negotiate = false; // Don't filter against remote codecs
|
||||
}
|
||||
// Determine if we have media codecs in common.
|
||||
bool has_common_media_codecs =
|
||||
std::find_if(filtered_codecs.begin(), filtered_codecs.end(),
|
||||
bool has_usable_media_codecs =
|
||||
std::find_if(codecs_to_include.begin(), codecs_to_include.end(),
|
||||
[](const Codec& c) {
|
||||
return c.IsMediaCodec() && !IsComfortNoiseCodec(c);
|
||||
}) != filtered_codecs.end();
|
||||
}) != codecs_to_include.end();
|
||||
|
||||
bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
|
||||
session_options.bundle_enabled;
|
||||
@ -2224,10 +2237,19 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer(
|
||||
} else {
|
||||
answer_content = std::make_unique<VideoContentDescription>();
|
||||
}
|
||||
if (!SetCodecsInAnswer(
|
||||
offer_content_description, filtered_codecs, media_description_options,
|
||||
session_options, ssrc_generator(), current_streams,
|
||||
answer_content.get(), transport_desc_factory_->trials())) {
|
||||
if (negotiate) {
|
||||
std::vector<Codec> negotiated_codecs;
|
||||
NegotiateCodecs(codecs_to_include, offer_content_description->codecs(),
|
||||
&negotiated_codecs,
|
||||
media_description_options.codec_preferences.empty());
|
||||
codecs_to_include = negotiated_codecs;
|
||||
}
|
||||
|
||||
if (!SetCodecsInAnswer(offer_content_description, codecs_to_include,
|
||||
media_description_options, session_options,
|
||||
ssrc_generator(), current_streams,
|
||||
answer_content.get(),
|
||||
transport_desc_factory_->trials())) {
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
|
||||
"Failed to set codecs in answer");
|
||||
}
|
||||
@ -2243,7 +2265,7 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer(
|
||||
bool secure = bundle_transport ? bundle_transport->description.secure()
|
||||
: transport->secure();
|
||||
bool rejected = media_description_options.stopped ||
|
||||
offer_content->rejected || !has_common_media_codecs ||
|
||||
offer_content->rejected || !has_usable_media_codecs ||
|
||||
!IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
|
||||
answer_content->protocol(), secure);
|
||||
if (rejected) {
|
||||
|
||||
@ -90,6 +90,9 @@ struct MediaDescriptionOptions {
|
||||
std::vector<SenderOptions> sender_options;
|
||||
std::vector<webrtc::RtpCodecCapability> codec_preferences;
|
||||
std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions;
|
||||
// Codecs to include in a generated offer or answer.
|
||||
// If this is used, session-level codec lists MUST be ignored.
|
||||
std::vector<Codec> codecs_to_include;
|
||||
|
||||
private:
|
||||
// Doesn't DCHECK on `type`.
|
||||
|
||||
@ -740,8 +740,92 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoOffer) {
|
||||
EXPECT_EQ(kMediaProtocolDtlsSavpf, vcd->protocol());
|
||||
}
|
||||
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateOfferWithCustomCodecs) {
|
||||
MediaSessionOptions opts;
|
||||
|
||||
webrtc::SdpAudioFormat audio_format("custom-audio", 8000, 2);
|
||||
Codec custom_audio_codec = CreateAudioCodec(audio_format);
|
||||
auto audio_options = MediaDescriptionOptions(
|
||||
MEDIA_TYPE_AUDIO, "0", RtpTransceiverDirection::kSendRecv, kActive);
|
||||
audio_options.codecs_to_include.push_back(custom_audio_codec);
|
||||
opts.media_description_options.push_back(audio_options);
|
||||
|
||||
Codec custom_video_codec = CreateVideoCodec("custom-video");
|
||||
auto video_options = MediaDescriptionOptions(
|
||||
MEDIA_TYPE_VIDEO, "1", RtpTransceiverDirection::kSendRecv, kActive);
|
||||
video_options.codecs_to_include.push_back(custom_video_codec);
|
||||
opts.media_description_options.push_back(video_options);
|
||||
|
||||
std::unique_ptr<SessionDescription> offer =
|
||||
f1_.CreateOfferOrError(opts, nullptr).MoveValue();
|
||||
ASSERT_TRUE(offer.get());
|
||||
const ContentInfo* ac = offer->GetContentByName("0");
|
||||
const ContentInfo* vc = offer->GetContentByName("1");
|
||||
ASSERT_TRUE(ac);
|
||||
ASSERT_TRUE(vc);
|
||||
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
|
||||
EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
|
||||
const MediaContentDescription* acd = ac->media_description();
|
||||
const MediaContentDescription* vcd = vc->media_description();
|
||||
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
|
||||
ASSERT_EQ(acd->codecs().size(), 1U);
|
||||
// Fields in codec are set during the gen process, so simple compare
|
||||
// does not work.
|
||||
EXPECT_EQ(acd->codecs()[0].name, custom_audio_codec.name);
|
||||
|
||||
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
|
||||
ASSERT_EQ(vcd->codecs().size(), 1U);
|
||||
EXPECT_EQ(vcd->codecs()[0].name, custom_video_codec.name);
|
||||
}
|
||||
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAnswerWithCustomCodecs) {
|
||||
MediaSessionOptions offer_opts;
|
||||
MediaSessionOptions answer_opts;
|
||||
|
||||
AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &offer_opts);
|
||||
// Create custom codecs and add to answer. These will override
|
||||
// the normally generated codec list in the answer.
|
||||
// This breaks O/A rules - the responsibility for obeying those is
|
||||
// on the caller, not on this function.
|
||||
webrtc::SdpAudioFormat audio_format("custom-audio", 8000, 2);
|
||||
Codec custom_audio_codec = CreateAudioCodec(audio_format);
|
||||
auto audio_options = MediaDescriptionOptions(
|
||||
MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kSendRecv, kActive);
|
||||
audio_options.codecs_to_include.push_back(custom_audio_codec);
|
||||
answer_opts.media_description_options.push_back(audio_options);
|
||||
|
||||
Codec custom_video_codec = CreateVideoCodec("custom-video");
|
||||
auto video_options = MediaDescriptionOptions(
|
||||
MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, kActive);
|
||||
video_options.codecs_to_include.push_back(custom_video_codec);
|
||||
answer_opts.media_description_options.push_back(video_options);
|
||||
|
||||
std::unique_ptr<SessionDescription> offer =
|
||||
f1_.CreateOfferOrError(offer_opts, nullptr).MoveValue();
|
||||
ASSERT_TRUE(offer.get());
|
||||
std::unique_ptr<SessionDescription> answer =
|
||||
f1_.CreateAnswerOrError(offer.get(), answer_opts, nullptr).MoveValue();
|
||||
const ContentInfo* ac = answer->GetContentByName("audio");
|
||||
const ContentInfo* vc = answer->GetContentByName("video");
|
||||
ASSERT_TRUE(ac);
|
||||
ASSERT_TRUE(vc);
|
||||
EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
|
||||
EXPECT_EQ(MediaProtocolType::kRtp, vc->type);
|
||||
const MediaContentDescription* acd = ac->media_description();
|
||||
const MediaContentDescription* vcd = vc->media_description();
|
||||
EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
|
||||
ASSERT_EQ(acd->codecs().size(), 1U);
|
||||
// Fields in codec are set during the gen process, so simple compare
|
||||
// does not work.
|
||||
EXPECT_EQ(acd->codecs()[0].name, custom_audio_codec.name);
|
||||
|
||||
EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type());
|
||||
ASSERT_EQ(vcd->codecs().size(), 1U);
|
||||
EXPECT_EQ(vcd->codecs()[0].name, custom_video_codec.name);
|
||||
}
|
||||
|
||||
// Test creating an offer with bundle where the Codecs have the same dynamic
|
||||
// RTP playlod type. The test verifies that the offer don't contain the
|
||||
// RTP paylod type. The test verifies that the offer don't contain the
|
||||
// duplicate RTP payload types.
|
||||
TEST_F(MediaSessionDescriptionFactoryTest, TestBundleOfferWithSameCodecPlType) {
|
||||
const Codec& offered_video_codec = f2_.video_sendrecv_codecs()[0];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user