Bug 1146462: Close ICE transports when m-sections are disabled. r=drno

This commit is contained in:
Byron Campen [:bwc] 2015-03-23 16:56:08 -07:00
parent 18ee011cb7
commit 75d2653edb
16 changed files with 289 additions and 117 deletions

View File

@ -531,14 +531,20 @@ NrIceCtx::~NrIceCtx() {
RefPtr<NrIceMediaStream>
NrIceCtx::CreateStream(const std::string& name, int components) {
RefPtr<NrIceMediaStream> stream =
NrIceMediaStream::Create(this, name, components);
return NrIceMediaStream::Create(this, name, components);
}
if (stream) {
streams_.push_back(stream);
void
NrIceCtx::SetStream(size_t index, NrIceMediaStream* stream) {
if (index >= streams_.size()) {
streams_.resize(index + 1);
}
return stream;
if (streams_[index]) {
streams_[index]->Close();
}
streams_[index] = stream;
}
std::string NrIceCtx::ufrag() const {

View File

@ -227,6 +227,8 @@ class NrIceCtx {
RefPtr<NrIceMediaStream> CreateStream(const std::string& name,
int components);
void SetStream(size_t index, NrIceMediaStream* stream);
RefPtr<NrIceMediaStream> GetStream(size_t index) {
if (index < streams_.size()) {
return streams_[index];
@ -234,9 +236,10 @@ class NrIceCtx {
return nullptr;
}
void RemoveStream(size_t index)
// Some might be null
size_t GetStreamCount() const
{
streams_[index] = nullptr;
return streams_.size();
}
// The name of the ctx

View File

@ -480,6 +480,11 @@ void NrIceMediaStream::Ready() {
void NrIceMediaStream::Close() {
MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'");
state_ = ICE_CLOSED;
stream_ = nullptr;
int r = nr_ice_remove_media_stream(ctx_->ctx(), &stream_);
if (r) {
MOZ_ASSERT(false, "Failed to remove stream");
MOZ_MTLOG(ML_ERROR, "Failed to remove stream, error=" << r);
}
}
} // close namespace

View File

@ -272,6 +272,7 @@ class IceTestPeer : public sigslot::has_slots<> {
mozilla::RefPtr<NrIceMediaStream> stream =
ice_ctx_->CreateStream(static_cast<char *>(name), components);
ice_ctx_->SetStream(streams_.size(), stream);
ASSERT_TRUE(stream);
streams_.push_back(stream);
@ -283,9 +284,19 @@ class IceTestPeer : public sigslot::has_slots<> {
void AddStream(int components)
{
test_utils->sts_target()->Dispatch(WrapRunnable(this,
&IceTestPeer::AddStream_s,
components),
test_utils->sts_target()->Dispatch(
WrapRunnable(this, &IceTestPeer::AddStream_s, components),
NS_DISPATCH_SYNC);
}
void RemoveStream_s(size_t index) {
streams_[index] = nullptr;
ice_ctx_->SetStream(index, nullptr);
}
void RemoveStream(size_t index) {
test_utils->sts_target()->Dispatch(
WrapRunnable(this, &IceTestPeer::RemoveStream_s, index),
NS_DISPATCH_SYNC);
}
@ -385,8 +396,10 @@ class IceTestPeer : public sigslot::has_slots<> {
std::vector<std::string> GetCandidates_s(size_t stream) {
std::vector<std::string> candidates;
if (stream >= streams_.size())
if (stream >= streams_.size() || !streams_[stream]) {
EXPECT_TRUE(false) << "No such stream " << stream;
return candidates;
}
std::vector<std::string> candidates_in =
streams_[stream]->GetCandidates();
@ -418,6 +431,10 @@ class IceTestPeer : public sigslot::has_slots<> {
bool gathering_complete() { return gathering_complete_; }
int ready_ct() { return ready_ct_; }
bool is_ready_s(size_t stream) {
if (!streams_[stream]) {
EXPECT_TRUE(false) << "No such stream " << stream;
return false;
}
return streams_[stream]->state() == NrIceMediaStream::ICE_OPEN;
}
bool is_ready(size_t stream)
@ -448,7 +465,7 @@ class IceTestPeer : public sigslot::has_slots<> {
if (trickle_mode == TRICKLE_NONE ||
trickle_mode == TRICKLE_REAL) {
for (size_t i=0; i<streams_.size(); ++i) {
if (streams_[i]->HasParsedAttributes()) {
if (!streams_[i] || streams_[i]->HasParsedAttributes()) {
continue;
}
std::vector<std::string> candidates =
@ -463,7 +480,7 @@ class IceTestPeer : public sigslot::has_slots<> {
} else {
// Parse empty attributes and then trickle them out later
for (size_t i=0; i<streams_.size(); ++i) {
if (streams_[i]->HasParsedAttributes()) {
if (!streams_[i] || streams_[i]->HasParsedAttributes()) {
continue;
}
std::vector<std::string> empty_attrs;
@ -496,6 +513,7 @@ class IceTestPeer : public sigslot::has_slots<> {
// The size of streams_ is not going to change out from under us, so should
// be safe here.
ASSERT_GT(remote_->streams_.size(), stream);
ASSERT_TRUE(remote_->streams_[stream]);
std::vector<SchedulableTrickleCandidate*>& candidates =
ControlTrickle(stream);
@ -523,6 +541,10 @@ class IceTestPeer : public sigslot::has_slots<> {
}
nsresult TrickleCandidate_s(const std::string &candidate, size_t stream) {
if (!streams_[stream]) {
// stream might have gone away before the trickle timer popped
return NS_OK;
}
return streams_[stream]->ParseTrickleCandidate(candidate);
}
@ -576,6 +598,10 @@ class IceTestPeer : public sigslot::has_slots<> {
void DumpAndCheckActiveCandidates_s() {
std::cerr << "Active candidates:" << std::endl;
for (size_t i=0; i < streams_.size(); ++i) {
if (!streams_[i]) {
continue;
}
for (size_t j=0; j < streams_[i]->components(); ++j) {
std::cerr << "Stream " << i << " component " << j+1 << std::endl;
@ -660,6 +686,12 @@ class IceTestPeer : public sigslot::has_slots<> {
std::cerr << "CANDIDATES:" << std::endl;
for (size_t i=0; i<streams_.size(); ++i) {
std::cerr << "Stream " << name_ << std::endl;
if (!streams_[i]) {
std::cerr << "DISABLED" << std::endl;
continue;
}
std::vector<std::string> candidates =
streams_[i]->GetCandidates();
@ -699,7 +731,7 @@ class IceTestPeer : public sigslot::has_slots<> {
std::vector<NrIceCandidatePair>* pairs)
{
MOZ_ASSERT(pairs);
if (stream_index >= streams_.size()) {
if (stream_index >= streams_.size() || !streams_[stream_index]) {
// Is there a better error for "no such index"?
ADD_FAILURE() << "No such media stream index: " << stream_index;
return NS_ERROR_INVALID_ARG;
@ -749,6 +781,9 @@ class IceTestPeer : public sigslot::has_slots<> {
void DumpCandidatePairs_s() {
std::cerr << "Dumping candidate pairs for all streams [" << std::endl;
for (size_t s = 0; s < streams_.size(); ++s) {
if (!streams_[s]) {
continue;
}
DumpCandidatePairs_s(streams_[s]);
}
std::cerr << "]" << std::endl;
@ -863,6 +898,11 @@ class IceTestPeer : public sigslot::has_slots<> {
void SendPacket(int stream, int component, const unsigned char *data,
int len) {
if (!streams_[stream]) {
ADD_FAILURE() << "No such stream " << stream;
return;
}
ASSERT_TRUE(NS_SUCCEEDED(streams_[stream]->SendPacket(component, data, len)));
++sent_;
@ -874,6 +914,8 @@ class IceTestPeer : public sigslot::has_slots<> {
}
void ParseCandidate_s(size_t i, const std::string& candidate) {
ASSERT_TRUE(streams_[i]) << "No such stream " << i;
std::vector<std::string> attributes;
attributes.push_back(candidate);
@ -892,6 +934,7 @@ class IceTestPeer : public sigslot::has_slots<> {
void DisableComponent_s(size_t stream, int component_id) {
ASSERT_LT(stream, streams_.size());
ASSERT_TRUE(streams_[stream]) << "No such stream " << stream;
nsresult res = streams_[stream]->DisableComponent(component_id);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
@ -1064,6 +1107,11 @@ class IceConnectTest : public ::testing::Test {
p2_->AddStream(components);
}
void RemoveStream(size_t index) {
p1_->RemoveStream(index);
p2_->RemoveStream(index);
}
void Init(bool set_priorities, bool allow_loopback) {
if (!initted_) {
p1_ = new IceTestPeer("P1", true, set_priorities, allow_loopback);
@ -1727,6 +1775,45 @@ TEST_F(IceConnectTest, TestConnectTrickleAddStreamAfterICE) {
AssertCheckingReached();
}
TEST_F(IceConnectTest, RemoveStream) {
AddStream("first", 1);
AddStream("second", 1);
ASSERT_TRUE(Gather());
ConnectTrickle();
RealisticTrickleDelay(p1_->ControlTrickle(0));
RealisticTrickleDelay(p2_->ControlTrickle(0));
RealisticTrickleDelay(p1_->ControlTrickle(1));
RealisticTrickleDelay(p2_->ControlTrickle(1));
ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
RemoveStream(0);
ASSERT_TRUE(Gather());
ConnectTrickle();
}
TEST_F(IceConnectTest, RemoveAndAddStream) {
AddStream("first", 1);
AddStream("second", 1);
ASSERT_TRUE(Gather());
ConnectTrickle();
RealisticTrickleDelay(p1_->ControlTrickle(0));
RealisticTrickleDelay(p2_->ControlTrickle(0));
RealisticTrickleDelay(p1_->ControlTrickle(1));
RealisticTrickleDelay(p2_->ControlTrickle(1));
ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
RemoveStream(0);
AddStream("third", 1);
ASSERT_TRUE(Gather());
ConnectTrickle();
RealisticTrickleDelay(p1_->ControlTrickle(2));
RealisticTrickleDelay(p2_->ControlTrickle(2));
ASSERT_TRUE_WAIT(p1_->ice_complete(), 1000);
ASSERT_TRUE_WAIT(p2_->ice_complete(), 1000);
}
TEST_F(IceConnectTest, TestConnectRealTrickleOneStreamOneComponent) {
AddStream("first", 1);
AddStream("second", 1);

View File

@ -599,7 +599,9 @@ class TransportTestPeer : public sigslot::has_slots<> {
// Create the media stream
mozilla::RefPtr<NrIceMediaStream> stream =
ice_ctx_->CreateStream(static_cast<char *>(name), 1);
ASSERT_TRUE(stream != nullptr);
ice_ctx_->SetStream(streams_.size(), stream);
streams_.push_back(stream);
// Listen for candidates

View File

@ -605,6 +605,33 @@ int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_m
return(_status);
}
int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp)
{
int r,_status;
nr_ice_peer_ctx *pctx;
nr_ice_media_stream *peer_stream;
pctx=STAILQ_FIRST(&ctx->peers);
while(pctx){
if(!nr_ice_peer_ctx_find_pstream(pctx, *streamp, &peer_stream)) {
if(r=nr_ice_peer_ctx_remove_pstream(pctx, &peer_stream)) {
ABORT(r);
}
}
pctx=STAILQ_NEXT(pctx,entry);
}
STAILQ_REMOVE(&ctx->streams,*streamp,nr_ice_media_stream_,entry);
if(r=nr_ice_media_stream_destroy(streamp)) {
ABORT(r);
}
_status=0;
abort:
return(_status);
}
int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp)
{
char **attrs=0;

View File

@ -162,6 +162,7 @@ int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg);
int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg);
int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp);
int nr_ice_remove_media_stream(nr_ice_ctx *ctx,nr_ice_media_stream **streamp);
int nr_ice_get_global_attributes(nr_ice_ctx *ctx,char ***attrsp, int *attrctp);
int nr_ice_ctx_deliver_packet(nr_ice_ctx *ctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
int nr_ice_ctx_is_known_id(nr_ice_ctx *ctx, UCHAR id[12]);

View File

@ -257,6 +257,21 @@ int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str
return(_status);
}
int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp)
{
int r,_status;
STAILQ_REMOVE(&pctx->peer_streams,*pstreamp,nr_ice_media_stream_,entry);
if(r=nr_ice_media_stream_destroy(pstreamp)) {
ABORT(r);
}
_status=0;
abort:
return(_status);
}
int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *candidate)
{
nr_ice_media_stream *pstream;

View File

@ -75,6 +75,7 @@ int nr_ice_peer_ctx_create(nr_ice_ctx *ctx, nr_ice_handler *handler,char *label,
int nr_ice_peer_ctx_destroy(nr_ice_peer_ctx **pctxp);
int nr_ice_peer_ctx_parse_stream_attributes(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char **attrs, int attr_ct);
int nr_ice_peer_ctx_find_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, nr_ice_media_stream **pstreamp);
int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp);
int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand);
int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx);

View File

@ -508,17 +508,6 @@ JsepSessionImpl::CreateReoffer(const Sdp& oldLocalSdp,
{
nsresult rv;
// Figure out which mids were bundled before we begin, so we know how to
// populate candidate attributes and other transport info properly.
std::set<std::string> bundleMids;
const SdpMediaSection* bundleMsection;
rv = GetBundleInfo(oldAnswer, &bundleMids, &bundleMsection);
if (NS_FAILED(rv)) {
MOZ_ASSERT(false);
mLastError += " (This should have been caught sooner!)";
return NS_ERROR_FAILURE;
}
for (size_t i = 0; i < oldLocalSdp.GetMediaSectionCount(); ++i) {
// We do not set the direction in this function (or disable when previously
// disabled), that happens in |AddOfferMSectionsByType|
@ -530,19 +519,6 @@ JsepSessionImpl::CreateReoffer(const Sdp& oldLocalSdp,
rv = CopyStickyParams(oldAnswer.GetMediaSection(i),
&newSdp->GetMediaSection(i));
NS_ENSURE_SUCCESS(rv, rv);
const SdpMediaSection* msectionWithTransportParams =
&oldLocalSdp.GetMediaSection(i);
auto& answerAttrs = oldAnswer.GetMediaSection(i).GetAttributeList();
if (answerAttrs.HasAttribute(SdpAttribute::kMidAttribute) &&
bundleMids.count(answerAttrs.GetMid())) {
msectionWithTransportParams = bundleMsection;
}
rv = CopyTransportParams(*msectionWithTransportParams,
&newSdp->GetMediaSection(i));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
@ -553,6 +529,9 @@ JsepSessionImpl::SetupBundle(Sdp* sdp) const
{
std::vector<std::string> mids;
// This has the effect of changing the bundle level if the first m-section
// goes from disabled to enabled. This is kinda inefficient.
for (size_t i = 0; i < sdp->GetMediaSectionCount(); ++i) {
auto& attrs = sdp->GetMediaSection(i).GetAttributeList();
if (attrs.HasAttribute(SdpAttribute::kMidAttribute)) {
@ -567,6 +546,28 @@ JsepSessionImpl::SetupBundle(Sdp* sdp) const
}
}
nsresult
JsepSessionImpl::SetupTransportAttributes(Sdp* offer)
{
const Sdp* oldAnswer = GetAnswer();
if (oldAnswer) {
// Renegotiation, we might have transport attributes to copy over
for (size_t i = 0; i < oldAnswer->GetMediaSectionCount(); ++i) {
if (!MsectionIsDisabled(offer->GetMediaSection(i)) &&
!MsectionIsDisabled(oldAnswer->GetMediaSection(i)) &&
!IsBundleSlave(*oldAnswer, i)) {
nsresult rv = CopyTransportParams(
mCurrentLocalDescription->GetMediaSection(i),
&offer->GetMediaSection(i));
NS_ENSURE_SUCCESS(rv, rv);
}
}
}
return NS_OK;
}
void
JsepSessionImpl::SetupMsidSemantic(const std::vector<std::string>& msids,
Sdp* sdp) const
@ -745,12 +746,9 @@ JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
{
mLastError.clear();
switch (mState) {
case kJsepStateStable:
break;
default:
JSEP_SET_ERROR("Cannot create offer in state " << GetStateStr(mState));
return NS_ERROR_UNEXPECTED;
if (mState != kJsepStateStable) {
JSEP_SET_ERROR("Cannot create offer in state " << GetStateStr(mState));
return NS_ERROR_UNEXPECTED;
}
UniquePtr<Sdp> sdp;
@ -761,11 +759,7 @@ JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
++mSessionVersion;
if (mCurrentLocalDescription) {
rv = CreateReoffer(*mCurrentLocalDescription,
mIsOfferer ?
*mCurrentRemoteDescription :
*mCurrentLocalDescription,
sdp.get());
rv = CreateReoffer(*mCurrentLocalDescription, *GetAnswer(), sdp.get());
NS_ENSURE_SUCCESS(rv, rv);
}
@ -785,6 +779,9 @@ JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
SetupBundle(sdp.get());
rv = SetupTransportAttributes(sdp.get());
NS_ENSURE_SUCCESS(rv,rv);
*offer = sdp->ToString();
mGeneratedLocalDescription = Move(sdp);
@ -972,12 +969,9 @@ JsepSessionImpl::CreateAnswer(const JsepAnswerOptions& options,
{
mLastError.clear();
switch (mState) {
case kJsepStateHaveRemoteOffer:
break;
default:
JSEP_SET_ERROR("Cannot create answer in state " << GetStateStr(mState));
return NS_ERROR_UNEXPECTED;
if (mState != kJsepStateHaveRemoteOffer) {
JSEP_SET_ERROR("Cannot create answer in state " << GetStateStr(mState));
return NS_ERROR_UNEXPECTED;
}
// This is the heart of the negotiation code. Depressing that it's
@ -2184,13 +2178,6 @@ JsepSessionImpl::ValidateRemoteDescription(const Sdp& description)
const SdpAttributeList& oldAttrs(
mCurrentRemoteDescription->GetMediaSection(i).GetAttributeList());
if (oldBundleMids.count(oldAttrs.GetMid()) &&
!newBundleMids.count(newAttrs.GetMid())) {
JSEP_SET_ERROR("Removing m-sections from a bundle group is unsupported "
"at this time.");
return NS_ERROR_INVALID_ARG;
}
if ((newAttrs.GetIceUfrag() != oldAttrs.GetIceUfrag()) ||
(newAttrs.GetIcePwd() != oldAttrs.GetIcePwd())) {
JSEP_SET_ERROR("ICE restart is unsupported at this time "
@ -2548,11 +2535,14 @@ JsepSessionImpl::AddLocalIceCandidate(const std::string& candidate,
return NS_OK;
}
if (IsBundleSlave(*sdp, level)) {
// We do not add candidate attributes to bundled m-sections unless they
// are the "master" bundle m-section.
*skipped = true;
return NS_OK;
if (mState == kJsepStateStable) {
const Sdp* answer(GetAnswer());
if (IsBundleSlave(*answer, level)) {
// We do not add candidate attributes to bundled m-sections unless they
// are the "master" bundle m-section.
*skipped = true;
return NS_OK;
}
}
*skipped = false;
@ -2732,19 +2722,18 @@ JsepSessionImpl::GetBundleInfo(const Sdp& sdp,
}
bool
JsepSessionImpl::IsBundleSlave(const Sdp& localSdp, uint16_t level)
JsepSessionImpl::IsBundleSlave(const Sdp& sdp, uint16_t level)
{
auto& localMsection = localSdp.GetMediaSection(level);
auto& msection = sdp.GetMediaSection(level);
if (!localMsection.GetAttributeList().HasAttribute(
SdpAttribute::kMidAttribute)) {
if (!msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
// No mid, definitely no bundle for this m-section
return false;
}
std::set<std::string> bundleMids;
const SdpMediaSection* bundleMsection;
nsresult rv = GetNegotiatedBundleInfo(&bundleMids, &bundleMsection);
nsresult rv = GetBundleInfo(sdp, &bundleMids, &bundleMsection);
if (NS_FAILED(rv)) {
// Should have been caught sooner.
MOZ_ASSERT(false);
@ -2755,7 +2744,7 @@ JsepSessionImpl::IsBundleSlave(const Sdp& localSdp, uint16_t level)
return false;
}
std::string mid(localMsection.GetAttributeList().GetMid());
std::string mid(msection.GetAttributeList().GetMid());
if (bundleMids.count(mid) && level != bundleMsection->GetLevel()) {
// mid is bundled, and isn't the bundle m-section
@ -2937,6 +2926,15 @@ JsepSessionImpl::MsectionIsDisabled(const SdpMediaSection& msection) const
SdpAttribute::kBundleOnlyAttribute);
}
const Sdp*
JsepSessionImpl::GetAnswer() const
{
MOZ_ASSERT(mState == kJsepStateStable);
return mIsOfferer ? mCurrentRemoteDescription.get()
: mCurrentLocalDescription.get();
}
nsresult
JsepSessionImpl::Close()
{

View File

@ -236,6 +236,7 @@ private:
const Sdp& oldAnswer,
Sdp* newSdp);
void SetupBundle(Sdp* sdp) const;
nsresult SetupTransportAttributes(Sdp* sdp);
void SetupMsidSemantic(const std::vector<std::string>& msids, Sdp* sdp) const;
nsresult GetIdsFromMsid(const Sdp& sdp,
const SdpMediaSection& msection,
@ -312,6 +313,7 @@ private:
std::vector<uint8_t>* payloadTypesOut);
std::string GetCNAME(const SdpMediaSection& msection) const;
bool MsectionIsDisabled(const SdpMediaSection& msection) const;
const Sdp* GetAnswer() const;
std::vector<JsepSendingTrack> mLocalTracks;
std::vector<JsepReceivingTrack> mRemoteTracks;

View File

@ -371,7 +371,7 @@ nsresult MediaPipeline::SendPacket(TransportFlow *flow, const void *data,
if (res == TE_WOULDBLOCK)
return NS_OK;
MOZ_MTLOG(ML_ERROR, "Failed write on stream");
MOZ_MTLOG(ML_ERROR, "Failed write on stream " << description_);
return NS_BASE_STREAM_CLOSED;
}

View File

@ -31,6 +31,7 @@
#include "nsIContentPolicy.h"
#include "nsIProxyInfo.h"
#include "nsIProtocolProxyService.h"
#include "nsProxyRelease.h"
#ifdef MOZILLA_INTERNAL_API
#include "MediaStreamList.h"
@ -400,12 +401,15 @@ PeerConnectionMedia::StartIceChecks(const JsepSession& session) {
std::vector<size_t> numComponentsByLevel;
auto transports = session.GetTransports();
for (auto i = transports.begin(); i != transports.end(); ++i) {
RefPtr<JsepTransport> transport = *i;
for (size_t i = 0; i < transports.size(); ++i) {
RefPtr<JsepTransport> transport = transports[i];
if (transport->mState == JsepTransport::kJsepTransportClosed) {
CSFLogDebug(logTag, "Transport %s is disabled",
transport->mTransportId.c_str());
numComponentsByLevel.push_back(0);
// Make sure the MediaPipelineFactory doesn't try to use these.
RemoveTransportFlow(i, false);
RemoveTransportFlow(i, true);
} else {
CSFLogDebug(logTag, "Transport %s has %u components",
transport->mTransportId.c_str(),
@ -463,9 +467,10 @@ PeerConnectionMedia::StartIceChecks_s(
continue;
}
if (!stream->HasParsedAttributes()) {
if (!aComponentCountByLevel[i]) {
// Inactive stream. Remove.
mIceCtx->RemoveStream(i);
mIceCtx->SetStream(i, nullptr);
continue;
}
for (size_t c = aComponentCountByLevel[i]; c < stream->components(); ++c) {
@ -494,16 +499,17 @@ void
PeerConnectionMedia::AddIceCandidate_s(const std::string& aCandidate,
const std::string& aMid,
uint32_t aMLine) {
if (aMLine >= mIceStreams.size()) {
CSFLogError(logTag, "Couldn't process ICE candidate for bogus level %u",
aMLine);
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aMLine));
if (!stream) {
CSFLogError(logTag, "No ICE stream for candidate at level %u: %s",
static_cast<unsigned>(aMLine), aCandidate.c_str());
return;
}
nsresult rv = mIceStreams[aMLine]->ParseTrickleCandidate(aCandidate);
nsresult rv = stream->ParseTrickleCandidate(aCandidate);
if (NS_FAILED(rv)) {
CSFLogError(logTag, "Couldn't process ICE candidate at level %u",
aMLine);
static_cast<unsigned>(aMLine));
return;
}
}
@ -562,24 +568,13 @@ PeerConnectionMedia::UpdateIceMediaStream_s(size_t aMLine,
const std::string& aPassword,
const std::vector<std::string>&
aCandidateList) {
if (aMLine > mIceStreams.size()) {
CSFLogError(logTag, "Missing stream for previous m-line %u, this can "
"happen if we failed to create a stream earlier.",
static_cast<unsigned>(aMLine - 1));
return;
}
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aMLine));
if (!stream) {
CSFLogDebug(logTag, "%s: Creating ICE media stream=%u components=%u",
mParentHandle.c_str(),
static_cast<unsigned>(aMLine),
static_cast<unsigned>(aComponentCount));
CSFLogDebug(logTag, "%s: Creating ICE media stream=%u components=%u",
mParentHandle.c_str(),
static_cast<unsigned>(aMLine),
static_cast<unsigned>(aComponentCount));
RefPtr<NrIceMediaStream> stream;
if (mIceStreams.size() == aMLine) {
mIceStreams.push_back(nullptr);
}
if (!mIceStreams[aMLine]) {
std::ostringstream os;
os << mParentName << " level=" << aMLine;
stream = mIceCtx->CreateStream(os.str().c_str(),
@ -595,9 +590,7 @@ PeerConnectionMedia::UpdateIceMediaStream_s(size_t aMLine,
stream->SignalCandidate.connect(this,
&PeerConnectionMedia::OnCandidateFound_s);
mIceStreams[aMLine] = stream;
} else {
stream = mIceStreams[aMLine];
mIceCtx->SetStream(aMLine, stream);
}
if (aHasAttrs && !stream->HasParsedAttributes()) {
@ -767,7 +760,6 @@ PeerConnectionMedia::ShutdownMediaTransport_s()
disconnect_all();
mTransportFlows.clear();
mIceStreams.clear();
mIceCtx = nullptr;
mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
@ -991,7 +983,7 @@ void
PeerConnectionMedia::AddTransportFlow(int aIndex, bool aRtcp,
const RefPtr<TransportFlow> &aFlow)
{
int index_inner = aIndex * 2 + (aRtcp ? 1 : 0);
int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
MOZ_ASSERT(!mTransportFlows[index_inner]);
mTransportFlows[index_inner] = aFlow;
@ -1001,6 +993,16 @@ PeerConnectionMedia::AddTransportFlow(int aIndex, bool aRtcp,
NS_DISPATCH_NORMAL);
}
void
PeerConnectionMedia::RemoveTransportFlow(int aIndex, bool aRtcp)
{
int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
TransportFlow* flow = mTransportFlows[index_inner].forget().take();
if (flow) {
NS_ProxyRelease(GetSTSThread(), flow);
}
}
void
PeerConnectionMedia::ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow)
{

View File

@ -236,16 +236,11 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
RefPtr<NrIceCtx> ice_ctx() const { return mIceCtx; }
RefPtr<NrIceMediaStream> ice_media_stream(size_t i) const {
// TODO(ekr@rtfm.com): If someone asks for a value that doesn't exist,
// make one.
if (i >= mIceStreams.size()) {
return nullptr;
}
return mIceStreams[i];
return mIceCtx->GetStream(i);
}
size_t num_ice_media_streams() const {
return mIceStreams.size();
return mIceCtx->GetStreamCount();
}
// Create and modify transports in response to negotiation events.
@ -319,11 +314,15 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
const nsCOMPtr<nsIThread>& GetMainThread() const { return mMainThread; }
const nsCOMPtr<nsIEventTarget>& GetSTSThread() const { return mSTSThread; }
static size_t GetTransportFlowIndex(int aStreamIndex, bool aRtcp)
{
return aStreamIndex * 2 + (aRtcp ? 1 : 0);
}
// Get a transport flow either RTP/RTCP for a particular stream
// A stream can be of audio/video/datachannel/budled(?) types
RefPtr<TransportFlow> GetTransportFlow(int aStreamIndex,
bool aIsRtcp) {
int index_inner = aStreamIndex * 2 + (aIsRtcp ? 1 : 0);
RefPtr<TransportFlow> GetTransportFlow(int aStreamIndex, bool aIsRtcp) {
int index_inner = GetTransportFlowIndex(aStreamIndex, aIsRtcp);
if (mTransportFlows.find(index_inner) == mTransportFlows.end())
return nullptr;
@ -334,6 +333,7 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
// Add a transport flow
void AddTransportFlow(int aIndex, bool aRtcp,
const RefPtr<TransportFlow> &aFlow);
void RemoveTransportFlow(int aIndex, bool aRtcp);
void ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow);
void DtlsConnected_s(TransportLayer* aFlow,
TransportLayer::State state);
@ -479,7 +479,6 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
// ICE objects
RefPtr<NrIceCtx> mIceCtx;
std::vector<RefPtr<NrIceMediaStream> > mIceStreams;
// DNS
nsRefPtr<NrIceResolver> mDNSResolver;

View File

@ -1880,6 +1880,14 @@ TEST_P(JsepSessionTest, RenegotiationOffererDisablesBundleTransport)
offererPairs[0].mRtpTransport.get());
ASSERT_NE(newAnswererPairs[0].mRtpTransport.get(),
answererPairs[0].mRtpTransport.get());
ASSERT_LE(1U, mSessionOff.GetTransports().size());
ASSERT_LE(1U, mSessionAns.GetTransports().size());
ASSERT_EQ(JsepTransport::kJsepTransportClosed,
mSessionOff.GetTransports()[0]->mState);
ASSERT_EQ(JsepTransport::kJsepTransportClosed,
mSessionAns.GetTransports()[0]->mState);
}
TEST_P(JsepSessionTest, RenegotiationAnswererDisablesBundleTransport)

View File

@ -2372,6 +2372,22 @@ TEST_P(SignalingTest, RenegotiationOffererRemovesTrack)
CloseStreams();
}
TEST_P(SignalingTest, RenegotiationBothRemoveThenAddTrack)
{
OfferOptions options;
OfferAnswer(options, OFFER_AV | ANSWER_AV);
a1_->RemoveTrack(0, false);
a2_->RemoveTrack(0, false);
OfferAnswer(options, OFFER_NONE);
// OFFER_AUDIO causes a new audio track to be added on both sides
OfferAnswer(options, OFFER_AUDIO);
CloseStreams();
}
TEST_P(SignalingTest, RenegotiationOffererReplacesTrack)
{
OfferOptions options;