Rejected simulcast layers will no longer appear in GetParameters().
Added a layer in RtpSender that bridges the gap between the layers that the user sees and the layer that the media engine sees. Media engine still maintains the invariant that the number of layers cannot be changed, while RtpSender adds and removes layers between the user GetParameters and SetParameters calls and the media engine. Bug: webrtc:10251 Change-Id: I33839c1f9a9052cb6130253e5a582606f2cbe54a Reviewed-on: https://webrtc-review.googlesource.com/c/122641 Commit-Queue: Amit Hilbuch <amithi@webrtc.org> Reviewed-by: Steve Anton <steveanton@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26756}
This commit is contained in:
parent
4e7058e621
commit
2297d3311a
@ -30,4 +30,16 @@ rtc::scoped_refptr<DtlsTransportInterface> RtpSenderInterface::dtls_transport()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO(amithi): Fix downstream dependencies and make GetParameters pure
|
||||
// virtual.
|
||||
RtpParameters RtpSenderInterface::GetParameters() {
|
||||
const RtpSenderInterface* interface = this;
|
||||
return interface->GetParameters();
|
||||
}
|
||||
|
||||
RtpParameters RtpSenderInterface::GetParameters() const {
|
||||
RtpSenderInterface* interface = const_cast<RtpSenderInterface*>(this);
|
||||
return interface->GetParameters();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -67,7 +67,9 @@ class RtpSenderInterface : public rtc::RefCountInterface {
|
||||
// TODO(orphis): Make it pure virtual once Chrome has updated
|
||||
virtual std::vector<RtpEncodingParameters> init_send_encodings() const;
|
||||
|
||||
virtual RtpParameters GetParameters() = 0;
|
||||
// TODO(amithi): Fix downstream dependecies and remove the non-const method.
|
||||
virtual RtpParameters GetParameters();
|
||||
virtual RtpParameters GetParameters() const;
|
||||
// Note that only a subset of the parameters can currently be changed. See
|
||||
// rtpparameters.h
|
||||
// The encodings are in increasing quality order for simulcast.
|
||||
@ -104,7 +106,7 @@ PROXY_CONSTMETHOD0(cricket::MediaType, media_type)
|
||||
PROXY_CONSTMETHOD0(std::string, id)
|
||||
PROXY_CONSTMETHOD0(std::vector<std::string>, stream_ids)
|
||||
PROXY_CONSTMETHOD0(std::vector<RtpEncodingParameters>, init_send_encodings)
|
||||
PROXY_METHOD0(RtpParameters, GetParameters);
|
||||
PROXY_CONSTMETHOD0(RtpParameters, GetParameters);
|
||||
PROXY_METHOD1(RTCError, SetParameters, const RtpParameters&)
|
||||
PROXY_CONSTMETHOD0(rtc::scoped_refptr<DtmfSenderInterface>, GetDtmfSender);
|
||||
PROXY_METHOD1(void,
|
||||
|
||||
@ -28,7 +28,7 @@ class MockRtpSender : public rtc::RefCountedObject<RtpSenderInterface> {
|
||||
MOCK_CONST_METHOD0(id, std::string());
|
||||
MOCK_CONST_METHOD0(stream_ids, std::vector<std::string>());
|
||||
MOCK_CONST_METHOD0(init_send_encodings, std::vector<RtpEncodingParameters>());
|
||||
MOCK_METHOD0(GetParameters, RtpParameters());
|
||||
MOCK_CONST_METHOD0(GetParameters, RtpParameters());
|
||||
MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&));
|
||||
MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr<DtmfSenderInterface>());
|
||||
};
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "api/video/video_bitrate_allocation.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
@ -40,6 +41,13 @@ webrtc::RtpParameters CreateRtpParametersWithEncodings(StreamParams sp) {
|
||||
for (size_t i = 0; i < encodings.size(); ++i) {
|
||||
encodings[i].ssrc = primary_ssrcs[i];
|
||||
}
|
||||
|
||||
const std::vector<RidDescription>& rids = sp.rids();
|
||||
RTC_DCHECK(rids.size() == 0 || rids.size() == encoding_count);
|
||||
for (size_t i = 0; i < rids.size(); ++i) {
|
||||
encodings[i].rid = rids[i].rid;
|
||||
}
|
||||
|
||||
webrtc::RtpParameters parameters;
|
||||
parameters.encodings = encodings;
|
||||
parameters.rtcp.cname = sp.cname;
|
||||
@ -115,13 +123,21 @@ webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
|
||||
RTCErrorType::INVALID_MODIFICATION,
|
||||
"Attempted to set RtpParameters with modified header extensions");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
|
||||
if (rtp_parameters.encodings[i].ssrc !=
|
||||
old_rtp_parameters.encodings[i].ssrc) {
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
|
||||
"Attempted to set RtpParameters with modified SSRC");
|
||||
}
|
||||
if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings,
|
||||
[](const webrtc::RtpEncodingParameters& encoding1,
|
||||
const webrtc::RtpEncodingParameters& encoding2) {
|
||||
return encoding1.rid == encoding2.rid;
|
||||
})) {
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
|
||||
"Attempted to change RID values in the encodings.");
|
||||
}
|
||||
if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings,
|
||||
[](const webrtc::RtpEncodingParameters& encoding1,
|
||||
const webrtc::RtpEncodingParameters& encoding2) {
|
||||
return encoding1.ssrc == encoding2.ssrc;
|
||||
})) {
|
||||
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
|
||||
"Attempted to set RtpParameters with modified SSRC");
|
||||
}
|
||||
|
||||
return CheckRtpParametersValues(rtp_parameters);
|
||||
|
||||
@ -3004,9 +3004,10 @@ static std::vector<RtpEncodingParameters> GetSendEncodingsFromRemoteDescription(
|
||||
|
||||
static RTCError UpdateSimulcastLayerStatusInSender(
|
||||
const std::vector<SimulcastLayer>& layers,
|
||||
RtpSenderInterface* sender) {
|
||||
rtc::scoped_refptr<RtpSenderInternal> sender) {
|
||||
RTC_DCHECK(sender);
|
||||
RtpParameters parameters = sender->GetParameters();
|
||||
std::vector<std::string> disabled_layers;
|
||||
|
||||
// The simulcast envelope cannot be changed, only the status of the streams.
|
||||
// So we will iterate over the send encodings rather than the layers.
|
||||
@ -3016,29 +3017,36 @@ static RTCError UpdateSimulcastLayerStatusInSender(
|
||||
return layer.rid == encoding.rid;
|
||||
});
|
||||
// A layer that cannot be found may have been removed by the remote party.
|
||||
encoding.active = iter != layers.end() && !iter->is_paused;
|
||||
if (iter == layers.end()) {
|
||||
disabled_layers.push_back(encoding.rid);
|
||||
continue;
|
||||
}
|
||||
|
||||
encoding.active = !iter->is_paused;
|
||||
}
|
||||
|
||||
return sender->SetParameters(parameters);
|
||||
RTCError result = sender->SetParameters(parameters);
|
||||
if (result.ok()) {
|
||||
result = sender->DisableEncodingLayers(disabled_layers);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static RTCError DisableSimulcastInSender(RtpSenderInterface* sender) {
|
||||
static RTCError DisableSimulcastInSender(
|
||||
rtc::scoped_refptr<RtpSenderInternal> sender) {
|
||||
RTC_DCHECK(sender);
|
||||
RtpParameters parameters = sender->GetParameters();
|
||||
if (parameters.encodings.empty()) {
|
||||
if (parameters.encodings.size() <= 1) {
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
bool first_layer_status = parameters.encodings[0].active;
|
||||
for (RtpEncodingParameters& encoding : parameters.encodings) {
|
||||
// TODO(bugs.webrtc.org/10251): Active does not really disable the layer.
|
||||
// The user might enable it at a later point in time even though it was
|
||||
// rejected.
|
||||
encoding.active = false;
|
||||
}
|
||||
parameters.encodings[0].active = first_layer_status;
|
||||
|
||||
return sender->SetParameters(parameters);
|
||||
std::vector<std::string> disabled_layers;
|
||||
std::transform(
|
||||
parameters.encodings.begin() + 1, parameters.encodings.end(),
|
||||
std::back_inserter(disabled_layers),
|
||||
[](const RtpEncodingParameters& encoding) { return encoding.rid; });
|
||||
return sender->DisableEncodingLayers(disabled_layers);
|
||||
}
|
||||
|
||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
|
||||
@ -3127,7 +3135,7 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
||||
old_local_content->media_description()->HasSimulcast() &&
|
||||
!media_desc->HasSimulcast()) {
|
||||
RTCError error =
|
||||
DisableSimulcastInSender(transceiver->internal()->sender());
|
||||
DisableSimulcastInSender(transceiver->internal()->sender_internal());
|
||||
if (!error.ok()) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast.";
|
||||
return std::move(error);
|
||||
@ -3148,7 +3156,7 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
||||
.receive_layers()
|
||||
.GetAllLayers();
|
||||
RTCError error = UpdateSimulcastLayerStatusInSender(
|
||||
layers, transceiver->internal()->sender());
|
||||
layers, transceiver->internal()->sender_internal());
|
||||
if (!error.ok()) {
|
||||
RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers.";
|
||||
return std::move(error);
|
||||
|
||||
@ -174,7 +174,7 @@ TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithRid) {
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
// Single RID should be removed.
|
||||
EXPECT_THAT(parameters.encodings,
|
||||
ElementsAre(Field(&RtpEncodingParameters::rid, Eq(""))));
|
||||
ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq(""))));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithSimulcast) {
|
||||
@ -195,7 +195,7 @@ TEST_F(PeerConnectionSimulcastTests, RidsAreAutogeneratedIfNotProvided) {
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
ASSERT_EQ(3u, parameters.encodings.size());
|
||||
EXPECT_THAT(parameters.encodings,
|
||||
Each(Field(&RtpEncodingParameters::rid, Ne(""))));
|
||||
Each(Field("rid", &RtpEncodingParameters::rid, Ne(""))));
|
||||
}
|
||||
|
||||
// Validates that an error is returned when there is a mix of supplied and not
|
||||
@ -211,8 +211,7 @@ TEST_F(PeerConnectionSimulcastTests, MustSupplyAllOrNoRidsInSimulcast) {
|
||||
EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.error().type());
|
||||
}
|
||||
|
||||
// Validates that a single RID does not get negotiated.
|
||||
// This test is currently disabled because a single RID is not supported.
|
||||
// Validates that a single RID is removed from the encoding layer.
|
||||
TEST_F(PeerConnectionSimulcastTests, SingleRidIsRemovedFromSessionDescription) {
|
||||
auto pc = CreatePeerConnectionWrapper();
|
||||
auto transceiver = AddTransceiver(pc.get(), CreateLayers({"1"}, true));
|
||||
@ -323,8 +322,8 @@ TEST_F(PeerConnectionSimulcastTests, PausedSimulcastLayersAreDisabledInSender) {
|
||||
}
|
||||
|
||||
// Checks that when Simulcast is not supported by the remote party, then all
|
||||
// the layers (except the first) are marked as disabled.
|
||||
TEST_F(PeerConnectionSimulcastTests, SimulcastRejectedDisablesExtraLayers) {
|
||||
// the layers (except the first) are removed.
|
||||
TEST_F(PeerConnectionSimulcastTests, SimulcastRejectedRemovesExtraLayers) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3", "4"}, true);
|
||||
@ -340,15 +339,19 @@ TEST_F(PeerConnectionSimulcastTests, SimulcastRejectedDisablesExtraLayers) {
|
||||
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
||||
auto answer = remote->CreateAnswerAndSetAsLocal();
|
||||
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
|
||||
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, expected_layers));
|
||||
auto parameters = transceiver->sender()->GetParameters();
|
||||
// Should only have the first layer.
|
||||
EXPECT_THAT(parameters.encodings,
|
||||
ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq("1"))));
|
||||
}
|
||||
|
||||
// Checks that if Simulcast is supported by remote party, but some layer is
|
||||
// rejected, then only that layer is marked as disabled.
|
||||
// Checks that if Simulcast is supported by remote party, but some layers are
|
||||
// rejected, then only rejected layers are removed from the sender.
|
||||
TEST_F(PeerConnectionSimulcastTests, RejectedSimulcastLayersAreDeactivated) {
|
||||
auto local = CreatePeerConnectionWrapper();
|
||||
auto remote = CreatePeerConnectionWrapper();
|
||||
auto layers = CreateLayers({"1", "2", "3", "4"}, true);
|
||||
auto expected_layers = CreateLayers({"2", "3", "4"}, true);
|
||||
auto transceiver = AddTransceiver(local.get(), layers);
|
||||
auto offer = local->CreateOfferAndSetAsLocal();
|
||||
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
||||
@ -368,8 +371,7 @@ TEST_F(PeerConnectionSimulcastTests, RejectedSimulcastLayersAreDeactivated) {
|
||||
}
|
||||
ASSERT_TRUE(mcd_answer->HasSimulcast());
|
||||
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
|
||||
layers[0].is_paused = true;
|
||||
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
||||
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, expected_layers));
|
||||
}
|
||||
|
||||
// Checks that simulcast is set up correctly when the server sends an offer
|
||||
|
||||
@ -82,6 +82,36 @@ void MaybeAttachFrameEncryptorToMediaChannel(
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveEncodingLayers(const std::vector<std::string>& rids,
|
||||
std::vector<RtpEncodingParameters>* encodings) {
|
||||
RTC_DCHECK(encodings);
|
||||
encodings->erase(
|
||||
std::remove_if(encodings->begin(), encodings->end(),
|
||||
[&rids](const RtpEncodingParameters& encoding) {
|
||||
return absl::c_linear_search(rids, encoding.rid);
|
||||
}),
|
||||
encodings->end());
|
||||
}
|
||||
|
||||
RtpParameters RestoreEncodingLayers(
|
||||
const RtpParameters& parameters,
|
||||
const std::vector<std::string>& removed_rids,
|
||||
const std::vector<RtpEncodingParameters>& all_layers) {
|
||||
RTC_DCHECK_EQ(parameters.encodings.size() + removed_rids.size(),
|
||||
all_layers.size());
|
||||
RtpParameters result(parameters);
|
||||
result.encodings.clear();
|
||||
size_t index = 0;
|
||||
for (const RtpEncodingParameters& encoding : all_layers) {
|
||||
if (absl::c_linear_search(removed_rids, encoding.rid)) {
|
||||
result.encodings.push_back(encoding);
|
||||
continue;
|
||||
}
|
||||
result.encodings.push_back(parameters.encodings[index++]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Returns true if any RtpParameters member that isn't implemented contains a
|
||||
@ -246,7 +276,7 @@ bool AudioRtpSender::SetTrack(MediaStreamTrackInterface* track) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RtpParameters AudioRtpSender::GetParameters() {
|
||||
RtpParameters AudioRtpSender::GetParameters() const {
|
||||
if (stopped_) {
|
||||
return RtpParameters();
|
||||
}
|
||||
@ -440,6 +470,13 @@ void AudioRtpSender::ClearAudioSend() {
|
||||
}
|
||||
}
|
||||
|
||||
RTCError AudioRtpSender::DisableEncodingLayers(
|
||||
const std::vector<std::string>& rids) {
|
||||
// Multiple encoding layers (and simulcast) are not supported in audio.
|
||||
return rids.empty() ? RTCError::OK()
|
||||
: RTCError(RTCErrorType::UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
VideoRtpSender::VideoRtpSender(rtc::Thread* worker_thread,
|
||||
const std::string& id)
|
||||
: worker_thread_(worker_thread), id_(id) {
|
||||
@ -501,7 +538,7 @@ bool VideoRtpSender::SetTrack(MediaStreamTrackInterface* track) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RtpParameters VideoRtpSender::GetParameters() {
|
||||
RtpParameters VideoRtpSender::GetParameters() const {
|
||||
if (stopped_) {
|
||||
return RtpParameters();
|
||||
}
|
||||
@ -513,6 +550,7 @@ RtpParameters VideoRtpSender::GetParameters() {
|
||||
}
|
||||
return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
|
||||
RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_);
|
||||
RemoveEncodingLayers(disabled_rids_, &result.encodings);
|
||||
last_transaction_id_ = rtc::CreateRandomUuid();
|
||||
result.transaction_id = last_transaction_id_.value();
|
||||
return result;
|
||||
@ -551,7 +589,16 @@ RTCError VideoRtpSender::SetParameters(const RtpParameters& parameters) {
|
||||
return result;
|
||||
}
|
||||
return worker_thread_->Invoke<RTCError>(RTC_FROM_HERE, [&] {
|
||||
RTCError result = media_channel_->SetRtpSendParameters(ssrc_, parameters);
|
||||
RtpParameters rtp_parameters = parameters;
|
||||
if (!disabled_rids_.empty()) {
|
||||
// Need to add the inactive layers.
|
||||
RtpParameters old_parameters =
|
||||
media_channel_->GetRtpSendParameters(ssrc_);
|
||||
rtp_parameters = RestoreEncodingLayers(parameters, disabled_rids_,
|
||||
old_parameters.encodings);
|
||||
}
|
||||
RTCError result =
|
||||
media_channel_->SetRtpSendParameters(ssrc_, rtp_parameters);
|
||||
last_transaction_id_.reset();
|
||||
return result;
|
||||
});
|
||||
@ -605,6 +652,7 @@ void VideoRtpSender::SetSsrc(uint32_t ssrc) {
|
||||
for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) {
|
||||
init_parameters_.encodings[i].ssrc =
|
||||
current_parameters.encodings[i].ssrc;
|
||||
init_parameters_.encodings[i].rid = current_parameters.encodings[i].rid;
|
||||
current_parameters.encodings[i] = init_parameters_.encodings[i];
|
||||
}
|
||||
current_parameters.degradation_preference =
|
||||
@ -684,4 +732,36 @@ void VideoRtpSender::ClearVideoSend() {
|
||||
});
|
||||
}
|
||||
|
||||
RTCError VideoRtpSender::DisableEncodingLayers(
|
||||
const std::vector<std::string>& rids) {
|
||||
if (stopped_) {
|
||||
return RTCError(RTCErrorType::INVALID_STATE);
|
||||
}
|
||||
|
||||
if (!media_channel_ || !ssrc_) {
|
||||
RemoveEncodingLayers(rids, &init_parameters_.encodings);
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
// Check that all the specified layers exist and disable them in the channel.
|
||||
RtpParameters parameters = GetParameters();
|
||||
for (const std::string& rid : rids) {
|
||||
auto iter = absl::c_find_if(parameters.encodings,
|
||||
[&rid](const RtpEncodingParameters& encoding) {
|
||||
return encoding.rid == rid;
|
||||
});
|
||||
if (iter == parameters.encodings.end()) {
|
||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
||||
"RID: " + rid + " does not refer to a valid layer.");
|
||||
}
|
||||
iter->active = false;
|
||||
}
|
||||
|
||||
RTCError result = SetParameters(parameters);
|
||||
if (result.ok()) {
|
||||
disabled_rids_.insert(disabled_rids_.end(), rids.begin(), rids.end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -59,6 +59,11 @@ class RtpSenderInternal : public RtpSenderInterface {
|
||||
// otherwise remains constant. Used to generate IDs for stats.
|
||||
// The special value zero means that no track is attached.
|
||||
virtual int AttachmentId() const = 0;
|
||||
|
||||
// Disables the layers identified by the specified RIDs.
|
||||
// If the specified list is empty, this is a no-op.
|
||||
virtual RTCError DisableEncodingLayers(
|
||||
const std::vector<std::string>& rid) = 0;
|
||||
};
|
||||
|
||||
// LocalAudioSinkAdapter receives data callback as a sink to the local
|
||||
@ -128,7 +133,7 @@ class AudioRtpSender : public DtmfProviderInterface,
|
||||
|
||||
std::vector<std::string> stream_ids() const override { return stream_ids_; }
|
||||
|
||||
RtpParameters GetParameters() override;
|
||||
RtpParameters GetParameters() const override;
|
||||
RTCError SetParameters(const RtpParameters& parameters) override;
|
||||
|
||||
rtc::scoped_refptr<DtmfSenderInterface> GetDtmfSender() const override;
|
||||
@ -163,6 +168,8 @@ class AudioRtpSender : public DtmfProviderInterface,
|
||||
|
||||
void SetMediaChannel(cricket::MediaChannel* media_channel) override;
|
||||
|
||||
RTCError DisableEncodingLayers(const std::vector<std::string>& rids) override;
|
||||
|
||||
private:
|
||||
// TODO(nisse): Since SSRC == 0 is technically valid, figure out
|
||||
// some other way to test if we have a valid SSRC.
|
||||
@ -184,7 +191,12 @@ class AudioRtpSender : public DtmfProviderInterface,
|
||||
rtc::scoped_refptr<AudioTrackInterface> track_;
|
||||
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport_;
|
||||
rtc::scoped_refptr<DtmfSenderInterface> dtmf_sender_proxy_;
|
||||
absl::optional<std::string> last_transaction_id_;
|
||||
// |last_transaction_id_| is used to verify that |SetParameters| is receiving
|
||||
// the parameters object that was last returned from |GetParameters|.
|
||||
// As such, it is used for internal verification and is not observable by the
|
||||
// the client. It is marked as mutable to enable |GetParameters| to be a
|
||||
// const method.
|
||||
mutable absl::optional<std::string> last_transaction_id_;
|
||||
uint32_t ssrc_ = 0;
|
||||
bool cached_track_enabled_ = false;
|
||||
bool stopped_ = false;
|
||||
@ -239,7 +251,7 @@ class VideoRtpSender : public ObserverInterface,
|
||||
dtls_transport_ = dtls_transport;
|
||||
}
|
||||
|
||||
RtpParameters GetParameters() override;
|
||||
RtpParameters GetParameters() const override;
|
||||
RTCError SetParameters(const RtpParameters& parameters) override;
|
||||
|
||||
rtc::scoped_refptr<DtmfSenderInterface> GetDtmfSender() const override;
|
||||
@ -262,6 +274,8 @@ class VideoRtpSender : public ObserverInterface,
|
||||
|
||||
void SetMediaChannel(cricket::MediaChannel* media_channel) override;
|
||||
|
||||
RTCError DisableEncodingLayers(const std::vector<std::string>& rids) override;
|
||||
|
||||
private:
|
||||
bool can_send_track() const { return track_ && ssrc_; }
|
||||
// Helper function to construct options for
|
||||
@ -276,7 +290,12 @@ class VideoRtpSender : public ObserverInterface,
|
||||
RtpParameters init_parameters_;
|
||||
cricket::VideoMediaChannel* media_channel_ = nullptr;
|
||||
rtc::scoped_refptr<VideoTrackInterface> track_;
|
||||
absl::optional<std::string> last_transaction_id_;
|
||||
// |last_transaction_id_| is used to verify that |SetParameters| is receiving
|
||||
// the parameters object that was last returned from |GetParameters|.
|
||||
// As such, it is used for internal verification and is not observable by the
|
||||
// the client. It is marked as mutable to enable |GetParameters| to be a
|
||||
// const method.
|
||||
mutable absl::optional<std::string> last_transaction_id_;
|
||||
uint32_t ssrc_ = 0;
|
||||
VideoTrackInterface::ContentHint cached_track_content_hint_ =
|
||||
VideoTrackInterface::ContentHint::kNone;
|
||||
@ -284,6 +303,7 @@ class VideoRtpSender : public ObserverInterface,
|
||||
int attachment_id_ = 0;
|
||||
rtc::scoped_refptr<FrameEncryptorInterface> frame_encryptor_;
|
||||
rtc::scoped_refptr<DtlsTransportInterface> dtls_transport_;
|
||||
std::vector<std::string> disabled_rids_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/audio_options.h"
|
||||
@ -63,9 +64,11 @@
|
||||
#include "test/gtest.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::ContainerEq;
|
||||
using ::testing::Exactly;
|
||||
using ::testing::InvokeWithoutArgs;
|
||||
using ::testing::Return;
|
||||
using RidList = std::vector<std::string>;
|
||||
|
||||
namespace {
|
||||
|
||||
@ -83,8 +86,10 @@ static const int kDefaultTimeout = 10000; // 10 seconds.
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpSenderReceiverTest : public testing::Test,
|
||||
public sigslot::has_slots<> {
|
||||
class RtpSenderReceiverTest
|
||||
: public testing::Test,
|
||||
public testing::WithParamInterface<std::pair<RidList, RidList>>,
|
||||
public sigslot::has_slots<> {
|
||||
public:
|
||||
RtpSenderReceiverTest()
|
||||
: network_thread_(rtc::Thread::Current()),
|
||||
@ -203,17 +208,38 @@ class RtpSenderReceiverTest : public testing::Test,
|
||||
|
||||
void CreateVideoRtpSender() { CreateVideoRtpSender(false); }
|
||||
|
||||
void CreateVideoRtpSenderWithSimulcast(
|
||||
int num_layers = kVideoSimulcastLayerCount) {
|
||||
cricket::StreamParams CreateSimulcastStreamParams(int num_layers) {
|
||||
std::vector<uint32_t> ssrcs;
|
||||
ssrcs.reserve(num_layers);
|
||||
for (int i = 0; i < num_layers; ++i)
|
||||
for (int i = 0; i < num_layers; ++i) {
|
||||
ssrcs.push_back(kVideoSsrcSimulcast + i);
|
||||
cricket::StreamParams stream_params =
|
||||
cricket::CreateSimStreamParams("cname", ssrcs);
|
||||
}
|
||||
return cricket::CreateSimStreamParams("cname", ssrcs);
|
||||
}
|
||||
|
||||
uint32_t CreateVideoRtpSender(const cricket::StreamParams& stream_params) {
|
||||
video_media_channel_->AddSendStream(stream_params);
|
||||
uint32_t primary_ssrc = stream_params.first_ssrc();
|
||||
CreateVideoRtpSender(primary_ssrc);
|
||||
return primary_ssrc;
|
||||
}
|
||||
|
||||
uint32_t CreateVideoRtpSenderWithSimulcast(
|
||||
int num_layers = kVideoSimulcastLayerCount) {
|
||||
return CreateVideoRtpSender(CreateSimulcastStreamParams(num_layers));
|
||||
}
|
||||
|
||||
uint32_t CreateVideoRtpSenderWithSimulcast(
|
||||
const std::vector<std::string>& rids) {
|
||||
cricket::StreamParams stream_params =
|
||||
CreateSimulcastStreamParams(rids.size());
|
||||
std::vector<cricket::RidDescription> rid_descriptions;
|
||||
absl::c_transform(
|
||||
rids, std::back_inserter(rid_descriptions), [](const std::string& rid) {
|
||||
return cricket::RidDescription(rid, cricket::RidDirection::kSend);
|
||||
});
|
||||
stream_params.set_rids(rid_descriptions);
|
||||
return CreateVideoRtpSender(stream_params);
|
||||
}
|
||||
|
||||
void CreateVideoRtpSender(bool is_screencast, uint32_t ssrc = kVideoSsrc) {
|
||||
@ -343,6 +369,91 @@ class RtpSenderReceiverTest : public testing::Test,
|
||||
EXPECT_FALSE(video_media_channel_->HasSink(kVideoSsrc));
|
||||
}
|
||||
|
||||
// Verifies that the encoding layers contain the specified RIDs.
|
||||
bool VerifyEncodingLayers(const VideoRtpSender& sender,
|
||||
const std::vector<std::string>& rids) {
|
||||
bool has_failure = HasFailure();
|
||||
RtpParameters parameters = sender.GetParameters();
|
||||
std::vector<std::string> encoding_rids;
|
||||
absl::c_transform(
|
||||
parameters.encodings, std::back_inserter(encoding_rids),
|
||||
[](const RtpEncodingParameters& encoding) { return encoding.rid; });
|
||||
EXPECT_THAT(rids, ContainerEq(encoding_rids));
|
||||
return has_failure || !HasFailure();
|
||||
}
|
||||
|
||||
// Runs a test for disabling the encoding layers on the specified sender.
|
||||
void RunDisableEncodingLayersTest(
|
||||
const std::vector<std::string>& all_layers,
|
||||
const std::vector<std::string>& disabled_layers,
|
||||
VideoRtpSender* sender) {
|
||||
std::vector<std::string> expected;
|
||||
absl::c_copy_if(all_layers, std::back_inserter(expected),
|
||||
[&disabled_layers](const std::string& rid) {
|
||||
return !absl::c_linear_search(disabled_layers, rid);
|
||||
});
|
||||
|
||||
EXPECT_TRUE(VerifyEncodingLayers(*sender, all_layers));
|
||||
sender->DisableEncodingLayers(disabled_layers);
|
||||
EXPECT_TRUE(VerifyEncodingLayers(*sender, expected));
|
||||
}
|
||||
|
||||
// Runs a test for setting an encoding layer as inactive.
|
||||
// This test assumes that some layers have already been disabled.
|
||||
void RunSetLastLayerAsInactiveTest(VideoRtpSender* sender) {
|
||||
auto parameters = sender->GetParameters();
|
||||
if (parameters.encodings.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
RtpEncodingParameters& encoding = parameters.encodings.back();
|
||||
auto rid = encoding.rid;
|
||||
EXPECT_TRUE(encoding.active);
|
||||
encoding.active = false;
|
||||
auto error = sender->SetParameters(parameters);
|
||||
ASSERT_TRUE(error.ok());
|
||||
parameters = sender->GetParameters();
|
||||
RtpEncodingParameters& result_encoding = parameters.encodings.back();
|
||||
EXPECT_EQ(rid, result_encoding.rid);
|
||||
EXPECT_FALSE(result_encoding.active);
|
||||
}
|
||||
|
||||
// Runs a test for disabling the encoding layers on a sender without a media
|
||||
// channel.
|
||||
void RunDisableSimulcastLayersWithoutMediaEngineTest(
|
||||
const std::vector<std::string>& all_layers,
|
||||
const std::vector<std::string>& disabled_layers) {
|
||||
VideoRtpSender sender(rtc::Thread::Current(), "1");
|
||||
RtpParameters parameters;
|
||||
parameters.encodings.resize(all_layers.size());
|
||||
for (size_t i = 0; i < all_layers.size(); ++i) {
|
||||
parameters.encodings[i].rid = all_layers[i];
|
||||
}
|
||||
sender.set_init_send_encodings(parameters.encodings);
|
||||
RunDisableEncodingLayersTest(all_layers, disabled_layers, &sender);
|
||||
RunSetLastLayerAsInactiveTest(&sender);
|
||||
}
|
||||
|
||||
// Runs a test for disabling the encoding layers on a sender with a media
|
||||
// channel.
|
||||
void RunDisableSimulcastLayersWithMediaEngineTest(
|
||||
const std::vector<std::string>& all_layers,
|
||||
const std::vector<std::string>& disabled_layers) {
|
||||
uint32_t ssrc = CreateVideoRtpSenderWithSimulcast(all_layers);
|
||||
RunDisableEncodingLayersTest(all_layers, disabled_layers,
|
||||
video_rtp_sender_.get());
|
||||
|
||||
auto channel_parameters = video_media_channel_->GetRtpSendParameters(ssrc);
|
||||
ASSERT_EQ(channel_parameters.encodings.size(), all_layers.size());
|
||||
for (size_t i = 0; i < all_layers.size(); ++i) {
|
||||
EXPECT_EQ(all_layers[i], channel_parameters.encodings[i].rid);
|
||||
bool is_active = !absl::c_linear_search(disabled_layers, all_layers[i]);
|
||||
EXPECT_EQ(is_active, channel_parameters.encodings[i].active);
|
||||
}
|
||||
|
||||
RunSetLastLayerAsInactiveTest(video_rtp_sender_.get());
|
||||
}
|
||||
|
||||
protected:
|
||||
rtc::Thread* const network_thread_;
|
||||
rtc::Thread* const worker_thread_;
|
||||
@ -1210,20 +1321,6 @@ TEST_F(RtpSenderReceiverTest,
|
||||
DestroyVideoRtpSender();
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderReceiverTest, VideoSenderCanSetRid) {
|
||||
CreateVideoRtpSender();
|
||||
RtpParameters params = video_rtp_sender_->GetParameters();
|
||||
EXPECT_EQ(1u, params.encodings.size());
|
||||
const std::string rid = "dummy_rid";
|
||||
params.encodings[0].rid = rid;
|
||||
EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok());
|
||||
params = video_rtp_sender_->GetParameters();
|
||||
EXPECT_EQ(1u, params.encodings.size());
|
||||
EXPECT_EQ(rid, params.encodings[0].rid);
|
||||
|
||||
DestroyVideoRtpSender();
|
||||
}
|
||||
|
||||
TEST_F(RtpSenderReceiverTest, VideoSenderCanSetScaleResolutionDownBy) {
|
||||
CreateVideoRtpSender();
|
||||
|
||||
@ -1669,4 +1766,44 @@ TEST_F(RtpSenderReceiverTest, VideoReceiverCannotSetFrameDecryptorAfterStop) {
|
||||
// TODO(webrtc:9926) - Validate media channel not set once fakes updated.
|
||||
}
|
||||
|
||||
// Helper method for syntactic sugar for accepting a vector with '{}' notation.
|
||||
std::pair<RidList, RidList> CreatePairOfRidVectors(
|
||||
const std::vector<std::string>& first,
|
||||
const std::vector<std::string>& second) {
|
||||
return std::make_pair(first, second);
|
||||
}
|
||||
|
||||
// These parameters are used to test disabling simulcast layers.
|
||||
const std::pair<RidList, RidList> kDisableSimulcastLayersParameters[] = {
|
||||
// Tests removing the first layer. This is a special case because
|
||||
// the first layer's SSRC is also the 'primary' SSRC used to associate the
|
||||
// parameters to the media channel.
|
||||
CreatePairOfRidVectors({"1", "2", "3", "4"}, {"1"}),
|
||||
// Tests removing some layers.
|
||||
CreatePairOfRidVectors({"1", "2", "3", "4"}, {"2", "4"}),
|
||||
// Tests simulcast rejected scenario all layers except first are rejected.
|
||||
CreatePairOfRidVectors({"1", "2", "3", "4"}, {"2", "3", "4"}),
|
||||
// Tests removing all layers.
|
||||
CreatePairOfRidVectors({"1", "2", "3", "4"}, {"1", "2", "3", "4"}),
|
||||
};
|
||||
|
||||
// Runs test for disabling layers on a sender without a media engine set.
|
||||
TEST_P(RtpSenderReceiverTest, DisableSimulcastLayersWithoutMediaEngine) {
|
||||
auto parameter = GetParam();
|
||||
RunDisableSimulcastLayersWithoutMediaEngineTest(parameter.first,
|
||||
parameter.second);
|
||||
}
|
||||
|
||||
// Runs test for disabling layers on a sender with a media engine set.
|
||||
TEST_P(RtpSenderReceiverTest, DisableSimulcastLayersWithMediaEngine) {
|
||||
auto parameter = GetParam();
|
||||
RunDisableSimulcastLayersWithMediaEngineTest(parameter.first,
|
||||
parameter.second);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DisableSimulcastLayersInSender,
|
||||
RtpSenderReceiverTest,
|
||||
::testing::ValuesIn(kDisableSimulcastLayersParameters));
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -33,7 +33,7 @@ class MockRtpSenderInternal : public RtpSenderInternal {
|
||||
MOCK_CONST_METHOD0(stream_ids, std::vector<std::string>());
|
||||
MOCK_CONST_METHOD0(init_send_encodings, std::vector<RtpEncodingParameters>());
|
||||
MOCK_METHOD1(set_transport, void(rtc::scoped_refptr<DtlsTransportInterface>));
|
||||
MOCK_METHOD0(GetParameters, RtpParameters());
|
||||
MOCK_CONST_METHOD0(GetParameters, RtpParameters());
|
||||
MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&));
|
||||
MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr<DtmfSenderInterface>());
|
||||
MOCK_METHOD1(SetFrameEncryptor,
|
||||
@ -49,6 +49,8 @@ class MockRtpSenderInternal : public RtpSenderInternal {
|
||||
void(const std::vector<RtpEncodingParameters>&));
|
||||
MOCK_METHOD0(Stop, void());
|
||||
MOCK_CONST_METHOD0(AttachmentId, int());
|
||||
MOCK_METHOD1(DisableEncodingLayers,
|
||||
RTCError(const std::vector<std::string>&));
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user