Bug 1164187 - Add jsep support for rtx; r=bwc

Differential Revision: https://phabricator.services.mozilla.com/D72225
This commit is contained in:
Dan Minor 2020-05-08 18:10:59 +00:00
Родитель 2143491530
Коммит 6a3b43fa14
5 изменённых файлов: 191 добавлений и 21 удалений

Просмотреть файл

@ -1275,7 +1275,8 @@ TEST_F(JsepTrackTest, SimulcastOfferer) {
CreateOffer();
CreateAnswer();
// Add simulcast/rid to answer
mRecvAns.AddToMsection(constraints, sdp::kRecv, mSsrcGenerator, &GetAnswer());
mRecvAns.AddToMsection(constraints, sdp::kRecv, mSsrcGenerator, false,
&GetAnswer());
Negotiate();
ASSERT_TRUE(mSendOff.GetNegotiatedDetails());
ASSERT_EQ(2U, mSendOff.GetNegotiatedDetails()->GetEncodingCount());
@ -1303,7 +1304,8 @@ TEST_F(JsepTrackTest, SimulcastAnswerer) {
mSendAns.SetJsConstraints(constraints);
CreateOffer();
// Add simulcast/rid to offer
mRecvOff.AddToMsection(constraints, sdp::kRecv, mSsrcGenerator, &GetOffer());
mRecvOff.AddToMsection(constraints, sdp::kRecv, mSsrcGenerator, false,
&GetOffer());
CreateAnswer();
Negotiate();
ASSERT_TRUE(mSendAns.GetNegotiatedDetails());

Просмотреть файл

@ -203,6 +203,7 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
mRembEnabled(false),
mFECEnabled(false),
mTransportCCEnabled(false),
mRtxEnabled(false),
mREDPayloadType(0),
mULPFECPayloadType(0),
mProfileLevelId(0),
@ -260,6 +261,11 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
}
}
void EnableRtx(const std::string& rtxPayloadType) {
mRtxEnabled = true;
mRtxPayloadType = rtxPayloadType;
}
void AddParametersToMSection(SdpMediaSection& msection) const override {
AddFmtpsToMSection(msection);
AddRtcpFbsToMSection(msection);
@ -311,6 +317,20 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, vp8Params));
}
}
if (mRtxEnabled && mDirection == sdp::kRecv) {
SdpFmtpAttributeList::RtxParameters params(
GetRtxParameters(mDefaultPt, msection));
uint16_t apt;
if (SdpHelper::GetPtAsInt(mDefaultPt, &apt)) {
if (apt <= 127) {
msection.AddCodec(mRtxPayloadType, "rtx", mClock, mChannels);
params.apt = apt;
msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mRtxPayloadType, params));
}
}
}
}
void AddRtcpFbsToMSection(SdpMediaSection& msection) const {
@ -364,6 +384,45 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
return result;
}
SdpFmtpAttributeList::RtxParameters GetRtxParameters(
const std::string& pt, const SdpMediaSection& msection) const {
SdpFmtpAttributeList::RtxParameters result;
const auto* params = msection.FindFmtp(pt);
if (params && params->codec_type == SdpRtpmapAttributeList::kRtx) {
result = static_cast<const SdpFmtpAttributeList::RtxParameters&>(*params);
}
return result;
}
Maybe<std::string> GetRtxPtByApt(const std::string& apt,
const SdpMediaSection& msection) const {
Maybe<std::string> result;
uint16_t aptAsInt;
if (!SdpHelper::GetPtAsInt(apt, &aptAsInt)) {
return result;
}
const SdpAttributeList& attrs = msection.GetAttributeList();
if (attrs.HasAttribute(SdpAttribute::kFmtpAttribute)) {
for (const auto& fmtpAttr : attrs.GetFmtp().mFmtps) {
if (fmtpAttr.parameters) {
auto* params = fmtpAttr.parameters.get();
if (params && params->codec_type == SdpRtpmapAttributeList::kRtx) {
const SdpFmtpAttributeList::RtxParameters* rtxParams =
static_cast<const SdpFmtpAttributeList::RtxParameters*>(params);
if (rtxParams->apt == aptAsInt) {
result = Some(fmtpAttr.format);
break;
}
}
}
}
}
return result;
}
SdpFmtpAttributeList::VP8Parameters GetVP8Parameters(
const std::string& pt, const SdpMediaSection& msection) const {
SdpRtpmapAttributeList::CodecType expectedType(
@ -458,6 +517,16 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
}
}
if (mRtxEnabled && (mDirection == sdp::kSend || isOffer)) {
Maybe<std::string> rtxPt = GetRtxPtByApt(mDefaultPt, remoteMsection);
if (rtxPt.isSome()) {
EnableRtx(*rtxPt);
} else {
mRtxEnabled = false;
mRtxPayloadType = "";
}
}
NegotiateRtcpFb(remoteMsection);
return true;
}
@ -693,8 +762,10 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
bool mRembEnabled;
bool mFECEnabled;
bool mTransportCCEnabled;
bool mRtxEnabled;
uint8_t mREDPayloadType;
uint8_t mULPFECPayloadType;
std::string mRtxPayloadType;
std::vector<uint8_t> mRedundantEncodings;
// H264-specific stuff

