Validate SIM ssrc-group parameters

similar to what is done for FID and FEC-FR but SIM can have more than
one secondary SSRC.

BUG=chromium:1477075

Change-Id: I4c9b4feaa421f53e424fc17bfc9ee2c185c68fb0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/318520
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@microsoft.com>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40679}
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 3478e2f..0f87ec6 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -338,6 +338,21 @@
       return false;
     }
   }
+  for (const auto& group : sp.ssrc_groups) {
+    if (group.semantics != kSimSsrcGroupSemantics)
+      continue;
+    for (uint32_t group_ssrc : group.ssrcs) {
+      auto it = absl::c_find_if(sp.ssrcs, [&group_ssrc](uint32_t ssrc) {
+        return ssrc == group_ssrc;
+      });
+      if (it == sp.ssrcs.end()) {
+        RTC_LOG(LS_ERROR) << "SSRC '" << group_ssrc
+                          << "' missing from StreamParams ssrcs with semantics "
+                          << kSimSsrcGroupSemantics << ": " << sp.ToString();
+        return false;
+      }
+    }
+  }
   return true;
 }
 
diff --git a/pc/sdp_offer_answer_unittest.cc b/pc/sdp_offer_answer_unittest.cc
index c03c08f..202b353 100644
--- a/pc/sdp_offer_answer_unittest.cc
+++ b/pc/sdp_offer_answer_unittest.cc
@@ -808,4 +808,86 @@
   EXPECT_FALSE(pc->SetLocalDescription(std::move(offer)));
 }
 
+TEST_F(SdpOfferAnswerTest,
+       DuplicateSsrcsAcrossMlinesDisallowedInLocalDescriptionTwoSsrc) {
+  auto pc = CreatePeerConnection();
+
+  pc->AddAudioTrack("audio_track", {});
+  pc->AddVideoTrack("video_track", {});
+  auto offer = pc->CreateOffer();
+  auto& offer_contents = offer->description()->contents();
+  ASSERT_EQ(offer_contents.size(), 2u);
+  uint32_t audio_ssrc = offer_contents[0].media_description()->first_ssrc();
+  ASSERT_EQ(offer_contents[1].media_description()->streams().size(), 1u);
+  auto& video_stream = offer->description()
+                           ->contents()[1]
+                           .media_description()
+                           ->mutable_streams()[0];
+  ASSERT_EQ(video_stream.ssrcs.size(), 2u);
+  ASSERT_EQ(video_stream.ssrc_groups.size(), 1u);
+  video_stream.ssrcs[1] = audio_ssrc;
+  video_stream.ssrc_groups[0].ssrcs[1] = audio_ssrc;
+  video_stream.ssrc_groups[0].semantics = cricket::kSimSsrcGroupSemantics;
+  std::string sdp;
+  offer->ToString(&sdp);
+
+  // Trim the last two lines which contain ssrc-specific attributes
+  // that we change/munge above. Guarded with expectation about what
+  // should be removed in case the SDP generation changes.
+  size_t end = sdp.rfind("\r\n");
+  end = sdp.rfind("\r\n", end - 2);
+  end = sdp.rfind("\r\n", end - 2);
+  EXPECT_EQ(sdp.substr(end + 2), "a=ssrc:" + rtc::ToString(audio_ssrc) +
+                                     " cname:" + video_stream.cname +
+                                     "\r\n"
+                                     "a=ssrc:" +
+                                     rtc::ToString(audio_ssrc) +
+                                     " msid:- video_track\r\n");
+
+  auto modified_offer =
+      CreateSessionDescription(SdpType::kOffer, sdp.substr(0, end + 2));
+  EXPECT_FALSE(pc->SetLocalDescription(std::move(modified_offer)));
+}
+
+TEST_F(SdpOfferAnswerTest,
+       DuplicateSsrcsAcrossMlinesDisallowedInLocalDescriptionThreeSsrcs) {
+  auto pc = CreatePeerConnection();
+
+  pc->AddAudioTrack("audio_track", {});
+  pc->AddVideoTrack("video_track", {});
+  auto offer = pc->CreateOffer();
+  auto& offer_contents = offer->description()->contents();
+  ASSERT_EQ(offer_contents.size(), 2u);
+  uint32_t audio_ssrc = offer_contents[0].media_description()->first_ssrc();
+  ASSERT_EQ(offer_contents[1].media_description()->streams().size(), 1u);
+  auto& video_stream = offer->description()
+                           ->contents()[1]
+                           .media_description()
+                           ->mutable_streams()[0];
+  ASSERT_EQ(video_stream.ssrcs.size(), 2u);
+  ASSERT_EQ(video_stream.ssrc_groups.size(), 1u);
+  video_stream.ssrcs.push_back(audio_ssrc);
+  video_stream.ssrc_groups[0].ssrcs.push_back(audio_ssrc);
+  video_stream.ssrc_groups[0].semantics = cricket::kSimSsrcGroupSemantics;
+  std::string sdp;
+  offer->ToString(&sdp);
+
+  // Trim the last two lines which contain ssrc-specific attributes
+  // that we change/munge above. Guarded with expectation about what
+  // should be removed in case the SDP generation changes.
+  size_t end = sdp.rfind("\r\n");
+  end = sdp.rfind("\r\n", end - 2);
+  end = sdp.rfind("\r\n", end - 2);
+  EXPECT_EQ(sdp.substr(end + 2), "a=ssrc:" + rtc::ToString(audio_ssrc) +
+                                     " cname:" + video_stream.cname +
+                                     "\r\n"
+                                     "a=ssrc:" +
+                                     rtc::ToString(audio_ssrc) +
+                                     " msid:- video_track\r\n");
+
+  auto modified_offer =
+      CreateSessionDescription(SdpType::kOffer, sdp.substr(0, end + 2));
+  EXPECT_FALSE(pc->SetLocalDescription(std::move(modified_offer)));
+}
+
 }  // namespace webrtc