Look through all candidates before falling back to default packetization
It's possible that a peer can signal the same payload with multiple
packetization options. As such, we shouldn't try to fall back to default
packetization until we have considered all the alternatives.
Bug: webrtc:15473
Change-Id: I21772b4d8c53819d1c3105988551ebdbea0df045
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/320241
Commit-Queue: Emil Lundmark <lndmrk@webrtc.org>
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Auto-Submit: Emil Lundmark <lndmrk@webrtc.org>
Reviewed-by: Sergey Sukhanov <sergeysu@webrtc.org>
Commit-Queue: Stefan Holmer <stefan@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40775}
diff --git a/media/base/codec.cc b/media/base/codec.cc
index 7ecf383..9ada18f 100644
--- a/media/base/codec.cc
+++ b/media/base/codec.cc
@@ -400,6 +400,19 @@
return nullptr;
}
+std::vector<const VideoCodec*> FindAllMatchingCodecs(
+ const std::vector<VideoCodec>& supported_codecs,
+ const VideoCodec& codec) {
+ std::vector<const VideoCodec*> result;
+ webrtc::SdpVideoFormat sdp(codec.name, codec.params);
+ for (const VideoCodec& supported_codec : supported_codecs) {
+ if (sdp.IsSameCodec({supported_codec.name, supported_codec.params})) {
+ result.push_back(&supported_codec);
+ }
+ }
+ return result;
+}
+
// If a decoder supports any H264 profile, it is implicitly assumed to also
// support constrained base line even though it's not explicitly listed.
void AddH264ConstrainedBaselineProfileToSupportedFormats(
diff --git a/media/base/codec.h b/media/base/codec.h
index 5595708..b8ef22e 100644
--- a/media/base/codec.h
+++ b/media/base/codec.h
@@ -214,12 +214,18 @@
bool HasRemb(const Codec& codec);
bool HasRrtr(const Codec& codec);
bool HasTransportCc(const Codec& codec);
+
// Returns the first codec in `supported_codecs` that matches `codec`, or
// nullptr if no codec matches.
const VideoCodec* FindMatchingCodec(
const std::vector<VideoCodec>& supported_codecs,
const VideoCodec& codec);
+// Returns all codecs in `supported_codecs` that matches `codec`.
+std::vector<const VideoCodec*> FindAllMatchingCodecs(
+ const std::vector<VideoCodec>& supported_codecs,
+ const VideoCodec& codec);
+
RTC_EXPORT void AddH264ConstrainedBaselineProfileToSupportedFormats(
std::vector<webrtc::SdpVideoFormat>* supported_formats);
diff --git a/pc/channel.cc b/pc/channel.cc
index c763b6b..4b8959c 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -1035,6 +1035,11 @@
VideoSenderParameters send_params = last_send_params_;
+ // Ensure that there is a matching packetization for each send codec. If the
+ // other peer offered to exclusively send non-standard packetization but we
+ // only accept to receive standard packetization we effectively amend their
+ // offer by ignoring the packetiztion and fall back to standard packetization
+ // instead.
bool needs_send_params_update = false;
if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
webrtc::flat_set<const VideoCodec*> matched_codecs;
@@ -1045,17 +1050,28 @@
continue;
}
- const VideoCodec* recv_codec =
- FindMatchingCodec(recv_params.codecs, send_codec);
- if (recv_codec == nullptr) {
+ std::vector<const VideoCodec*> recv_codecs =
+ FindAllMatchingCodecs(recv_params.codecs, send_codec);
+ if (recv_codecs.empty()) {
continue;
}
- if (!recv_codec->packetization.has_value() &&
- send_codec.packetization.has_value()) {
+ bool may_ignore_packetization = false;
+ bool has_matching_packetization = false;
+ for (const VideoCodec* recv_codec : recv_codecs) {
+ if (!recv_codec->packetization.has_value() &&
+ send_codec.packetization.has_value()) {
+ may_ignore_packetization = true;
+ } else if (recv_codec->packetization == send_codec.packetization) {
+ has_matching_packetization = true;
+ break;
+ }
+ }
+
+ if (may_ignore_packetization) {
send_codec.packetization = absl::nullopt;
needs_send_params_update = true;
- } else if (recv_codec->packetization != send_codec.packetization) {
+ } else if (!has_matching_packetization) {
error_desc = StringFormat(
"Failed to set local answer due to incompatible codec "
"packetization for pt='%d' specified in m-section with mid='%s'.",
@@ -1063,7 +1079,7 @@
return false;
}
- if (recv_codec->packetization == send_codec.packetization) {
+ if (has_matching_packetization) {
matched_codecs.insert(&send_codec);
}
}
@@ -1135,6 +1151,11 @@
VideoReceiverParameters recv_params = last_recv_params_;
+ // Ensure that there is a matching packetization for each receive codec. If we
+ // offered to exclusively receive a non-standard packetization but the other
+ // peer only accepts to send standard packetization we effectively amend our
+ // offer by ignoring the packetiztion and fall back to standard packetization
+ // instead.
bool needs_recv_params_update = false;
if (type == SdpType::kAnswer || type == SdpType::kPrAnswer) {
webrtc::flat_set<const VideoCodec*> matched_codecs;
@@ -1145,17 +1166,28 @@
continue;
}
- const VideoCodec* send_codec =
- FindMatchingCodec(send_params.codecs, recv_codec);
- if (send_codec == nullptr) {
+ std::vector<const VideoCodec*> send_codecs =
+ FindAllMatchingCodecs(send_params.codecs, recv_codec);
+ if (send_codecs.empty()) {
continue;
}
- if (!send_codec->packetization.has_value() &&
- recv_codec.packetization.has_value()) {
+ bool may_ignore_packetization = false;
+ bool has_matching_packetization = false;
+ for (const VideoCodec* send_codec : send_codecs) {
+ if (!send_codec->packetization.has_value() &&
+ recv_codec.packetization.has_value()) {
+ may_ignore_packetization = true;
+ } else if (send_codec->packetization == recv_codec.packetization) {
+ has_matching_packetization = true;
+ break;
+ }
+ }
+
+ if (may_ignore_packetization) {
recv_codec.packetization = absl::nullopt;
needs_recv_params_update = true;
- } else if (send_codec->packetization != recv_codec.packetization) {
+ } else if (!has_matching_packetization) {
error_desc = StringFormat(
"Failed to set remote answer due to incompatible codec "
"packetization for pt='%d' specified in m-section with mid='%s'.",
@@ -1163,7 +1195,7 @@
return false;
}
- if (send_codec->packetization == recv_codec.packetization) {
+ if (has_matching_packetization) {
matched_codecs.insert(&recv_codec);
}
}
diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc
index c86f2cc..7d60376 100644
--- a/pc/channel_unittest.cc
+++ b/pc/channel_unittest.cc
@@ -2349,6 +2349,70 @@
Field(&cricket::Codec::packetization, absl::nullopt))));
}
+TEST_F(VideoChannelSingleThreadTest,
+ ConsidersAllCodecsWithDiffrentPacketizationsInRemoteAnswer) {
+ cricket::VideoCodec vp8 = cricket::CreateVideoCodec(96, "VP8");
+ cricket::VideoCodec vp8_raw = cricket::CreateVideoCodec(97, "VP8");
+ vp8_raw.packetization = cricket::kPacketizationParamRaw;
+ cricket::VideoContentDescription local;
+ local.set_codecs({vp8, vp8_raw});
+ cricket::VideoContentDescription remote;
+ remote.set_codecs({vp8_raw, vp8});
+
+ CreateChannels(0, 0);
+ std::string err;
+ ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kOffer, err)) << err;
+ ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kAnswer, err))
+ << err;
+
+ EXPECT_THAT(
+ media_receive_channel1_impl()->recv_codecs(),
+ ElementsAre(AllOf(Field(&cricket::Codec::id, 96),
+ Field(&cricket::Codec::packetization, absl::nullopt)),
+ AllOf(Field(&cricket::Codec::id, 97),
+ Field(&cricket::Codec::packetization,
+ cricket::kPacketizationParamRaw))));
+ EXPECT_THAT(
+ media_send_channel1_impl()->send_codecs(),
+ ElementsAre(AllOf(Field(&cricket::Codec::id, 97),
+ Field(&cricket::Codec::packetization,
+ cricket::kPacketizationParamRaw)),
+ AllOf(Field(&cricket::Codec::id, 96),
+ Field(&cricket::Codec::packetization, absl::nullopt))));
+}
+
+TEST_F(VideoChannelSingleThreadTest,
+ ConsidersAllCodecsWithDiffrentPacketizationsInLocalAnswer) {
+ cricket::VideoCodec vp8 = cricket::CreateVideoCodec(96, "VP8");
+ cricket::VideoCodec vp8_raw = cricket::CreateVideoCodec(97, "VP8");
+ vp8_raw.packetization = cricket::kPacketizationParamRaw;
+ cricket::VideoContentDescription local;
+ local.set_codecs({vp8_raw, vp8});
+ cricket::VideoContentDescription remote;
+ remote.set_codecs({vp8, vp8_raw});
+
+ CreateChannels(0, 0);
+ std::string err;
+ ASSERT_TRUE(channel1_->SetRemoteContent(&remote, SdpType::kOffer, err))
+ << err;
+ ASSERT_TRUE(channel1_->SetLocalContent(&local, SdpType::kAnswer, err)) << err;
+
+ EXPECT_THAT(
+ media_receive_channel1_impl()->recv_codecs(),
+ ElementsAre(AllOf(Field(&cricket::Codec::id, 97),
+ Field(&cricket::Codec::packetization,
+ cricket::kPacketizationParamRaw)),
+ AllOf(Field(&cricket::Codec::id, 96),
+ Field(&cricket::Codec::packetization, absl::nullopt))));
+ EXPECT_THAT(
+ media_send_channel1_impl()->send_codecs(),
+ ElementsAre(AllOf(Field(&cricket::Codec::id, 96),
+ Field(&cricket::Codec::packetization, absl::nullopt)),
+ AllOf(Field(&cricket::Codec::id, 97),
+ Field(&cricket::Codec::packetization,
+ cricket::kPacketizationParamRaw))));
+}
+
// VideoChannelDoubleThreadTest
TEST_F(VideoChannelDoubleThreadTest, TestInit) {
Base::TestInit();