Implement legacy offer_to_receive options for Unified Plan

This implements the WebRTC specification for handling
the legacy offer options offer_to_receive_audio and
offer_to_receive_video. They are not implemented for CreateAnswer.

With Unified Plan semantics, clients should switch to the
RtpTransceiver API for ensuring the correct media sections are
offered.

Bug: webrtc:7600
Change-Id: I6ced00b86b165a352bd0ca3d64b48fadcfd12235
Reviewed-on: https://webrtc-review.googlesource.com/41341
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21784}
This commit is contained in:
Steve Anton
2018-01-25 13:58:07 -08:00
committed by Commit Bot
parent 1204448a68
commit 22da89f502
8 changed files with 222 additions and 61 deletions

View File

@ -1259,7 +1259,8 @@ RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>
PeerConnection::AddTransceiver( PeerConnection::AddTransceiver(
cricket::MediaType media_type, cricket::MediaType media_type,
rtc::scoped_refptr<MediaStreamTrackInterface> track, rtc::scoped_refptr<MediaStreamTrackInterface> track,
const RtpTransceiverInit& init) { const RtpTransceiverInit& init,
bool fire_callback) {
RTC_DCHECK((media_type == cricket::MEDIA_TYPE_AUDIO || RTC_DCHECK((media_type == cricket::MEDIA_TYPE_AUDIO ||
media_type == cricket::MEDIA_TYPE_VIDEO)); media_type == cricket::MEDIA_TYPE_VIDEO));
if (track) { if (track) {
@ -1285,7 +1286,9 @@ PeerConnection::AddTransceiver(
auto transceiver = CreateAndAddTransceiver(sender, receiver); auto transceiver = CreateAndAddTransceiver(sender, receiver);
transceiver->internal()->set_direction(init.direction); transceiver->internal()->set_direction(init.direction);
if (fire_callback) {
observer_->OnRenegotiationNeeded(); observer_->OnRenegotiationNeeded();
}
return rtc::scoped_refptr<RtpTransceiverInterface>(transceiver); return rtc::scoped_refptr<RtpTransceiverInterface>(transceiver);
} }
@ -1565,11 +1568,80 @@ void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
return; return;
} }
// Legacy handling for offer_to_receive_audio and offer_to_receive_video.
// Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions".
if (IsUnifiedPlan()) {
RTCError error = HandleLegacyOfferOptions(options);
if (!error.ok()) {
PostCreateSessionDescriptionFailure(observer, error.message());
return;
}
}
cricket::MediaSessionOptions session_options; cricket::MediaSessionOptions session_options;
GetOptionsForOffer(options, &session_options); GetOptionsForOffer(options, &session_options);
webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); webrtc_session_desc_factory_->CreateOffer(observer, options, session_options);
} }
RTCError PeerConnection::HandleLegacyOfferOptions(
const RTCOfferAnswerOptions& options) {
RTC_DCHECK(IsUnifiedPlan());
if (options.offer_to_receive_audio == 0) {
RemoveRecvDirectionFromReceivingTransceiversOfType(
cricket::MEDIA_TYPE_AUDIO);
} else if (options.offer_to_receive_audio == 1) {
AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO);
} else if (options.offer_to_receive_audio > 1) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
"offer_to_receive_audio > 1 is not supported.");
}
if (options.offer_to_receive_video == 0) {
RemoveRecvDirectionFromReceivingTransceiversOfType(
cricket::MEDIA_TYPE_VIDEO);
} else if (options.offer_to_receive_video == 1) {
AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO);
} else if (options.offer_to_receive_video > 1) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER,
"offer_to_receive_video > 1 is not supported.");
}
return RTCError::OK();
}
void PeerConnection::RemoveRecvDirectionFromReceivingTransceiversOfType(
cricket::MediaType media_type) {
for (auto transceiver : GetReceivingTransceiversOfType(media_type)) {
transceiver->internal()->set_direction(
RtpTransceiverDirectionWithRecvSet(transceiver->direction(), false));
}
}
void PeerConnection::AddUpToOneReceivingTransceiverOfType(
cricket::MediaType media_type) {
if (GetReceivingTransceiversOfType(media_type).empty()) {
RtpTransceiverInit init;
init.direction = RtpTransceiverDirection::kRecvOnly;
AddTransceiver(media_type, nullptr, init, /*fire_callback=*/false);
}
}
std::vector<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
PeerConnection::GetReceivingTransceiversOfType(cricket::MediaType media_type) {
std::vector<
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
receiving_transceivers;
for (auto transceiver : transceivers_) {
if (!transceiver->stopped() &&
transceiver->internal()->media_type() == media_type &&
RtpTransceiverDirectionHasRecv(transceiver->direction())) {
receiving_transceivers.push_back(transceiver);
}
}
return receiving_transceivers;
}
void PeerConnection::CreateAnswer( void PeerConnection::CreateAnswer(
CreateSessionDescriptionObserver* observer, CreateSessionDescriptionObserver* observer,
const MediaConstraintsInterface* constraints) { const MediaConstraintsInterface* constraints) {
@ -1615,6 +1687,19 @@ void PeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer,
return; return;
} }
if (IsUnifiedPlan()) {
if (options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) {
RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not "
"supported with Unified Plan semantics. Use the "
"RtpTransceiver API instead.";
}
if (options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) {
RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not "
"supported with Unified Plan semantics. Use the "
"RtpTransceiver API instead.";
}
}
cricket::MediaSessionOptions session_options; cricket::MediaSessionOptions session_options;
GetOptionsForAnswer(options, &session_options); GetOptionsForAnswer(options, &session_options);

