diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h index a7b4ef641f..7c8f2f9b93 100644 --- a/api/peerconnectioninterface.h +++ b/api/peerconnectioninterface.h @@ -82,6 +82,7 @@ #include "api/rtceventlogoutput.h" #include "api/rtpreceiverinterface.h" #include "api/rtpsenderinterface.h" +#include "api/rtptransceiverinterface.h" #include "api/setremotedescriptionobserverinterface.h" #include "api/stats/rtcstatscollectorcallback.h" #include "api/statstypes.h" @@ -601,6 +602,57 @@ class PeerConnectionInterface : public rtc::RefCountInterface { // Returns true on success. virtual bool RemoveTrack(RtpSenderInterface* sender) = 0; + // AddTransceiver creates a new RtpTransceiver and adds it to the set of + // transceivers. Adding a transceiver will cause future calls to CreateOffer + // to add a media description for the corresponding transceiver. + // + // The initial value of |mid| in the returned transceiver is null. Setting a + // new session description may change it to a non-null value. + // + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver + // + // Optionally, an RtpTransceiverInit structure can be specified to configure + // the transceiver from construction. If not specified, the transceiver will + // default to having a direction of kSendRecv and not be part of any streams. + // + // These methods are only available when Unified Plan is enabled (see + // RTCConfiguration). + // + // Common errors: + // - INTERNAL_ERROR: The configuration does not have Unified Plan enabled. + // TODO(steveanton): Make these pure virtual once downstream projects have + // updated. + + // Adds a transceiver with a sender set to transmit the given track. The kind + // of the transceiver (and sender/receiver) will be derived from the kind of + // the track. + // Errors: + // - INVALID_PARAMETER: |track| is null. + virtual RTCErrorOr> + AddTransceiver(rtc::scoped_refptr track) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, "not implemented"); + } + virtual RTCErrorOr> + AddTransceiver(rtc::scoped_refptr track, + const RtpTransceiverInit& init) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, "not implemented"); + } + + // Adds a transceiver with the given kind. Can either be MEDIA_TYPE_AUDIO or + // MEDIA_TYPE_VIDEO. + // Errors: + // - INVALID_PARAMETER: |media_type| is not MEDIA_TYPE_AUDIO or + // MEDIA_TYPE_VIDEO. + virtual RTCErrorOr> + AddTransceiver(cricket::MediaType media_type) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, "not implemented"); + } + virtual RTCErrorOr> + AddTransceiver(cricket::MediaType media_type, + const RtpTransceiverInit& init) { + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, "not implemented"); + } + // Returns pointer to a DtmfSender on success. Otherwise returns null. // // This API is no longer part of the standard; instead DtmfSenders are @@ -650,6 +702,15 @@ class PeerConnectionInterface : public rtc::RefCountInterface { return std::vector>(); } + // Get all RtpTransceivers, created either through AddTransceiver, AddTrack or + // by a remote description applied with SetRemoteDescription. + // Note: This method is only available when Unified Plan is enabled (see + // RTCConfiguration). + virtual std::vector> + GetTransceivers() const { + return {}; + } + virtual bool GetStats(StatsObserver* observer, MediaStreamTrackInterface* track, StatsOutputLevel level) = 0; diff --git a/api/peerconnectionproxy.h b/api/peerconnectionproxy.h index bdd9fcb16d..1d8cf5faaa 100644 --- a/api/peerconnectionproxy.h +++ b/api/peerconnectionproxy.h @@ -33,6 +33,20 @@ BEGIN_SIGNALING_PROXY_MAP(PeerConnection) MediaStreamTrackInterface*, std::vector) PROXY_METHOD1(bool, RemoveTrack, RtpSenderInterface*) + PROXY_METHOD1(RTCErrorOr>, + AddTransceiver, + rtc::scoped_refptr) + PROXY_METHOD2(RTCErrorOr>, + AddTransceiver, + rtc::scoped_refptr, + const RtpTransceiverInit&) + PROXY_METHOD1(RTCErrorOr>, + AddTransceiver, + cricket::MediaType) + PROXY_METHOD2(RTCErrorOr>, + AddTransceiver, + cricket::MediaType, + const RtpTransceiverInit&) PROXY_METHOD1(rtc::scoped_refptr, CreateDtmfSender, AudioTrackInterface*) @@ -44,6 +58,8 @@ BEGIN_SIGNALING_PROXY_MAP(PeerConnection) GetSenders) PROXY_CONSTMETHOD0(std::vector>, GetReceivers) + PROXY_CONSTMETHOD0(std::vector>, + GetTransceivers) PROXY_METHOD3(bool, GetStats, StatsObserver*, diff --git a/api/rtptransceiverinterface.h b/api/rtptransceiverinterface.h index 3eb246a0cf..88607b2ed4 100644 --- a/api/rtptransceiverinterface.h +++ b/api/rtptransceiverinterface.h @@ -12,6 +12,7 @@ #define API_RTPTRANSCEIVERINTERFACE_H_ #include +#include #include "api/optional.h" #include "api/rtpreceiverinterface.h" @@ -20,6 +21,7 @@ namespace webrtc { +// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverdirection enum class RtpTransceiverDirection { kSendRecv, kSendOnly, @@ -27,6 +29,21 @@ enum class RtpTransceiverDirection { kInactive }; +// Structure for initializing an RtpTransceiver in a call to +// PeerConnectionInterface::AddTransceiver. +// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverinit +struct RtpTransceiverInit final { + // Direction of the RtpTransceiver. See RtpTransceiverInterface::direction(). + RtpTransceiverDirection direction = RtpTransceiverDirection::kSendRecv; + + // The added RtpTransceiver will be added to these streams. + // TODO(bugs.webrtc.org/7600): Not implemented. + std::vector> streams; + + // TODO(bugs.webrtc.org/7600): Not implemented. + std::vector send_encodings; +}; + // The RtpTransceiverInterface maps to the RTCRtpTransceiver defined by the // WebRTC specification. A transceiver represents a combination of an RtpSender // and an RtpReceiver than share a common mid. As defined in JSEP, an diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 65eb603720..2c6cc4b97d 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -1205,6 +1205,110 @@ bool PeerConnection::RemoveTrack(RtpSenderInterface* sender) { return true; } +RTCErrorOr> +PeerConnection::AddTransceiver( + rtc::scoped_refptr track) { + return AddTransceiver(track, RtpTransceiverInit()); +} + +RTCErrorOr> +PeerConnection::AddTransceiver( + rtc::scoped_refptr track, + const RtpTransceiverInit& init) { + if (!IsUnifiedPlan()) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INTERNAL_ERROR, + "AddTransceiver only supported when Unified Plan is enabled."); + } + if (!track) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "track is null"); + } + cricket::MediaType media_type; + if (track->kind() == MediaStreamTrackInterface::kAudioKind) { + media_type = cricket::MEDIA_TYPE_AUDIO; + } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) { + media_type = cricket::MEDIA_TYPE_VIDEO; + } else { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "Track kind is not audio or video"); + } + return AddTransceiver(media_type, track, init); +} + +RTCErrorOr> +PeerConnection::AddTransceiver(cricket::MediaType media_type) { + return AddTransceiver(media_type, RtpTransceiverInit()); +} + +RTCErrorOr> +PeerConnection::AddTransceiver(cricket::MediaType media_type, + const RtpTransceiverInit& init) { + if (!IsUnifiedPlan()) { + LOG_AND_RETURN_ERROR( + RTCErrorType::INTERNAL_ERROR, + "AddTransceiver only supported when Unified Plan is enabled."); + } + if (!(media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO)) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, + "media type is not audio or video"); + } + return AddTransceiver(media_type, nullptr, init); +} + +RTCErrorOr> +PeerConnection::AddTransceiver( + cricket::MediaType media_type, + rtc::scoped_refptr track, + const RtpTransceiverInit& init) { + RTC_DCHECK((media_type == cricket::MEDIA_TYPE_AUDIO || + media_type == cricket::MEDIA_TYPE_VIDEO)); + if (track) { + RTC_DCHECK_EQ(media_type, + (track->kind() == MediaStreamTrackInterface::kAudioKind + ? cricket::MEDIA_TYPE_AUDIO + : cricket::MEDIA_TYPE_VIDEO)); + } + + // TODO(bugs.webrtc.org/7600): Verify init. + + rtc::scoped_refptr> sender; + rtc::scoped_refptr> + receiver; + std::string receiver_id = rtc::CreateRandomUuid(); + if (media_type == cricket::MEDIA_TYPE_AUDIO) { + sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), new AudioRtpSender(nullptr, stats_.get())); + receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), new AudioRtpReceiver(receiver_id, {}, 0, nullptr)); + } else { + RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, media_type); + sender = RtpSenderProxyWithInternal::Create( + signaling_thread(), new VideoRtpSender(nullptr)); + receiver = RtpReceiverProxyWithInternal::Create( + signaling_thread(), + new VideoRtpReceiver(receiver_id, {}, worker_thread(), 0, nullptr)); + } + // TODO(bugs.webrtc.org/7600): Initializing the sender/receiver with a null + // channel prevents users from calling SetParameters on them, which is needed + // to be in compliance with the spec. + + if (track) { + sender->SetTrack(track); + } + + rtc::scoped_refptr> + transceiver = RtpTransceiverProxyWithInternal::Create( + signaling_thread(), new RtpTransceiver(sender, receiver)); + transceiver->SetDirection(init.direction); + + transceivers_.push_back(transceiver); + + observer_->OnRenegotiationNeeded(); + + return rtc::scoped_refptr(transceiver); +} + rtc::scoped_refptr PeerConnection::CreateDtmfSender( AudioTrackInterface* track) { TRACE_EVENT0("webrtc", "PeerConnection::CreateDtmfSender"); @@ -1297,6 +1401,16 @@ PeerConnection::GetReceiversInternal() const { return all_receivers; } +std::vector> +PeerConnection::GetTransceivers() const { + RTC_DCHECK(IsUnifiedPlan()); + std::vector> all_transceivers; + for (auto transceiver : transceivers_) { + all_transceivers.push_back(transceiver); + } + return all_transceivers; +} + bool PeerConnection::GetStats(StatsObserver* observer, MediaStreamTrackInterface* track, StatsOutputLevel level) { diff --git a/pc/peerconnection.h b/pc/peerconnection.h index 2bbce48c03..d262edecdd 100644 --- a/pc/peerconnection.h +++ b/pc/peerconnection.h @@ -94,6 +94,17 @@ class PeerConnection : public PeerConnectionInterface, std::vector streams) override; bool RemoveTrack(RtpSenderInterface* sender) override; + RTCErrorOr> AddTransceiver( + rtc::scoped_refptr track) override; + RTCErrorOr> AddTransceiver( + rtc::scoped_refptr track, + const RtpTransceiverInit& init) override; + RTCErrorOr> AddTransceiver( + cricket::MediaType media_type) override; + RTCErrorOr> AddTransceiver( + cricket::MediaType media_type, + const RtpTransceiverInit& init) override; + // Gets the DTLS SSL certificate associated with the audio transport on the // remote side. This will become populated once the DTLS connection with the // peer has been completed, as indicated by the ICE connection state @@ -114,6 +125,8 @@ class PeerConnection : public PeerConnectionInterface, const override; std::vector> GetReceivers() const override; + std::vector> GetTransceivers() + const override; rtc::scoped_refptr CreateDataChannel( const std::string& label, @@ -327,6 +340,11 @@ class PeerConnection : public PeerConnectionInterface, void RemoveVideoTrack(VideoTrackInterface* track, MediaStreamInterface* stream); + RTCErrorOr> AddTransceiver( + cricket::MediaType media_type, + rtc::scoped_refptr track, + const RtpTransceiverInit& init); + void SetIceConnectionState(IceConnectionState new_state); // Called any time the IceGatheringState changes void OnIceGatheringChange(IceGatheringState new_state); diff --git a/pc/peerconnection_rtp_unittest.cc b/pc/peerconnection_rtp_unittest.cc index 0421124b83..e7ac8c7f61 100644 --- a/pc/peerconnection_rtp_unittest.cc +++ b/pc/peerconnection_rtp_unittest.cc @@ -27,11 +27,16 @@ #include "rtc_base/refcountedobject.h" #include "rtc_base/scoped_ref_ptr.h" #include "rtc_base/thread.h" +#include "test/gmock.h" // This file contains tests for RTP Media API-related behavior of // |webrtc::PeerConnection|, see https://w3c.github.io/webrtc-pc/#rtp-media-api. -namespace { +namespace webrtc { + +using RTCConfiguration = PeerConnectionInterface::RTCConfiguration; +using ::testing::ElementsAre; +using ::testing::UnorderedElementsAre; const uint32_t kDefaultTimeout = 10000u; @@ -55,28 +60,37 @@ class OnSuccessObserver : public rtc::RefCountedObject< class PeerConnectionRtpTest : public testing::Test { public: PeerConnectionRtpTest() - : pc_factory_(webrtc::CreatePeerConnectionFactory( - rtc::Thread::Current(), - rtc::Thread::Current(), - rtc::Thread::Current(), - FakeAudioCaptureModule::Create(), - webrtc::CreateBuiltinAudioEncoderFactory(), - webrtc::CreateBuiltinAudioDecoderFactory(), - nullptr, - nullptr)) {} + : pc_factory_( + CreatePeerConnectionFactory(rtc::Thread::Current(), + rtc::Thread::Current(), + rtc::Thread::Current(), + FakeAudioCaptureModule::Create(), + CreateBuiltinAudioEncoderFactory(), + CreateBuiltinAudioDecoderFactory(), + nullptr, + nullptr)) {} - std::unique_ptr CreatePeerConnection() { - webrtc::PeerConnectionInterface::RTCConfiguration config; - auto observer = rtc::MakeUnique(); + std::unique_ptr CreatePeerConnection() { + return CreatePeerConnection(RTCConfiguration()); + } + + std::unique_ptr CreatePeerConnectionWithUnifiedPlan() { + RTCConfiguration config; + config.sdp_semantics = SdpSemantics::kUnifiedPlan; + return CreatePeerConnection(config); + } + + std::unique_ptr CreatePeerConnection( + const RTCConfiguration& config) { + auto observer = rtc::MakeUnique(); auto pc = pc_factory_->CreatePeerConnection(config, nullptr, nullptr, observer.get()); - return std::unique_ptr( - new webrtc::PeerConnectionWrapper(pc_factory_, pc, - std::move(observer))); + return rtc::MakeUnique(pc_factory_, pc, + std::move(observer)); } protected: - rtc::scoped_refptr pc_factory_; + rtc::scoped_refptr pc_factory_; }; // These tests cover |webrtc::PeerConnectionObserver| callbacks firing upon @@ -87,7 +101,7 @@ TEST_F(PeerConnectionRtpCallbacksTest, AddTrackWithoutStreamFiresOnAddTrack) { auto caller = CreatePeerConnection(); auto callee = CreatePeerConnection(); - rtc::scoped_refptr audio_track( + rtc::scoped_refptr audio_track( pc_factory_->CreateAudioTrack("audio_track", nullptr)); EXPECT_TRUE(caller->pc()->AddTrack(audio_track.get(), {})); ASSERT_TRUE( @@ -107,9 +121,9 @@ TEST_F(PeerConnectionRtpCallbacksTest, AddTrackWithStreamFiresOnAddTrack) { auto caller = CreatePeerConnection(); auto callee = CreatePeerConnection(); - rtc::scoped_refptr audio_track( + rtc::scoped_refptr audio_track( pc_factory_->CreateAudioTrack("audio_track", nullptr)); - auto stream = webrtc::MediaStream::Create("audio_stream"); + auto stream = MediaStream::Create("audio_stream"); EXPECT_TRUE(caller->pc()->AddTrack(audio_track.get(), {stream.get()})); ASSERT_TRUE( callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(), @@ -128,7 +142,7 @@ TEST_F(PeerConnectionRtpCallbacksTest, auto caller = CreatePeerConnection(); auto callee = CreatePeerConnection(); - rtc::scoped_refptr audio_track( + rtc::scoped_refptr audio_track( pc_factory_->CreateAudioTrack("audio_track", nullptr)); auto sender = caller->pc()->AddTrack(audio_track.get(), {}); ASSERT_TRUE( @@ -150,9 +164,9 @@ TEST_F(PeerConnectionRtpCallbacksTest, auto caller = CreatePeerConnection(); auto callee = CreatePeerConnection(); - rtc::scoped_refptr audio_track( + rtc::scoped_refptr audio_track( pc_factory_->CreateAudioTrack("audio_track", nullptr)); - auto stream = webrtc::MediaStream::Create("audio_stream"); + auto stream = MediaStream::Create("audio_stream"); auto sender = caller->pc()->AddTrack(audio_track.get(), {stream.get()}); ASSERT_TRUE( callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(), @@ -173,12 +187,12 @@ TEST_F(PeerConnectionRtpCallbacksTest, auto caller = CreatePeerConnection(); auto callee = CreatePeerConnection(); - rtc::scoped_refptr audio_track1( + rtc::scoped_refptr audio_track1( pc_factory_->CreateAudioTrack("audio_track1", nullptr)); - rtc::scoped_refptr audio_track2( + rtc::scoped_refptr audio_track2( pc_factory_->CreateAudioTrack("audio_track2", nullptr)); - auto stream = webrtc::MediaStream::Create("shared_audio_stream"); - std::vector streams{stream.get()}; + auto stream = MediaStream::Create("shared_audio_stream"); + std::vector streams{stream.get()}; auto sender1 = caller->pc()->AddTrack(audio_track1.get(), streams); auto sender2 = caller->pc()->AddTrack(audio_track2.get(), streams); ASSERT_TRUE( @@ -194,7 +208,7 @@ TEST_F(PeerConnectionRtpCallbacksTest, static_cast(nullptr))); ASSERT_EQ(callee->observer()->add_track_events_.size(), 2u); EXPECT_EQ( - std::vector>{ + std::vector>{ callee->observer()->add_track_events_[0].receiver}, callee->observer()->remove_track_events_); @@ -438,4 +452,146 @@ TEST_F(PeerConnectionRtpLegacyObserverTest, EXPECT_FALSE(observer->called()); } -} // namespace +// RtpTransceiver Tests + +// Test that by default there are no transceivers with Unified Plan. +TEST_F(PeerConnectionRtpTest, PeerConnectionHasNoTransceivers) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + EXPECT_THAT(caller->pc()->GetTransceivers(), ElementsAre()); +} + +// Test that a transceiver created with the audio kind has the correct initial +// properties. +TEST_F(PeerConnectionRtpTest, AddTransceiverHasCorrectInitProperties) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + + auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + EXPECT_EQ(rtc::nullopt, transceiver->mid()); + EXPECT_FALSE(transceiver->stopped()); + EXPECT_EQ(RtpTransceiverDirection::kSendRecv, transceiver->direction()); + EXPECT_EQ(rtc::nullopt, transceiver->current_direction()); +} + +// Test that adding a transceiver with the audio kind creates an audio sender +// and audio receiver with the receiver having a live audio track. +TEST_F(PeerConnectionRtpTest, + AddAudioTransceiverCreatesAudioSenderAndReceiver) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + + auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + + ASSERT_TRUE(transceiver->sender()); + EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, transceiver->sender()->media_type()); + + ASSERT_TRUE(transceiver->receiver()); + EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, transceiver->receiver()->media_type()); + + auto track = transceiver->receiver()->track(); + ASSERT_TRUE(track); + EXPECT_EQ(MediaStreamTrackInterface::kAudioKind, track->kind()); + EXPECT_EQ(MediaStreamTrackInterface::TrackState::kLive, track->state()); +} + +// Test that adding a transceiver with the video kind creates an video sender +// and video receiver with the receiver having a live video track. +TEST_F(PeerConnectionRtpTest, + AddAudioTransceiverCreatesVideoSenderAndReceiver) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + + auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO); + + ASSERT_TRUE(transceiver->sender()); + EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, transceiver->sender()->media_type()); + + ASSERT_TRUE(transceiver->receiver()); + EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, transceiver->receiver()->media_type()); + + auto track = transceiver->receiver()->track(); + ASSERT_TRUE(track); + EXPECT_EQ(MediaStreamTrackInterface::kVideoKind, track->kind()); + EXPECT_EQ(MediaStreamTrackInterface::TrackState::kLive, track->state()); +} + +// Test that after a call to AddTransceiver, the transceiver shows in +// GetTransceivers(), the transceiver's sender shows in GetSenders(), and the +// transceiver's receiver shows in GetReceivers(). +TEST_F(PeerConnectionRtpTest, AddTransceiverShowsInLists) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + + auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + EXPECT_EQ( + std::vector>{transceiver}, + caller->pc()->GetTransceivers()); + EXPECT_EQ( + std::vector>{ + transceiver->sender()}, + caller->pc()->GetSenders()); + EXPECT_EQ( + std::vector>{ + transceiver->receiver()}, + caller->pc()->GetReceivers()); +} + +// Test that the direction passed in through the AddTransceiver init parameter +// is set in the returned transceiver. +TEST_F(PeerConnectionRtpTest, AddTransceiverWithDirectionIsReflected) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + + RtpTransceiverInit init; + init.direction = RtpTransceiverDirection::kSendOnly; + auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init); + EXPECT_EQ(RtpTransceiverDirection::kSendOnly, transceiver->direction()); +} + +TEST_F(PeerConnectionRtpTest, AddTransceiverWithInvalidKindReturnsError) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + + auto result = caller->pc()->AddTransceiver(cricket::MEDIA_TYPE_DATA); + EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); +} + +// Test that calling AddTransceiver with a track creates a transceiver which has +// its sender's track set to the passed-in track. +TEST_F(PeerConnectionRtpTest, AddTransceiverWithTrackCreatesSenderWithTrack) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + + auto audio_track = caller->CreateAudioTrack("audio track"); + auto transceiver = caller->AddTransceiver(audio_track); + + auto sender = transceiver->sender(); + ASSERT_TRUE(sender->track()); + EXPECT_EQ(audio_track, sender->track()); + + auto receiver = transceiver->receiver(); + ASSERT_TRUE(receiver->track()); + EXPECT_EQ(MediaStreamTrackInterface::kAudioKind, receiver->track()->kind()); + EXPECT_EQ(MediaStreamTrackInterface::TrackState::kLive, + receiver->track()->state()); +} + +// Test that calling AddTransceiver twice with the same track creates distinct +// transceivers, senders with the same track. +TEST_F(PeerConnectionRtpTest, + AddTransceiverTwiceWithSameTrackCreatesMultipleTransceivers) { + auto caller = CreatePeerConnectionWithUnifiedPlan(); + + auto audio_track = caller->CreateAudioTrack("audio track"); + + auto transceiver1 = caller->AddTransceiver(audio_track); + auto transceiver2 = caller->AddTransceiver(audio_track); + + EXPECT_NE(transceiver1, transceiver2); + + auto sender1 = transceiver1->sender(); + auto sender2 = transceiver2->sender(); + EXPECT_NE(sender1, sender2); + EXPECT_EQ(audio_track, sender1->track()); + EXPECT_EQ(audio_track, sender2->track()); + + EXPECT_THAT(caller->pc()->GetTransceivers(), + UnorderedElementsAre(transceiver1, transceiver2)); + EXPECT_THAT(caller->pc()->GetSenders(), + UnorderedElementsAre(sender1, sender2)); +} + +} // namespace webrtc diff --git a/pc/peerconnectionwrapper.cc b/pc/peerconnectionwrapper.cc index 1b92efaac3..121cc64144 100644 --- a/pc/peerconnectionwrapper.cc +++ b/pc/peerconnectionwrapper.cc @@ -179,22 +179,64 @@ bool PeerConnectionWrapper::SetSdp( return observer->result(); } +rtc::scoped_refptr +PeerConnectionWrapper::AddTransceiver(cricket::MediaType media_type) { + RTCErrorOr> result = + pc()->AddTransceiver(media_type); + EXPECT_EQ(RTCErrorType::NONE, result.error().type()); + return result.MoveValue(); +} + +rtc::scoped_refptr +PeerConnectionWrapper::AddTransceiver(cricket::MediaType media_type, + const RtpTransceiverInit& init) { + RTCErrorOr> result = + pc()->AddTransceiver(media_type, init); + EXPECT_EQ(RTCErrorType::NONE, result.error().type()); + return result.MoveValue(); +} + +rtc::scoped_refptr +PeerConnectionWrapper::AddTransceiver( + rtc::scoped_refptr track) { + RTCErrorOr> result = + pc()->AddTransceiver(track); + EXPECT_EQ(RTCErrorType::NONE, result.error().type()); + return result.MoveValue(); +} + +rtc::scoped_refptr +PeerConnectionWrapper::AddTransceiver( + rtc::scoped_refptr track, + const RtpTransceiverInit& init) { + RTCErrorOr> result = + pc()->AddTransceiver(track, init); + EXPECT_EQ(RTCErrorType::NONE, result.error().type()); + return result.MoveValue(); +} + +rtc::scoped_refptr PeerConnectionWrapper::CreateAudioTrack( + const std::string& label) { + return pc_factory()->CreateAudioTrack(label, nullptr); +} + +rtc::scoped_refptr PeerConnectionWrapper::CreateVideoTrack( + const std::string& label) { + auto video_source = pc_factory()->CreateVideoSource( + rtc::MakeUnique()); + return pc_factory()->CreateVideoTrack(label, video_source); +} + rtc::scoped_refptr PeerConnectionWrapper::AddAudioTrack( const std::string& track_label, std::vector streams) { - auto media_stream_track = - pc_factory()->CreateAudioTrack(track_label, nullptr); - return pc()->AddTrack(media_stream_track, std::move(streams)); + return pc()->AddTrack(CreateAudioTrack(track_label), std::move(streams)); } rtc::scoped_refptr PeerConnectionWrapper::AddVideoTrack( const std::string& track_label, std::vector streams) { - auto video_source = pc_factory()->CreateVideoSource( - rtc::MakeUnique()); - auto media_stream_track = - pc_factory()->CreateVideoTrack(track_label, video_source); - return pc()->AddTrack(media_stream_track, std::move(streams)); + return pc()->AddTrack(CreateVideoTrack(track_label), std::move(streams)); } PeerConnectionInterface::SignalingState diff --git a/pc/peerconnectionwrapper.h b/pc/peerconnectionwrapper.h index 8918a712f1..e7d19eaa9b 100644 --- a/pc/peerconnectionwrapper.h +++ b/pc/peerconnectionwrapper.h @@ -93,6 +93,28 @@ class PeerConnectionWrapper { bool SetRemoteDescription(std::unique_ptr desc, RTCError* error_out); + // The following are wrappers for the underlying PeerConnection's + // AddTransceiver method. They return the result of calling AddTransceiver + // with the given arguments, DCHECKing if there is an error. + rtc::scoped_refptr AddTransceiver( + cricket::MediaType media_type); + rtc::scoped_refptr AddTransceiver( + cricket::MediaType media_type, + const RtpTransceiverInit& init); + rtc::scoped_refptr AddTransceiver( + rtc::scoped_refptr track); + rtc::scoped_refptr AddTransceiver( + rtc::scoped_refptr track, + const RtpTransceiverInit& init); + + // Returns a new dummy audio track with the given label. + rtc::scoped_refptr CreateAudioTrack( + const std::string& label); + + // Returns a new dummy video track with the given label. + rtc::scoped_refptr CreateVideoTrack( + const std::string& label); + // Calls the underlying PeerConnection's AddTrack method with an audio media // stream track not bound to any source. rtc::scoped_refptr AddAudioTrack( diff --git a/pc/rtpreceiver.cc b/pc/rtpreceiver.cc index da7e64213f..4968ac1d67 100644 --- a/pc/rtpreceiver.cc +++ b/pc/rtpreceiver.cc @@ -22,16 +22,16 @@ namespace webrtc { AudioRtpReceiver::AudioRtpReceiver( - const std::string& track_id, + const std::string& receiver_id, std::vector> streams, uint32_t ssrc, cricket::VoiceChannel* channel) - : id_(track_id), + : id_(receiver_id), ssrc_(ssrc), channel_(channel), track_(AudioTrackProxy::Create( rtc::Thread::Current(), - AudioTrack::Create(track_id, + AudioTrack::Create(receiver_id, RemoteAudioSource::Create(ssrc, channel)))), streams_(std::move(streams)), cached_track_enabled_(track_->enabled()) { diff --git a/pc/rtpreceiver.h b/pc/rtpreceiver.h index a1af13cead..6df4642718 100644 --- a/pc/rtpreceiver.h +++ b/pc/rtpreceiver.h @@ -50,11 +50,10 @@ class AudioRtpReceiver : public ObserverInterface, // TODO(deadbeef): Use rtc::Optional, or have another constructor that // doesn't take an SSRC, and make this one DCHECK(ssrc != 0). AudioRtpReceiver( - const std::string& track_id, + const std::string& receiver_id, std::vector> streams, uint32_t ssrc, cricket::VoiceChannel* channel); - virtual ~AudioRtpReceiver(); // ObserverInterface implementation