Bug 1203246 - Factor track negotiation stuff out of JsepSessionImpl, and other simplification. r=mt

This commit is contained in:
Byron Campen [:bwc] 2015-08-25 08:16:38 -05:00
parent 6a05a4729d
commit 583bdbaf30
21 changed files with 1026 additions and 1072 deletions

View File

@ -159,6 +159,7 @@
'./src/sdp/SdpHelper.h',
'./src/sdp/SdpHelper.cpp',
'./src/sdp/SdpMediaSection.h',
'./src/sdp/SdpMediaSection.cpp',
'./src/sdp/SipccSdp.h',
'./src/sdp/SipccSdpAttributeList.h',
'./src/sdp/SipccSdpAttributeList.cpp',
@ -173,6 +174,7 @@
'./src/jsep/JsepSession.h',
'./src/jsep/JsepSessionImpl.cpp',
'./src/jsep/JsepSessionImpl.h',
'./src/jsep/JsepTrack.cpp',
'./src/jsep/JsepTrack.h',
'./src/jsep/JsepTransport.h'
],

View File

@ -5,7 +5,6 @@
#ifndef _JSEPCODECDESCRIPTION_H_
#define _JSEPCODECDESCRIPTION_H_
#include <iostream>
#include <string>
#include "signaling/src/sdp/SdpMediaSection.h"
#include "signaling/src/sdp/SdpHelper.h"
@ -20,7 +19,8 @@ namespace mozilla {
}
// A single entry in our list of known codecs.
struct JsepCodecDescription {
class JsepCodecDescription {
public:
JsepCodecDescription(mozilla::SdpMediaSection::MediaType type,
const std::string& defaultPt,
const std::string& name,
@ -34,14 +34,12 @@ struct JsepCodecDescription {
mChannels(channels),
mEnabled(enabled),
mStronglyPreferred(false),
mNegotiated(false)
mDirection(sdp::kSend)
{
}
virtual ~JsepCodecDescription() {}
virtual JsepCodecDescription* Clone() const = 0;
virtual void AddFmtps(SdpFmtpAttributeList& fmtp) const = 0;
virtual void AddRtcpFbs(SdpRtcpFbAttributeList& rtcpfb) const = 0;
bool
GetPtAsInt(uint16_t* ptOutparam) const
@ -75,21 +73,9 @@ struct JsepCodecDescription {
}
virtual bool
Negotiate(const SdpMediaSection& remoteMsection)
{
mNegotiated = true;
return true;
}
virtual bool LoadSendParameters(
const mozilla::SdpMediaSection& remoteMsection)
{
return true;
}
virtual bool LoadRecvParameters(
const mozilla::SdpMediaSection& remoteMsection)
Negotiate(const std::string& pt, const SdpMediaSection& remoteMsection)
{
mDefaultPt = pt;
return true;
}
@ -97,56 +83,21 @@ struct JsepCodecDescription {
AddToMediaSection(SdpMediaSection& msection) const
{
if (mEnabled && msection.GetMediaType() == mType) {
if (mType == SdpMediaSection::kApplication) {
// Hack: using mChannels for number of streams
msection.AddDataChannel(mDefaultPt, mName, mChannels);
} else {
msection.AddCodec(mDefaultPt, mName, mClock, mChannels);
// Both send and recv codec will have the same pt, so don't add twice
if (!msection.HasFormat(mDefaultPt)) {
if (mType == SdpMediaSection::kApplication) {
// Hack: using mChannels for number of streams
msection.AddDataChannel(mDefaultPt, mName, mChannels);
} else {
msection.AddCodec(mDefaultPt, mName, mClock, mChannels);
}
}
AddFmtpsToMSection(msection);
AddRtcpFbsToMSection(msection);
AddParametersToMSection(msection);
}
}
virtual void
AddFmtpsToMSection(SdpMediaSection& msection) const
{
SdpAttributeList& attrs = msection.GetAttributeList();
UniquePtr<SdpFmtpAttributeList> fmtps;
if (attrs.HasAttribute(SdpAttribute::kFmtpAttribute)) {
fmtps.reset(new SdpFmtpAttributeList(attrs.GetFmtp()));
} else {
fmtps.reset(new SdpFmtpAttributeList);
}
AddFmtps(*fmtps);
if (!fmtps->mFmtps.empty()) {
attrs.SetAttribute(fmtps.release());
}
}
virtual void
AddRtcpFbsToMSection(SdpMediaSection& msection) const
{
SdpAttributeList& attrs = msection.GetAttributeList();
UniquePtr<SdpRtcpFbAttributeList> rtcpfbs;
if (attrs.HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
rtcpfbs.reset(new SdpRtcpFbAttributeList(attrs.GetRtcpFb()));
} else {
rtcpfbs.reset(new SdpRtcpFbAttributeList);
}
AddRtcpFbs(*rtcpfbs);
if (!rtcpfbs->mFeedbacks.empty()) {
attrs.SetAttribute(rtcpfbs.release());
}
}
virtual void AddParametersToMSection(SdpMediaSection& msection) const {}
mozilla::SdpMediaSection::MediaType mType;
std::string mDefaultPt;
@ -155,10 +106,11 @@ struct JsepCodecDescription {
uint32_t mChannels;
bool mEnabled;
bool mStronglyPreferred;
bool mNegotiated;
sdp::Direction mDirection;
};
struct JsepAudioCodecDescription : public JsepCodecDescription {
class JsepAudioCodecDescription : public JsepCodecDescription {
public:
JsepAudioCodecDescription(const std::string& defaultPt,
const std::string& name,
uint32_t clock,
@ -173,25 +125,14 @@ struct JsepAudioCodecDescription : public JsepCodecDescription {
{
}
virtual void
AddFmtps(SdpFmtpAttributeList& fmtp) const override
{
// TODO
}
virtual void
AddRtcpFbs(SdpRtcpFbAttributeList& rtcpfb) const override
{
// TODO: Do we want to add anything?
}
JSEP_CODEC_CLONE(JsepAudioCodecDescription)
uint32_t mPacketSize;
uint32_t mBitrate;
};
struct JsepVideoCodecDescription : public JsepCodecDescription {
class JsepVideoCodecDescription : public JsepCodecDescription {
public:
JsepVideoCodecDescription(const std::string& defaultPt,
const std::string& name,
uint32_t clock,
@ -217,52 +158,83 @@ struct JsepVideoCodecDescription : public JsepCodecDescription {
mCcmFbTypes.push_back(SdpRtcpFbAttributeList::tmmbr);
}
virtual void
AddFmtps(SdpFmtpAttributeList& fmtp) const override
void
AddParametersToMSection(SdpMediaSection& msection) const override
{
AddFmtpsToMSection(msection);
AddRtcpFbsToMSection(msection);
}
void
AddFmtpsToMSection(SdpMediaSection& msection) const
{
if (mName == "H264") {
UniquePtr<SdpFmtpAttributeList::H264Parameters> params =
MakeUnique<SdpFmtpAttributeList::H264Parameters>();
SdpFmtpAttributeList::H264Parameters h264Params(
GetH264Parameters(mDefaultPt, msection));
params->packetization_mode = mPacketizationMode;
if (mDirection == sdp::kSend) {
if (!h264Params.level_asymmetry_allowed) {
// First time the fmtp has been set; set just in case this is for a
// sendonly m-line, since even though we aren't receiving the level
// negotiation still needs to happen (sigh).
h264Params.profile_level_id = mProfileLevelId;
}
} else {
// Parameters that only apply to what we receive
h264Params.max_mbps = mMaxMbps;
h264Params.max_fs = mMaxFs;
h264Params.max_cpb = mMaxCpb;
h264Params.max_dpb = mMaxDpb;
h264Params.max_br = mMaxBr;
strncpy(h264Params.sprop_parameter_sets,
mSpropParameterSets.c_str(),
sizeof(h264Params.sprop_parameter_sets) - 1);
h264Params.profile_level_id = mProfileLevelId;
}
// Parameters that apply to both the send and recv directions
h264Params.packetization_mode = mPacketizationMode;
// Hard-coded, may need to change someday?
params->level_asymmetry_allowed = true;
params->profile_level_id = mProfileLevelId;
params->max_mbps = mMaxMbps;
params->max_fs = mMaxFs;
params->max_cpb = mMaxCpb;
params->max_dpb = mMaxDpb;
params->max_br = mMaxBr;
strncpy(params->sprop_parameter_sets,
mSpropParameterSets.c_str(),
sizeof(params->sprop_parameter_sets) - 1);
fmtp.PushEntry(mDefaultPt, "", mozilla::Move(params));
} else if (mName == "VP8" || mName == "VP9") {
// VP8 and VP9 share the same SDP parameters thus far
UniquePtr<SdpFmtpAttributeList::VP8Parameters> params =
MakeUnique<SdpFmtpAttributeList::VP8Parameters>(
mName == "VP8" ?
SdpRtpmapAttributeList::kVP8 :
SdpRtpmapAttributeList::kVP9);
h264Params.level_asymmetry_allowed = true;
params->max_fs = mMaxFs;
params->max_fr = mMaxFr;
fmtp.PushEntry(mDefaultPt, "", mozilla::Move(params));
msection.SetFmtp(
SdpFmtpAttributeList::Fmtp(mDefaultPt, "", h264Params));
} else if (mName == "VP8" || mName == "VP9") {
if (mDirection == sdp::kRecv) {
// VP8 and VP9 share the same SDP parameters thus far
SdpFmtpAttributeList::VP8Parameters vp8Params(
GetVP8Parameters(mDefaultPt, msection));
vp8Params.max_fs = mMaxFs;
vp8Params.max_fr = mMaxFr;
msection.SetFmtp(
SdpFmtpAttributeList::Fmtp(mDefaultPt, "", vp8Params));
}
}
}
virtual void
AddRtcpFbs(SdpRtcpFbAttributeList& rtcpfb) const override
void
AddRtcpFbsToMSection(SdpMediaSection& msection) const
{
SdpRtcpFbAttributeList rtcpfbs(msection.GetRtcpFbs());
for (const auto& rtcpfb : rtcpfbs.mFeedbacks) {
if (rtcpfb.pt == mDefaultPt) {
// Already set by the codec for the other direction.
return;
}
}
for (const std::string& type : mAckFbTypes) {
rtcpfb.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kAck, type);
rtcpfbs.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kAck, type);
}
for (const std::string& type : mNackFbTypes) {
rtcpfb.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kNack, type);
rtcpfbs.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kNack, type);
}
for (const std::string& type : mCcmFbTypes) {
rtcpfb.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kCcm, type);
rtcpfbs.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kCcm, type);
}
msection.SetRtcpFbs(rtcpfbs);
}
SdpFmtpAttributeList::H264Parameters
@ -326,22 +298,50 @@ struct JsepVideoCodecDescription : public JsepCodecDescription {
}
virtual bool
Negotiate(const SdpMediaSection& remoteMsection) override
Negotiate(const std::string& pt,
const SdpMediaSection& remoteMsection) override
{
JsepCodecDescription::Negotiate(pt, remoteMsection);
if (mName == "H264") {
SdpFmtpAttributeList::H264Parameters h264Params(
GetH264Parameters(mDefaultPt, remoteMsection));
// Level is negotiated symmetrically if level asymmetry is disallowed
if (!h264Params.level_asymmetry_allowed) {
SetSaneH264Level(std::min(GetSaneH264Level(h264Params.profile_level_id),
GetSaneH264Level(mProfileLevelId)),
&mProfileLevelId);
}
// TODO(bug 1143709): max-recv-level support
if (mDirection == sdp::kSend) {
// Remote values of these apply only to the send codec.
mMaxFs = h264Params.max_fs;
mMaxMbps = h264Params.max_mbps;
mMaxCpb = h264Params.max_cpb;
mMaxDpb = h264Params.max_dpb;
mMaxBr = h264Params.max_br;
mSpropParameterSets = h264Params.sprop_parameter_sets;
// Only do this if we didn't symmetrically negotiate above
if (h264Params.level_asymmetry_allowed) {
SetSaneH264Level(GetSaneH264Level(h264Params.profile_level_id),
&mProfileLevelId);
}
} else {
// TODO(bug 1143709): max-recv-level support
}
} else if (mName == "VP8" || mName == "VP9") {
if (mDirection == sdp::kSend) {
SdpFmtpAttributeList::VP8Parameters vp8Params(
GetVP8Parameters(mDefaultPt, remoteMsection));
mMaxFs = vp8Params.max_fs;
mMaxFr = vp8Params.max_fr;
}
}
NegotiateRtcpFb(remoteMsection);
return JsepCodecDescription::Negotiate(remoteMsection);
return true;
}
// Maps the not-so-sane encoding of H264 level into something that is
@ -396,47 +396,6 @@ struct JsepVideoCodecDescription : public JsepCodecDescription {
*profileLevelId = (*profileLevelId & ~levelMask) | level;
}
virtual bool
LoadSendParameters(const mozilla::SdpMediaSection& remoteMsection) override
{
if (mName == "H264") {
SdpFmtpAttributeList::H264Parameters h264Params(
GetH264Parameters(mDefaultPt, remoteMsection));
if (!h264Params.level_asymmetry_allowed) {
SetSaneH264Level(std::min(GetSaneH264Level(h264Params.profile_level_id),
GetSaneH264Level(mProfileLevelId)),
&mProfileLevelId);
} else {
SetSaneH264Level(GetSaneH264Level(h264Params.profile_level_id),
&mProfileLevelId);
}
mMaxFs = h264Params.max_fs;
mMaxMbps = h264Params.max_mbps;
mMaxCpb = h264Params.max_cpb;
mMaxDpb = h264Params.max_dpb;
mMaxBr = h264Params.max_br;
mSpropParameterSets = h264Params.sprop_parameter_sets;
} else if (mName == "VP8" || mName == "VP9") {
SdpFmtpAttributeList::VP8Parameters vp8Params(
GetVP8Parameters(mDefaultPt, remoteMsection));
mMaxFs = vp8Params.max_fs;
mMaxFr = vp8Params.max_fr;
}
NegotiateRtcpFb(remoteMsection);
return true;
}
virtual bool
LoadRecvParameters(const mozilla::SdpMediaSection& remoteMsection) override
{
return Negotiate(remoteMsection);
}
enum Subprofile {
kH264ConstrainedBaseline,
kH264Baseline,
@ -600,7 +559,8 @@ struct JsepVideoCodecDescription : public JsepCodecDescription {
std::string mSpropParameterSets;
};
struct JsepApplicationCodecDescription : public JsepCodecDescription {
class JsepApplicationCodecDescription : public JsepCodecDescription {
public:
JsepApplicationCodecDescription(const std::string& defaultPt,
const std::string& name,
uint16_t channels,
@ -610,18 +570,6 @@ struct JsepApplicationCodecDescription : public JsepCodecDescription {
{
}
virtual void
AddFmtps(SdpFmtpAttributeList& fmtp) const override
{
// TODO: Is there anything to do here?
}
virtual void
AddRtcpFbs(SdpRtcpFbAttributeList& rtcpfb) const override
{
// Nothing to do here.
}
JSEP_CODEC_CLONE(JsepApplicationCodecDescription)
// Override, uses sctpmap instead of rtpmap

View File

@ -19,7 +19,7 @@
namespace mozilla {
// Forward declarations
struct JsepCodecDescription;
class JsepCodecDescription;
class JsepTrack;
struct JsepTrackPair;

View File

@ -19,7 +19,6 @@
#include "signaling/src/jsep/JsepTrack.h"
#include "signaling/src/jsep/JsepTrack.h"
#include "signaling/src/jsep/JsepTrackImpl.h"
#include "signaling/src/jsep/JsepTransport.h"
#include "signaling/src/sdp/Sdp.h"
#include "signaling/src/sdp/SipccSdp.h"
@ -38,15 +37,6 @@ MOZ_MTLOG_MODULE("jsep")
MOZ_MTLOG(ML_ERROR, mLastError); \
} while (0);
JsepSessionImpl::~JsepSessionImpl()
{
for (auto& codecs : mCodecsByLevel) {
for (JsepCodecDescription* codec : codecs) {
delete codec;
}
}
}
nsresult
JsepSessionImpl::Init()
{
@ -113,7 +103,7 @@ nsresult
JsepSessionImpl::AddTrack(const RefPtr<JsepTrack>& track)
{
mLastError.clear();
MOZ_ASSERT(track->GetDirection() == JsepTrack::kJsepTrackSending);
MOZ_ASSERT(track->GetDirection() == sdp::kSend);
if (track->GetMediaType() != SdpMediaSection::kApplication) {
track->SetCNAME(mCNAME);
@ -126,9 +116,10 @@ JsepSessionImpl::AddTrack(const RefPtr<JsepTrack>& track)
}
}
track->PopulateCodecs(mSupportedCodecs.values);
JsepSendingTrack strack;
strack.mTrack = track;
strack.mNegotiated = false;
mLocalTracks.push_back(strack);
@ -354,7 +345,7 @@ JsepSessionImpl::SetupOfferMSectionsByType(SdpMediaSection::MediaType mediatype,
// Make sure that m-sections that previously had a remote track have the
// recv bit set. Only matters for renegotiation.
rv = EnsureRecvForRemoteTracks(mediatype, sdp, offerToReceiveCountPtr);
rv = BindRemoteTracks(mediatype, sdp, offerToReceiveCountPtr);
NS_ENSURE_SUCCESS(rv, rv);
// If we need more recv sections, start setting the recv bit on other
@ -374,65 +365,45 @@ JsepSessionImpl::SetupOfferMSectionsByType(SdpMediaSection::MediaType mediatype,
}
nsresult
JsepSessionImpl::BindLocalTracks(SdpMediaSection::MediaType mediatype,
Sdp* sdp)
JsepSessionImpl::BindLocalTracks(SdpMediaSection::MediaType mediatype, Sdp* sdp)
{
for (auto track = mLocalTracks.begin(); track != mLocalTracks.end();
++track) {
if (mediatype != track->mTrack->GetMediaType()) {
for (JsepSendingTrack& track : mLocalTracks) {
if (mediatype != track.mTrack->GetMediaType()) {
continue;
}
SdpMediaSection* msection;
if (track->mAssignedMLine.isSome()) {
// Renegotiation
msection = &sdp->GetMediaSection(*track->mAssignedMLine);
if (track.mAssignedMLine.isSome()) {
msection = &sdp->GetMediaSection(*track.mAssignedMLine);
} else {
nsresult rv = GetFreeMsectionForSend(track->mTrack->GetMediaType(),
nsresult rv = GetFreeMsectionForSend(track.mTrack->GetMediaType(),
sdp,
&msection);
NS_ENSURE_SUCCESS(rv, rv);
track.mAssignedMLine = Some(msection->GetLevel());
}
nsresult rv = BindTrackToMsection(&(*track), msection);
NS_ENSURE_SUCCESS(rv, rv);
track.mTrack->AddToOffer(msection);
}
return NS_OK;
}
nsresult
JsepSessionImpl::BindTrackToMsection(
JsepSendingTrack* track,
SdpMediaSection* msection)
JsepSessionImpl::BindRemoteTracks(SdpMediaSection::MediaType mediatype,
Sdp* sdp,
size_t* offerToReceive)
{
if (msection->GetMediaType() != SdpMediaSection::kApplication) {
mSdpHelper.SetSsrcs(track->mTrack->GetSsrcs(), mCNAME, msection);
AddLocalIds(*track->mTrack, msection);
}
msection->SetSending(true);
track->mAssignedMLine = Some(msection->GetLevel());
track->mNegotiated = false;
return NS_OK;
}
nsresult
JsepSessionImpl::EnsureRecvForRemoteTracks(SdpMediaSection::MediaType mediatype,
Sdp* sdp,
size_t* offerToReceive)
{
for (auto track = mRemoteTracks.begin(); track != mRemoteTracks.end();
++track) {
if (mediatype != track->mTrack->GetMediaType()) {
for (JsepReceivingTrack& track : mRemoteTracks) {
if (mediatype != track.mTrack->GetMediaType()) {
continue;
}
if (!track->mAssignedMLine.isSome()) {
if (!track.mAssignedMLine.isSome()) {
MOZ_ASSERT(false);
continue;
}
auto& msection = sdp->GetMediaSection(*track->mAssignedMLine);
auto& msection = sdp->GetMediaSection(*track.mAssignedMLine);
if (mSdpHelper.MsectionIsDisabled(msection)) {
// TODO(bug 1095226) Content probably disabled this? Should we allow
@ -440,7 +411,8 @@ JsepSessionImpl::EnsureRecvForRemoteTracks(SdpMediaSection::MediaType mediatype,
continue;
}
msection.SetReceiving(true);
track.mTrack->AddToOffer(&msection);
if (offerToReceive && *offerToReceive) {
--(*offerToReceive);
}
@ -465,12 +437,12 @@ JsepSessionImpl::SetRecvAsNeededOrDisable(SdpMediaSection::MediaType mediatype,
if (offerToRecv) {
if (*offerToRecv) {
msection.SetReceiving(true);
SetupOfferToReceiveMsection(&msection);
--(*offerToRecv);
continue;
}
} else if (msection.IsSending()) {
msection.SetReceiving(true);
SetupOfferToReceiveMsection(&msection);
continue;
}
@ -483,6 +455,18 @@ JsepSessionImpl::SetRecvAsNeededOrDisable(SdpMediaSection::MediaType mediatype,
return NS_OK;
}
void
JsepSessionImpl::SetupOfferToReceiveMsection(SdpMediaSection* offer)
{
// Create a dummy recv track, and have it add codecs, set direction, etc.
RefPtr<JsepTrack> dummy = new JsepTrack(offer->GetMediaType(),
"",
"",
sdp::kRecv);
dummy->PopulateCodecs(mSupportedCodecs.values);
dummy->AddToOffer(offer);
}
nsresult
JsepSessionImpl::AddRecvonlyMsections(SdpMediaSection::MediaType mediatype,
size_t count,
@ -496,6 +480,8 @@ JsepSessionImpl::AddRecvonlyMsections(SdpMediaSection::MediaType mediatype,
sdp);
NS_ENSURE_SUCCESS(rv, rv);
SetupOfferToReceiveMsection(
&sdp->GetMediaSection(sdp->GetMediaSectionCount() - 1));
}
return NS_OK;
}
@ -622,9 +608,9 @@ JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
// Undo track assignments from a previous call to CreateOffer
// (ie; if the track has not been negotiated yet, it doesn't necessarily need
// to stay in the same m-section that it was in)
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
if (!i->mNegotiated) {
i->mAssignedMLine.reset();
for (JsepSendingTrack& trackWrapper : mLocalTracks) {
if (!trackWrapper.mTrack->GetNegotiatedDetails()) {
trackWrapper.mAssignedMLine.reset();
}
}
@ -683,128 +669,6 @@ JsepSessionImpl::GetRemoteDescription() const
return os.str();
}
void
JsepSessionImpl::AddCodecs(SdpMediaSection* msection) const
{
msection->ClearCodecs();
for (const JsepCodecDescription* codec :
mCodecsByLevel[msection->GetLevel()]) {
codec->AddToMediaSection(*msection);
}
}
void
JsepSessionImpl::PopulateCodecsByLevel(size_t numLevels)
{
while (mCodecsByLevel.size() < numLevels) {
mCodecsByLevel.push_back(CreateCodecClones());
}
}
void
JsepSessionImpl::UpdateCodecsForOffer(size_t level)
{
if (mCodecsByLevel.size() <= level) {
// New m-section, populate with defaults
PopulateCodecsByLevel(level + 1);
return;
}
ResetNonNegotiatedCodecs(level);
EnsureNoDuplicatePayloadTypes(level);
}
void
JsepSessionImpl::ResetNonNegotiatedCodecs(size_t level)
{
for (size_t i = 0; i < mSupportedCodecs.values.size(); ++i) {
if (mCodecsByLevel[level][i]->mNegotiated) {
continue;
}
delete mCodecsByLevel[level][i];
mCodecsByLevel[level][i] = mSupportedCodecs.values[i]->Clone();
}
}
void
JsepSessionImpl::GetNegotiatedPayloadTypes(size_t level,
std::set<uint16_t>* types) const
{
MOZ_RELEASE_ASSERT(level < mCodecsByLevel.size());
MOZ_RELEASE_ASSERT(mCodecsByLevel[level].size() ==
mSupportedCodecs.values.size());
for (size_t i = 0; i < mSupportedCodecs.values.size(); ++i) {
if (!mCodecsByLevel[level][i]->mNegotiated) {
continue;
}
uint16_t pt;
if (!mCodecsByLevel[level][i]->GetPtAsInt(&pt)) {
MOZ_ASSERT(false);
continue;
}
MOZ_ASSERT(!types->count(pt));
types->insert(pt);
}
}
void
JsepSessionImpl::EnsureNoDuplicatePayloadTypes(size_t level)
{
std::set<uint16_t> payloadTypes;
// Negotiated codecs need to keep their payload type. Codecs that were not
// negotiated last time can use whatever is left.
GetNegotiatedPayloadTypes(level, &payloadTypes);
for (JsepCodecDescription* codec : mCodecsByLevel[level]) {
// We assume that no duplicates were negotiated.
if (codec->mNegotiated || !codec->mEnabled) {
continue;
}
// Disable, and only re-enable if we can ensure it has a unique pt.
codec->mEnabled = false;
uint16_t currentPt;
if (!codec->GetPtAsInt(&currentPt)) {
MOZ_ASSERT(false);
continue;
}
if (!payloadTypes.count(currentPt)) {
codec->mEnabled = true;
payloadTypes.insert(currentPt);
continue;
}
// |codec| cannot use its current payload type. Try to find another.
for (uint16_t freePt = 0; freePt <= 128; ++freePt) {
// Not super efficient, but readability is probably more important.
if (!payloadTypes.count(freePt)) {
payloadTypes.insert(freePt);
codec->mEnabled = true;
std::ostringstream os;
os << freePt;
codec->mDefaultPt = os.str();
break;
}
}
}
}
std::vector<JsepCodecDescription*>
JsepSessionImpl::CreateCodecClones() const
{
std::vector<JsepCodecDescription*> clones;
for (const JsepCodecDescription* codec : mSupportedCodecs.values) {
clones.push_back(codec->Clone());
}
return clones;
}
void
JsepSessionImpl::AddExtmap(SdpMediaSection* msection) const
{
@ -825,37 +689,6 @@ JsepSessionImpl::AddMid(const std::string& mid,
SdpAttribute::kMidAttribute, mid));
}
void
JsepSessionImpl::AddLocalIds(const JsepTrack& track,
SdpMediaSection* msection) const
{
if (track.GetMediaType() == SdpMediaSection::kApplication) {
return;
}
UniquePtr<SdpMsidAttributeList> msids(new SdpMsidAttributeList);
if (msection->GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
msids->mMsids = msection->GetAttributeList().GetMsid().mMsids;
}
msids->PushEntry(track.GetStreamId(), track.GetTrackId());
msection->GetAttributeList().SetAttribute(msids.release());
}
JsepCodecDescription*
JsepSessionImpl::FindMatchingCodec(const std::string& fmt,
const SdpMediaSection& msection) const
{
for (JsepCodecDescription* codec : mCodecsByLevel[msection.GetLevel()]) {
if (codec->mEnabled && codec->Matches(fmt, msection)) {
return codec;
}
}
return nullptr;
}
const std::vector<SdpExtmapAttributeList::Extmap>*
JsepSessionImpl::GetRtpExtensions(SdpMediaSection::MediaType type) const
{
@ -869,30 +702,6 @@ JsepSessionImpl::GetRtpExtensions(SdpMediaSection::MediaType type) const
}
}
static bool
CompareCodec(const JsepCodecDescription* lhs, const JsepCodecDescription* rhs)
{
return lhs->mStronglyPreferred && !rhs->mStronglyPreferred;
}
std::vector<JsepCodecDescription*>
JsepSessionImpl::GetCommonCodecs(const SdpMediaSection& offerMsection)
{
MOZ_ASSERT(!mIsOfferer);
std::vector<JsepCodecDescription*> matchingCodecs;
for (const std::string& fmt : offerMsection.GetFormats()) {
JsepCodecDescription* codec = FindMatchingCodec(fmt, offerMsection);
if (codec) {
codec->mDefaultPt = fmt; // Remember the other side's PT
matchingCodecs.push_back(codec);
}
}
std::stable_sort(matchingCodecs.begin(), matchingCodecs.end(), CompareCodec);
return matchingCodecs;
}
void
JsepSessionImpl::AddCommonExtmaps(const SdpMediaSection& remoteMsection,
SdpMediaSection* msection)
@ -940,26 +749,24 @@ JsepSessionImpl::CreateAnswer(const JsepAnswerOptions& options,
// Disable send for local tracks if the offer no longer allows it
// (i.e., the m-section is recvonly, inactive or disabled)
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
if (!i->mAssignedMLine.isSome()) {
for (JsepSendingTrack& trackWrapper : mLocalTracks) {
if (!trackWrapper.mAssignedMLine.isSome()) {
continue;
}
// Get rid of all m-line assignments that have not been negotiated
if (!i->mNegotiated) {
i->mAssignedMLine.reset();
if (!trackWrapper.mTrack->GetNegotiatedDetails()) {
trackWrapper.mAssignedMLine.reset();
continue;
}
if (!offer.GetMediaSection(*i->mAssignedMLine).IsReceiving()) {
i->mAssignedMLine.reset();
if (!offer.GetMediaSection(*trackWrapper.mAssignedMLine).IsReceiving()) {
trackWrapper.mAssignedMLine.reset();
}
}
size_t numMsections = offer.GetMediaSectionCount();
PopulateCodecsByLevel(numMsections);
for (size_t i = 0; i < numMsections; ++i) {
const SdpMediaSection& remoteMsection = offer.GetMediaSection(i);
rv = CreateAnswerMSection(options, i, remoteMsection, sdp.get());
@ -1065,35 +872,22 @@ JsepSessionImpl::CreateAnswerMSection(const JsepAnswerOptions& options,
// Only attempt to match up local tracks if the offerer has elected to
// receive traffic.
if (remoteMsection.IsReceiving()) {
rv = BindMatchingLocalTrackForAnswer(&msection);
rv = BindMatchingLocalTrackToAnswer(&msection);
NS_ENSURE_SUCCESS(rv, rv);
}
if (remoteMsection.IsSending()) {
msection.SetReceiving(true);
BindMatchingRemoteTrackToAnswer(&msection);
}
// Now add the codecs.
std::vector<JsepCodecDescription*> matchingCodecs(
GetCommonCodecs(remoteMsection));
for (JsepCodecDescription* codec : matchingCodecs) {
if (codec->Negotiate(remoteMsection)) {
codec->AddToMediaSection(msection);
// TODO(bug 1099351): Once bug 1073475 is fixed on all supported
// versions, we can remove this limitation.
break;
}
}
// Add extmap attributes.
AddCommonExtmaps(remoteMsection, &msection);
if (!msection.IsReceiving() && !msection.IsSending()) {
mSdpHelper.DisableMsection(sdp, &msection);
return NS_OK;
}
// Add extmap attributes.
AddCommonExtmaps(remoteMsection, &msection);
if (msection.GetFormats().empty()) {
// Could not negotiate anything. Disable m-section.
mSdpHelper.DisableMsection(sdp, &msection);
@ -1115,12 +909,12 @@ JsepSessionImpl::SetRecvonlySsrc(SdpMediaSection* msection)
std::vector<uint32_t> ssrcs;
ssrcs.push_back(mRecvonlySsrcs[msection->GetLevel()]);
mSdpHelper.SetSsrcs(ssrcs, mCNAME, msection);
msection->SetSsrcs(ssrcs, mCNAME);
return NS_OK;
}
nsresult
JsepSessionImpl::BindMatchingLocalTrackForAnswer(SdpMediaSection* msection)
JsepSessionImpl::BindMatchingLocalTrackToAnswer(SdpMediaSection* msection)
{
auto track = FindTrackByLevel(mLocalTracks, msection->GetLevel());
@ -1147,13 +941,31 @@ JsepSessionImpl::BindMatchingLocalTrackForAnswer(SdpMediaSection* msection)
}
if (track != mLocalTracks.end()) {
nsresult rv = BindTrackToMsection(&(*track), msection);
NS_ENSURE_SUCCESS(rv, rv);
track->mAssignedMLine = Some(msection->GetLevel());
track->mTrack->AddToAnswer(
mPendingRemoteDescription->GetMediaSection(msection->GetLevel()),
msection);
}
return NS_OK;
}
nsresult
JsepSessionImpl::BindMatchingRemoteTrackToAnswer(SdpMediaSection* msection)
{
auto it = FindTrackByLevel(mRemoteTracks, msection->GetLevel());
if (it == mRemoteTracks.end()) {
MOZ_ASSERT(false);
JSEP_SET_ERROR("Failed to find remote track for local answer m-section");
return NS_ERROR_FAILURE;
}
it->mTrack->AddToAnswer(
mPendingRemoteDescription->GetMediaSection(msection->GetLevel()),
msection);
return NS_OK;
}
nsresult
JsepSessionImpl::DetermineAnswererSetupRole(
const SdpMediaSection& remoteMsection,
@ -1408,14 +1220,22 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
nsresult rv = mSdpHelper.GetBundledMids(answer, &bundledMids);
NS_ENSURE_SUCCESS(rv, rv);
std::vector<JsepTrackPair> trackPairs;
if (mTransports.size() < local->GetMediaSectionCount()) {
JSEP_SET_ERROR("Fewer transports set up than m-lines");
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
for (JsepSendingTrack& trackWrapper : mLocalTracks) {
trackWrapper.mTrack->ClearNegotiatedDetails();
}
for (JsepReceivingTrack& trackWrapper : mRemoteTracks) {
trackWrapper.mTrack->ClearNegotiatedDetails();
}
std::vector<JsepTrackPair> trackPairs;
// Now walk through the m-sections, make sure they match, and create
// track pairs that describe the media to be set up.
for (size_t i = 0; i < local->GetMediaSectionCount(); ++i) {
@ -1472,20 +1292,12 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
trackPairs.push_back(trackPair);
}
rv = SetUniquePayloadTypes();
NS_ENSURE_SUCCESS(rv, rv);
JsepTrack::SetUniquePayloadTypes(GetTracks(mRemoteTracks));
// Ouch, this probably needs some dirty bit instead of just clearing
// stuff for renegotiation.
mNegotiatedTrackPairs = trackPairs;
// Mark all assigned local tracks as negotiated
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
if (i->mAssignedMLine.isSome()) {
i->mNegotiated = true;
}
}
mGeneratedLocalDescription.reset();
return NS_OK;
}
@ -1538,11 +1350,7 @@ JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
return NS_ERROR_FAILURE;
}
nsresult rv = NegotiateTrack(remote,
local,
JsepTrack::kJsepTrackSending,
&sendTrack->mTrack);
NS_ENSURE_SUCCESS(rv, rv);
sendTrack->mTrack->Negotiate(answer, remote);
trackPairOut->mSending = sendTrack->mTrack;
}
@ -1557,18 +1365,7 @@ JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
return NS_ERROR_FAILURE;
}
nsresult rv = NegotiateTrack(remote,
local,
JsepTrack::kJsepTrackReceiving,
&recvTrack->mTrack);
NS_ENSURE_SUCCESS(rv, rv);
if (remote.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
auto& ssrcs = remote.GetAttributeList().GetSsrc().mSsrcs;
for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
recvTrack->mTrack->AddSsrc(i->ssrc);
}
}
recvTrack->mTrack->Negotiate(answer, remote);
if (trackPairOut->mBundleLevel.isSome() &&
recvTrack->mTrack->GetSsrcs().empty() &&
@ -1592,87 +1389,6 @@ JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
return NS_OK;
}
nsresult
JsepSessionImpl::NegotiateTrack(const SdpMediaSection& remoteMsection,
const SdpMediaSection& localMsection,
JsepTrack::Direction direction,
RefPtr<JsepTrack>* track)
{
UniquePtr<JsepTrackNegotiatedDetailsImpl> negotiatedDetails =
MakeUnique<JsepTrackNegotiatedDetailsImpl>();
negotiatedDetails->mProtocol = remoteMsection.GetProtocol();
auto& answerMsection = mIsOfferer ? remoteMsection : localMsection;
for (auto& format : answerMsection.GetFormats()) {
JsepCodecDescription* origCodec = FindMatchingCodec(format, answerMsection);
if (!origCodec) {
continue;
}
// Make sure codec->mDefaultPt is consistent with what is in the remote
// msection, since the following logic needs to look stuff up there.
for (auto& remoteFormat : remoteMsection.GetFormats()) {
if (origCodec->Matches(remoteFormat, remoteMsection)) {
origCodec->mDefaultPt = remoteFormat;
break;
}
}
UniquePtr<JsepCodecDescription> codec(origCodec->Clone());
bool sending = (direction == JsepTrack::kJsepTrackSending);
// Everywhere else in JsepSessionImpl, a JsepCodecDescription describes
// what one side puts in its SDP. However, we don't want that here; we want
// a JsepCodecDescription that instead encapsulates all the parameters
// that deal with sending (or receiving). For sending, some of these
// parameters will come from the remote SDP (eg; max-fps), and others can
// only be determined by inspecting both local config and remote SDP (eg;
// profile-level-id when level-asymmetry-allowed is 0).
if (sending) {
if (!codec->LoadSendParameters(remoteMsection)) {
continue;
}
} else {
if (!codec->LoadRecvParameters(remoteMsection)) {
continue;
}
}
if (remoteMsection.GetMediaType() == SdpMediaSection::kAudio ||
remoteMsection.GetMediaType() == SdpMediaSection::kVideo) {
// Sanity-check that payload type can work with RTP
uint16_t payloadType;
if (!codec->GetPtAsInt(&payloadType) ||
payloadType > UINT8_MAX) {
JSEP_SET_ERROR("audio/video payload type is not an 8 bit unsigned int: "
<< codec->mDefaultPt);
return NS_ERROR_INVALID_ARG;
}
}
negotiatedDetails->mCodecs.values.push_back(codec.release());
break;
}
if (negotiatedDetails->mCodecs.values.empty()) {
JSEP_SET_ERROR("Failed to negotiate codec details for all codecs");
return NS_ERROR_INVALID_ARG;
}
if (answerMsection.GetAttributeList().HasAttribute(
SdpAttribute::kExtmapAttribute)) {
auto& extmap = answerMsection.GetAttributeList().GetExtmap().mExtmaps;
for (auto i = extmap.begin(); i != extmap.end(); ++i) {
negotiatedDetails->mExtmap[i->extensionname] = *i;
}
}
(*track)->SetNegotiatedDetails(Move(negotiatedDetails));
return NS_OK;
}
void
JsepSessionImpl::UpdateTransport(const SdpMediaSection& msection,
JsepTransport* transport)
@ -1819,7 +1535,8 @@ JsepSessionImpl::ParseSdp(const std::string& sdp, UniquePtr<Sdp>* parsedp)
continue;
}
auto& mediaAttrs = parsed->GetMediaSection(i).GetAttributeList();
const SdpMediaSection& msection(parsed->GetMediaSection(i));
auto& mediaAttrs = msection.GetAttributeList();
if (mediaAttrs.GetIceUfrag().empty()) {
JSEP_SET_ERROR("Invalid description, no ice-ufrag attribute");
@ -1882,6 +1599,19 @@ JsepSessionImpl::ParseSdp(const std::string& sdp, UniquePtr<Sdp>* parsedp)
// Error has already been set
return rv;
}
if (msection.GetMediaType() == SdpMediaSection::kAudio ||
msection.GetMediaType() == SdpMediaSection::kVideo) {
// Sanity-check that payload type can work with RTP
for (const std::string& fmt : msection.GetFormats()) {
uint16_t payloadType;
// TODO (bug 1204099): Make this check for reserved ranges.
if (!SdpHelper::GetPtAsInt(fmt, &payloadType) || payloadType > 127) {
JSEP_SET_ERROR("audio/video payload type is too large: " << fmt);
return NS_ERROR_INVALID_ARG;
}
}
}
}
*parsedp = Move(parsed);
@ -2170,9 +1900,10 @@ JsepSessionImpl::CreateReceivingTrack(size_t mline,
*track = new JsepTrack(msection.GetMediaType(),
streamId,
trackId,
JsepTrack::kJsepTrackReceiving);
sdp::kRecv);
(*track)->SetCNAME(mSdpHelper.GetCNAME(msection));
(*track)->PopulateCodecs(mSupportedCodecs.values);
return NS_OK;
}
@ -2532,10 +2263,6 @@ JsepSessionImpl::EnableOfferMsection(SdpMediaSection* msection)
rv = SetRecvonlySsrc(msection);
NS_ENSURE_SUCCESS(rv, rv);
UpdateCodecsForOffer(msection->GetLevel());
AddCodecs(msection);
AddExtmap(msection);
std::ostringstream osMid;
@ -2545,96 +2272,6 @@ JsepSessionImpl::EnableOfferMsection(SdpMediaSection* msection)
return NS_OK;
}
nsresult
JsepSessionImpl::GetAllPayloadTypes(
const JsepTrackNegotiatedDetails& trackDetails,
std::vector<uint8_t>* payloadTypesOut)
{
for (size_t j = 0; j < trackDetails.GetCodecCount(); ++j) {
const JsepCodecDescription* codec;
nsresult rv = trackDetails.GetCodec(j, &codec);
if (NS_FAILED(rv)) {
JSEP_SET_ERROR("GetCodec failed in GetAllPayloadTypes. rv="
<< static_cast<uint32_t>(rv));
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
uint16_t payloadType;
if (!codec->GetPtAsInt(&payloadType) || payloadType > UINT8_MAX) {
JSEP_SET_ERROR("Non-UINT8 payload type in GetAllPayloadTypes ("
<< codec->mType
<< "), this should have been caught sooner.");
MOZ_ASSERT(false);
return NS_ERROR_INVALID_ARG;
}
payloadTypesOut->push_back(payloadType);
}
return NS_OK;
}
// When doing bundle, if all else fails we can try to figure out which m-line a
// given RTP packet belongs to by looking at the payload type field. This only
// works, however, if that payload type appeared in only one m-section.
// We figure that out here.
nsresult
JsepSessionImpl::SetUniquePayloadTypes()
{
// Maps to track details if no other track contains the payload type,
// otherwise maps to nullptr.
std::map<uint8_t, JsepTrackNegotiatedDetails*> payloadTypeToDetailsMap;
for (size_t i = 0; i < mRemoteTracks.size(); ++i) {
auto track = mRemoteTracks[i].mTrack;
if (track->GetMediaType() == SdpMediaSection::kApplication) {
continue;
}
auto* details = track->GetNegotiatedDetails();
if (!details) {
// Can happen if negotiation fails on a track
continue;
}
// Renegotiation might cause a PT to no longer be unique
details->ClearUniquePayloadTypes();
std::vector<uint8_t> payloadTypesForTrack;
nsresult rv = GetAllPayloadTypes(*details, &payloadTypesForTrack);
NS_ENSURE_SUCCESS(rv, rv);
for (auto j = payloadTypesForTrack.begin();
j != payloadTypesForTrack.end();
++j) {
if (payloadTypeToDetailsMap.count(*j)) {
// Found in more than one track, not unique
payloadTypeToDetailsMap[*j] = nullptr;
} else {
payloadTypeToDetailsMap[*j] = details;
}
}
}
for (auto i = payloadTypeToDetailsMap.begin();
i != payloadTypeToDetailsMap.end();
++i) {
uint8_t uniquePt = i->first;
auto trackDetails = i->second;
if (!trackDetails) {
continue;
}
trackDetails->AddUniquePayloadType(uniquePt);
}
return NS_OK;
}
const Sdp*
JsepSessionImpl::GetAnswer() const
{

View File

@ -13,7 +13,6 @@
#include "signaling/src/jsep/JsepTrack.h"
#include "signaling/src/jsep/JsepSession.h"
#include "signaling/src/jsep/JsepTrack.h"
#include "signaling/src/jsep/JsepTrackImpl.h"
#include "signaling/src/sdp/SipccSdpParser.h"
#include "signaling/src/sdp/SdpHelper.h"
#include "signaling/src/common/PtrVector.h"
@ -44,8 +43,6 @@ public:
{
}
virtual ~JsepSessionImpl();
// Implement JsepSession methods.
virtual nsresult Init() override;
@ -171,7 +168,6 @@ private:
struct JsepSendingTrack {
RefPtr<JsepTrack> mTrack;
Maybe<size_t> mAssignedMLine;
bool mNegotiated;
};
struct JsepReceivingTrack {
@ -181,25 +177,11 @@ private:
// Non-const so it can set mLastError
nsresult CreateGenericSDP(UniquePtr<Sdp>* sdp);
void AddCodecs(SdpMediaSection* msection) const;
void PopulateCodecsByLevel(size_t numLevels);
void UpdateCodecsForOffer(size_t level);
void ResetNonNegotiatedCodecs(size_t level);
void GetNegotiatedPayloadTypes(size_t level,
std::set<uint16_t>* types) const;
void EnsureNoDuplicatePayloadTypes(size_t level);
std::vector<JsepCodecDescription*> CreateCodecClones() const;
void AddExtmap(SdpMediaSection* msection) const;
void AddMid(const std::string& mid, SdpMediaSection* msection) const;
void AddLocalIds(const JsepTrack& track, SdpMediaSection* msection) const;
JsepCodecDescription* FindMatchingCodec(
const std::string& pt,
const SdpMediaSection& msection) const;
const std::vector<SdpExtmapAttributeList::Extmap>* GetRtpExtensions(
SdpMediaSection::MediaType type) const;
std::vector<JsepCodecDescription*> GetCommonCodecs(
const SdpMediaSection& offerMsection);
void AddCommonExtmaps(const SdpMediaSection& remoteMsection,
SdpMediaSection* msection);
nsresult SetupIds();
@ -236,14 +218,13 @@ private:
Sdp* sdp);
nsresult BindLocalTracks(SdpMediaSection::MediaType mediatype,
Sdp* sdp);
nsresult BindTrackToMsection(JsepSendingTrack* track,
SdpMediaSection* msection);
nsresult EnsureRecvForRemoteTracks(SdpMediaSection::MediaType mediatype,
Sdp* sdp,
size_t* offerToReceive);
nsresult BindRemoteTracks(SdpMediaSection::MediaType mediatype,
Sdp* sdp,
size_t* offerToReceive);
nsresult SetRecvAsNeededOrDisable(SdpMediaSection::MediaType mediatype,
Sdp* sdp,
size_t* offerToRecv);
void SetupOfferToReceiveMsection(SdpMediaSection* offer);
nsresult AddRecvonlyMsections(SdpMediaSection::MediaType mediatype,
size_t count,
Sdp* sdp);
@ -267,7 +248,8 @@ private:
const SdpMediaSection& remoteMsection,
Sdp* sdp);
nsresult SetRecvonlySsrc(SdpMediaSection* msection);
nsresult BindMatchingLocalTrackForAnswer(SdpMediaSection* msection);
nsresult BindMatchingLocalTrackToAnswer(SdpMediaSection* msection);
nsresult BindMatchingRemoteTrackToAnswer(SdpMediaSection* msection);
nsresult DetermineAnswererSetupRole(const SdpMediaSection& remoteMsection,
SdpSetupAttribute::Role* rolep);
nsresult MakeNegotiatedTrackPair(const SdpMediaSection& remote,
@ -276,11 +258,6 @@ private:
bool usingBundle,
size_t transportLevel,
JsepTrackPair* trackPairOut);
nsresult NegotiateTrack(const SdpMediaSection& remoteMsection,
const SdpMediaSection& localMsection,
JsepTrack::Direction,
RefPtr<JsepTrack>* track);
void UpdateTransport(const SdpMediaSection& msection,
JsepTransport* transport);
@ -292,9 +269,6 @@ private:
nsresult EnableOfferMsection(SdpMediaSection* msection);
nsresult SetUniquePayloadTypes();
nsresult GetAllPayloadTypes(const JsepTrackNegotiatedDetails& trackDetails,
std::vector<uint8_t>* payloadTypesOut);
const Sdp* GetAnswer() const;
std::vector<JsepSendingTrack> mLocalTracks;
@ -336,10 +310,6 @@ private:
UniquePtr<Sdp> mPendingLocalDescription;
UniquePtr<Sdp> mPendingRemoteDescription;
PtrVector<JsepCodecDescription> mSupportedCodecs;
// For each level, contains a full clone of
// mSupportedCodecs. If any have been negotiated, this negotiation is taken
// into account.
std::vector<std::vector<JsepCodecDescription*>> mCodecsByLevel;
std::string mLastError;
SipccSdpParser mParser;
SdpHelper mSdpHelper;

View File

@ -0,0 +1,303 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "signaling/src/jsep/JsepTrack.h"
#include "signaling/src/jsep/JsepCodecDescription.h"
#include <algorithm>
namespace mozilla
{
void
JsepTrack::GetNegotiatedPayloadTypes(std::vector<uint16_t>* payloadTypes)
{
if (!mNegotiatedDetails) {
return;
}
GetPayloadTypes(mNegotiatedDetails->mCodecs.values, payloadTypes);
}
/* static */
void
JsepTrack::GetPayloadTypes(
const std::vector<JsepCodecDescription*>& codecs,
std::vector<uint16_t>* payloadTypes)
{
for (JsepCodecDescription* codec : codecs) {
uint16_t pt;
if (!codec->GetPtAsInt(&pt)) {
MOZ_ASSERT(false);
continue;
}
payloadTypes->push_back(pt);
}
}
void
JsepTrack::EnsureNoDuplicatePayloadTypes(
std::vector<JsepCodecDescription*>* codecs)
{
std::set<uint16_t> uniquePayloadTypes;
for (JsepCodecDescription* codec : *codecs) {
// We assume there are no dupes in negotiated codecs; unnegotiated codecs
// need to change if there is a clash.
if (!codec->mEnabled) {
continue;
}
// Disable, and only re-enable if we can ensure it has a unique pt.
codec->mEnabled = false;
uint16_t currentPt;
if (!codec->GetPtAsInt(&currentPt)) {
MOZ_ASSERT(false);
continue;
}
if (!uniquePayloadTypes.count(currentPt)) {
codec->mEnabled = true;
uniquePayloadTypes.insert(currentPt);
continue;
}
// |codec| cannot use its current payload type. Try to find another.
for (uint16_t freePt = 96; freePt <= 127; ++freePt) {
// Not super efficient, but readability is probably more important.
if (!uniquePayloadTypes.count(freePt)) {
uniquePayloadTypes.insert(freePt);
codec->mEnabled = true;
std::ostringstream os;
os << freePt;
codec->mDefaultPt = os.str();
break;
}
}
}
}
void
JsepTrack::PopulateCodecs(const std::vector<JsepCodecDescription*>& prototype)
{
for (const JsepCodecDescription* prototypeCodec : prototype) {
if (prototypeCodec->mType == mType) {
mPrototypeCodecs.values.push_back(prototypeCodec->Clone());
mPrototypeCodecs.values.back()->mDirection = mDirection;
}
}
EnsureNoDuplicatePayloadTypes(&mPrototypeCodecs.values);
}
void
JsepTrack::AddToOffer(SdpMediaSection* offer) const
{
AddToMsection(mPrototypeCodecs.values, offer);
}
void
JsepTrack::AddToAnswer(const SdpMediaSection& offer,
SdpMediaSection* answer) const
{
// We do not modify mPrototypeCodecs here, since we're only creating an answer. Once
// offer/answer concludes, we will update mPrototypeCodecs.
PtrVector<JsepCodecDescription> codecs;
codecs.values = GetCodecClones();
NegotiateCodecs(offer, &codecs.values);
if (codecs.values.empty()) {
return;
}
AddToMsection(codecs.values, answer);
}
void
JsepTrack::AddToMsection(const std::vector<JsepCodecDescription*>& codecs,
SdpMediaSection* msection) const
{
MOZ_ASSERT(msection->GetMediaType() == mType);
MOZ_ASSERT(!codecs.empty());
for (const JsepCodecDescription* codec : codecs) {
codec->AddToMediaSection(*msection);
}
if (mDirection == sdp::kSend) {
if (msection->GetMediaType() != SdpMediaSection::kApplication) {
msection->SetSsrcs(mSsrcs, mCNAME);
msection->AddMsid(mStreamId, mTrackId);
}
msection->SetSending(true);
} else {
msection->SetReceiving(true);
}
}
std::vector<JsepCodecDescription*>
JsepTrack::GetCodecClones() const
{
std::vector<JsepCodecDescription*> clones;
for (const JsepCodecDescription* codec : mPrototypeCodecs.values) {
clones.push_back(codec->Clone());
}
return clones;
}
static bool
CompareCodec(const JsepCodecDescription* lhs, const JsepCodecDescription* rhs)
{
return lhs->mStronglyPreferred && !rhs->mStronglyPreferred;
}
void
JsepTrack::NegotiateCodecs(
const SdpMediaSection& remote,
std::vector<JsepCodecDescription*>* codecs,
const SdpMediaSection* answer,
std::map<std::string, std::string>* formatChanges) const
{
PtrVector<JsepCodecDescription> unnegotiatedCodecs;
std::swap(unnegotiatedCodecs.values, *codecs);
// Outer loop establishes the remote side's preference
for (const std::string& fmt : remote.GetFormats()) {
for (size_t i = 0; i < unnegotiatedCodecs.values.size(); ++i) {
JsepCodecDescription* codec = unnegotiatedCodecs.values[i];
if (!codec || !codec->mEnabled || !codec->Matches(fmt, remote)) {
continue;
}
std::string originalFormat = codec->mDefaultPt;
if(codec->Negotiate(fmt, remote)) {
codecs->push_back(codec);
unnegotiatedCodecs.values[i] = nullptr;
if (answer) {
// Answer's formats are authoritative, and they might be different
for (const std::string& answerFmt : answer->GetFormats()) {
if (codec->Matches(answerFmt, *answer)) {
codec->mDefaultPt = answerFmt;
break; // We found the corresponding format in |answer|, bail
}
}
}
if (formatChanges) {
(*formatChanges)[originalFormat] = codec->mDefaultPt;
}
break;
}
}
}
// Make sure strongly preferred codecs are up front, overriding the remote
// side's preference.
std::stable_sort(codecs->begin(), codecs->end(), CompareCodec);
// TODO(bug 814227): Remove this once we're ready to put multiple codecs in an
// answer
if (!codecs->empty()) {
for (size_t i = 1; i < codecs->size(); ++i) {
delete (*codecs)[i];
(*codecs)[i] = nullptr;
}
codecs->resize(1);
}
}
void
JsepTrack::Negotiate(const SdpMediaSection& answer,
const SdpMediaSection& remote)
{
UniquePtr<JsepTrackNegotiatedDetails> negotiatedDetails =
MakeUnique<JsepTrackNegotiatedDetails>();
negotiatedDetails->mCodecs.values = GetCodecClones();
std::map<std::string, std::string> formatChanges;
NegotiateCodecs(remote,
&negotiatedDetails->mCodecs.values,
&answer,
&formatChanges);
// Use |formatChanges| to update mPrototypeCodecs
size_t insertPos = 0;
for (size_t i = 0; i < mPrototypeCodecs.values.size(); ++i) {
if (formatChanges.count(mPrototypeCodecs.values[i]->mDefaultPt)) {
// Update the payload type to what was negotiated
mPrototypeCodecs.values[i]->mDefaultPt =
formatChanges[mPrototypeCodecs.values[i]->mDefaultPt];
// Move this negotiated codec up front
std::swap(mPrototypeCodecs.values[insertPos],
mPrototypeCodecs.values[i]);
++insertPos;
}
}
EnsureNoDuplicatePayloadTypes(&mPrototypeCodecs.values);
if (answer.GetAttributeList().HasAttribute(SdpAttribute::kExtmapAttribute)) {
for (auto& extmapAttr : answer.GetAttributeList().GetExtmap().mExtmaps) {
negotiatedDetails->mExtmap[extmapAttr.extensionname] = extmapAttr;
}
}
if ((mDirection == sdp::kRecv) &&
remote.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
for (auto& ssrcAttr : remote.GetAttributeList().GetSsrc().mSsrcs) {
AddSsrc(ssrcAttr.ssrc);
}
}
mNegotiatedDetails = Move(negotiatedDetails);
}
// When doing bundle, if all else fails we can try to figure out which m-line a
// given RTP packet belongs to by looking at the payload type field. This only
// works, however, if that payload type appeared in only one m-section.
// We figure that out here.
/* static */
void
JsepTrack::SetUniquePayloadTypes(const std::vector<RefPtr<JsepTrack>>& tracks)
{
// Maps to track details if no other track contains the payload type,
// otherwise maps to nullptr.
std::map<uint16_t, JsepTrackNegotiatedDetails*> payloadTypeToDetailsMap;
for (const RefPtr<JsepTrack>& track : tracks) {
if (track->GetMediaType() == SdpMediaSection::kApplication) {
continue;
}
auto* details = track->GetNegotiatedDetails();
if (!details) {
// Can happen if negotiation fails on a track
continue;
}
std::vector<uint16_t> payloadTypesForTrack;
track->GetNegotiatedPayloadTypes(&payloadTypesForTrack);
for (uint16_t pt : payloadTypesForTrack) {
if (payloadTypeToDetailsMap.count(pt)) {
// Found in more than one track, not unique
payloadTypeToDetailsMap[pt] = nullptr;
} else {
payloadTypeToDetailsMap[pt] = details;
}
}
}
for (auto ptAndDetails : payloadTypeToDetailsMap) {
uint16_t uniquePt = ptAndDetails.first;
MOZ_ASSERT(uniquePt <= UINT8_MAX);
auto trackDetails = ptAndDetails.second;
if (trackDetails) {
trackDetails->mUniquePayloadTypes.push_back(
static_cast<uint8_t>(uniquePt));
}
}
}
} // namespace mozilla

View File

@ -6,6 +6,8 @@
#define _JSEPTRACK_H_
#include <string>
#include <map>
#include <set>
#include <mozilla/RefPtr.h>
#include <mozilla/UniquePtr.h>
@ -15,46 +17,66 @@
#include "signaling/src/jsep/JsepTransport.h"
#include "signaling/src/sdp/Sdp.h"
#include "signaling/src/sdp/SdpAttribute.h"
#include "signaling/src/sdp/SdpMediaSection.h"
#include "signaling/src/common/PtrVector.h"
namespace mozilla {
// Forward reference.
struct JsepCodecDescription;
class JsepCodecDescription;
class JsepTrackNegotiatedDetails
{
public:
virtual ~JsepTrackNegotiatedDetails() {}
size_t
GetCodecCount() const
{
return mCodecs.values.size();
}
virtual mozilla::SdpMediaSection::Protocol GetProtocol() const = 0;
virtual Maybe<std::string> GetBandwidth(const std::string& type) const = 0;
virtual size_t GetCodecCount() const = 0;
virtual nsresult GetCodec(size_t index,
const JsepCodecDescription** config) const = 0;
virtual const SdpExtmapAttributeList::Extmap* GetExt(
const std::string& ext_name) const = 0;
virtual std::vector<uint8_t> GetUniquePayloadTypes() const = 0;
const JsepCodecDescription*
GetCodec(size_t index) const
{
MOZ_RELEASE_ASSERT(index < mCodecs.values.size());
return mCodecs.values[index];
}
virtual void AddUniquePayloadType(uint8_t pt) = 0;
virtual void ClearUniquePayloadTypes() = 0;
const SdpExtmapAttributeList::Extmap*
GetExt(const std::string& ext_name) const
{
auto it = mExtmap.find(ext_name);
if (it != mExtmap.end()) {
return &it->second;
}
return nullptr;
}
std::vector<uint8_t> GetUniquePayloadTypes() const
{
return mUniquePayloadTypes;
}
private:
friend class JsepTrack;
std::map<std::string, SdpExtmapAttributeList::Extmap> mExtmap;
std::vector<uint8_t> mUniquePayloadTypes;
PtrVector<JsepCodecDescription> mCodecs;
};
class JsepTrack
{
public:
enum Direction { kJsepTrackSending, kJsepTrackReceiving };
JsepTrack(mozilla::SdpMediaSection::MediaType type,
const std::string& streamid,
const std::string& trackid,
Direction direction = kJsepTrackSending)
sdp::Direction direction = sdp::kSend)
: mType(type),
mStreamId(streamid),
mTrackId(trackid),
mDirection(direction)
{
}
{}
virtual mozilla::SdpMediaSection::MediaType
GetMediaType() const
@ -98,7 +120,7 @@ public:
mCNAME = cname;
}
virtual Direction
virtual sdp::Direction
GetDirection() const
{
return mDirection;
@ -116,6 +138,17 @@ public:
mSsrcs.push_back(ssrc);
}
virtual void PopulateCodecs(
const std::vector<JsepCodecDescription*>& prototype);
virtual void AddToOffer(SdpMediaSection* offer) const;
virtual void AddToAnswer(const SdpMediaSection& offer,
SdpMediaSection* answer) const;
virtual void Negotiate(const SdpMediaSection& answer,
const SdpMediaSection& remote);
static void SetUniquePayloadTypes(
const std::vector<RefPtr<JsepTrack>>& tracks);
virtual void GetNegotiatedPayloadTypes(std::vector<uint16_t>* payloadTypes);
// This will be set when negotiation is carried out.
virtual const JsepTrackNegotiatedDetails*
GetNegotiatedDetails() const
@ -135,11 +168,10 @@ public:
return nullptr;
}
// This is for JsepSession's use.
virtual void
SetNegotiatedDetails(UniquePtr<JsepTrackNegotiatedDetails> details)
ClearNegotiatedDetails()
{
mNegotiatedDetails = Move(details);
mNegotiatedDetails.reset();
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JsepTrack);
@ -148,11 +180,34 @@ protected:
virtual ~JsepTrack() {}
private:
virtual std::vector<JsepCodecDescription*> GetCodecClones() const;
static void EnsureNoDuplicatePayloadTypes(
std::vector<JsepCodecDescription*>* codecs);
static void GetPayloadTypes(
const std::vector<JsepCodecDescription*>& codecs,
std::vector<uint16_t>* pts);
static void EnsurePayloadTypeIsUnique(std::set<uint16_t>* uniquePayloadTypes,
JsepCodecDescription* codec);
virtual void AddToMsection(const std::vector<JsepCodecDescription*>& codecs,
SdpMediaSection* msection) const;
// |answer| is set when performing the final negotiation on completion of
// offer/answer, and is used to update the formats in |codecs|, since the
// answer is authoritative. |formatChanges| is also set on completion of
// offer/answer, and records how the formats in |codecs| were changed, which
// is used by |Negotiate| to update |mPrototypeCodecs|.
virtual void NegotiateCodecs(
const SdpMediaSection& remote,
std::vector<JsepCodecDescription*>* codecs,
const SdpMediaSection* answer = nullptr,
std::map<std::string, std::string>* formatChanges = nullptr) const;
const mozilla::SdpMediaSection::MediaType mType;
std::string mStreamId;
std::string mTrackId;
std::string mCNAME;
const Direction mDirection;
const sdp::Direction mDirection;
PtrVector<JsepCodecDescription> mPrototypeCodecs;
UniquePtr<JsepTrackNegotiatedDetails> mNegotiatedDetails;
std::vector<uint32_t> mSsrcs;
};

View File

@ -1,92 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _JSEPTRACKIMPL_H_
#define _JSEPTRACKIMPL_H_
#include <map>
#include <mozilla/RefPtr.h>
#include <mozilla/UniquePtr.h>
#include "signaling/src/jsep/JsepCodecDescription.h"
#include "signaling/src/jsep/JsepTrack.h"
#include "signaling/src/sdp/Sdp.h"
#include "signaling/src/sdp/SdpMediaSection.h"
#include "signaling/src/common/PtrVector.h"
namespace mozilla {
class JsepTrackNegotiatedDetailsImpl : public JsepTrackNegotiatedDetails
{
public:
virtual ~JsepTrackNegotiatedDetailsImpl()
{}
// Implement JsepTrackNegotiatedDetails.
virtual mozilla::SdpMediaSection::Protocol
GetProtocol() const override
{
return mProtocol;
}
virtual Maybe<std::string>
GetBandwidth(const std::string& type) const override
{
return mBandwidth;
}
virtual size_t
GetCodecCount() const override
{
return mCodecs.values.size();
}
virtual nsresult
GetCodec(size_t index, const JsepCodecDescription** config) const override
{
if (index >= mCodecs.values.size()) {
return NS_ERROR_INVALID_ARG;
}
*config = mCodecs.values[index];
return NS_OK;
}
virtual const SdpExtmapAttributeList::Extmap*
GetExt(const std::string& ext_name) const override
{
auto it = mExtmap.find(ext_name);
if (it != mExtmap.end()) {
return &it->second;
}
return nullptr;
}
virtual std::vector<uint8_t> GetUniquePayloadTypes() const override
{
return mUniquePayloadTypes;
}
virtual void AddUniquePayloadType(uint8_t pt) override
{
mUniquePayloadTypes.push_back(pt);
}
virtual void ClearUniquePayloadTypes() override
{
mUniquePayloadTypes.clear();
}
private:
// Make these friends to JsepSessionImpl to avoid having to
// write setters.
friend class JsepSessionImpl;
mozilla::SdpMediaSection::Protocol mProtocol;
Maybe<std::string> mBandwidth;
PtrVector<JsepCodecDescription> mCodecs;
std::map<std::string, SdpExtmapAttributeList::Extmap> mExtmap;
std::vector<uint8_t> mUniquePayloadTypes;
};
} // namespace mozilla
#endif

View File

@ -281,8 +281,7 @@ MediaPipelineFactory::GetTransportParameters(
}
if (aTrackPair.mBundleLevel.isSome()) {
bool receiving =
aTrack.GetDirection() == JsepTrack::Direction::kJsepTrackReceiving;
bool receiving = aTrack.GetDirection() == sdp::kRecv;
*aFilterOut = new MediaPipelineFilter;
@ -329,8 +328,7 @@ MediaPipelineFactory::CreateOrUpdateMediaPipeline(
MOZ_ASSERT(aTrackPair.mRtpTransport);
bool receiving =
aTrack.GetDirection() == JsepTrack::Direction::kJsepTrackReceiving;
bool receiving = aTrack.GetDirection() == sdp::kRecv;
size_t level;
RefPtr<TransportFlow> rtpFlow;
@ -570,6 +568,15 @@ MediaPipelineFactory::CreateMediaPipelineSending(
return NS_OK;
}
static const JsepCodecDescription*
GetBestCodec(const JsepTrackNegotiatedDetails& details)
{
if (details.GetCodecCount()) {
return details.GetCodec(0);
}
return nullptr;
}
nsresult
MediaPipelineFactory::GetOrCreateAudioConduit(
const JsepTrackPair& aTrackPair,
@ -582,8 +589,7 @@ MediaPipelineFactory::GetOrCreateAudioConduit(
return NS_ERROR_INVALID_ARG;
}
bool receiving =
aTrack.GetDirection() == JsepTrack::Direction::kJsepTrackReceiving;
bool receiving = aTrack.GetDirection() == sdp::kRecv;
RefPtr<AudioSessionConduit> conduit =
mPCMedia->GetAudioConduit(aTrackPair.mLevel);
@ -598,27 +604,21 @@ MediaPipelineFactory::GetOrCreateAudioConduit(
mPCMedia->AddAudioConduit(aTrackPair.mLevel, conduit);
}
size_t numCodecs = aTrack.GetNegotiatedDetails()->GetCodecCount();
if (numCodecs == 0) {
if (!GetBestCodec(*aTrack.GetNegotiatedDetails())) {
MOZ_MTLOG(ML_ERROR, "Can't set up a conduit with 0 codecs");
return NS_ERROR_FAILURE;
}
size_t numCodecs = aTrack.GetNegotiatedDetails()->GetCodecCount();
if (receiving) {
PtrVector<AudioCodecConfig> configs;
for (size_t i = 0; i < numCodecs; i++) {
const JsepCodecDescription* cdesc;
nsresult rv = aTrack.GetNegotiatedDetails()->GetCodec(i, &cdesc);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
MOZ_MTLOG(ML_ERROR, "Failed to get codec from jsep track, rv="
<< static_cast<uint32_t>(rv));
return rv;
}
const JsepCodecDescription* cdesc =
aTrack.GetNegotiatedDetails()->GetCodec(i);
AudioCodecConfig* configRaw;
rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
if (NS_FAILED(rv))
return rv;
@ -652,18 +652,11 @@ MediaPipelineFactory::GetOrCreateAudioConduit(
conduit->SetLocalCNAME(aTrack.GetCNAME().c_str());
const JsepCodecDescription* cdesc;
// Best codec.
nsresult rv = aTrack.GetNegotiatedDetails()->GetCodec(0, &cdesc);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
MOZ_MTLOG(ML_ERROR, "Failed to get codec from jsep track, rv="
<< static_cast<uint32_t>(rv));
return rv;
}
const JsepCodecDescription* cdesc =
GetBestCodec(*aTrack.GetNegotiatedDetails());
AudioCodecConfig* configRaw;
rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
if (NS_FAILED(rv))
return rv;
@ -706,8 +699,7 @@ MediaPipelineFactory::GetOrCreateVideoConduit(
return NS_ERROR_INVALID_ARG;
}
bool receiving =
aTrack.GetDirection() == JsepTrack::Direction::kJsepTrackReceiving;
bool receiving = aTrack.GetDirection() == sdp::kRecv;
RefPtr<VideoSessionConduit> conduit =
mPCMedia->GetVideoConduit(aTrackPair.mLevel);
@ -715,35 +707,35 @@ MediaPipelineFactory::GetOrCreateVideoConduit(
if (!conduit) {
conduit = VideoSessionConduit::Create();
if (!conduit) {
MOZ_MTLOG(ML_ERROR, "Could not create audio conduit");
MOZ_MTLOG(ML_ERROR, "Could not create video conduit");
return NS_ERROR_FAILURE;
}
mPCMedia->AddVideoConduit(aTrackPair.mLevel, conduit);
}
size_t numCodecs = aTrack.GetNegotiatedDetails()->GetCodecCount();
if (numCodecs == 0) {
if (!GetBestCodec(*aTrack.GetNegotiatedDetails())) {
MOZ_MTLOG(ML_ERROR, "Can't set up a conduit with 0 codecs");
return NS_ERROR_FAILURE;
}
size_t numCodecs = aTrack.GetNegotiatedDetails()->GetCodecCount();
bool configuredH264 = false;
if (receiving) {
PtrVector<VideoCodecConfig> configs;
for (size_t i = 0; i < numCodecs; i++) {
const JsepCodecDescription* cdesc;
const JsepCodecDescription* cdesc =
aTrack.GetNegotiatedDetails()->GetCodec(i);
nsresult rv = aTrack.GetNegotiatedDetails()->GetCodec(i, &cdesc);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
MOZ_MTLOG(ML_ERROR, "Failed to get codec from jsep track, rv="
<< static_cast<uint32_t>(rv));
return rv;
// We can only handle configuring one recv H264 codec
if (configuredH264 && (cdesc->mName == "H264")) {
continue;
}
VideoCodecConfig* configRaw;
rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
if (NS_FAILED(rv))
return rv;
@ -752,6 +744,9 @@ MediaPipelineFactory::GetOrCreateVideoConduit(
continue;
}
if (cdesc->mName == "H264") {
configuredH264 = true;
}
configs.values.push_back(config.release());
}
@ -782,18 +777,11 @@ MediaPipelineFactory::GetOrCreateVideoConduit(
conduit->SetLocalCNAME(aTrack.GetCNAME().c_str());
const JsepCodecDescription* cdesc;
// Best codec.
nsresult rv = aTrack.GetNegotiatedDetails()->GetCodec(0, &cdesc);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
MOZ_MTLOG(ML_ERROR, "Failed to get codec from jsep track, rv="
<< static_cast<uint32_t>(rv));
return rv;
}
const JsepCodecDescription* cdesc =
GetBestCodec(*aTrack.GetNegotiatedDetails());
VideoCodecConfig* configRaw;
rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
if (NS_FAILED(rv))
return rv;

View File

@ -389,6 +389,7 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
, mUuidGen(MakeUnique<PCUuidGenerator>())
, mNumAudioStreams(0)
, mNumVideoStreams(0)
, mHaveConfiguredCodecs(false)
, mHaveDataStream(false)
, mAddCandidateErrorCount(0)
, mTrickle(true) // TODO(ekr@rtfm.com): Use pref
@ -929,6 +930,11 @@ class CompareCodecPriority {
nsresult
PeerConnectionImpl::ConfigureJsepSessionCodecs() {
if (mHaveConfiguredCodecs) {
return NS_OK;
}
mHaveConfiguredCodecs = true;
#if !defined(MOZILLA_XPCOMRT_API)
nsresult res;
nsCOMPtr<nsIPrefService> prefs =
@ -1150,15 +1156,8 @@ PeerConnectionImpl::GetDatachannelParameters(
for (size_t i = 0;
i < trackPair.mSending->GetNegotiatedDetails()->GetCodecCount();
++i) {
const JsepCodecDescription* codec;
nsresult res =
trackPair.mSending->GetNegotiatedDetails()->GetCodec(i, &codec);
if (NS_FAILED(res)) {
CSFLogError(logTag, "%s: Failed getting codec for m=application.",
__FUNCTION__);
continue;
}
const JsepCodecDescription* codec =
trackPair.mSending->GetNegotiatedDetails()->GetCodec(i);
if (codec->mType != SdpMediaSection::kApplication) {
CSFLogError(logTag, "%s: Codec type for m=application was %u, this "
@ -1194,6 +1193,63 @@ PeerConnectionImpl::GetDatachannelParameters(
return NS_OK;
}
/* static */
void
PeerConnectionImpl::DeferredAddTrackToJsepSession(
const std::string& pcHandle,
SdpMediaSection::MediaType type,
const std::string& streamId,
const std::string& trackId)
{
PeerConnectionWrapper wrapper(pcHandle);
if (wrapper.impl()) {
if (!PeerConnectionCtx::GetInstance()->isReady()) {
MOZ_CRASH("Why is DeferredAddTrackToJsepSession being executed when the "
"PeerConnectionCtx isn't ready?");
}
wrapper.impl()->AddTrackToJsepSession(type, streamId, trackId);
}
}
nsresult
PeerConnectionImpl::AddTrackToJsepSession(SdpMediaSection::MediaType type,
const std::string& streamId,
const std::string& trackId)
{
if (!PeerConnectionCtx::GetInstance()->isReady()) {
// We are not ready to configure codecs for this track. We need to defer.
PeerConnectionCtx::GetInstance()->queueJSEPOperation(
WrapRunnableNM(DeferredAddTrackToJsepSession,
mHandle,
type,
streamId,
trackId));
return NS_OK;
}
nsresult res = ConfigureJsepSessionCodecs();
if (NS_FAILED(res)) {
CSFLogError(logTag, "Failed to configure codecs");
return res;
}
res = mJsepSession->AddTrack(
new JsepTrack(type, streamId, trackId, sdp::kSend));
if (NS_FAILED(res)) {
std::string errorString = mJsepSession->GetLastError();
CSFLogError(logTag, "%s (%s) : pc = %s, error = %s",
__FUNCTION__,
type == SdpMediaSection::kAudio ? "audio" : "video",
mHandle.c_str(),
errorString.c_str());
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
PeerConnectionImpl::InitializeDataChannel()
{
@ -1319,7 +1375,7 @@ PeerConnectionImpl::CreateDataChannel(const nsAString& aLabel,
mozilla::SdpMediaSection::kApplication,
streamId,
trackId,
JsepTrack::kJsepTrackSending));
sdp::kSend));
rv = mJsepSession->AddTrack(track);
if (NS_FAILED(rv)) {
@ -2077,6 +2133,7 @@ PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
CSFLogError(logTag, "%s: At least one stream arg required", __FUNCTION__);
return NS_ERROR_FAILURE;
}
return AddTrack(aTrack, aStreams[0]);
}
@ -2105,16 +2162,9 @@ PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
}
if (aTrack.AsAudioStreamTrack()) {
res = mJsepSession->AddTrack(new JsepTrack(
mozilla::SdpMediaSection::kAudio,
streamId,
trackId,
JsepTrack::kJsepTrackSending));
res = AddTrackToJsepSession(SdpMediaSection::kAudio, streamId, trackId);
if (NS_FAILED(res)) {
std::string errorString = mJsepSession->GetLastError();
CSFLogError(logTag, "%s (audio) : pc = %s, error = %s",
__FUNCTION__, mHandle.c_str(), errorString.c_str());
return NS_ERROR_FAILURE;
return res;
}
mNumAudioStreams++;
}
@ -2128,16 +2178,9 @@ PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
}
#endif
res = mJsepSession->AddTrack(new JsepTrack(
mozilla::SdpMediaSection::kVideo,
streamId,
trackId,
JsepTrack::kJsepTrackSending));
res = AddTrackToJsepSession(SdpMediaSection::kVideo, streamId, trackId);
if (NS_FAILED(res)) {
std::string errorString = mJsepSession->GetLastError();
CSFLogError(logTag, "%s (video) : pc = %s, error = %s",
__FUNCTION__, mHandle.c_str(), errorString.c_str());
return NS_ERROR_FAILURE;
return res;
}
mNumVideoStreams++;
}

View File

@ -673,6 +673,15 @@ private:
const mozilla::JsepApplicationCodecDescription** codec,
uint16_t* level) const;
static void DeferredAddTrackToJsepSession(const std::string& pcHandle,
SdpMediaSection::MediaType type,
const std::string& streamId,
const std::string& trackId);
nsresult AddTrackToJsepSession(SdpMediaSection::MediaType type,
const std::string& streamId,
const std::string& trackId);
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
static void GetStatsForPCObserver_s(
const std::string& pcHandle,
@ -777,6 +786,7 @@ private:
// Bug 840728.
int mNumAudioStreams;
int mNumVideoStreams;
bool mHaveConfiguredCodecs;
bool mHaveDataStream;

View File

@ -5,6 +5,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "signaling/src/sdp/SdpAttribute.h"
#include "signaling/src/sdp/SdpHelper.h"
#include <iomanip>
@ -941,6 +942,29 @@ SdpSimulcastAttribute::Version::Parse(std::istream& is, std::string* error)
return true;
}
void
SdpSimulcastAttribute::Version::AppendAsStrings(
std::vector<std::string>* formats) const
{
for (uint16_t pt : choices) {
std::ostringstream os;
os << pt;
formats->push_back(os.str());
}
}
void
SdpSimulcastAttribute::Version::AddChoice(const std::string& pt)
{
uint16_t ptAsInt;
if (!SdpHelper::GetPtAsInt(pt, &ptAsInt)) {
MOZ_ASSERT(false);
return;
}
choices.push_back(ptAsInt);
}
void
SdpSimulcastAttribute::Versions::Serialize(std::ostream& os) const
{

View File

@ -178,14 +178,11 @@ inline std::ostream& operator<<(std::ostream& os,
class SdpDirectionAttribute : public SdpAttribute
{
public:
static const unsigned kSendFlag = 1;
static const unsigned kRecvFlag = 1 << 1;
enum Direction {
kInactive = 0,
kSendonly = kSendFlag,
kRecvonly = kRecvFlag,
kSendrecv = kSendFlag | kRecvFlag
kSendonly = sdp::kSend,
kRecvonly = sdp::kRecv,
kSendrecv = sdp::kSend | sdp::kRecv
};
explicit SdpDirectionAttribute(Direction value)
@ -1153,6 +1150,14 @@ public:
{
}
Fmtp(const std::string& aFormat, const std::string& aParametersString,
const Parameters& aParameters)
: format(aFormat),
parameters_string(aParametersString),
parameters(aParameters.Clone())
{
}
// TODO: Rip all of this out when we have move semantics in the stl.
Fmtp(const Fmtp& orig) { *this = orig; }
@ -1323,6 +1328,8 @@ public:
return !choices.empty();
}
bool Parse(std::istream& is, std::string* error);
void AppendAsStrings(std::vector<std::string>* formats) const;
void AddChoice(const std::string& pt);
std::vector<uint16_t> choices;
};

View File

@ -46,6 +46,12 @@ inline std::ostream& operator<<(std::ostream& os, sdp::AddrType t)
MOZ_CRASH("Unknown AddrType");
}
enum Direction {
// Start at 1 so these can be used as flags
kSend = 1,
kRecv = 2
};
} // namespace sdp
} // namespace mozilla

View File

@ -118,7 +118,7 @@ SdpHelper::MsectionIsDisabled(const SdpMediaSection& msection) const
}
void
SdpHelper::DisableMsection(Sdp* sdp, SdpMediaSection* msection) const
SdpHelper::DisableMsection(Sdp* sdp, SdpMediaSection* msection)
{
// Make sure to remove the mid from any group attributes
if (msection->GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
@ -554,28 +554,6 @@ SdpHelper::FindMsectionByMid(Sdp& sdp,
return nullptr;
}
void
SdpHelper::SetSsrcs(const std::vector<uint32_t>& ssrcs,
const std::string& cname,
SdpMediaSection* msection) const
{
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 += cname;
ssrcAttr->PushEntry(ssrc, cnameAttr);
}
msection->GetAttributeList().SetAttribute(ssrcAttr.release());
}
nsresult
SdpHelper::CopyStickyParams(const SdpMediaSection& source,
SdpMediaSection* dest)

View File

@ -36,7 +36,7 @@ class SdpHelper {
size_t level);
bool MsectionIsDisabled(const SdpMediaSection& msection) const;
void DisableMsection(Sdp* sdp, SdpMediaSection* msection) const;
static void DisableMsection(Sdp* sdp, SdpMediaSection* msection);
// Maps each mid to the m-section that is the master of its bundle.
// Mids that do not appear in an a=group:BUNDLE do not appear here.
@ -81,9 +81,6 @@ class SdpHelper {
Sdp* sdp) const;
std::string GetCNAME(const SdpMediaSection& msection) const;
void SetSsrcs(const std::vector<uint32_t>& ssrcs,
const std::string& cname,
SdpMediaSection* msection) const;
SdpMediaSection* FindMsectionByMid(Sdp& sdp,
const std::string& mid) const;

View File

@ -0,0 +1,160 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "signaling/src/sdp/SdpMediaSection.h"
namespace mozilla
{
const SdpFmtpAttributeList::Parameters*
SdpMediaSection::FindFmtp(const std::string& pt) const
{
const SdpAttributeList& attrs = GetAttributeList();
if (attrs.HasAttribute(SdpAttribute::kFmtpAttribute)) {
for (auto& fmtpAttr : attrs.GetFmtp().mFmtps) {
if (fmtpAttr.format == pt && fmtpAttr.parameters) {
return fmtpAttr.parameters.get();
}
}
}
return nullptr;
}
void
SdpMediaSection::SetFmtp(const SdpFmtpAttributeList::Fmtp& fmtpToSet)
{
UniquePtr<SdpFmtpAttributeList> fmtps(new SdpFmtpAttributeList);
if (GetAttributeList().HasAttribute(SdpAttribute::kFmtpAttribute)) {
*fmtps = GetAttributeList().GetFmtp();
}
bool found = false;
for (SdpFmtpAttributeList::Fmtp& fmtp : fmtps->mFmtps) {
if (fmtp.format == fmtpToSet.format) {
fmtp = fmtpToSet;
found = true;
}
}
if (!found) {
fmtps->mFmtps.push_back(fmtpToSet);
}
GetAttributeList().SetAttribute(fmtps.release());
}
const SdpRtpmapAttributeList::Rtpmap*
SdpMediaSection::FindRtpmap(const std::string& pt) const
{
auto& attrs = GetAttributeList();
if (!attrs.HasAttribute(SdpAttribute::kRtpmapAttribute)) {
return nullptr;
}
const SdpRtpmapAttributeList& rtpmap = attrs.GetRtpmap();
if (!rtpmap.HasEntry(pt)) {
return nullptr;
}
return &rtpmap.GetEntry(pt);
}
const SdpSctpmapAttributeList::Sctpmap*
SdpMediaSection::FindSctpmap(const std::string& pt) const
{
auto& attrs = GetAttributeList();
if (!attrs.HasAttribute(SdpAttribute::kSctpmapAttribute)) {
return nullptr;
}
const SdpSctpmapAttributeList& sctpmap = attrs.GetSctpmap();
if (!sctpmap.HasEntry(pt)) {
return nullptr;
}
return &sctpmap.GetEntry(pt);
}
bool
SdpMediaSection::HasRtcpFb(const std::string& pt,
SdpRtcpFbAttributeList::Type type,
const std::string& subType) const
{
const SdpAttributeList& attrs(GetAttributeList());
if (!attrs.HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
return false;
}
for (auto& rtcpfb : attrs.GetRtcpFb().mFeedbacks) {
if (rtcpfb.type == type) {
if (rtcpfb.pt == "*" || rtcpfb.pt == pt) {
if (rtcpfb.parameter == subType) {
return true;
}
}
}
}
return false;
}
SdpRtcpFbAttributeList
SdpMediaSection::GetRtcpFbs() const
{
SdpRtcpFbAttributeList result;
if (GetAttributeList().HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
result = GetAttributeList().GetRtcpFb();
}
return result;
}
void
SdpMediaSection::SetRtcpFbs(const SdpRtcpFbAttributeList& rtcpfbs)
{
if (rtcpfbs.mFeedbacks.empty()) {
GetAttributeList().RemoveAttribute(SdpAttribute::kRtcpFbAttribute);
return;
}
GetAttributeList().SetAttribute(new SdpRtcpFbAttributeList(rtcpfbs));
}
void
SdpMediaSection::SetSsrcs(const std::vector<uint32_t>& ssrcs,
const std::string& cname)
{
if (ssrcs.empty()) {
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 += cname;
ssrcAttr->PushEntry(ssrc, cnameAttr);
}
GetAttributeList().SetAttribute(ssrcAttr.release());
}
void
SdpMediaSection::AddMsid(const std::string& id, const std::string& appdata)
{
UniquePtr<SdpMsidAttributeList> msids(new SdpMsidAttributeList);
if (GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
msids->mMsids = GetAttributeList().GetMsid().mMsids;
}
msids->PushEntry(id, appdata);
GetAttributeList().SetAttribute(msids.release());
}
} // namespace mozilla

View File

@ -12,8 +12,7 @@
#include "signaling/src/sdp/SdpAttributeList.h"
#include <string>
#include <vector>
#include "signaling/src/sdp/SdpEnum.h"
#include <iostream>
namespace mozilla
{
@ -81,6 +80,8 @@ public:
virtual uint32_t GetBandwidth(const std::string& type) const = 0;
virtual const std::vector<std::string>& GetFormats() const = 0;
std::vector<std::string> GetFormatsForSimulcastVersion(
size_t simulcastVersion, bool send, bool recv) const;
virtual const SdpAttributeList& GetAttributeList() const = 0;
virtual SdpAttributeList& GetAttributeList() = 0;
@ -104,20 +105,20 @@ public:
inline bool
IsReceiving() const
{
return GetDirectionAttribute().mValue & SdpDirectionAttribute::kRecvFlag;
return GetDirectionAttribute().mValue & sdp::kRecv;
}
inline bool
IsSending() const
{
return GetDirectionAttribute().mValue & SdpDirectionAttribute::kSendFlag;
return GetDirectionAttribute().mValue & sdp::kSend;
}
inline void
SetReceiving(bool receiving)
{
auto direction = GetDirectionAttribute().mValue;
if (direction & SdpDirectionAttribute::kSendFlag) {
if (direction & sdp::kSend) {
SetDirection(receiving ?
SdpDirectionAttribute::kSendrecv :
SdpDirectionAttribute::kSendonly);
@ -132,7 +133,7 @@ public:
SetSending(bool sending)
{
auto direction = GetDirectionAttribute().mValue;
if (direction & SdpDirectionAttribute::kRecvFlag) {
if (direction & sdp::kRecv) {
SetDirection(sending ?
SdpDirectionAttribute::kSendrecv :
SdpDirectionAttribute::kRecvonly);
@ -148,79 +149,24 @@ public:
GetAttributeList().SetAttribute(new SdpDirectionAttribute(direction));
}
const SdpFmtpAttributeList::Parameters*
FindFmtp(const std::string& pt) const
const SdpFmtpAttributeList::Parameters* FindFmtp(const std::string& pt) const;
void SetFmtp(const SdpFmtpAttributeList::Fmtp& fmtp);
const SdpRtpmapAttributeList::Rtpmap* FindRtpmap(const std::string& pt) const;
const SdpSctpmapAttributeList::Sctpmap* FindSctpmap(
const std::string& pt) const;
bool HasRtcpFb(const std::string& pt,
SdpRtcpFbAttributeList::Type type,
const std::string& subType) const;
SdpRtcpFbAttributeList GetRtcpFbs() const;
void SetRtcpFbs(const SdpRtcpFbAttributeList& rtcpfbs);
bool HasFormat(const std::string& format) const
{
const SdpAttributeList& attrs = GetAttributeList();
if (attrs.HasAttribute(SdpAttribute::kFmtpAttribute)) {
const SdpFmtpAttributeList& fmtps = attrs.GetFmtp();
for (auto i = fmtps.mFmtps.begin(); i != fmtps.mFmtps.end(); ++i) {
if (i->format == pt && i->parameters) {
return i->parameters.get();
}
}
}
return nullptr;
return std::find(GetFormats().begin(), GetFormats().end(), format) !=
GetFormats().end();
}
const SdpRtpmapAttributeList::Rtpmap*
FindRtpmap(const std::string& pt) const
{
auto& attrs = GetAttributeList();
if (!attrs.HasAttribute(SdpAttribute::kRtpmapAttribute)) {
return nullptr;
}
const SdpRtpmapAttributeList& rtpmap = attrs.GetRtpmap();
if (!rtpmap.HasEntry(pt)) {
return nullptr;
}
return &rtpmap.GetEntry(pt);
}
const SdpSctpmapAttributeList::Sctpmap*
FindSctpmap(const std::string& pt) const
{
auto& attrs = GetAttributeList();
if (!attrs.HasAttribute(SdpAttribute::kSctpmapAttribute)) {
return nullptr;
}
const SdpSctpmapAttributeList& sctpmap = attrs.GetSctpmap();
if (!sctpmap.HasEntry(pt)) {
return nullptr;
}
return &sctpmap.GetEntry(pt);
}
bool
HasRtcpFb(const std::string& pt,
SdpRtcpFbAttributeList::Type type,
const std::string& subType) const
{
const SdpAttributeList& attrs(GetAttributeList());
if (!attrs.HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
return false;
}
for (auto& rtcpfb : attrs.GetRtcpFb().mFeedbacks) {
if (rtcpfb.type == type) {
if (rtcpfb.pt == "*" || rtcpfb.pt == pt) {
if (rtcpfb.parameter == subType) {
return true;
}
}
}
}
return false;
}
void SetSsrcs(const std::vector<uint32_t>& ssrcs,
const std::string& cname);
void AddMsid(const std::string& id, const std::string& appdata);
private:
size_t mLevel;

View File

@ -127,7 +127,10 @@ SipccSdpMediaSection::Load(sdp_t* sdp, uint16_t level,
if (!LoadProtocol(sdp, level, errorHolder)) {
return false;
}
LoadFormats(sdp, level);
if (!LoadFormats(sdp, level, errorHolder)) {
return false;
}
if (!mAttributeList.Load(sdp, level, errorHolder)) {
return false;
@ -181,8 +184,10 @@ SipccSdpMediaSection::LoadProtocol(sdp_t* sdp, uint16_t level,
return true;
}
void
SipccSdpMediaSection::LoadFormats(sdp_t* sdp, uint16_t level)
bool
SipccSdpMediaSection::LoadFormats(sdp_t* sdp,
uint16_t level,
SdpErrorHolder& errorHolder)
{
sdp_media_e mtype = sdp_get_media_type(sdp, level);
@ -198,6 +203,12 @@ SipccSdpMediaSection::LoadFormats(sdp_t* sdp, uint16_t level)
uint32_t ptype =
sdp_get_media_payload_type(sdp, level, i + 1, &indicator);
if (GET_DYN_PAYLOAD_TYPE_VALUE(ptype) > UINT8_MAX) {
errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
"Format is too large");
return false;
}
std::ostringstream osPayloadType;
// sipcc stores payload types in a funny way. When sipcc and the SDP it
// parsed differ on what payload type number should be used for a given
@ -208,6 +219,8 @@ SipccSdpMediaSection::LoadFormats(sdp_t* sdp, uint16_t level)
mFormats.push_back(osPayloadType.str());
}
}
return true;
}
bool

View File

@ -75,7 +75,7 @@ private:
bool Load(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder);
bool LoadConnection(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder);
bool LoadProtocol(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder);
void LoadFormats(sdp_t* sdp, uint16_t level);
bool LoadFormats(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder);
// the following values are cached on first get
MediaType mMediaType;

View File

@ -838,22 +838,7 @@ protected:
UniquePtr<Sdp> parsed(Parse(*sdp));
ASSERT_TRUE(parsed.get());
ASSERT_LT(level, parsed->GetMediaSectionCount());
parsed->GetMediaSection(level).SetPort(0);
auto& attrs = parsed->GetMediaSection(level).GetAttributeList();
ASSERT_TRUE(attrs.HasAttribute(SdpAttribute::kMidAttribute));
std::string mid = attrs.GetMid();
attrs.Clear();
ASSERT_TRUE(
parsed->GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute));
SdpGroupAttributeList* newGroupAttr(new SdpGroupAttributeList(
parsed->GetAttributeList().GetGroup()));
newGroupAttr->RemoveMid(mid);
parsed->GetAttributeList().SetAttribute(newGroupAttr);
SdpHelper::DisableMsection(parsed.get(), &parsed->GetMediaSection(level));
(*sdp) = parsed->ToString();
}
@ -861,13 +846,11 @@ protected:
DumpTrack(const JsepTrack& track)
{
std::cerr << " type=" << track.GetMediaType() << std::endl;
std::cerr << " protocol=" << track.GetNegotiatedDetails()->GetProtocol()
<< std::endl;
std::cerr << " codecs=" << std::endl;
size_t num_codecs = track.GetNegotiatedDetails()->GetCodecCount();
for (size_t i = 0; i < num_codecs; ++i) {
const JsepCodecDescription* codec;
ASSERT_EQ(NS_OK, track.GetNegotiatedDetails()->GetCodec(i, &codec));
const JsepCodecDescription* codec =
track.GetNegotiatedDetails()->GetCodec(i);
std::cerr << " " << codec->mName << std::endl;
}
}
@ -2652,50 +2635,50 @@ TEST_F(JsepSessionTest, ValidateOfferedCodecParams)
ASSERT_EQ(4U, fmtps.size());
// VP8
ASSERT_EQ("120", fmtps[0].format);
ASSERT_TRUE(!!fmtps[0].parameters);
ASSERT_EQ(SdpRtpmapAttributeList::kVP8, fmtps[0].parameters->codec_type);
const SdpFmtpAttributeList::Parameters* vp8_params =
video_section.FindFmtp("120");
ASSERT_TRUE(vp8_params);
ASSERT_EQ(SdpRtpmapAttributeList::kVP8, vp8_params->codec_type);
auto& parsed_vp8_params =
*static_cast<const SdpFmtpAttributeList::VP8Parameters*>(
fmtps[0].parameters.get());
*static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp8_params);
ASSERT_EQ((uint32_t)12288, parsed_vp8_params.max_fs);
ASSERT_EQ((uint32_t)60, parsed_vp8_params.max_fr);
// VP9
ASSERT_EQ("121", fmtps[1].format);
ASSERT_TRUE(!!fmtps[1].parameters);
ASSERT_EQ(SdpRtpmapAttributeList::kVP9, fmtps[1].parameters->codec_type);
const SdpFmtpAttributeList::Parameters* vp9_params =
video_section.FindFmtp("121");
ASSERT_TRUE(vp9_params);
ASSERT_EQ(SdpRtpmapAttributeList::kVP9, vp9_params->codec_type);
auto& parsed_vp9_params =
*static_cast<const SdpFmtpAttributeList::VP8Parameters*>(
fmtps[1].parameters.get());
*static_cast<const SdpFmtpAttributeList::VP8Parameters*>(vp9_params);
ASSERT_EQ((uint32_t)12288, parsed_vp9_params.max_fs);
ASSERT_EQ((uint32_t)60, parsed_vp9_params.max_fr);
// H264 packetization mode 1
ASSERT_EQ("126", fmtps[2].format);
ASSERT_TRUE(!!fmtps[2].parameters);
ASSERT_EQ(SdpRtpmapAttributeList::kH264, fmtps[2].parameters->codec_type);
const SdpFmtpAttributeList::Parameters* h264_1_params =
video_section.FindFmtp("126");
ASSERT_TRUE(h264_1_params);
ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_1_params->codec_type);
auto& parsed_h264_1_params =
*static_cast<const SdpFmtpAttributeList::H264Parameters*>(
fmtps[2].parameters.get());
*static_cast<const SdpFmtpAttributeList::H264Parameters*>(h264_1_params);
ASSERT_EQ((uint32_t)0x42e00d, parsed_h264_1_params.profile_level_id);
ASSERT_TRUE(parsed_h264_1_params.level_asymmetry_allowed);
ASSERT_EQ(1U, parsed_h264_1_params.packetization_mode);
// H264 packetization mode 0
ASSERT_EQ("97", fmtps[3].format);
ASSERT_TRUE(!!fmtps[3].parameters);
ASSERT_EQ(SdpRtpmapAttributeList::kH264, fmtps[3].parameters->codec_type);
const SdpFmtpAttributeList::Parameters* h264_0_params =
video_section.FindFmtp("97");
ASSERT_TRUE(h264_0_params);
ASSERT_EQ(SdpRtpmapAttributeList::kH264, h264_0_params->codec_type);
auto& parsed_h264_0_params =
*static_cast<const SdpFmtpAttributeList::H264Parameters*>(
fmtps[3].parameters.get());
*static_cast<const SdpFmtpAttributeList::H264Parameters*>(h264_0_params);
ASSERT_EQ((uint32_t)0x42e00d, parsed_h264_0_params.profile_level_id);
ASSERT_TRUE(parsed_h264_0_params.level_asymmetry_allowed);
@ -2808,16 +2791,6 @@ TEST_F(JsepSessionTest, ValidateAnsweredCodecParams)
offerPairs[1].mSending->GetNegotiatedDetails()->GetCodecCount());
ASSERT_EQ(1U,
offerPairs[1].mReceiving->GetNegotiatedDetails()->GetCodecCount());
const JsepCodecDescription* offerRecvCodec;
ASSERT_EQ(NS_OK,
offerPairs[1].mReceiving->GetNegotiatedDetails()->GetCodec(
0,
&offerRecvCodec));
const JsepCodecDescription* offerSendCodec;
ASSERT_EQ(NS_OK,
offerPairs[1].mSending->GetNegotiatedDetails()->GetCodec(
0,
&offerSendCodec));
auto answerPairs = mSessionAns.GetNegotiatedTrackPairs();
ASSERT_EQ(2U, answerPairs.size());
@ -2829,16 +2802,6 @@ TEST_F(JsepSessionTest, ValidateAnsweredCodecParams)
answerPairs[1].mSending->GetNegotiatedDetails()->GetCodecCount());
ASSERT_EQ(1U,
answerPairs[1].mReceiving->GetNegotiatedDetails()->GetCodecCount());
const JsepCodecDescription* answerRecvCodec;
ASSERT_EQ(NS_OK,
answerPairs[1].mReceiving->GetNegotiatedDetails()->GetCodec(
0,
&answerRecvCodec));
const JsepCodecDescription* answerSendCodec;
ASSERT_EQ(NS_OK,
answerPairs[1].mSending->GetNegotiatedDetails()->GetCodec(
0,
&answerSendCodec));
#if 0
// H264 packetization mode 1
@ -2888,16 +2851,10 @@ static void ReplaceAll(const std::string& toReplace,
}
}
typedef enum
{
kSending,
kReceiving
} Direction;
static void
GetCodec(JsepSession& session,
size_t pairIndex,
Direction direction,
sdp::Direction direction,
size_t codecIndex,
const JsepCodecDescription** codecOut)
{
@ -2905,12 +2862,11 @@ GetCodec(JsepSession& session,
ASSERT_LT(pairIndex, session.GetNegotiatedTrackPairs().size());
JsepTrackPair pair(session.GetNegotiatedTrackPairs().front());
RefPtr<JsepTrack> track(
(direction == kSending) ? pair.mSending : pair.mReceiving);
(direction == sdp::kSend) ? pair.mSending : pair.mReceiving);
ASSERT_TRUE(track);
ASSERT_TRUE(track->GetNegotiatedDetails());
ASSERT_LT(codecIndex, track->GetNegotiatedDetails()->GetCodecCount());
ASSERT_EQ(NS_OK,
track->GetNegotiatedDetails()->GetCodec(codecIndex, codecOut));
*codecOut = track->GetNegotiatedDetails()->GetCodec(codecIndex);
}
static void
@ -2945,7 +2901,7 @@ TEST_F(JsepSessionTest, TestH264Negotiation)
SetLocalAnswer(answer, CHECK_SUCCESS);
const JsepCodecDescription* offererSendCodec;
GetCodec(mSessionOff, 0, kSending, 0, &offererSendCodec);
GetCodec(mSessionOff, 0, sdp::kSend, 0, &offererSendCodec);
ASSERT_TRUE(offererSendCodec);
ASSERT_EQ("H264", offererSendCodec->mName);
const JsepVideoCodecDescription* offererVideoSendCodec(
@ -2953,14 +2909,14 @@ TEST_F(JsepSessionTest, TestH264Negotiation)
ASSERT_EQ((uint32_t)0x42e00d, offererVideoSendCodec->mProfileLevelId);
const JsepCodecDescription* offererRecvCodec;
GetCodec(mSessionOff, 0, kReceiving, 0, &offererRecvCodec);
GetCodec(mSessionOff, 0, sdp::kRecv, 0, &offererRecvCodec);
ASSERT_EQ("H264", offererRecvCodec->mName);
const JsepVideoCodecDescription* offererVideoRecvCodec(
static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
const JsepCodecDescription* answererSendCodec;
GetCodec(mSessionAns, 0, kSending, 0, &answererSendCodec);
GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
ASSERT_TRUE(answererSendCodec);
ASSERT_EQ("H264", answererSendCodec->mName);
const JsepVideoCodecDescription* answererVideoSendCodec(
@ -2968,7 +2924,7 @@ TEST_F(JsepSessionTest, TestH264Negotiation)
ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
const JsepCodecDescription* answererRecvCodec;
GetCodec(mSessionAns, 0, kReceiving, 0, &answererRecvCodec);
GetCodec(mSessionAns, 0, sdp::kRecv, 0, &answererRecvCodec);
ASSERT_EQ("H264", answererRecvCodec->mName);
const JsepVideoCodecDescription* answererVideoRecvCodec(
static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
@ -3018,7 +2974,7 @@ TEST_F(JsepSessionTest, TestH264NegotiationOffererDefault)
SetLocalAnswer(answer, CHECK_SUCCESS);
const JsepCodecDescription* answererSendCodec;
GetCodec(mSessionAns, 0, kSending, 0, &answererSendCodec);
GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
ASSERT_TRUE(answererSendCodec);
ASSERT_EQ("H264", answererSendCodec->mName);
const JsepVideoCodecDescription* answererVideoSendCodec(
@ -3046,7 +3002,7 @@ TEST_F(JsepSessionTest, TestH264NegotiationOffererNoFmtp)
SetLocalAnswer(answer, CHECK_SUCCESS);
const JsepCodecDescription* answererSendCodec;
GetCodec(mSessionAns, 0, kSending, 0, &answererSendCodec);
GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
ASSERT_TRUE(answererSendCodec);
ASSERT_EQ("H264", answererSendCodec->mName);
const JsepVideoCodecDescription* answererVideoSendCodec(
@ -3054,7 +3010,7 @@ TEST_F(JsepSessionTest, TestH264NegotiationOffererNoFmtp)
ASSERT_EQ((uint32_t)0x420010, answererVideoSendCodec->mProfileLevelId);
const JsepCodecDescription* answererRecvCodec;
GetCodec(mSessionAns, 0, kReceiving, 0, &answererRecvCodec);
GetCodec(mSessionAns, 0, sdp::kRecv, 0, &answererRecvCodec);
ASSERT_EQ("H264", answererRecvCodec->mName);
const JsepVideoCodecDescription* answererVideoRecvCodec(
static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
@ -3086,7 +3042,7 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithLowLevel)
// behave normally, and we test the normal behavior elsewhere.
const JsepCodecDescription* answererSendCodec;
GetCodec(mSessionAns, 0, kSending, 0, &answererSendCodec);
GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
ASSERT_TRUE(answererSendCodec);
ASSERT_EQ("H264", answererSendCodec->mName);
const JsepVideoCodecDescription* answererVideoSendCodec(
@ -3094,7 +3050,7 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithLowLevel)
ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
const JsepCodecDescription* answererRecvCodec;
GetCodec(mSessionAns, 0, kReceiving, 0, &answererRecvCodec);
GetCodec(mSessionAns, 0, sdp::kRecv, 0, &answererRecvCodec);
ASSERT_EQ("H264", answererRecvCodec->mName);
const JsepVideoCodecDescription* answererVideoRecvCodec(
static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
@ -3126,7 +3082,7 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithHighLevel)
// behave normally, and we test the normal behavior elsewhere.
const JsepCodecDescription* answererSendCodec;
GetCodec(mSessionAns, 0, kSending, 0, &answererSendCodec);
GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
ASSERT_TRUE(answererSendCodec);
ASSERT_EQ("H264", answererSendCodec->mName);
const JsepVideoCodecDescription* answererVideoSendCodec(
@ -3134,7 +3090,7 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithHighLevel)
ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
const JsepCodecDescription* answererRecvCodec;
GetCodec(mSessionAns, 0, kReceiving, 0, &answererRecvCodec);
GetCodec(mSessionAns, 0, sdp::kRecv, 0, &answererRecvCodec);
ASSERT_EQ("H264", answererRecvCodec->mName);
const JsepVideoCodecDescription* answererVideoRecvCodec(
static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
@ -3162,7 +3118,7 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByAnswererWithLowLevel)
SetLocalAnswer(answer, CHECK_SUCCESS);
const JsepCodecDescription* offererSendCodec;
GetCodec(mSessionOff, 0, kSending, 0, &offererSendCodec);
GetCodec(mSessionOff, 0, sdp::kSend, 0, &offererSendCodec);
ASSERT_TRUE(offererSendCodec);
ASSERT_EQ("H264", offererSendCodec->mName);
const JsepVideoCodecDescription* offererVideoSendCodec(
@ -3170,7 +3126,7 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByAnswererWithLowLevel)
ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId);
const JsepCodecDescription* offererRecvCodec;
GetCodec(mSessionOff, 0, kReceiving, 0, &offererRecvCodec);
GetCodec(mSessionOff, 0, sdp::kRecv, 0, &offererRecvCodec);
ASSERT_EQ("H264", offererRecvCodec->mName);
const JsepVideoCodecDescription* offererVideoRecvCodec(
static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
@ -3202,7 +3158,7 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByAnswererWithHighLevel)
SetLocalAnswer(answer, CHECK_SUCCESS);
const JsepCodecDescription* offererSendCodec;
GetCodec(mSessionOff, 0, kSending, 0, &offererSendCodec);
GetCodec(mSessionOff, 0, sdp::kSend, 0, &offererSendCodec);
ASSERT_TRUE(offererSendCodec);
ASSERT_EQ("H264", offererSendCodec->mName);
const JsepVideoCodecDescription* offererVideoSendCodec(
@ -3210,7 +3166,7 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByAnswererWithHighLevel)
ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId);
const JsepCodecDescription* offererRecvCodec;
GetCodec(mSessionOff, 0, kReceiving, 0, &offererRecvCodec);
GetCodec(mSessionOff, 0, sdp::kRecv, 0, &offererRecvCodec);
ASSERT_EQ("H264", offererRecvCodec->mName);
const JsepVideoCodecDescription* offererVideoRecvCodec(
static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
@ -3223,8 +3179,9 @@ TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByAnswererWithHighLevel)
TEST_P(JsepSessionTest, TestRejectMline)
{
AddTracks(mSessionOff);
AddTracks(mSessionAns);
// We need to do this before adding tracks
types = BuildTypes(GetParam());
std::sort(types.begin(), types.end());
switch (types.front()) {
case SdpMediaSection::kAudio:
@ -3243,6 +3200,9 @@ TEST_P(JsepSessionTest, TestRejectMline)
ASSERT_TRUE(false) << "Unknown media type";
}
AddTracks(mSessionOff);
AddTracks(mSessionAns);
std::string offer = CreateOffer();
mSessionOff.SetLocalDescription(kJsepSdpOffer, offer);
mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer);
@ -3399,8 +3359,7 @@ TEST_F(JsepSessionTest, TestRtcpFbStar)
ASSERT_TRUE(track->GetNegotiatedDetails());
auto* details = track->GetNegotiatedDetails();
for (size_t i = 0; i < details->GetCodecCount(); ++i) {
const JsepCodecDescription* codec;
ASSERT_EQ(NS_OK, details->GetCodec(i, &codec));
const JsepCodecDescription* codec = details->GetCodec(i);
const JsepVideoCodecDescription* videoCodec =
static_cast<const JsepVideoCodecDescription*>(codec);
ASSERT_EQ(1U, videoCodec->mNackFbTypes.size());
@ -3535,10 +3494,10 @@ TEST_F(JsepSessionTest, StronglyPreferredCodec)
OfferAnswer();
const JsepCodecDescription* codec;
GetCodec(mSessionAns, 0, kSending, 0, &codec);
GetCodec(mSessionAns, 0, sdp::kSend, 0, &codec);
ASSERT_TRUE(codec);
ASSERT_EQ("H264", codec->mName);
GetCodec(mSessionAns, 0, kReceiving, 0, &codec);
GetCodec(mSessionAns, 0, sdp::kRecv, 0, &codec);
ASSERT_TRUE(codec);
ASSERT_EQ("H264", codec->mName);
}
@ -3552,11 +3511,11 @@ TEST_F(JsepSessionTest, LowDynamicPayloadType)
OfferAnswer();
const JsepCodecDescription* codec;
GetCodec(mSessionAns, 0, kSending, 0, &codec);
GetCodec(mSessionAns, 0, sdp::kSend, 0, &codec);
ASSERT_TRUE(codec);
ASSERT_EQ("opus", codec->mName);
ASSERT_EQ("12", codec->mDefaultPt);
GetCodec(mSessionAns, 0, kReceiving, 0, &codec);
GetCodec(mSessionAns, 0, sdp::kRecv, 0, &codec);
ASSERT_TRUE(codec);
ASSERT_EQ("opus", codec->mName);
ASSERT_EQ("12", codec->mDefaultPt);
@ -3574,11 +3533,11 @@ TEST_F(JsepSessionTest, PayloadTypeClash)
OfferAnswer();
const JsepCodecDescription* codec;
GetCodec(mSessionAns, 0, kSending, 0, &codec);
GetCodec(mSessionAns, 0, sdp::kSend, 0, &codec);
ASSERT_TRUE(codec);
ASSERT_EQ("opus", codec->mName);
ASSERT_EQ("0", codec->mDefaultPt);
GetCodec(mSessionAns, 0, kReceiving, 0, &codec);
GetCodec(mSessionAns, 0, sdp::kRecv, 0, &codec);
ASSERT_TRUE(codec);
ASSERT_EQ("opus", codec->mName);
ASSERT_EQ("0", codec->mDefaultPt);