From fc1acd23644af0a8d3cfadb1888f8c15c68b5642 Mon Sep 17 00:00:00 2001 From: Jonas Oreland Date: Fri, 24 Aug 2018 10:58:37 +0200 Subject: [PATCH] Add support for enabling simulcast in "Plan B" using MediaConstraints. BUG=webrtc:9655 Change-Id: Ieb5fe5d97b6d4381608a51593bca5423979d1b9f Reviewed-on: https://webrtc-review.googlesource.com/95481 Commit-Queue: Jonas Oreland Reviewed-by: Rasmus Brandt Reviewed-by: Seth Hampson Cr-Commit-Position: refs/heads/master@{#24424} --- api/mediaconstraintsinterface.cc | 10 ++++++ api/mediaconstraintsinterface.h | 5 +++ api/peerconnectioninterface.h | 3 ++ pc/peerconnection.cc | 12 ++++--- pc/peerconnection_media_unittest.cc | 50 +++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 4 deletions(-) diff --git a/api/mediaconstraintsinterface.cc b/api/mediaconstraintsinterface.cc index c8926063a0..80c447dc7c 100644 --- a/api/mediaconstraintsinterface.cc +++ b/api/mediaconstraintsinterface.cc @@ -146,6 +146,9 @@ const char MediaConstraintsInterface::kCpuOveruseDetection[] = "googCpuOveruseDetection"; const char MediaConstraintsInterface::kPayloadPadding[] = "googPayloadPadding"; +const char MediaConstraintsInterface::kNumSimulcastLayers[] = + "googNumSimulcastLayers"; + // Set |value| to the value associated with the first appearance of |key|, or // return false if |key| is not found. bool MediaConstraintsInterface::Constraints::FindFirst( @@ -301,6 +304,13 @@ bool CopyConstraintsIntoOfferAnswerOptions( offer_answer_options->ice_restart = value; } + int layers; + if (FindConstraint(constraints, + MediaConstraintsInterface::kNumSimulcastLayers, + &layers, &mandatory_constraints_satisfied)) { + offer_answer_options->num_simulcast_layers = layers; + } + return mandatory_constraints_satisfied == constraints->GetMandatory().size(); } diff --git a/api/mediaconstraintsinterface.h b/api/mediaconstraintsinterface.h index 3c85dfb293..6128e6a03b 100644 --- a/api/mediaconstraintsinterface.h +++ b/api/mediaconstraintsinterface.h @@ -119,6 +119,11 @@ class MediaConstraintsInterface { // stripped by Chrome before passed down to Libjingle. static const char kInternalConstraintPrefix[]; + // Specifies number of simulcast layers for all video tracks + // with a Plan B offer/answer + // (see RTCOfferAnswerOptions::num_simulcast_layers). + static const char kNumSimulcastLayers[]; + virtual ~MediaConstraintsInterface() = default; virtual const Constraints& GetMandatory() const = 0; diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h index 84a0501b1a..2b94ee8ad5 100644 --- a/api/peerconnectioninterface.h +++ b/api/peerconnectioninterface.h @@ -596,6 +596,9 @@ class PeerConnectionInterface : public rtc::RefCountInterface { // confused with RTCP mux (multiplexing RTP and RTCP together). bool use_rtp_mux = true; + // This will apply to all video tracks with a Plan B SDP offer/answer. + int num_simulcast_layers = 1; + RTCOfferAnswerOptions() = default; RTCOfferAnswerOptions(int offer_to_receive_video, diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 9155b7119a..7bc25706f0 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -173,7 +173,8 @@ void AddRtpSenderOptions( const std::vector>>& senders, cricket::MediaDescriptionOptions* audio_media_description_options, - cricket::MediaDescriptionOptions* video_media_description_options) { + cricket::MediaDescriptionOptions* video_media_description_options, + int num_sim_layers) { for (const auto& sender : senders) { if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) { if (audio_media_description_options) { @@ -184,7 +185,8 @@ void AddRtpSenderOptions( RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO); if (video_media_description_options) { video_media_description_options->AddVideoSender( - sender->id(), sender->internal()->stream_ids(), 1); + sender->id(), sender->internal()->stream_ids(), + num_sim_layers); } } } @@ -3686,7 +3688,8 @@ void PeerConnection::GetOptionsForPlanBOffer( : &session_options->media_description_options[*video_index]; AddRtpSenderOptions(GetSendersInternal(), audio_media_description_options, - video_media_description_options); + video_media_description_options, + offer_answer_options.num_simulcast_layers); } // Find a new MID that is not already in |used_mids|, then add it to |used_mids| @@ -3910,7 +3913,8 @@ void PeerConnection::GetOptionsForPlanBAnswer( : &session_options->media_description_options[*video_index]; AddRtpSenderOptions(GetSendersInternal(), audio_media_description_options, - video_media_description_options); + video_media_description_options, + offer_answer_options.num_simulcast_layers); } void PeerConnection::GetOptionsForUnifiedPlanAnswer( diff --git a/pc/peerconnection_media_unittest.cc b/pc/peerconnection_media_unittest.cc index cc7ae9afaf..6f4fe1e3f4 100644 --- a/pc/peerconnection_media_unittest.cc +++ b/pc/peerconnection_media_unittest.cc @@ -273,6 +273,56 @@ TEST_F(PeerConnectionMediaTestPlanB, EmptyRemoteOfferRemovesRecvStreams) { EXPECT_EQ(0u, callee_video->recv_streams().size()); } +// Test enabling of simulcast with Plan B semantics. +// This test creating an offer. +TEST_F(PeerConnectionMediaTestPlanB, SimulcastOffer) { + auto caller = CreatePeerConnection(); + auto caller_video_track = caller->AddVideoTrack("v"); + RTCOfferAnswerOptions options; + options.num_simulcast_layers = 3; + auto offer = caller->CreateOffer(options); + auto* description = cricket::GetFirstMediaContent( + offer->description(), + cricket::MEDIA_TYPE_VIDEO)->media_description(); + ASSERT_EQ(1u, description->streams().size()); + ASSERT_TRUE(description->streams()[0].get_ssrc_group("SIM")); + EXPECT_EQ(3u, description->streams()[0].get_ssrc_group("SIM")->ssrcs.size()); + + // Check that it actually creates simulcast aswell. + caller->SetLocalDescription(std::move(offer)); + auto senders = caller->pc()->GetSenders(); + ASSERT_EQ(1u, senders.size()); + EXPECT_EQ(cricket::MediaType::MEDIA_TYPE_VIDEO, senders[0]->media_type()); + EXPECT_EQ(3u, senders[0]->GetParameters().encodings.size()); +} + +// Test enabling of simulcast with Plan B semantics. +// This test creating an answer. +TEST_F(PeerConnectionMediaTestPlanB, SimulcastAnswer) { + auto caller = CreatePeerConnection(); + caller->AddVideoTrack("v0"); + auto offer = caller->CreateOffer(); + auto callee = CreatePeerConnection(); + auto callee_video_track = callee->AddVideoTrack("v1"); + ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer))); + RTCOfferAnswerOptions options; + options.num_simulcast_layers = 3; + auto answer = callee->CreateAnswer(options); + auto* description = cricket::GetFirstMediaContent( + answer->description(), + cricket::MEDIA_TYPE_VIDEO)->media_description(); + ASSERT_EQ(1u, description->streams().size()); + ASSERT_TRUE(description->streams()[0].get_ssrc_group("SIM")); + EXPECT_EQ(3u, description->streams()[0].get_ssrc_group("SIM")->ssrcs.size()); + + // Check that it actually creates simulcast aswell. + callee->SetLocalDescription(std::move(answer)); + auto senders = callee->pc()->GetSenders(); + ASSERT_EQ(1u, senders.size()); + EXPECT_EQ(cricket::MediaType::MEDIA_TYPE_VIDEO, senders[0]->media_type()); + EXPECT_EQ(3u, senders[0]->GetParameters().encodings.size()); +} + // Test that stopping the callee transceivers causes the media channels to be // destroyed on the callee after calling SetLocalDescription on the local // answer.