View File

@ -354,10 +354,13 @@ class PeerConnection : public PeerConnectionInternal,
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>> rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
FindTransceiverBySender(rtc::scoped_refptr<RtpSenderInterface> sender); FindTransceiverBySender(rtc::scoped_refptr<RtpSenderInterface> sender);
// Internal implementation for AddTransceiver family of methods. If
// |fire_callback| is set, fires OnRenegotiationNeeded callback if successful.
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> AddTransceiver( RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> AddTransceiver(
cricket::MediaType media_type, cricket::MediaType media_type,
rtc::scoped_refptr<MediaStreamTrackInterface> track, rtc::scoped_refptr<MediaStreamTrackInterface> track,
const RtpTransceiverInit& init); const RtpTransceiverInit& init,
bool fire_callback = true);
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>
CreateSender(cricket::MediaType media_type, CreateSender(cricket::MediaType media_type,
@ -482,6 +485,14 @@ class PeerConnection : public PeerConnectionInternal,
offer_answer_options, offer_answer_options,
cricket::MediaSessionOptions* session_options); cricket::MediaSessionOptions* session_options);
RTCError HandleLegacyOfferOptions(const RTCOfferAnswerOptions& options);
void RemoveRecvDirectionFromReceivingTransceiversOfType(
cricket::MediaType media_type);
void AddUpToOneReceivingTransceiverOfType(cricket::MediaType media_type);
std::vector<
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
GetReceivingTransceiversOfType(cricket::MediaType media_type);
// Returns a MediaSessionOptions struct with options decided by // Returns a MediaSessionOptions struct with options decided by
// |constraints|, the local MediaStreams and DataChannels. // |constraints|, the local MediaStreams and DataChannels.
void GetOptionsForAnswer(const RTCOfferAnswerOptions& offer_answer_options, void GetOptionsForAnswer(const RTCOfferAnswerOptions& offer_answer_options,

View File

@ -361,40 +361,39 @@ TEST_P(PeerConnectionMediaTest, NewStreamInRemoteOfferAddsRecvStreams) {
// Test that a new stream in a subsequent answer causes a new send stream to be // Test that a new stream in a subsequent answer causes a new send stream to be
// created on the callee when added locally. // created on the callee when added locally.
TEST_P(PeerConnectionMediaTest, NewStreamInLocalAnswerAddsSendStreams) { TEST_P(PeerConnectionMediaTest, NewStreamInLocalAnswerAddsSendStreams) {
// TODO(bugs.webrtc.org/8765): Enable this test under Unified Plan once
// offer_to_receive_audio is implemented.
if (IsUnifiedPlan()) {
return;
}
auto caller = CreatePeerConnection(); auto caller = CreatePeerConnection();
auto callee = CreatePeerConnectionWithAudioVideo(); auto callee = CreatePeerConnectionWithAudioVideo();
RTCOfferAnswerOptions options; RTCOfferAnswerOptions offer_options;
options.offer_to_receive_audio = offer_options.offer_to_receive_audio =
RTCOfferAnswerOptions::kOfferToReceiveMediaTrue; RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
options.offer_to_receive_video = offer_options.offer_to_receive_video =
RTCOfferAnswerOptions::kOfferToReceiveMediaTrue; RTCOfferAnswerOptions::kOfferToReceiveMediaTrue;
RTCOfferAnswerOptions answer_options;
ASSERT_TRUE( ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get(), offer_options,
callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options))); answer_options));
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
// Add second set of tracks to the callee. // Add second set of tracks to the callee.
callee->AddAudioTrack("a2"); callee->AddAudioTrack("a2");
callee->AddVideoTrack("v2"); callee->AddVideoTrack("v2");
ASSERT_TRUE( ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get(), offer_options,
callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal(options))); answer_options));
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
auto callee_voice = callee->media_engine()->GetVoiceChannel(0); auto callee_voice = callee->media_engine()->GetVoiceChannel(0);
EXPECT_EQ(2u, callee_voice->send_streams().size()); ASSERT_TRUE(callee_voice);
auto callee_video = callee->media_engine()->GetVideoChannel(0); auto callee_video = callee->media_engine()->GetVideoChannel(0);
ASSERT_TRUE(callee_video);
if (IsUnifiedPlan()) {
EXPECT_EQ(1u, callee_voice->send_streams().size());
EXPECT_EQ(1u, callee_video->send_streams().size());
} else {
EXPECT_EQ(2u, callee_voice->send_streams().size());
EXPECT_EQ(2u, callee_video->send_streams().size()); EXPECT_EQ(2u, callee_video->send_streams().size());
} }
}
// A PeerConnection with no local streams and no explicit answer constraints // A PeerConnection with no local streams and no explicit answer constraints
// should not reject any offered media sections. // should not reject any offered media sections.
@ -438,11 +437,6 @@ class PeerConnectionMediaOfferDirectionTest
// Tests that the correct direction is set on the media description according // Tests that the correct direction is set on the media description according
// to the presence of a local media track and the offer_to_receive setting. // to the presence of a local media track and the offer_to_receive setting.
TEST_P(PeerConnectionMediaOfferDirectionTest, VerifyDirection) { TEST_P(PeerConnectionMediaOfferDirectionTest, VerifyDirection) {
// TODO(bugs.webrtc.org/8765): Enable this test under Unified Plan once
// offer_to_receive_audio is implemented.
if (IsUnifiedPlan()) {
return;
}
auto caller = CreatePeerConnection(); auto caller = CreatePeerConnection();
if (send_media_) { if (send_media_) {
caller->AddAudioTrack("a"); caller->AddAudioTrack("a");
@ -496,11 +490,13 @@ class PeerConnectionMediaAnswerDirectionTest
// in the offer, the presence of a local media track on the receive side and the // in the offer, the presence of a local media track on the receive side and the
// offer_to_receive setting. // offer_to_receive setting.
TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyDirection) { TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyDirection) {
// TODO(bugs.webrtc.org/8765): Enable this test under Unified Plan once if (IsUnifiedPlan() &&
// offer_to_receive_audio is implemented. offer_to_receive_ != RTCOfferAnswerOptions::kUndefined) {
if (IsUnifiedPlan()) { // offer_to_receive_ is not implemented when creating answers with Unified
// Plan semantics specified.
return; return;
} }
auto caller = CreatePeerConnection(); auto caller = CreatePeerConnection();
caller->AddAudioTrack("a"); caller->AddAudioTrack("a");
@ -544,11 +540,13 @@ TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyDirection) {
// local media track and has set offer_to_receive to 0, no matter which // local media track and has set offer_to_receive to 0, no matter which
// direction the caller indicated in the offer. // direction the caller indicated in the offer.
TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyRejected) { TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyRejected) {
// TODO(bugs.webrtc.org/8765): Enable this test under Unified Plan once if (IsUnifiedPlan() &&
// offer_to_receive_audio is implemented. offer_to_receive_ != RTCOfferAnswerOptions::kUndefined) {
if (IsUnifiedPlan()) { // offer_to_receive_ is not implemented when creating answers with Unified
// Plan semantics specified.
return; return;
} }
auto caller = CreatePeerConnection(); auto caller = CreatePeerConnection();
caller->AddAudioTrack("a"); caller->AddAudioTrack("a");
@ -587,12 +585,6 @@ INSTANTIATE_TEST_CASE_P(PeerConnectionMediaTest,
Values(-1, 0, 1))); Values(-1, 0, 1)));
TEST_P(PeerConnectionMediaTest, OfferHasDifferentDirectionForAudioVideo) { TEST_P(PeerConnectionMediaTest, OfferHasDifferentDirectionForAudioVideo) {
// TODO(bugs.webrtc.org/8765): Enable this test under Unified Plan once
// offer_to_receive_audio is implemented.
if (IsUnifiedPlan()) {
return;
}
auto caller = CreatePeerConnection(); auto caller = CreatePeerConnection();
caller->AddVideoTrack("v"); caller->AddVideoTrack("v");
@ -608,9 +600,9 @@ TEST_P(PeerConnectionMediaTest, OfferHasDifferentDirectionForAudioVideo) {
} }
TEST_P(PeerConnectionMediaTest, AnswerHasDifferentDirectionsForAudioVideo) { TEST_P(PeerConnectionMediaTest, AnswerHasDifferentDirectionsForAudioVideo) {
// TODO(bugs.webrtc.org/8765): Enable this test under Unified Plan once
// offer_to_receive_audio is implemented.
if (IsUnifiedPlan()) { if (IsUnifiedPlan()) {
// offer_to_receive_ is not implemented when creating answers with Unified
// Plan semantics specified.
return; return;
} }
@ -780,9 +772,9 @@ INSTANTIATE_TEST_CASE_P(
// a series of offer/answers where audio/video are both sent, then audio is // a series of offer/answers where audio/video are both sent, then audio is
// rejected, then both audio/video sent again. // rejected, then both audio/video sent again.
TEST_P(PeerConnectionMediaTest, TestAVOfferWithAudioOnlyAnswer) { TEST_P(PeerConnectionMediaTest, TestAVOfferWithAudioOnlyAnswer) {
// TODO(bugs.webrtc.org/8765): Enable this test under Unified Plan once
// offer_to_receive_audio is implemented.
if (IsUnifiedPlan()) { if (IsUnifiedPlan()) {
// offer_to_receive_ is not implemented when creating answers with Unified
// Plan semantics specified.
return; return;
} }
@ -844,9 +836,9 @@ TEST_P(PeerConnectionMediaTest, TestAVOfferWithAudioOnlyAnswer) {
// a series of offer/answers where audio/video are both sent, then video is // a series of offer/answers where audio/video are both sent, then video is
// rejected, then both audio/video sent again. // rejected, then both audio/video sent again.
TEST_P(PeerConnectionMediaTest, TestAVOfferWithVideoOnlyAnswer) { TEST_P(PeerConnectionMediaTest, TestAVOfferWithVideoOnlyAnswer) {
// TODO(bugs.webrtc.org/8765): Enable this test under Unified Plan once
// offer_to_receive_audio is implemented.
if (IsUnifiedPlan()) { if (IsUnifiedPlan()) {
// offer_to_receive_ is not implemented when creating answers with Unified
// Plan semantics specified.
return; return;
} }

View File

@ -24,6 +24,8 @@
namespace webrtc { namespace webrtc {
using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
namespace { namespace {
const uint32_t kDefaultTimeout = 10000U; const uint32_t kDefaultTimeout = 10000U;
} }
@ -57,7 +59,7 @@ MockPeerConnectionObserver* PeerConnectionWrapper::observer() {
std::unique_ptr<SessionDescriptionInterface> std::unique_ptr<SessionDescriptionInterface>
PeerConnectionWrapper::CreateOffer() { PeerConnectionWrapper::CreateOffer() {
return CreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions()); return CreateOffer(RTCOfferAnswerOptions());
} }
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateOffer( std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateOffer(
@ -72,8 +74,7 @@ std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateOffer(
std::unique_ptr<SessionDescriptionInterface> std::unique_ptr<SessionDescriptionInterface>
PeerConnectionWrapper::CreateOfferAndSetAsLocal() { PeerConnectionWrapper::CreateOfferAndSetAsLocal() {
return CreateOfferAndSetAsLocal( return CreateOfferAndSetAsLocal(RTCOfferAnswerOptions());
PeerConnectionInterface::RTCOfferAnswerOptions());
} }
std::unique_ptr<SessionDescriptionInterface> std::unique_ptr<SessionDescriptionInterface>
@ -89,7 +90,7 @@ PeerConnectionWrapper::CreateOfferAndSetAsLocal(
std::unique_ptr<SessionDescriptionInterface> std::unique_ptr<SessionDescriptionInterface>
PeerConnectionWrapper::CreateAnswer() { PeerConnectionWrapper::CreateAnswer() {
return CreateAnswer(PeerConnectionInterface::RTCOfferAnswerOptions()); return CreateAnswer(RTCOfferAnswerOptions());
} }
std::unique_ptr<SessionDescriptionInterface> std::unique_ptr<SessionDescriptionInterface>
@ -105,8 +106,7 @@ PeerConnectionWrapper::CreateAnswer(
std::unique_ptr<SessionDescriptionInterface> std::unique_ptr<SessionDescriptionInterface>
PeerConnectionWrapper::CreateAnswerAndSetAsLocal() { PeerConnectionWrapper::CreateAnswerAndSetAsLocal() {
return CreateAnswerAndSetAsLocal( return CreateAnswerAndSetAsLocal(RTCOfferAnswerOptions());
PeerConnectionInterface::RTCOfferAnswerOptions());
} }
std::unique_ptr<SessionDescriptionInterface> std::unique_ptr<SessionDescriptionInterface>
@ -181,12 +181,20 @@ bool PeerConnectionWrapper::SetSdp(
bool PeerConnectionWrapper::ExchangeOfferAnswerWith( bool PeerConnectionWrapper::ExchangeOfferAnswerWith(
PeerConnectionWrapper* answerer) { PeerConnectionWrapper* answerer) {
return ExchangeOfferAnswerWith(answerer, RTCOfferAnswerOptions(),
RTCOfferAnswerOptions());
}
bool PeerConnectionWrapper::ExchangeOfferAnswerWith(
PeerConnectionWrapper* answerer,
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_options,
const PeerConnectionInterface::RTCOfferAnswerOptions& answer_options) {
RTC_DCHECK(answerer); RTC_DCHECK(answerer);
if (answerer == this) { if (answerer == this) {
RTC_LOG(LS_ERROR) << "Cannot exchange offer/answer with ourself!"; RTC_LOG(LS_ERROR) << "Cannot exchange offer/answer with ourself!";
return false; return false;
} }
auto offer = CreateOffer(); auto offer = CreateOffer(offer_options);
EXPECT_TRUE(offer); EXPECT_TRUE(offer);
if (!offer) { if (!offer) {
return false; return false;
@ -202,7 +210,7 @@ bool PeerConnectionWrapper::ExchangeOfferAnswerWith(
if (!set_remote_offer) { if (!set_remote_offer) {
return false; return false;
} }
auto answer = answerer->CreateAnswer(); auto answer = answerer->CreateAnswer(answer_options);
EXPECT_TRUE(answer); EXPECT_TRUE(answer);
if (!answer) { if (!answer) {
return false; return false;

View File

@ -97,16 +97,20 @@ class PeerConnectionWrapper {
// generating the offer and the given PeerConnectionWrapper generating the // generating the offer and the given PeerConnectionWrapper generating the
// answer. // answer.
// Equivalent to: // Equivalent to:
// 1. this->CreateOffer() // 1. this->CreateOffer(offer_options)
// 2. this->SetLocalDescription(offer) // 2. this->SetLocalDescription(offer)
// 3. answerer->SetRemoteDescription(offer) // 3. answerer->SetRemoteDescription(offer)
// 4. answerer->CreateAnswer() // 4. answerer->CreateAnswer(answer_options)
// 5. answerer->SetLocalDescription(answer) // 5. answerer->SetLocalDescription(answer)
// 6. this->SetRemoteDescription(answer) // 6. this->SetRemoteDescription(answer)
// Returns true if all steps succeed, false otherwise. // Returns true if all steps succeed, false otherwise.
// Suggested usage: // Suggested usage:
// ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get())); // ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
bool ExchangeOfferAnswerWith(PeerConnectionWrapper* answerer); bool ExchangeOfferAnswerWith(PeerConnectionWrapper* answerer);
bool ExchangeOfferAnswerWith(
PeerConnectionWrapper* answerer,
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_options,
const PeerConnectionInterface::RTCOfferAnswerOptions& answer_options);
// The following are wrappers for the underlying PeerConnection's // The following are wrappers for the underlying PeerConnection's
// AddTransceiver method. They return the result of calling AddTransceiver // AddTransceiver method. They return the result of calling AddTransceiver

View File

@ -50,6 +50,20 @@ RtpTransceiverDirection RtpTransceiverDirectionReversed(
return direction; return direction;
} }
RtpTransceiverDirection RtpTransceiverDirectionWithSendSet(
RtpTransceiverDirection direction,
bool send) {
return RtpTransceiverDirectionFromSendRecv(
send, RtpTransceiverDirectionHasRecv(direction));
}
RtpTransceiverDirection RtpTransceiverDirectionWithRecvSet(
RtpTransceiverDirection direction,
bool recv) {
return RtpTransceiverDirectionFromSendRecv(
RtpTransceiverDirectionHasSend(direction), recv);
}
const char* RtpTransceiverDirectionToString(RtpTransceiverDirection direction) { const char* RtpTransceiverDirectionToString(RtpTransceiverDirection direction) {
switch (direction) { switch (direction) {
case RtpTransceiverDirection::kSendRecv: case RtpTransceiverDirection::kSendRecv:

View File

@ -31,6 +31,16 @@ bool RtpTransceiverDirectionHasRecv(RtpTransceiverDirection direction);
RtpTransceiverDirection RtpTransceiverDirectionReversed( RtpTransceiverDirection RtpTransceiverDirectionReversed(
RtpTransceiverDirection direction); RtpTransceiverDirection direction);
// Returns the RtpTransceiverDirection with its send component set to |send|.
RtpTransceiverDirection RtpTransceiverDirectionWithSendSet(
RtpTransceiverDirection direction,
bool send = true);
// Returns the RtpTransceiverDirection with its recv component set to |recv|.
RtpTransceiverDirection RtpTransceiverDirectionWithRecvSet(
RtpTransceiverDirection direction,
bool recv = true);
// Returns an unspecified string representation of the given direction. // Returns an unspecified string representation of the given direction.
const char* RtpTransceiverDirectionToString(RtpTransceiverDirection direction); const char* RtpTransceiverDirectionToString(RtpTransceiverDirection direction);

View File

@ -8,16 +8,24 @@
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
#include <tuple>
#include "pc/rtpmediautils.h" #include "pc/rtpmediautils.h"
#include "test/gtest.h" #include "test/gtest.h"
namespace webrtc { namespace webrtc {
using ::testing::Bool;
using ::testing::Combine;
using ::testing::Values; using ::testing::Values;
using ::testing::ValuesIn;
RtpTransceiverDirection kAllDirections[] = {
RtpTransceiverDirection::kSendRecv, RtpTransceiverDirection::kSendOnly,
RtpTransceiverDirection::kRecvOnly, RtpTransceiverDirection::kInactive};
class EnumerateAllDirectionsTest class EnumerateAllDirectionsTest
: public ::testing::Test, : public ::testing::TestWithParam<RtpTransceiverDirection> {};
public ::testing::WithParamInterface<RtpTransceiverDirection> {};
// Test that converting the direction to send/recv and back again results in the // Test that converting the direction to send/recv and back again results in the
// same direction. // same direction.
@ -51,9 +59,38 @@ TEST_P(EnumerateAllDirectionsTest, TestReversedIdentity) {
INSTANTIATE_TEST_CASE_P(RtpTransceiverDirectionTest, INSTANTIATE_TEST_CASE_P(RtpTransceiverDirectionTest,
EnumerateAllDirectionsTest, EnumerateAllDirectionsTest,
Values(RtpTransceiverDirection::kSendRecv, ValuesIn(kAllDirections));
RtpTransceiverDirection::kSendOnly,
RtpTransceiverDirection::kRecvOnly, class EnumerateAllDirectionsAndBool
RtpTransceiverDirection::kInactive)); : public ::testing::TestWithParam<
std::tuple<RtpTransceiverDirection, bool>> {};
TEST_P(EnumerateAllDirectionsAndBool, TestWithSendSet) {
RtpTransceiverDirection direction = std::get<0>(GetParam());
bool send = std::get<1>(GetParam());
RtpTransceiverDirection result =
RtpTransceiverDirectionWithSendSet(direction, send);
EXPECT_EQ(send, RtpTransceiverDirectionHasSend(result));
EXPECT_EQ(RtpTransceiverDirectionHasRecv(direction),
RtpTransceiverDirectionHasRecv(result));
}
TEST_P(EnumerateAllDirectionsAndBool, TestWithRecvSet) {
RtpTransceiverDirection direction = std::get<0>(GetParam());
bool recv = std::get<1>(GetParam());
RtpTransceiverDirection result =
RtpTransceiverDirectionWithRecvSet(direction, recv);
EXPECT_EQ(RtpTransceiverDirectionHasSend(direction),
RtpTransceiverDirectionHasSend(result));
EXPECT_EQ(recv, RtpTransceiverDirectionHasRecv(result));
}
INSTANTIATE_TEST_CASE_P(RtpTransceiverDirectionTest,
EnumerateAllDirectionsAndBool,
Combine(ValuesIn(kAllDirections), Bool()));
} // namespace webrtc } // namespace webrtc