From f93d2800d9b0d5818a5a383def0aaef3d441df3a Mon Sep 17 00:00:00 2001 From: Steve Anton Date: Mon, 27 Nov 2017 10:59:33 -0800 Subject: [PATCH] Add AddTransceiver and GetTransceivers to PeerConnection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebRTC 1.0 has added the transceiver API to PeerConnection. This is the first step towards exposing this to WebRTC consumers. For now, transceivers can be added and fetched but there is not yet support for creating offers/answers or setting local/remote descriptions. That support ("Unified Plan") will be added in follow-up CLs. The transceiver API is currently only available if the application opts in by specifying the kUnifiedPlan SDP semantics when creating the PeerConnection. Bug: webrtc:7600 Change-Id: I0b8ee24b489b45bb4c5f60b699bd20c61af01d8e Reviewed-on: https://webrtc-review.googlesource.com/23880 Commit-Queue: Steve Anton Reviewed-by: Peter Thatcher Reviewed-by: Henrik Boström Cr-Commit-Position: refs/heads/master@{#20896} --- api/peerconnectioninterface.h | 61 +++++++++ api/peerconnectionproxy.h | 16 +++ api/rtptransceiverinterface.h | 17 +++ pc/peerconnection.cc | 114 ++++++++++++++++ pc/peerconnection.h | 18 +++ pc/peerconnection_rtp_unittest.cc | 214 ++++++++++++++++++++++++++---- pc/peerconnectionwrapper.cc | 58 ++++++-- pc/peerconnectionwrapper.h | 22 +++ pc/rtpreceiver.cc | 6 +- pc/rtpreceiver.h | 3 +- 10 files changed, 487 insertions(+), 42 deletions(-) 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