mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 1146462: Close ICE transports when m-sections are disabled. r=drno
--HG-- extra : rebase_source : eee309eccf681f2ef606fb1e78ab6c04f59befd0
This commit is contained in:
parent
f67ba290dd
commit
c24085b44b
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user