Просмотреть файл

@ -94,7 +94,7 @@ nsresult JsepSessionImpl::AddTransceiver(RefPtr<JsepTransceiver> transceiver) {
if (transceiver->GetMediaType() != SdpMediaSection::kApplication) {
// Make sure we have an ssrc. Might already be set.
transceiver->mSendTrack.EnsureSsrcs(mSsrcGenerator);
transceiver->mSendTrack.EnsureSsrcs(mSsrcGenerator, 1U);
transceiver->mSendTrack.SetCNAME(mCNAME);
// Make sure we have identifiers for send track, just in case.
@ -1950,6 +1950,7 @@ void JsepSessionImpl::SetupDefaultCodecs() {
// Defaults for mandatory params
vp8->mConstraints.maxFs = 12288; // Enough for 2048x1536
vp8->mConstraints.maxFps = 60;
vp8->EnableRtx("124");
mSupportedCodecs.push_back(std::move(vp8));
UniquePtr<JsepVideoCodecDescription> vp9(
@ -1957,6 +1958,7 @@ void JsepSessionImpl::SetupDefaultCodecs() {
// Defaults for mandatory params
vp9->mConstraints.maxFs = 12288; // Enough for 2048x1536
vp9->mConstraints.maxFps = 60;
vp9->EnableRtx("125");
mSupportedCodecs.push_back(std::move(vp9));
UniquePtr<JsepVideoCodecDescription> h264_1(
@ -1964,6 +1966,7 @@ void JsepSessionImpl::SetupDefaultCodecs() {
h264_1->mPacketizationMode = 1;
// Defaults for mandatory params
h264_1->mProfileLevelId = 0x42E00D;
h264_1->EnableRtx("127");
mSupportedCodecs.push_back(std::move(h264_1));
UniquePtr<JsepVideoCodecDescription> h264_0(
@ -1971,6 +1974,7 @@ void JsepSessionImpl::SetupDefaultCodecs() {
h264_0->mPacketizationMode = 0;
// Defaults for mandatory params
h264_0->mProfileLevelId = 0x42E00D;
h264_0->EnableRtx("98");
mSupportedCodecs.push_back(std::move(h264_0));
UniquePtr<JsepVideoCodecDescription> ulpfec(new JsepVideoCodecDescription(

Просмотреть файл

@ -83,13 +83,16 @@ void JsepTrack::EnsureNoDuplicatePayloadTypes(
}
}
void JsepTrack::EnsureSsrcs(SsrcGenerator& ssrcGenerator) {
if (mSsrcs.empty()) {
uint32_t ssrc;
if (!ssrcGenerator.GenerateSsrc(&ssrc)) {
void JsepTrack::EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber) {
while (mSsrcs.size() < aNumber) {
uint32_t ssrc, rtxSsrc;
if (!ssrcGenerator.GenerateSsrc(&ssrc) ||
!ssrcGenerator.GenerateSsrc(&rtxSsrc)) {
return;
}
mSsrcs.push_back(ssrc);
mSsrcToRtxSsrc[ssrc] = rtxSsrc;
MOZ_ASSERT(mSsrcs.size() == mSsrcToRtxSsrc.size());
}
}
@ -115,7 +118,9 @@ void JsepTrack::AddToOffer(SsrcGenerator& ssrcGenerator,
if (offer->IsSending()) {
constraints = mJsEncodeConstraints;
}
AddToMsection(constraints, sdp::kSend, ssrcGenerator, offer);
AddToMsection(constraints, sdp::kSend, ssrcGenerator,
IsRtxEnabled(mPrototypeCodecs), offer);
}
}
@ -140,7 +145,9 @@ void JsepTrack::AddToAnswer(const SdpMediaSection& offer,
GetRids(offer, sdp::kRecv, &rids);
NegotiateRids(rids, &constraints);
}
AddToMsection(constraints, sdp::kSend, ssrcGenerator, answer);
AddToMsection(constraints, sdp::kSend, ssrcGenerator, IsRtxEnabled(codecs),
answer);
}
}
@ -214,22 +221,44 @@ void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
// Right now, the spec does not permit changing the number of encodings after
// the initial creation of the sender, so we don't need to worry about things
// like a new encoding inserted in between two pre-existing encodings.
while (mSsrcs.size() < numSsrcs) {
uint32_t ssrc;
if (!ssrcGenerator.GenerateSsrc(&ssrc)) {
return;
EnsureSsrcs(ssrcGenerator, numSsrcs);
PruneSsrcs(numSsrcs);
MOZ_ASSERT(!mSsrcs.empty());
}
void JsepTrack::PruneSsrcs(size_t aNumSsrcs) {
mSsrcs.resize(aNumSsrcs);
// We might have duplicate entries in mSsrcs, so we need to resize first and
// then remove ummatched rtx ssrcs.
auto itor = mSsrcToRtxSsrc.begin();
while (itor != mSsrcToRtxSsrc.end()) {
if (std::find(mSsrcs.begin(), mSsrcs.end(), itor->first) == mSsrcs.end()) {
itor = mSsrcToRtxSsrc.erase(itor);
} else {
++itor;
}
}
}
bool JsepTrack::IsRtxEnabled(
const std::vector<UniquePtr<JsepCodecDescription>>& codecs) const {
for (const auto& codec : codecs) {
if (codec->mType == SdpMediaSection::kVideo &&
static_cast<const JsepVideoCodecDescription*>(codec.get())
->mRtxEnabled) {
return true;
}
mSsrcs.push_back(ssrc);
}
mSsrcs.resize(numSsrcs);
MOZ_ASSERT(!mSsrcs.empty());
return false;
}
void JsepTrack::AddToMsection(const std::vector<JsConstraints>& constraintsList,
sdp::Direction direction,
SsrcGenerator& ssrcGenerator,
SdpMediaSection* msection) {
bool requireRtxSsrcs, SdpMediaSection* msection) {
UniquePtr<SdpSimulcastAttribute> simulcast(new SdpSimulcastAttribute);
UniquePtr<SdpRidAttributeList> rids(new SdpRidAttributeList);
for (const JsConstraints& constraints : constraintsList) {
@ -257,7 +286,21 @@ void JsepTrack::AddToMsection(const std::vector<JsConstraints>& constraintsList,
if (mType != SdpMediaSection::kApplication && mDirection == sdp::kSend) {
UpdateSsrcs(ssrcGenerator, constraintsList.size());
msection->SetSsrcs(mSsrcs, mCNAME);
if (requireRtxSsrcs) {
MOZ_ASSERT(mSsrcs.size() == mSsrcToRtxSsrc.size());
std::vector<uint32_t> allSsrcs;
UniquePtr<SdpSsrcGroupAttributeList> group(new SdpSsrcGroupAttributeList);
for (const auto& [ssrc, rtxSsrc] : mSsrcToRtxSsrc) {
allSsrcs.push_back(ssrc);
allSsrcs.push_back(rtxSsrc);
group->PushEntry(SdpSsrcGroupAttributeList::kFid, {ssrc, rtxSsrc});
}
msection->SetSsrcs(allSsrcs, mCNAME);
msection->GetAttributeList().SetAttribute(group.release());
} else {
msection->SetSsrcs(mSsrcs, mCNAME);
}
}
}
@ -330,7 +373,7 @@ void JsepTrack::CreateEncodings(
// Drop SSRCs if less RIDs were offered than we have encoding constraints
// Just in case.
if (mSsrcs.size() > max_streams) {
mSsrcs.resize(max_streams);
PruneSsrcs(max_streams);
}
// For each stream make sure we have an encoding, and configure
@ -391,6 +434,17 @@ std::vector<UniquePtr<JsepCodecDescription>> JsepTrack::NegotiateCodecs(
if (clone->Negotiate(fmt, remote, isOffer)) {
// If negotiation changed the payload type, remember that for later
codec->mDefaultPt = clone->mDefaultPt;
// Remember whether we negotiated rtx and the associated pt for later.
if (codec->mType == SdpMediaSection::kVideo) {
JsepVideoCodecDescription* videoCodec =
static_cast<JsepVideoCodecDescription*>(codec.get());
JsepVideoCodecDescription* cloneVideoCodec =
static_cast<JsepVideoCodecDescription*>(clone.get());
videoCodec->mRtxEnabled = cloneVideoCodec->mRtxEnabled;
videoCodec->mRtxPayloadType = cloneVideoCodec->mRtxPayloadType;
}
// Moves the codec out of mPrototypeCodecs, leaving an empty
// UniquePtr, so we don't use it again. Also causes successfully
// negotiated codecs to be placed up front in the future.

Просмотреть файл

@ -13,6 +13,7 @@
#include <vector>
#include <mozilla/UniquePtr.h>
#include "mozilla/Preferences.h"
#include "nsError.h"
#include "signaling/src/jsep/JsepTrackEncoding.h"
@ -117,6 +118,31 @@ class JsepTrack {
mSsrcs.push_back(ssrcAttr.ssrc);
}
}
// Use FID ssrc-group to associate rtx ssrcs with "regular" ssrcs. Despite
// not being part of RFC 4588, this is how rtx is negotiated by libwebrtc
// and jitsi.
mSsrcToRtxSsrc.clear();
if (msection.GetAttributeList().HasAttribute(
SdpAttribute::kSsrcGroupAttribute)) {
for (const auto& group :
msection.GetAttributeList().GetSsrcGroup().mSsrcGroups) {
if (group.semantics == SdpSsrcGroupAttributeList::kFid &&
group.ssrcs.size() == 2) {
// Ensure we have a "regular" ssrc for each rtx ssrc.
if (std::find(mSsrcs.begin(), mSsrcs.end(), group.ssrcs[0]) !=
mSsrcs.end()) {
mSsrcToRtxSsrc[group.ssrcs[0]] = group.ssrcs[1];
// Remove rtx ssrcs from mSsrcs
auto res = std::remove_if(
mSsrcs.begin(), mSsrcs.end(),
[group](uint32_t ssrc) { return ssrc == group.ssrcs[1]; });
mSsrcs.erase(res, mSsrcs.end());
}
}
}
}
}
JsepTrack(const JsepTrack& orig) { *this = orig; }
@ -133,6 +159,7 @@ class JsepTrack {
mDirection = rhs.mDirection;
mJsEncodeConstraints = rhs.mJsEncodeConstraints;
mSsrcs = rhs.mSsrcs;
mSsrcToRtxSsrc = rhs.mSsrcToRtxSsrc;
mActive = rhs.mActive;
mRemoteSetSendBit = rhs.mRemoteSetSendBit;
@ -164,7 +191,15 @@ class JsepTrack {
virtual const std::vector<uint32_t>& GetSsrcs() const { return mSsrcs; }
virtual void EnsureSsrcs(SsrcGenerator& ssrcGenerator);
virtual std::vector<uint32_t> GetRtxSsrcs() const {
std::vector<uint32_t> result;
std::for_each(
mSsrcToRtxSsrc.begin(), mSsrcToRtxSsrc.end(),
[&result](const auto& pair) { result.push_back(pair.second); });
return result;
}
virtual void EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber);
bool GetActive() const { return mActive; }
@ -234,7 +269,7 @@ class JsepTrack {
void AddToMsection(const std::vector<JsConstraints>& constraintsList,
sdp::Direction direction, SsrcGenerator& ssrcGenerator,
SdpMediaSection* msection);
bool requireRtxSsrcs, SdpMediaSection* msection);
private:
std::vector<UniquePtr<JsepCodecDescription>> GetCodecClones() const;
@ -263,6 +298,9 @@ class JsepTrack {
const std::vector<std::pair<SdpRidAttributeList::Rid, bool>>& rids,
std::vector<JsConstraints>* constraints) const;
void UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings);
void PruneSsrcs(size_t aNumSsrcs);
bool IsRtxEnabled(
const std::vector<UniquePtr<JsepCodecDescription>>& codecs) const;
mozilla::SdpMediaSection::MediaType mType;
// These are the ids that everyone outside of JsepSession care about
@ -277,6 +315,7 @@ class JsepTrack {
std::vector<JsConstraints> mJsEncodeConstraints;
UniquePtr<JsepTrackNegotiatedDetails> mNegotiatedDetails;
std::vector<uint32_t> mSsrcs;
std::map<uint32_t, uint32_t> mSsrcToRtxSsrc;
bool mActive;
bool mRemoteSetSendBit;
};