Bug 1160280: Put ssrc attributes in recvonly m-sections. r=ekr

This commit is contained in:
Byron Campen [:bwc] 2015-04-30 13:03:16 -07:00
parent 1814e0f1e8
commit 521fa356e9
7 changed files with 216 additions and 87 deletions

View File

@ -111,16 +111,8 @@ JsepSessionImpl::AddTrack(const RefPtr<JsepTrack>& track)
if (track->GetSsrcs().empty()) {
uint32_t ssrc;
do {
SECStatus rv = PK11_GenerateRandom(
reinterpret_cast<unsigned char*>(&ssrc), sizeof(ssrc));
if (rv != SECSuccess) {
JSEP_SET_ERROR("Failed to generate SSRC, error=" << rv);
return NS_ERROR_FAILURE;
}
} while (mSsrcs.count(ssrc));
mSsrcs.insert(ssrc);
nsresult rv = CreateSsrc(&ssrc);
NS_ENSURE_SUCCESS(rv, rv);
track->AddSsrc(ssrc);
}
}
@ -403,7 +395,7 @@ JsepSessionImpl::BindTrackToMsection(
SdpMediaSection* msection)
{
if (msection->GetMediaType() != SdpMediaSection::kApplication) {
AddLocalSsrcs(*track->mTrack, msection);
SetSsrcs(track->mTrack->GetSsrcs(), msection);
AddLocalIds(*track->mTrack, msection);
}
msection->SetSending(true);
@ -841,21 +833,24 @@ JsepSessionImpl::AddMid(const std::string& mid,
}
void
JsepSessionImpl::AddLocalSsrcs(const JsepTrack& track,
SdpMediaSection* msection) const
JsepSessionImpl::SetSsrcs(const std::vector<uint32_t>& ssrcs,
SdpMediaSection* msection) const
{
UniquePtr<SdpSsrcAttributeList> ssrcs(new SdpSsrcAttributeList);
for (auto i = track.GetSsrcs().begin(); i != track.GetSsrcs().end(); ++i) {
if (ssrcs.empty()) {
msection->GetAttributeList().RemoveAttribute(SdpAttribute::kSsrcAttribute);
return;
}
UniquePtr<SdpSsrcAttributeList> ssrcAttr(new SdpSsrcAttributeList);
for (auto ssrc : ssrcs) {
// When using ssrc attributes, we are required to at least have a cname.
// (See https://tools.ietf.org/html/rfc5576#section-6.1)
std::string cnameAttr("cname:");
cnameAttr += track.GetCNAME();
ssrcs->PushEntry(*i, cnameAttr);
cnameAttr += mCNAME;
ssrcAttr->PushEntry(ssrc, cnameAttr);
}
if (!ssrcs->mSsrcs.empty()) {
msection->GetAttributeList().SetAttribute(ssrcs.release());
}
msection->GetAttributeList().SetAttribute(ssrcAttr.release());
}
void
@ -1024,15 +1019,7 @@ JsepSessionImpl::CreateAnswer(const JsepAnswerOptions& options,
for (size_t i = 0; i < numMsections; ++i) {
const SdpMediaSection& remoteMsection = offer.GetMediaSection(i);
SdpMediaSection& msection =
sdp->AddMediaSection(remoteMsection.GetMediaType(),
SdpDirectionAttribute::kInactive,
9,
remoteMsection.GetProtocol(),
sdp::kIPv4,
"0.0.0.0");
rv = CreateAnswerMSection(options, i, remoteMsection, &msection, sdp.get());
rv = CreateAnswerMSection(options, i, remoteMsection, sdp.get());
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1101,7 +1088,7 @@ JsepSessionImpl::CreateOfferMSection(SdpMediaSection::MediaType mediatype,
SdpMediaSection* msection =
&sdp->AddMediaSection(mediatype, dir, 0, proto, sdp::kIPv4, "0.0.0.0");
return EnableMsection(msection);
return EnableOfferMsection(msection);
}
nsresult
@ -1126,7 +1113,7 @@ JsepSessionImpl::GetFreeMsectionForSend(
if (MsectionIsDisabled(msection)) {
// Was disabled; revive
nsresult rv = EnableMsection(&msection);
nsresult rv = EnableOfferMsection(&msection);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1148,14 +1135,21 @@ nsresult
JsepSessionImpl::CreateAnswerMSection(const JsepAnswerOptions& options,
size_t mlineIndex,
const SdpMediaSection& remoteMsection,
SdpMediaSection* msection,
Sdp* sdp)
{
nsresult rv = CopyStickyParams(remoteMsection, msection);
SdpMediaSection& msection =
sdp->AddMediaSection(remoteMsection.GetMediaType(),
SdpDirectionAttribute::kInactive,
9,
remoteMsection.GetProtocol(),
sdp::kIPv4,
"0.0.0.0");
nsresult rv = CopyStickyParams(remoteMsection, &msection);
NS_ENSURE_SUCCESS(rv, rv);
if (MsectionIsDisabled(remoteMsection)) {
DisableMsection(sdp, msection);
DisableMsection(sdp, &msection);
return NS_OK;
}
@ -1163,18 +1157,21 @@ JsepSessionImpl::CreateAnswerMSection(const JsepAnswerOptions& options,
rv = DetermineAnswererSetupRole(remoteMsection, &role);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddTransportAttributes(msection, role);
rv = AddTransportAttributes(&msection, role);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetRecvonlySsrc(&msection);
NS_ENSURE_SUCCESS(rv, rv);
// Only attempt to match up local tracks if the offerer has elected to
// receive traffic.
if (remoteMsection.IsReceiving()) {
rv = BindMatchingLocalTrackForAnswer(msection);
rv = BindMatchingLocalTrackForAnswer(&msection);
NS_ENSURE_SUCCESS(rv, rv);
}
if (remoteMsection.IsSending()) {
msection->SetReceiving(true);
msection.SetReceiving(true);
}
// Now add the codecs.
@ -1185,7 +1182,7 @@ JsepSessionImpl::CreateAnswerMSection(const JsepAnswerOptions& options,
UniquePtr<JsepCodecDescription> negotiated(
codec->MakeNegotiatedCodec(remoteMsection));
if (negotiated) {
negotiated->AddToMediaSection(*msection);
negotiated->AddToMediaSection(msection);
// TODO(bug 1099351): Once bug 1073475 is fixed on all supported
// versions, we can remove this limitation.
break;
@ -1193,21 +1190,38 @@ JsepSessionImpl::CreateAnswerMSection(const JsepAnswerOptions& options,
}
// Add extmap attributes.
AddCommonExtmaps(remoteMsection, msection);
AddCommonExtmaps(remoteMsection, &msection);
if (!msection->IsReceiving() && !msection->IsSending()) {
DisableMsection(sdp, msection);
if (!msection.IsReceiving() && !msection.IsSending()) {
DisableMsection(sdp, &msection);
return NS_OK;
}
if (msection->GetFormats().empty()) {
if (msection.GetFormats().empty()) {
// Could not negotiate anything. Disable m-section.
DisableMsection(sdp, msection);
DisableMsection(sdp, &msection);
}
return NS_OK;
}
nsresult
JsepSessionImpl::SetRecvonlySsrc(SdpMediaSection* msection)
{
// If previous m-sections are disabled, we do not call this function for them
while (mRecvonlySsrcs.size() <= msection->GetLevel()) {
uint32_t ssrc;
nsresult rv = CreateSsrc(&ssrc);
NS_ENSURE_SUCCESS(rv, rv);
mRecvonlySsrcs.push_back(ssrc);
}
std::vector<uint32_t> ssrcs;
ssrcs.push_back(mRecvonlySsrcs[msection->GetLevel()]);
SetSsrcs(ssrcs, msection);
return NS_OK;
}
nsresult
JsepSessionImpl::BindMatchingLocalTrackForAnswer(SdpMediaSection* msection)
{
@ -1598,6 +1612,10 @@ JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
trackPairOut->mLevel = local.GetLevel();
MOZ_ASSERT(mRecvonlySsrcs.size() > local.GetLevel(),
"Failed to set the default ssrc for an active m-section");
trackPairOut->mRecvonlySsrc = mRecvonlySsrcs[local.GetLevel()];
if (usingBundle) {
trackPairOut->mBundleLevel = Some(transportLevel);
}
@ -2363,6 +2381,22 @@ JsepSessionImpl::SetupIds()
return NS_OK;
}
nsresult
JsepSessionImpl::CreateSsrc(uint32_t* ssrc)
{
do {
SECStatus rv = PK11_GenerateRandom(
reinterpret_cast<unsigned char*>(ssrc), sizeof(uint32_t));
if (rv != SECSuccess) {
JSEP_SET_ERROR("Failed to generate SSRC, error=" << rv);
return NS_ERROR_FAILURE;
}
} while (mSsrcs.count(*ssrc));
mSsrcs.insert(*ssrc);
return NS_OK;
}
void
JsepSessionImpl::SetupDefaultCodecs()
{
@ -2836,7 +2870,7 @@ JsepSessionImpl::DisableMsection(Sdp* sdp, SdpMediaSection* msection) const
}
nsresult
JsepSessionImpl::EnableMsection(SdpMediaSection* msection)
JsepSessionImpl::EnableOfferMsection(SdpMediaSection* msection)
{
// We assert here because adding rtcp-mux to a non-disabled m-section that
// did not already have rtcp-mux can cause problems.
@ -2855,6 +2889,9 @@ JsepSessionImpl::EnableMsection(SdpMediaSection* msection)
nsresult rv = AddTransportAttributes(msection, SdpSetupAttribute::kActpass);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetRecvonlySsrc(msection);
NS_ENSURE_SUCCESS(rv, rv);
AddCodecs(msection);
AddExtmap(msection);

View File

@ -177,7 +177,8 @@ private:
void AddCodecs(SdpMediaSection* msection) const;
void AddExtmap(SdpMediaSection* msection) const;
void AddMid(const std::string& mid, SdpMediaSection* msection) const;
void AddLocalSsrcs(const JsepTrack& track, SdpMediaSection* msection) const;
void SetSsrcs(const std::vector<uint32_t>& ssrcs,
SdpMediaSection* msection) const;
void AddLocalIds(const JsepTrack& track, SdpMediaSection* msection) const;
JsepCodecDescription* FindMatchingCodec(
const std::string& pt,
@ -190,6 +191,7 @@ private:
void AddCommonExtmaps(const SdpMediaSection& remoteMsection,
SdpMediaSection* msection);
nsresult SetupIds();
nsresult CreateSsrc(uint32_t* ssrc);
void SetupDefaultCodecs();
void SetupDefaultRtpExtensions();
void SetState(JsepSignalingState state);
@ -262,8 +264,8 @@ private:
nsresult CreateAnswerMSection(const JsepAnswerOptions& options,
size_t mlineIndex,
const SdpMediaSection& remoteMsection,
SdpMediaSection* msection,
Sdp* sdp);
nsresult SetRecvonlySsrc(SdpMediaSection* msection);
nsresult BindMatchingLocalTrackForAnswer(SdpMediaSection* msection);
nsresult DetermineAnswererSetupRole(const SdpMediaSection& remoteMsection,
SdpSetupAttribute::Role* rolep);
@ -308,7 +310,7 @@ private:
bool IsBundleSlave(const Sdp& localSdp, uint16_t level);
void DisableMsection(Sdp* sdp, SdpMediaSection* msection) const;
nsresult EnableMsection(SdpMediaSection* msection);
nsresult EnableOfferMsection(SdpMediaSection* msection);
nsresult SetUniquePayloadTypes();
nsresult GetAllPayloadTypes(const JsepTrackNegotiatedDetails& trackDetails,
@ -343,6 +345,9 @@ private:
// Used to prevent duplicate local SSRCs. Not used to prevent local/remote or
// remote-only duplication, which will be important for EKT but not now.
std::set<uint32_t> mSsrcs;
// When an m-section doesn't have a local track, it still needs an ssrc, which
// is stored here.
std::vector<uint32_t> mRecvonlySsrcs;
UniquePtr<Sdp> mGeneratedLocalDescription; // Created but not set.
UniquePtr<Sdp> mCurrentLocalDescription;
UniquePtr<Sdp> mCurrentRemoteDescription;

View File

@ -162,6 +162,7 @@ struct JsepTrackPair {
size_t mLevel;
// Is this track pair sharing a transport with another?
Maybe<size_t> mBundleLevel;
uint32_t mRecvonlySsrc;
RefPtr<JsepTrack> mSending;
RefPtr<JsepTrack> mReceiving;
RefPtr<JsepTransport> mRtpTransport;

View File

@ -112,7 +112,31 @@ WebrtcAudioConduit::~WebrtcAudioConduit()
bool WebrtcAudioConduit::SetLocalSSRC(unsigned int ssrc)
{
return !mPtrRTP->SetLocalSSRC(mChannel, ssrc);
unsigned int oldSsrc;
if (!GetLocalSSRC(&oldSsrc)) {
MOZ_ASSERT(false, "GetLocalSSRC failed");
return false;
}
if (oldSsrc == ssrc) {
return true;
}
bool wasTransmitting = mEngineTransmitting;
if (StopTransmitting() != kMediaConduitNoError) {
return false;
}
if (mPtrRTP->SetLocalSSRC(mChannel, ssrc)) {
return false;
}
if (wasTransmitting) {
if (StartTransmitting() != kMediaConduitNoError) {
return false;
}
}
return true;
}
bool WebrtcAudioConduit::GetLocalSSRC(unsigned int* ssrc) {

View File

@ -163,7 +163,31 @@ WebrtcVideoConduit::~WebrtcVideoConduit()
bool WebrtcVideoConduit::SetLocalSSRC(unsigned int ssrc)
{
return !mPtrRTP->SetLocalSSRC(mChannel, ssrc);
unsigned int oldSsrc;
if (!GetLocalSSRC(&oldSsrc)) {
MOZ_ASSERT(false, "GetLocalSSRC failed");
return false;
}
if (oldSsrc == ssrc) {
return true;
}
bool wasTransmitting = mEngineTransmitting;
if (StopTransmitting() != kMediaConduitNoError) {
return false;
}
if (mPtrRTP->SetLocalSSRC(mChannel, ssrc)) {
return false;
}
if (wasTransmitting) {
if (StartTransmitting() != kMediaConduitNoError) {
return false;
}
}
return true;
}
bool WebrtcVideoConduit::GetLocalSSRC(unsigned int* ssrc)

View File

@ -365,6 +365,20 @@ MediaPipelineFactory::CreateOrUpdateMediaPipeline(
return NS_ERROR_FAILURE;
}
RefPtr<MediaSessionConduit> conduit;
if (aTrack.GetMediaType() == SdpMediaSection::kAudio) {
rv = GetOrCreateAudioConduit(aTrackPair, aTrack, &conduit);
if (NS_FAILED(rv))
return rv;
} else if (aTrack.GetMediaType() == SdpMediaSection::kVideo) {
rv = GetOrCreateVideoConduit(aTrackPair, aTrack, &conduit);
if (NS_FAILED(rv))
return rv;
} else {
// We've created the TransportFlow, nothing else to do here.
return NS_OK;
}
RefPtr<MediaPipeline> pipeline =
stream->GetPipelineByTrackId_m(aTrack.GetTrackId());
@ -391,20 +405,6 @@ MediaPipelineFactory::CreateOrUpdateMediaPipeline(
<< " type=" << aTrack.GetMediaType()
<< " direction=" << aTrack.GetDirection());
RefPtr<MediaSessionConduit> conduit;
if (aTrack.GetMediaType() == SdpMediaSection::kAudio) {
rv = GetOrCreateAudioConduit(aTrackPair, aTrack, &conduit);
if (NS_FAILED(rv))
return rv;
} else if (aTrack.GetMediaType() == SdpMediaSection::kVideo) {
rv = GetOrCreateVideoConduit(aTrackPair, aTrack, &conduit);
if (NS_FAILED(rv))
return rv;
} else {
// We've created the TransportFlow, nothing else to do here.
return NS_OK;
}
if (receiving) {
rv = CreateMediaPipelineReceiving(aTrackPair, aTrack,
level, rtpFlow, rtcpFlow, filter,
@ -619,6 +619,15 @@ MediaPipelineFactory::GetOrCreateAudioConduit(
MOZ_MTLOG(ML_ERROR, "ConfigureRecvMediaCodecs failed: " << error);
return NS_ERROR_FAILURE;
}
if (!aTrackPair.mSending) {
// No send track, but we still need to configure an SSRC for receiver
// reports.
if (!conduit->SetLocalSSRC(aTrackPair.mRecvonlySsrc)) {
MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed");
return NS_ERROR_FAILURE;
}
}
} else {
// For now we only expect to have one ssrc per local track.
auto ssrcs = aTrack.GetSsrcs();
@ -740,6 +749,15 @@ MediaPipelineFactory::GetOrCreateVideoConduit(
MOZ_MTLOG(ML_ERROR, "ConfigureRecvMediaCodecs failed: " << error);
return NS_ERROR_FAILURE;
}
if (!aTrackPair.mSending) {
// No send track, but we still need to configure an SSRC for receiver
// reports.
if (!conduit->SetLocalSSRC(aTrackPair.mRecvonlySsrc)) {
MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed");
return NS_ERROR_FAILURE;
}
}
} else {
// For now we only expect to have one ssrc per local track.
auto ssrcs = aTrack.GetSsrcs();

View File

@ -2042,29 +2042,37 @@ TEST_F(JsepSessionTest, OfferAnswerRecvOnlyLines)
std::string offer = CreateOffer(Some(options));
SipccSdpParser parser;
auto outputSdp = parser.Parse(offer);
ASSERT_TRUE(!!outputSdp) << "Should have valid SDP" << std::endl
UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
ASSERT_TRUE(!!parsedOffer) << "Should have valid SDP" << std::endl
<< "Errors were: " << GetParseErrors(parser);
ASSERT_EQ(3U, outputSdp->GetMediaSectionCount());
ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount());
ASSERT_EQ(SdpMediaSection::kAudio,
outputSdp->GetMediaSection(0).GetMediaType());
parsedOffer->GetMediaSection(0).GetMediaType());
ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
outputSdp->GetMediaSection(0).GetAttributeList().GetDirection());
ASSERT_EQ(SdpMediaSection::kVideo,
outputSdp->GetMediaSection(1).GetMediaType());
ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
outputSdp->GetMediaSection(1).GetAttributeList().GetDirection());
ASSERT_EQ(SdpMediaSection::kVideo,
outputSdp->GetMediaSection(2).GetMediaType());
ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
outputSdp->GetMediaSection(2).GetAttributeList().GetDirection());
parsedOffer->GetMediaSection(0).GetAttributeList().GetDirection());
ASSERT_TRUE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
SdpAttribute::kSsrcAttribute));
ASSERT_TRUE(outputSdp->GetMediaSection(0).GetAttributeList().HasAttribute(
ASSERT_EQ(SdpMediaSection::kVideo,
parsedOffer->GetMediaSection(1).GetMediaType());
ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
parsedOffer->GetMediaSection(1).GetAttributeList().GetDirection());
ASSERT_TRUE(parsedOffer->GetMediaSection(1).GetAttributeList().HasAttribute(
SdpAttribute::kSsrcAttribute));
ASSERT_EQ(SdpMediaSection::kVideo,
parsedOffer->GetMediaSection(2).GetMediaType());
ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
parsedOffer->GetMediaSection(2).GetAttributeList().GetDirection());
ASSERT_TRUE(parsedOffer->GetMediaSection(2).GetAttributeList().HasAttribute(
SdpAttribute::kSsrcAttribute));
ASSERT_TRUE(parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
SdpAttribute::kRtcpMuxAttribute));
ASSERT_TRUE(outputSdp->GetMediaSection(1).GetAttributeList().HasAttribute(
ASSERT_TRUE(parsedOffer->GetMediaSection(1).GetAttributeList().HasAttribute(
SdpAttribute::kRtcpMuxAttribute));
ASSERT_TRUE(outputSdp->GetMediaSection(2).GetAttributeList().HasAttribute(
ASSERT_TRUE(parsedOffer->GetMediaSection(2).GetAttributeList().HasAttribute(
SdpAttribute::kRtcpMuxAttribute));
SetLocalOffer(offer, CHECK_SUCCESS);
@ -2073,21 +2081,33 @@ TEST_F(JsepSessionTest, OfferAnswerRecvOnlyLines)
SetRemoteOffer(offer, CHECK_SUCCESS);
std::string answer = CreateAnswer();
outputSdp = parser.Parse(answer);
UniquePtr<Sdp> parsedAnswer = parser.Parse(answer);
ASSERT_EQ(3U, outputSdp->GetMediaSectionCount());
ASSERT_EQ(3U, parsedAnswer->GetMediaSectionCount());
ASSERT_EQ(SdpMediaSection::kAudio,
outputSdp->GetMediaSection(0).GetMediaType());
parsedAnswer->GetMediaSection(0).GetMediaType());
ASSERT_EQ(SdpDirectionAttribute::kSendonly,
outputSdp->GetMediaSection(0).GetAttributeList().GetDirection());
parsedAnswer->GetMediaSection(0).GetAttributeList().GetDirection());
ASSERT_EQ(SdpMediaSection::kVideo,
outputSdp->GetMediaSection(1).GetMediaType());
parsedAnswer->GetMediaSection(1).GetMediaType());
ASSERT_EQ(SdpDirectionAttribute::kSendonly,
outputSdp->GetMediaSection(1).GetAttributeList().GetDirection());
parsedAnswer->GetMediaSection(1).GetAttributeList().GetDirection());
ASSERT_EQ(SdpMediaSection::kVideo,
outputSdp->GetMediaSection(2).GetMediaType());
parsedAnswer->GetMediaSection(2).GetMediaType());
ASSERT_EQ(SdpDirectionAttribute::kInactive,
outputSdp->GetMediaSection(2).GetAttributeList().GetDirection());
parsedAnswer->GetMediaSection(2).GetAttributeList().GetDirection());
SetLocalAnswer(answer, CHECK_SUCCESS);
SetRemoteAnswer(answer, CHECK_SUCCESS);
std::vector<JsepTrackPair> trackPairs(mSessionOff.GetNegotiatedTrackPairs());
ASSERT_EQ(2U, trackPairs.size());
for (auto pair : trackPairs) {
auto ssrcs = parsedOffer->GetMediaSection(pair.mLevel).GetAttributeList()
.GetSsrc().mSsrcs;
ASSERT_EQ(1U, ssrcs.size());
ASSERT_EQ(pair.mRecvonlySsrc, ssrcs.front().ssrc);
}
}
TEST_F(JsepSessionTest, OfferAnswerSendOnlyLines)