Revert "Fix bug where we assume new m= sections will always be bundled."
This reverts commit d2b885fd91909f1b17fb11292a8c989d5d883b22. Reason for revert: Speculative revert for Chromium importer Original change's description: > Fix bug where we assume new m= sections will always be bundled. > > A recent change [1] assumes that all new m= sections will share the > first BUNDLE group (if one already exists), which avoids generating > ICE candidates that are ultimately unnecessary. This is fine for JSEP > endpoints, but it breaks the following scenarios for non-JSEP endpoints: > > * Remote offer adding a new m= section that's not part of any BUNDLE > group. > * Remote offer adding an m= section to the second BUNDLE group. > > The latter is specifically problematic for any application that wants > to bundle all audio streams in one group and all video streams in > another group when using Unified Plan SDP, to replicate the behavior of > using Plan B without bundling. It may try to add a video stream only > for WebRTC to bundle it with audio. > > This is fixed by doing some minor re-factoring, having BundleManager > update the bundle groups at offer time. > > Also: > * Added some additional validation for multiple bundle groups in a > subsequent offer, since that now becomes relevant. > * Improved rollback support, because now rolling back an offer may need > to not only remove mid->transport mappings but alter them. > > [1]: https://webrtc-review.googlesource.com/c/src/+/221601 > > Bug: webrtc:12906, webrtc:12999 > Change-Id: I4c6e7020c0be33a782d3608dee88e4e2fceb1be1 > Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/225642 > Reviewed-by: Harald Alvestrand <hta@webrtc.org> > Reviewed-by: Henrik Boström <hbos@webrtc.org> > Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org> > Cr-Commit-Position: refs/heads/master@{#34544} # Not skipping CQ checks because original CL landed > 1 day ago. Bug: webrtc:12906, webrtc:12999 Change-Id: I00179d7573f322ad539ff16cad1f85320cfb2270 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227081 Reviewed-by: Björn Terelius <terelius@google.com> Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org> Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org> Cr-Commit-Position: refs/heads/master@{#34578}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
fd05d6f504
commit
dc364e5bc2
@ -20,21 +20,8 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
void BundleManager::Update(const cricket::SessionDescription* description,
|
void BundleManager::Update(const cricket::SessionDescription* description) {
|
||||||
SdpType type) {
|
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
// Rollbacks should call Rollback, not Update.
|
|
||||||
RTC_DCHECK(type != SdpType::kRollback);
|
|
||||||
bool bundle_groups_changed = false;
|
|
||||||
// TODO(bugs.webrtc.org/3349): Do this for kPrAnswer as well. To make this
|
|
||||||
// work, we also need to make sure PRANSWERs don't call
|
|
||||||
// MaybeDestroyJsepTransport, because the final answer may need the destroyed
|
|
||||||
// transport if it changes the BUNDLE group.
|
|
||||||
if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle ||
|
|
||||||
type == SdpType::kAnswer) {
|
|
||||||
// If our policy is "max-bundle" or this is an answer, update all bundle
|
|
||||||
// groups.
|
|
||||||
bundle_groups_changed = true;
|
|
||||||
bundle_groups_.clear();
|
bundle_groups_.clear();
|
||||||
for (const cricket::ContentGroup* new_bundle_group :
|
for (const cricket::ContentGroup* new_bundle_group :
|
||||||
description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) {
|
description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) {
|
||||||
@ -43,31 +30,12 @@ void BundleManager::Update(const cricket::SessionDescription* description,
|
|||||||
RTC_DLOG(LS_VERBOSE) << "Establishing bundle group "
|
RTC_DLOG(LS_VERBOSE) << "Establishing bundle group "
|
||||||
<< new_bundle_group->ToString();
|
<< new_bundle_group->ToString();
|
||||||
}
|
}
|
||||||
} else if (type == SdpType::kOffer) {
|
established_bundle_groups_by_mid_.clear();
|
||||||
// If this is an offer, update existing bundle groups.
|
for (const auto& bundle_group : bundle_groups_) {
|
||||||
// We do this because as per RFC 8843, section 7.3.2, the answerer cannot
|
for (const std::string& content_name : bundle_group->content_names()) {
|
||||||
// remove an m= section from an existing BUNDLE group without rejecting it.
|
established_bundle_groups_by_mid_[content_name] = bundle_group.get();
|
||||||
// Thus any m= sections added to a BUNDLE group in this offer can
|
|
||||||
// preemptively start using the bundled transport, as there is no possible
|
|
||||||
// non-bundled fallback.
|
|
||||||
for (const cricket::ContentGroup* new_bundle_group :
|
|
||||||
description->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)) {
|
|
||||||
// Attempt to find a matching existing group.
|
|
||||||
for (const std::string& mid : new_bundle_group->content_names()) {
|
|
||||||
auto it = established_bundle_groups_by_mid_.find(mid);
|
|
||||||
if (it != established_bundle_groups_by_mid_.end()) {
|
|
||||||
*it->second = *new_bundle_group;
|
|
||||||
bundle_groups_changed = true;
|
|
||||||
RTC_DLOG(LS_VERBOSE)
|
|
||||||
<< "Establishing bundle group " << new_bundle_group->ToString();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bundle_groups_changed) {
|
|
||||||
RefreshEstablishedBundleGroupsByMid();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cricket::ContentGroup* BundleManager::LookupGroupByMid(
|
const cricket::ContentGroup* BundleManager::LookupGroupByMid(
|
||||||
@ -124,34 +92,6 @@ void BundleManager::DeleteGroup(const cricket::ContentGroup* bundle_group) {
|
|||||||
bundle_groups_.erase(bundle_group_it);
|
bundle_groups_.erase(bundle_group_it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BundleManager::Rollback() {
|
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
||||||
bundle_groups_.clear();
|
|
||||||
for (const auto& bundle_group : stable_bundle_groups_) {
|
|
||||||
bundle_groups_.push_back(
|
|
||||||
std::make_unique<cricket::ContentGroup>(*bundle_group));
|
|
||||||
}
|
|
||||||
RefreshEstablishedBundleGroupsByMid();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BundleManager::Commit() {
|
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
||||||
stable_bundle_groups_.clear();
|
|
||||||
for (const auto& bundle_group : bundle_groups_) {
|
|
||||||
stable_bundle_groups_.push_back(
|
|
||||||
std::make_unique<cricket::ContentGroup>(*bundle_group));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BundleManager::RefreshEstablishedBundleGroupsByMid() {
|
|
||||||
established_bundle_groups_by_mid_.clear();
|
|
||||||
for (const auto& bundle_group : bundle_groups_) {
|
|
||||||
for (const std::string& content_name : bundle_group->content_names()) {
|
|
||||||
established_bundle_groups_by_mid_[content_name] = bundle_group.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JsepTransportCollection::RegisterTransport(
|
void JsepTransportCollection::RegisterTransport(
|
||||||
const std::string& mid,
|
const std::string& mid,
|
||||||
std::unique_ptr<cricket::JsepTransport> transport) {
|
std::unique_ptr<cricket::JsepTransport> transport) {
|
||||||
@ -217,6 +157,8 @@ bool JsepTransportCollection::SetTransportForMid(
|
|||||||
if (it != mid_to_transport_.end() && it->second == jsep_transport)
|
if (it != mid_to_transport_.end() && it->second == jsep_transport)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
pending_mids_.push_back(mid);
|
||||||
|
|
||||||
// The map_change_callback must be called before destroying the
|
// The map_change_callback must be called before destroying the
|
||||||
// transport, because it removes references to the transport
|
// transport, because it removes references to the transport
|
||||||
// in the RTP demuxer.
|
// in the RTP demuxer.
|
||||||
@ -249,33 +191,17 @@ void JsepTransportCollection::RemoveTransportForMid(const std::string& mid) {
|
|||||||
RTC_DCHECK(IsConsistent());
|
RTC_DCHECK(IsConsistent());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JsepTransportCollection::RollbackTransports() {
|
void JsepTransportCollection::RollbackTransports() {
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
bool ret = true;
|
for (auto&& mid : pending_mids_) {
|
||||||
// First, remove any new mid->transport mappings.
|
RemoveTransportForMid(mid);
|
||||||
for (const auto& kv : mid_to_transport_) {
|
|
||||||
if (stable_mid_to_transport_.count(kv.first) == 0) {
|
|
||||||
ret = ret && map_change_callback_(kv.first, nullptr);
|
|
||||||
}
|
}
|
||||||
}
|
pending_mids_.clear();
|
||||||
// Next, restore old mappings.
|
|
||||||
for (const auto& kv : stable_mid_to_transport_) {
|
|
||||||
auto it = mid_to_transport_.find(kv.first);
|
|
||||||
if (it == mid_to_transport_.end() || it->second != kv.second) {
|
|
||||||
ret = ret && map_change_callback_(kv.first, kv.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mid_to_transport_ = stable_mid_to_transport_;
|
|
||||||
DestroyUnusedTransports();
|
|
||||||
RTC_DCHECK(IsConsistent());
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsepTransportCollection::CommitTransports() {
|
void JsepTransportCollection::CommitTransports() {
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
stable_mid_to_transport_ = mid_to_transport_;
|
pending_mids_.clear();
|
||||||
DestroyUnusedTransports();
|
|
||||||
RTC_DCHECK(IsConsistent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JsepTransportCollection::TransportInUse(
|
bool JsepTransportCollection::TransportInUse(
|
||||||
@ -286,11 +212,6 @@ bool JsepTransportCollection::TransportInUse(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto& kv : stable_mid_to_transport_) {
|
|
||||||
if (kv.second == jsep_transport) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +219,7 @@ void JsepTransportCollection::MaybeDestroyJsepTransport(
|
|||||||
cricket::JsepTransport* transport) {
|
cricket::JsepTransport* transport) {
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
// Don't destroy the JsepTransport if there are still media sections referring
|
// Don't destroy the JsepTransport if there are still media sections referring
|
||||||
// to it, or if it will be needed in case of rollback.
|
// to it.
|
||||||
if (TransportInUse(transport)) {
|
if (TransportInUse(transport)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -312,23 +233,6 @@ void JsepTransportCollection::MaybeDestroyJsepTransport(
|
|||||||
RTC_DCHECK(IsConsistent());
|
RTC_DCHECK(IsConsistent());
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsepTransportCollection::DestroyUnusedTransports() {
|
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
|
||||||
bool need_state_change_callback = false;
|
|
||||||
auto it = jsep_transports_by_name_.begin();
|
|
||||||
while (it != jsep_transports_by_name_.end()) {
|
|
||||||
if (TransportInUse(it->second.get())) {
|
|
||||||
++it;
|
|
||||||
} else {
|
|
||||||
it = jsep_transports_by_name_.erase(it);
|
|
||||||
need_state_change_callback = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (need_state_change_callback) {
|
|
||||||
state_change_callback_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JsepTransportCollection::IsConsistent() {
|
bool JsepTransportCollection::IsConsistent() {
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
for (const auto& it : jsep_transports_by_name_) {
|
for (const auto& it : jsep_transports_by_name_) {
|
||||||
@ -337,6 +241,13 @@ bool JsepTransportCollection::IsConsistent() {
|
|||||||
<< " is not in use, transport " << it.second.get();
|
<< " is not in use, transport " << it.second.get();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const auto& lookup = mid_to_transport_.find(it.first);
|
||||||
|
if (lookup->second != it.second.get()) {
|
||||||
|
// Not an error, but unusual.
|
||||||
|
RTC_DLOG(LS_INFO) << "Note: Mid " << it.first << " was registered to "
|
||||||
|
<< it.second.get() << " but currently maps to "
|
||||||
|
<< lookup->second;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api/peer_connection_interface.h"
|
|
||||||
#include "api/sequence_checker.h"
|
#include "api/sequence_checker.h"
|
||||||
#include "pc/jsep_transport.h"
|
#include "pc/jsep_transport.h"
|
||||||
#include "pc/session_description.h"
|
#include "pc/session_description.h"
|
||||||
@ -42,8 +41,7 @@ namespace webrtc {
|
|||||||
// 6) Change the logic to do what's right.
|
// 6) Change the logic to do what's right.
|
||||||
class BundleManager {
|
class BundleManager {
|
||||||
public:
|
public:
|
||||||
explicit BundleManager(PeerConnectionInterface::BundlePolicy bundle_policy)
|
BundleManager() {
|
||||||
: bundle_policy_(bundle_policy) {
|
|
||||||
// Allow constructor to be called on a different thread.
|
// Allow constructor to be called on a different thread.
|
||||||
sequence_checker_.Detach();
|
sequence_checker_.Detach();
|
||||||
}
|
}
|
||||||
@ -60,27 +58,17 @@ class BundleManager {
|
|||||||
bool IsFirstMidInGroup(const std::string& mid) const;
|
bool IsFirstMidInGroup(const std::string& mid) const;
|
||||||
// Update the groups description. This completely replaces the group
|
// Update the groups description. This completely replaces the group
|
||||||
// description with the one from the SessionDescription.
|
// description with the one from the SessionDescription.
|
||||||
void Update(const cricket::SessionDescription* description, SdpType type);
|
void Update(const cricket::SessionDescription* description);
|
||||||
// Delete a MID from the group that contains it.
|
// Delete a MID from the group that contains it.
|
||||||
void DeleteMid(const cricket::ContentGroup* bundle_group,
|
void DeleteMid(const cricket::ContentGroup* bundle_group,
|
||||||
const std::string& mid);
|
const std::string& mid);
|
||||||
// Delete a group.
|
// Delete a group.
|
||||||
void DeleteGroup(const cricket::ContentGroup* bundle_group);
|
void DeleteGroup(const cricket::ContentGroup* bundle_group);
|
||||||
// Roll back to previous stable state.
|
|
||||||
void Rollback();
|
|
||||||
// Commit current bundle groups.
|
|
||||||
void Commit();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Recalculate established_bundle_groups_by_mid_ from bundle_groups_.
|
|
||||||
void RefreshEstablishedBundleGroupsByMid() RTC_RUN_ON(sequence_checker_);
|
|
||||||
|
|
||||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
|
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
|
||||||
PeerConnectionInterface::BundlePolicy bundle_policy_;
|
|
||||||
std::vector<std::unique_ptr<cricket::ContentGroup>> bundle_groups_
|
std::vector<std::unique_ptr<cricket::ContentGroup>> bundle_groups_
|
||||||
RTC_GUARDED_BY(sequence_checker_);
|
RTC_GUARDED_BY(sequence_checker_);
|
||||||
std::vector<std::unique_ptr<cricket::ContentGroup>> stable_bundle_groups_
|
|
||||||
RTC_GUARDED_BY(sequence_checker_);
|
|
||||||
std::map<std::string, cricket::ContentGroup*>
|
std::map<std::string, cricket::ContentGroup*>
|
||||||
established_bundle_groups_by_mid_;
|
established_bundle_groups_by_mid_;
|
||||||
};
|
};
|
||||||
@ -120,25 +108,17 @@ class JsepTransportCollection {
|
|||||||
// Remove a transport for a MID. This may destroy a transport if it is
|
// Remove a transport for a MID. This may destroy a transport if it is
|
||||||
// no longer in use.
|
// no longer in use.
|
||||||
void RemoveTransportForMid(const std::string& mid);
|
void RemoveTransportForMid(const std::string& mid);
|
||||||
// Roll back to previous stable mid-to-transport mappings.
|
// Roll back pending mid-to-transport mappings.
|
||||||
bool RollbackTransports();
|
void RollbackTransports();
|
||||||
// Commit pending mid-transport mappings (rollback is no longer possible),
|
// Commit pending mid-transport mappings (rollback is no longer possible).
|
||||||
// and destroy unused transports because we know now we'll never need them
|
|
||||||
// again.
|
|
||||||
void CommitTransports();
|
void CommitTransports();
|
||||||
|
// Returns true if any mid currently maps to this transport.
|
||||||
private:
|
|
||||||
// Returns true if any mid currently maps to this transport, in either the
|
|
||||||
// pending or stable mapping.
|
|
||||||
bool TransportInUse(cricket::JsepTransport* jsep_transport) const;
|
bool TransportInUse(cricket::JsepTransport* jsep_transport) const;
|
||||||
|
|
||||||
// Destroy a transport if it's no longer in use. This includes whether it
|
private:
|
||||||
// will be needed in case of rollback.
|
// Destroy a transport if it's no longer in use.
|
||||||
void MaybeDestroyJsepTransport(cricket::JsepTransport* transport);
|
void MaybeDestroyJsepTransport(cricket::JsepTransport* transport);
|
||||||
|
|
||||||
// Destroys all transports that are no longer in use.
|
|
||||||
void DestroyUnusedTransports();
|
|
||||||
|
|
||||||
bool IsConsistent(); // For testing only: Verify internal structure.
|
bool IsConsistent(); // For testing only: Verify internal structure.
|
||||||
|
|
||||||
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
|
RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
|
||||||
@ -150,10 +130,8 @@ class JsepTransportCollection {
|
|||||||
// (BaseChannel/SctpTransport) and the JsepTransport underneath.
|
// (BaseChannel/SctpTransport) and the JsepTransport underneath.
|
||||||
std::map<std::string, cricket::JsepTransport*> mid_to_transport_
|
std::map<std::string, cricket::JsepTransport*> mid_to_transport_
|
||||||
RTC_GUARDED_BY(sequence_checker_);
|
RTC_GUARDED_BY(sequence_checker_);
|
||||||
// A snapshot of mid_to_transport_ at the last stable state. Used for
|
// Keep track of mids that have been mapped to transports. Used for rollback.
|
||||||
// rollback.
|
std::vector<std::string> pending_mids_ RTC_GUARDED_BY(sequence_checker_);
|
||||||
std::map<std::string, cricket::JsepTransport*> stable_mid_to_transport_
|
|
||||||
RTC_GUARDED_BY(sequence_checker_);
|
|
||||||
// Callback used to inform subscribers of altered transports.
|
// Callback used to inform subscribers of altered transports.
|
||||||
const std::function<bool(const std::string& mid,
|
const std::function<bool(const std::string& mid,
|
||||||
cricket::JsepTransport* transport)>
|
cricket::JsepTransport* transport)>
|
||||||
|
@ -55,8 +55,7 @@ JsepTransportController::JsepTransportController(
|
|||||||
UpdateAggregateStates_n();
|
UpdateAggregateStates_n();
|
||||||
}),
|
}),
|
||||||
config_(config),
|
config_(config),
|
||||||
active_reset_srtp_params_(config.active_reset_srtp_params),
|
active_reset_srtp_params_(config.active_reset_srtp_params) {
|
||||||
bundles_(config.bundle_policy) {
|
|
||||||
// The |transport_observer| is assumed to be non-null.
|
// The |transport_observer| is assumed to be non-null.
|
||||||
RTC_DCHECK(config_.transport_observer);
|
RTC_DCHECK(config_.transport_observer);
|
||||||
RTC_DCHECK(config_.rtcp_handler);
|
RTC_DCHECK(config_.rtcp_handler);
|
||||||
@ -375,18 +374,13 @@ void JsepTransportController::SetActiveResetSrtpParams(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RTCError JsepTransportController::RollbackTransports() {
|
void JsepTransportController::RollbackTransports() {
|
||||||
if (!network_thread_->IsCurrent()) {
|
if (!network_thread_->IsCurrent()) {
|
||||||
return network_thread_->Invoke<RTCError>(
|
network_thread_->Invoke<void>(RTC_FROM_HERE, [=] { RollbackTransports(); });
|
||||||
RTC_FROM_HERE, [=] { return RollbackTransports(); });
|
return;
|
||||||
}
|
}
|
||||||
RTC_DCHECK_RUN_ON(network_thread_);
|
RTC_DCHECK_RUN_ON(network_thread_);
|
||||||
bundles_.Rollback();
|
transports_.RollbackTransports();
|
||||||
if (!transports_.RollbackTransports()) {
|
|
||||||
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
|
|
||||||
"Failed to roll back transport state.");
|
|
||||||
}
|
|
||||||
return RTCError::OK();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc::scoped_refptr<webrtc::IceTransportInterface>
|
rtc::scoped_refptr<webrtc::IceTransportInterface>
|
||||||
@ -557,6 +551,17 @@ RTCError JsepTransportController::ApplyDescription_n(
|
|||||||
MergeEncryptedHeaderExtensionIdsForBundles(description);
|
MergeEncryptedHeaderExtensionIdsForBundles(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Because the creation of transports depends on whether
|
||||||
|
// certain mids are present, we have to process rejection
|
||||||
|
// before we try to create transports.
|
||||||
|
for (size_t i = 0; i < description->contents().size(); ++i) {
|
||||||
|
const cricket::ContentInfo& content_info = description->contents()[i];
|
||||||
|
if (content_info.rejected) {
|
||||||
|
// This may cause groups to be removed from |bundles_.bundle_groups()|.
|
||||||
|
HandleRejectedContent(content_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const cricket::ContentInfo& content_info : description->contents()) {
|
for (const cricket::ContentInfo& content_info : description->contents()) {
|
||||||
// Don't create transports for rejected m-lines and bundled m-lines.
|
// Don't create transports for rejected m-lines and bundled m-lines.
|
||||||
if (content_info.rejected ||
|
if (content_info.rejected ||
|
||||||
@ -577,8 +582,6 @@ RTCError JsepTransportController::ApplyDescription_n(
|
|||||||
description->transport_infos()[i];
|
description->transport_infos()[i];
|
||||||
|
|
||||||
if (content_info.rejected) {
|
if (content_info.rejected) {
|
||||||
// This may cause groups to be removed from |bundles_.bundle_groups()|.
|
|
||||||
HandleRejectedContent(content_info);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,7 +647,6 @@ RTCError JsepTransportController::ApplyDescription_n(
|
|||||||
}
|
}
|
||||||
if (type == SdpType::kAnswer) {
|
if (type == SdpType::kAnswer) {
|
||||||
transports_.CommitTransports();
|
transports_.CommitTransports();
|
||||||
bundles_.Commit();
|
|
||||||
}
|
}
|
||||||
return RTCError::OK();
|
return RTCError::OK();
|
||||||
}
|
}
|
||||||
@ -680,44 +682,7 @@ RTCError JsepTransportController::ValidateAndMaybeUpdateBundleGroups(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == SdpType::kOffer) {
|
if (type == SdpType::kAnswer) {
|
||||||
// For an offer, we need to verify that there is not a conflicting mapping
|
|
||||||
// between existing and new bundle groups. For example, if the existing
|
|
||||||
// groups are [[1,2],[3,4]] and new are [[1,3],[2,4]] or [[1,2,3,4]], or
|
|
||||||
// vice versa. Switching things around like this requires a separate offer
|
|
||||||
// that removes the relevant sections from their group, as per RFC 8843,
|
|
||||||
// section 7.5.2.
|
|
||||||
std::map<const cricket::ContentGroup*, const cricket::ContentGroup*>
|
|
||||||
new_bundle_groups_by_existing_bundle_groups;
|
|
||||||
std::map<const cricket::ContentGroup*, const cricket::ContentGroup*>
|
|
||||||
existing_bundle_groups_by_new_bundle_groups;
|
|
||||||
for (const cricket::ContentGroup* new_bundle_group : new_bundle_groups) {
|
|
||||||
for (const std::string& mid : new_bundle_group->content_names()) {
|
|
||||||
cricket::ContentGroup* existing_bundle_group =
|
|
||||||
bundles_.LookupGroupByMid(mid);
|
|
||||||
if (!existing_bundle_group) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto it = new_bundle_groups_by_existing_bundle_groups.find(
|
|
||||||
existing_bundle_group);
|
|
||||||
if (it != new_bundle_groups_by_existing_bundle_groups.end() &&
|
|
||||||
it->second != new_bundle_group) {
|
|
||||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
|
||||||
"MID " + mid + " in the offer has changed group.");
|
|
||||||
}
|
|
||||||
new_bundle_groups_by_existing_bundle_groups.insert(
|
|
||||||
std::make_pair(existing_bundle_group, new_bundle_group));
|
|
||||||
it = existing_bundle_groups_by_new_bundle_groups.find(new_bundle_group);
|
|
||||||
if (it != existing_bundle_groups_by_new_bundle_groups.end() &&
|
|
||||||
it->second != existing_bundle_group) {
|
|
||||||
return RTCError(RTCErrorType::INVALID_PARAMETER,
|
|
||||||
"MID " + mid + " in the offer has changed group.");
|
|
||||||
}
|
|
||||||
existing_bundle_groups_by_new_bundle_groups.insert(
|
|
||||||
std::make_pair(new_bundle_group, existing_bundle_group));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (type == SdpType::kAnswer) {
|
|
||||||
std::vector<const cricket::ContentGroup*> offered_bundle_groups =
|
std::vector<const cricket::ContentGroup*> offered_bundle_groups =
|
||||||
local ? remote_desc_->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)
|
local ? remote_desc_->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE)
|
||||||
: local_desc_->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
|
: local_desc_->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
|
||||||
@ -797,7 +762,9 @@ RTCError JsepTransportController::ValidateAndMaybeUpdateBundleGroups(
|
|||||||
"max-bundle is used but no bundle group found.");
|
"max-bundle is used but no bundle group found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bundles_.Update(description, type);
|
if (ShouldUpdateBundleGroup(type, description)) {
|
||||||
|
bundles_.Update(description);
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& bundle_group : bundles_.bundle_groups()) {
|
for (const auto& bundle_group : bundles_.bundle_groups()) {
|
||||||
if (!bundle_group->FirstContentName())
|
if (!bundle_group->FirstContentName())
|
||||||
@ -905,6 +872,26 @@ JsepTransportController::CreateJsepTransportDescription(
|
|||||||
rtp_abs_sendtime_extn_id, transport_info.description);
|
rtp_abs_sendtime_extn_id, transport_info.description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JsepTransportController::ShouldUpdateBundleGroup(
|
||||||
|
SdpType type,
|
||||||
|
const cricket::SessionDescription* description) {
|
||||||
|
if (config_.bundle_policy ==
|
||||||
|
PeerConnectionInterface::kBundlePolicyMaxBundle) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != SdpType::kAnswer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTC_DCHECK(local_desc_ && remote_desc_);
|
||||||
|
std::vector<const cricket::ContentGroup*> local_bundles =
|
||||||
|
local_desc_->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
|
||||||
|
std::vector<const cricket::ContentGroup*> remote_bundles =
|
||||||
|
remote_desc_->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
|
||||||
|
return !local_bundles.empty() && !remote_bundles.empty();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int> JsepTransportController::GetEncryptedHeaderExtensionIds(
|
std::vector<int> JsepTransportController::GetEncryptedHeaderExtensionIds(
|
||||||
const cricket::ContentInfo& content_info) {
|
const cricket::ContentInfo& content_info) {
|
||||||
const cricket::MediaContentDescription* content_desc =
|
const cricket::MediaContentDescription* content_desc =
|
||||||
@ -1000,6 +987,21 @@ RTCError JsepTransportController::MaybeCreateJsepTransport(
|
|||||||
if (transport) {
|
if (transport) {
|
||||||
return RTCError::OK();
|
return RTCError::OK();
|
||||||
}
|
}
|
||||||
|
// If we have agreed to a bundle, the new mid will be added to the bundle
|
||||||
|
// according to JSEP, and the responder can't move it out of the group
|
||||||
|
// according to BUNDLE. So don't create a transport.
|
||||||
|
// The MID will be added to the bundle elsewhere in the code.
|
||||||
|
if (bundles_.bundle_groups().size() > 0) {
|
||||||
|
const auto& default_bundle_group = bundles_.bundle_groups()[0];
|
||||||
|
if (default_bundle_group->content_names().size() > 0) {
|
||||||
|
auto bundle_transport =
|
||||||
|
GetJsepTransportByName(default_bundle_group->content_names()[0]);
|
||||||
|
if (bundle_transport) {
|
||||||
|
transports_.SetTransportForMid(content_info.name, bundle_transport);
|
||||||
|
return RTCError::OK();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
const cricket::MediaContentDescription* content_desc =
|
const cricket::MediaContentDescription* content_desc =
|
||||||
content_info.media_description();
|
content_info.media_description();
|
||||||
if (certificate_ && !content_desc->cryptos().empty()) {
|
if (certificate_ && !content_desc->cryptos().empty()) {
|
||||||
|
@ -224,7 +224,9 @@ class JsepTransportController : public sigslot::has_slots<> {
|
|||||||
|
|
||||||
void SetActiveResetSrtpParams(bool active_reset_srtp_params);
|
void SetActiveResetSrtpParams(bool active_reset_srtp_params);
|
||||||
|
|
||||||
RTCError RollbackTransports();
|
// For now the rollback only removes mid to transport mappings
|
||||||
|
// and deletes unused transports, but doesn't consider anything more complex.
|
||||||
|
void RollbackTransports();
|
||||||
|
|
||||||
// F: void(const std::string&, const std::vector<cricket::Candidate>&)
|
// F: void(const std::string&, const std::vector<cricket::Candidate>&)
|
||||||
template <typename F>
|
template <typename F>
|
||||||
@ -340,6 +342,9 @@ class JsepTransportController : public sigslot::has_slots<> {
|
|||||||
const std::vector<int>& encrypted_extension_ids,
|
const std::vector<int>& encrypted_extension_ids,
|
||||||
int rtp_abs_sendtime_extn_id);
|
int rtp_abs_sendtime_extn_id);
|
||||||
|
|
||||||
|
bool ShouldUpdateBundleGroup(SdpType type,
|
||||||
|
const cricket::SessionDescription* description);
|
||||||
|
|
||||||
std::map<const cricket::ContentGroup*, std::vector<int>>
|
std::map<const cricket::ContentGroup*, std::vector<int>>
|
||||||
MergeEncryptedHeaderExtensionIdsForBundles(
|
MergeEncryptedHeaderExtensionIdsForBundles(
|
||||||
const cricket::SessionDescription* description);
|
const cricket::SessionDescription* description);
|
||||||
|
@ -1608,356 +1608,6 @@ TEST_F(JsepTransportControllerTest, MultipleBundleGroupsChangeFirstMid) {
|
|||||||
EXPECT_EQ(mid5_transport, mid6_transport);
|
EXPECT_EQ(mid5_transport, mid6_transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(JsepTransportControllerTest,
|
|
||||||
MultipleBundleGroupsSectionsAddedInSubsequentOffer) {
|
|
||||||
static const char kMid1Audio[] = "1_audio";
|
|
||||||
static const char kMid2Audio[] = "2_audio";
|
|
||||||
static const char kMid3Audio[] = "3_audio";
|
|
||||||
static const char kMid4Video[] = "4_video";
|
|
||||||
static const char kMid5Video[] = "5_video";
|
|
||||||
static const char kMid6Video[] = "6_video";
|
|
||||||
|
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
|
||||||
// Start by grouping (kMid1Audio,kMid2Audio) and (kMid4Video,kMid4f5Video).
|
|
||||||
cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group1.AddContentName(kMid1Audio);
|
|
||||||
bundle_group1.AddContentName(kMid2Audio);
|
|
||||||
cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group2.AddContentName(kMid4Video);
|
|
||||||
bundle_group2.AddContentName(kMid5Video);
|
|
||||||
|
|
||||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
local_offer->AddGroup(bundle_group1);
|
|
||||||
local_offer->AddGroup(bundle_group2);
|
|
||||||
|
|
||||||
auto remote_answer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(remote_answer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
remote_answer->AddGroup(bundle_group1);
|
|
||||||
remote_answer->AddGroup(bundle_group2);
|
|
||||||
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
|
||||||
.ok());
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
// Add kMid3Audio and kMid6Video to the respective audio/video bundle groups.
|
|
||||||
cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group1.AddContentName(kMid3Audio);
|
|
||||||
cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group2.AddContentName(kMid6Video);
|
|
||||||
|
|
||||||
auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid3Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid5Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid6Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
subsequent_offer->AddGroup(bundle_group1);
|
|
||||||
subsequent_offer->AddGroup(bundle_group2);
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
|
|
||||||
.ok());
|
|
||||||
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
|
||||||
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
|
||||||
auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
|
||||||
auto mid4_transport = transport_controller_->GetRtpTransport(kMid4Video);
|
|
||||||
auto mid5_transport = transport_controller_->GetRtpTransport(kMid5Video);
|
|
||||||
auto mid6_transport = transport_controller_->GetRtpTransport(kMid6Video);
|
|
||||||
EXPECT_NE(mid1_transport, mid4_transport);
|
|
||||||
EXPECT_EQ(mid1_transport, mid2_transport);
|
|
||||||
EXPECT_EQ(mid2_transport, mid3_transport);
|
|
||||||
EXPECT_EQ(mid4_transport, mid5_transport);
|
|
||||||
EXPECT_EQ(mid5_transport, mid6_transport);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(JsepTransportControllerTest,
|
|
||||||
MultipleBundleGroupsCombinedInSubsequentOffer) {
|
|
||||||
static const char kMid1Audio[] = "1_audio";
|
|
||||||
static const char kMid2Audio[] = "2_audio";
|
|
||||||
static const char kMid3Video[] = "3_video";
|
|
||||||
static const char kMid4Video[] = "4_video";
|
|
||||||
|
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
|
||||||
// Start by grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
|
|
||||||
cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group1.AddContentName(kMid1Audio);
|
|
||||||
bundle_group1.AddContentName(kMid2Audio);
|
|
||||||
cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group2.AddContentName(kMid3Video);
|
|
||||||
bundle_group2.AddContentName(kMid4Video);
|
|
||||||
|
|
||||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
local_offer->AddGroup(bundle_group1);
|
|
||||||
local_offer->AddGroup(bundle_group2);
|
|
||||||
|
|
||||||
auto remote_answer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
remote_answer->AddGroup(bundle_group1);
|
|
||||||
remote_answer->AddGroup(bundle_group2);
|
|
||||||
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
|
||||||
.ok());
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
// Switch to grouping (kMid1Audio,kMid2Audio,kMid3Video,kMid4Video).
|
|
||||||
// This is a illegal without first removing m= sections from their groups.
|
|
||||||
cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
new_bundle_group.AddContentName(kMid1Audio);
|
|
||||||
new_bundle_group.AddContentName(kMid2Audio);
|
|
||||||
new_bundle_group.AddContentName(kMid3Video);
|
|
||||||
new_bundle_group.AddContentName(kMid4Video);
|
|
||||||
|
|
||||||
auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
subsequent_offer->AddGroup(new_bundle_group);
|
|
||||||
EXPECT_FALSE(
|
|
||||||
transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
|
|
||||||
.ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(JsepTransportControllerTest,
|
|
||||||
MultipleBundleGroupsSplitInSubsequentOffer) {
|
|
||||||
static const char kMid1Audio[] = "1_audio";
|
|
||||||
static const char kMid2Audio[] = "2_audio";
|
|
||||||
static const char kMid3Video[] = "3_video";
|
|
||||||
static const char kMid4Video[] = "4_video";
|
|
||||||
|
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
|
||||||
// Start by grouping (kMid1Audio,kMid2Audio,kMid3Video,kMid4Video).
|
|
||||||
cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group.AddContentName(kMid1Audio);
|
|
||||||
bundle_group.AddContentName(kMid2Audio);
|
|
||||||
bundle_group.AddContentName(kMid3Video);
|
|
||||||
bundle_group.AddContentName(kMid4Video);
|
|
||||||
|
|
||||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
local_offer->AddGroup(bundle_group);
|
|
||||||
|
|
||||||
auto remote_answer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
remote_answer->AddGroup(bundle_group);
|
|
||||||
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
|
||||||
.ok());
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
// Switch to grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
|
|
||||||
// This is a illegal without first removing m= sections from their groups.
|
|
||||||
cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
new_bundle_group1.AddContentName(kMid1Audio);
|
|
||||||
new_bundle_group1.AddContentName(kMid2Audio);
|
|
||||||
cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
new_bundle_group2.AddContentName(kMid3Video);
|
|
||||||
new_bundle_group2.AddContentName(kMid4Video);
|
|
||||||
|
|
||||||
auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
subsequent_offer->AddGroup(new_bundle_group1);
|
|
||||||
subsequent_offer->AddGroup(new_bundle_group2);
|
|
||||||
EXPECT_FALSE(
|
|
||||||
transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
|
|
||||||
.ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(JsepTransportControllerTest,
|
|
||||||
MultipleBundleGroupsShuffledInSubsequentOffer) {
|
|
||||||
static const char kMid1Audio[] = "1_audio";
|
|
||||||
static const char kMid2Audio[] = "2_audio";
|
|
||||||
static const char kMid3Video[] = "3_video";
|
|
||||||
static const char kMid4Video[] = "4_video";
|
|
||||||
|
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
|
||||||
// Start by grouping (kMid1Audio,kMid2Audio) and (kMid3Video,kMid4Video).
|
|
||||||
cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group1.AddContentName(kMid1Audio);
|
|
||||||
bundle_group1.AddContentName(kMid2Audio);
|
|
||||||
cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group2.AddContentName(kMid3Video);
|
|
||||||
bundle_group2.AddContentName(kMid4Video);
|
|
||||||
|
|
||||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(local_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
local_offer->AddGroup(bundle_group1);
|
|
||||||
local_offer->AddGroup(bundle_group2);
|
|
||||||
|
|
||||||
auto remote_answer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(remote_answer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(remote_answer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(remote_answer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(remote_answer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
remote_answer->AddGroup(bundle_group1);
|
|
||||||
remote_answer->AddGroup(bundle_group2);
|
|
||||||
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
|
||||||
.ok());
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
// Switch to grouping (kMid1Audio,kMid3Video) and (kMid2Audio,kMid3Video).
|
|
||||||
// This is a illegal without first removing m= sections from their groups.
|
|
||||||
cricket::ContentGroup new_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
new_bundle_group1.AddContentName(kMid1Audio);
|
|
||||||
new_bundle_group1.AddContentName(kMid3Video);
|
|
||||||
cricket::ContentGroup new_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
new_bundle_group2.AddContentName(kMid2Audio);
|
|
||||||
new_bundle_group2.AddContentName(kMid4Video);
|
|
||||||
|
|
||||||
auto subsequent_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(subsequent_offer.get(), kMid2Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid3Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer.get(), kMid4Video, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
subsequent_offer->AddGroup(new_bundle_group1);
|
|
||||||
subsequent_offer->AddGroup(new_bundle_group2);
|
|
||||||
EXPECT_FALSE(
|
|
||||||
transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer.get())
|
|
||||||
.ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that only a subset of all the m= sections are bundled.
|
// Tests that only a subset of all the m= sections are bundled.
|
||||||
TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
|
TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
CreateJsepTransportController(JsepTransportController::Config());
|
||||||
@ -2409,220 +2059,4 @@ TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
|
|||||||
.ok());
|
.ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(JsepTransportControllerTest, RollbackRestoresRejectedTransport) {
|
|
||||||
static const char kMid1Audio[] = "1_audio";
|
|
||||||
|
|
||||||
// Perform initial offer/answer.
|
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
|
||||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
std::unique_ptr<cricket::SessionDescription> remote_answer(
|
|
||||||
local_offer->Clone());
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
|
||||||
.ok());
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
|
||||||
|
|
||||||
// Apply a reoffer which rejects the m= section, causing the transport to be
|
|
||||||
// set to null.
|
|
||||||
auto local_reoffer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_reoffer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
local_reoffer->contents()[0].rejected = true;
|
|
||||||
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
|
|
||||||
.ok());
|
|
||||||
auto old_mid1_transport = mid1_transport;
|
|
||||||
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
|
||||||
EXPECT_EQ(nullptr, mid1_transport);
|
|
||||||
|
|
||||||
// Rolling back shouldn't just create a new transport for MID 1, it should
|
|
||||||
// restore the old transport.
|
|
||||||
EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
|
|
||||||
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
|
||||||
EXPECT_EQ(old_mid1_transport, mid1_transport);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an offer with a modified BUNDLE group causes a MID->transport mapping to
|
|
||||||
// change, rollback should restore the previous mapping.
|
|
||||||
TEST_F(JsepTransportControllerTest, RollbackRestoresPreviousTransportMapping) {
|
|
||||||
static const char kMid1Audio[] = "1_audio";
|
|
||||||
static const char kMid2Audio[] = "2_audio";
|
|
||||||
static const char kMid3Audio[] = "3_audio";
|
|
||||||
|
|
||||||
// Perform an initial offer/answer to establish a (kMid1Audio,kMid2Audio)
|
|
||||||
// group.
|
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
|
||||||
cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group.AddContentName(kMid1Audio);
|
|
||||||
bundle_group.AddContentName(kMid2Audio);
|
|
||||||
|
|
||||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(local_offer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
local_offer->AddGroup(bundle_group);
|
|
||||||
|
|
||||||
std::unique_ptr<cricket::SessionDescription> remote_answer(
|
|
||||||
local_offer->Clone());
|
|
||||||
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
|
||||||
.ok());
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
|
||||||
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
|
||||||
auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
|
||||||
EXPECT_EQ(mid1_transport, mid2_transport);
|
|
||||||
EXPECT_NE(mid1_transport, mid3_transport);
|
|
||||||
|
|
||||||
// Apply a reoffer adding kMid3Audio to the group; transport mapping should
|
|
||||||
// change, even without an answer, since this is an existing group.
|
|
||||||
bundle_group.AddContentName(kMid3Audio);
|
|
||||||
auto local_reoffer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_reoffer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_reoffer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddAudioSection(local_reoffer.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
local_reoffer->AddGroup(bundle_group);
|
|
||||||
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
// Store the old transport pointer and verify that the offer actually changed
|
|
||||||
// transports.
|
|
||||||
auto old_mid3_transport = mid3_transport;
|
|
||||||
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
|
||||||
mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
|
||||||
mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
|
||||||
EXPECT_EQ(mid1_transport, mid2_transport);
|
|
||||||
EXPECT_EQ(mid1_transport, mid3_transport);
|
|
||||||
|
|
||||||
// Rolling back shouldn't just create a new transport for MID 3, it should
|
|
||||||
// restore the old transport.
|
|
||||||
EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
|
|
||||||
mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
|
||||||
EXPECT_EQ(old_mid3_transport, mid3_transport);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that if an offer adds a MID to a specific BUNDLE group and is then
|
|
||||||
// rolled back, it can be added to a different BUNDLE group in a new offer.
|
|
||||||
// This is effectively testing that rollback resets the BundleManager state.
|
|
||||||
TEST_F(JsepTransportControllerTest, RollbackAndAddToDifferentBundleGroup) {
|
|
||||||
static const char kMid1Audio[] = "1_audio";
|
|
||||||
static const char kMid2Audio[] = "2_audio";
|
|
||||||
static const char kMid3Audio[] = "3_audio";
|
|
||||||
|
|
||||||
// Perform an initial offer/answer to establish two bundle groups, each with
|
|
||||||
// one MID.
|
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
|
||||||
cricket::ContentGroup bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group1.AddContentName(kMid1Audio);
|
|
||||||
cricket::ContentGroup bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group2.AddContentName(kMid2Audio);
|
|
||||||
|
|
||||||
auto local_offer = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(local_offer.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(local_offer.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
local_offer->AddGroup(bundle_group1);
|
|
||||||
local_offer->AddGroup(bundle_group2);
|
|
||||||
|
|
||||||
std::unique_ptr<cricket::SessionDescription> remote_answer(
|
|
||||||
local_offer->Clone());
|
|
||||||
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, local_offer.get())
|
|
||||||
.ok());
|
|
||||||
EXPECT_TRUE(transport_controller_
|
|
||||||
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
// Apply an offer that adds kMid3Audio to the first BUNDLE group.,
|
|
||||||
cricket::ContentGroup modified_bundle_group1(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
modified_bundle_group1.AddContentName(kMid1Audio);
|
|
||||||
modified_bundle_group1.AddContentName(kMid3Audio);
|
|
||||||
auto subsequent_offer_1 = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(subsequent_offer_1.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer_1.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer_1.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
subsequent_offer_1->AddGroup(modified_bundle_group1);
|
|
||||||
subsequent_offer_1->AddGroup(bundle_group2);
|
|
||||||
|
|
||||||
EXPECT_TRUE(
|
|
||||||
transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer_1.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
auto mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
|
||||||
auto mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
|
||||||
auto mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
|
||||||
EXPECT_NE(mid1_transport, mid2_transport);
|
|
||||||
EXPECT_EQ(mid1_transport, mid3_transport);
|
|
||||||
|
|
||||||
// Rollback and expect the transport to be reset.
|
|
||||||
EXPECT_TRUE(transport_controller_->RollbackTransports().ok());
|
|
||||||
EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kMid3Audio));
|
|
||||||
|
|
||||||
// Apply an offer that adds kMid3Audio to the second BUNDLE group.,
|
|
||||||
cricket::ContentGroup modified_bundle_group2(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
modified_bundle_group2.AddContentName(kMid2Audio);
|
|
||||||
modified_bundle_group2.AddContentName(kMid3Audio);
|
|
||||||
auto subsequent_offer_2 = std::make_unique<cricket::SessionDescription>();
|
|
||||||
AddAudioSection(subsequent_offer_2.get(), kMid1Audio, kIceUfrag1, kIcePwd1,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer_2.get(), kMid2Audio, kIceUfrag2, kIcePwd2,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
AddVideoSection(subsequent_offer_2.get(), kMid3Audio, kIceUfrag3, kIcePwd3,
|
|
||||||
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
|
|
||||||
nullptr);
|
|
||||||
subsequent_offer_2->AddGroup(bundle_group1);
|
|
||||||
subsequent_offer_2->AddGroup(modified_bundle_group2);
|
|
||||||
|
|
||||||
EXPECT_TRUE(
|
|
||||||
transport_controller_
|
|
||||||
->SetLocalDescription(SdpType::kOffer, subsequent_offer_2.get())
|
|
||||||
.ok());
|
|
||||||
|
|
||||||
mid1_transport = transport_controller_->GetRtpTransport(kMid1Audio);
|
|
||||||
mid2_transport = transport_controller_->GetRtpTransport(kMid2Audio);
|
|
||||||
mid3_transport = transport_controller_->GetRtpTransport(kMid3Audio);
|
|
||||||
EXPECT_NE(mid1_transport, mid2_transport);
|
|
||||||
EXPECT_EQ(mid2_transport, mid3_transport);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -909,11 +909,11 @@ TEST_F(PeerConnectionBundleTestUnifiedPlan, MultipleBundleGroups) {
|
|||||||
|
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
||||||
EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
callee->SetRemoteDescription(std::move(offer));
|
||||||
auto answer = callee->CreateAnswer();
|
auto answer = callee->CreateAnswer();
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
||||||
EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
caller->SetRemoteDescription(std::move(answer));
|
||||||
|
|
||||||
// Verify bundling on sender side.
|
// Verify bundling on sender side.
|
||||||
auto senders = caller->pc()->GetSenders();
|
auto senders = caller->pc()->GetSenders();
|
||||||
@ -938,59 +938,4 @@ TEST_F(PeerConnectionBundleTestUnifiedPlan, MultipleBundleGroups) {
|
|||||||
EXPECT_NE(receiver0_transport, receiver2_transport);
|
EXPECT_NE(receiver0_transport, receiver2_transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that, with the "max-compat" bundle policy, it's possible to add an m=
|
|
||||||
// section that's not part of an existing bundle group.
|
|
||||||
TEST_F(PeerConnectionBundleTestUnifiedPlan, AddNonBundledSection) {
|
|
||||||
RTCConfiguration config;
|
|
||||||
config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxCompat;
|
|
||||||
auto caller = CreatePeerConnection(config);
|
|
||||||
caller->AddAudioTrack("0_audio");
|
|
||||||
caller->AddAudioTrack("1_audio");
|
|
||||||
auto callee = CreatePeerConnection(config);
|
|
||||||
|
|
||||||
// Establish an existing BUNDLE group.
|
|
||||||
auto offer = caller->CreateOffer(RTCOfferAnswerOptions());
|
|
||||||
EXPECT_TRUE(
|
|
||||||
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
||||||
EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
||||||
auto answer = callee->CreateAnswer();
|
|
||||||
EXPECT_TRUE(
|
|
||||||
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
|
||||||
EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
|
||||||
|
|
||||||
// Add a track but munge SDP so it's not part of the bundle group.
|
|
||||||
caller->AddAudioTrack("3_audio");
|
|
||||||
offer = caller->CreateOffer(RTCOfferAnswerOptions());
|
|
||||||
offer->description()->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
|
|
||||||
bundle_group.AddContentName("0");
|
|
||||||
bundle_group.AddContentName("1");
|
|
||||||
offer->description()->AddGroup(bundle_group);
|
|
||||||
EXPECT_TRUE(
|
|
||||||
caller->SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
||||||
EXPECT_TRUE(callee->SetRemoteDescription(std::move(offer)));
|
|
||||||
answer = callee->CreateAnswer();
|
|
||||||
EXPECT_TRUE(
|
|
||||||
callee->SetLocalDescription(CloneSessionDescription(answer.get())));
|
|
||||||
EXPECT_TRUE(caller->SetRemoteDescription(std::move(answer)));
|
|
||||||
|
|
||||||
// Verify bundling on the sender side.
|
|
||||||
auto senders = caller->pc()->GetSenders();
|
|
||||||
ASSERT_EQ(senders.size(), 3u);
|
|
||||||
auto sender0_transport = senders[0]->dtls_transport();
|
|
||||||
auto sender1_transport = senders[1]->dtls_transport();
|
|
||||||
auto sender2_transport = senders[2]->dtls_transport();
|
|
||||||
EXPECT_EQ(sender0_transport, sender1_transport);
|
|
||||||
EXPECT_NE(sender0_transport, sender2_transport);
|
|
||||||
|
|
||||||
// Verify bundling on receiver side.
|
|
||||||
auto receivers = callee->pc()->GetReceivers();
|
|
||||||
ASSERT_EQ(receivers.size(), 3u);
|
|
||||||
auto receiver0_transport = receivers[0]->dtls_transport();
|
|
||||||
auto receiver1_transport = receivers[1]->dtls_transport();
|
|
||||||
auto receiver2_transport = receivers[2]->dtls_transport();
|
|
||||||
EXPECT_EQ(receiver0_transport, receiver1_transport);
|
|
||||||
EXPECT_NE(receiver0_transport, receiver2_transport);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -2737,10 +2737,7 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) {
|
|||||||
transceiver->internal()->set_mid(state.mid());
|
transceiver->internal()->set_mid(state.mid());
|
||||||
transceiver->internal()->set_mline_index(state.mline_index());
|
transceiver->internal()->set_mline_index(state.mline_index());
|
||||||
}
|
}
|
||||||
RTCError e = transport_controller()->RollbackTransports();
|
transport_controller()->RollbackTransports();
|
||||||
if (!e.ok()) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
transceivers()->DiscardStableStates();
|
transceivers()->DiscardStableStates();
|
||||||
pending_local_description_.reset();
|
pending_local_description_.reset();
|
||||||
pending_remote_description_.reset();
|
pending_remote_description_.reset();
|
||||||
|
Reference in New Issue
Block a user