Bug 1275360 - Add sdp handling for video FEC (red/ulpfec). r=drno

MozReview-Commit-ID: BIt7SkSEquj

--HG--
extra : transplant_source : %A9%27%7B%D7%AA%FBI%94%9D%EC%9E%A2%2C%ED%3D%06%2C%90f%DB
This commit is contained in:
Michael Froman 2016-06-30 00:40:40 -05:00
Родитель 82187afe2d
Коммит 089e002c9b
16 изменённых файлов: 654 добавлений и 21 удалений

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

@ -50,6 +50,7 @@ class JsepCodecDescription {
virtual bool
Matches(const std::string& fmt, const SdpMediaSection& remoteMsection) const
{
// note: fmt here is remote fmt (to go with remoteMsection)
if (mType != remoteMsection.GetMediaType()) {
return false;
}
@ -214,6 +215,7 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
clock, 0, enabled),
mTmmbrEnabled(false),
mRembEnabled(false),
mFECEnabled(false),
mPacketizationMode(0)
{
// Add supported rtcp-fb types
@ -242,6 +244,16 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
}
}
virtual void
EnableFec() {
// Enabling FEC for video works a little differently than enabling
// REMB or TMMBR. Support for FEC is indicated by the presence of
// particular codes (red and ulpfec) instead of using rtcpfb
// attributes on a given codec. There is no rtcpfb to push for FEC
// as can be seen above when REMB or TMMBR are enabled.
mFECEnabled = true;
}
void
AddParametersToMSection(SdpMediaSection& msection) const override
{
@ -282,6 +294,11 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
h264Params.level_asymmetry_allowed = true;
msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, h264Params));
} else if (mName == "red") {
SdpFmtpAttributeList::RedParameters redParams(
GetRedParameters(mDefaultPt, msection));
redParams.encodings = mRedundantEncodings;
msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, redParams));
} else if (mName == "VP8" || mName == "VP9") {
if (mDirection == sdp::kRecv) {
// VP8 and VP9 share the same SDP parameters thus far
@ -338,6 +355,21 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
return result;
}
SdpFmtpAttributeList::RedParameters
GetRedParameters(const std::string& pt,
const SdpMediaSection& msection) const
{
SdpFmtpAttributeList::RedParameters result;
auto* params = msection.FindFmtp(pt);
if (params && params->codec_type == SdpRtpmapAttributeList::kRed) {
result =
static_cast<const SdpFmtpAttributeList::RedParameters&>(*params);
}
return result;
}
SdpFmtpAttributeList::VP8Parameters
GetVP8Parameters(const std::string& pt,
const SdpMediaSection& msection) const
@ -427,7 +459,10 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
} else {
// TODO(bug 1143709): max-recv-level support
}
} else if (mName == "red") {
SdpFmtpAttributeList::RedParameters redParams(
GetRedParameters(mDefaultPt, remoteMsection));
mRedundantEncodings = redParams.encodings;
} else if (mName == "VP8" || mName == "VP9") {
if (mDirection == sdp::kSend) {
SdpFmtpAttributeList::VP8Parameters vp8Params(
@ -649,6 +684,24 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
return false;
}
virtual void
UpdateRedundantEncodings(std::vector<JsepCodecDescription*> codecs)
{
for (const auto codec : codecs) {
if (codec->mType == SdpMediaSection::kVideo &&
codec->mEnabled &&
codec->mName != "red") {
uint8_t pt = (uint8_t)strtoul(codec->mDefaultPt.c_str(), nullptr, 10);
// returns 0 if failed to convert, and since zero could
// be valid, check the defaultPt for 0
if (pt == 0 && codec->mDefaultPt != "0") {
continue;
}
mRedundantEncodings.push_back(pt);
}
}
}
JSEP_CODEC_CLONE(JsepVideoCodecDescription)
std::vector<std::string> mAckFbTypes;
@ -657,6 +710,8 @@ class JsepVideoCodecDescription : public JsepCodecDescription {
std::vector<SdpRtcpFbAttributeList::Feedback> mOtherFbTypes;
bool mTmmbrEnabled;
bool mRembEnabled;
bool mFECEnabled;
std::vector<uint8_t> mRedundantEncodings;
// H264-specific stuff
uint32_t mProfileLevelId;

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

@ -2182,11 +2182,29 @@ JsepSessionImpl::SetupDefaultCodecs()
h264_0->mProfileLevelId = 0x42E00D;
mSupportedCodecs.values.push_back(h264_0);
JsepVideoCodecDescription* red = new JsepVideoCodecDescription(
"122", // payload type
"red", // codec name
90000 // clock rate (match other video codecs)
);
mSupportedCodecs.values.push_back(red);
JsepVideoCodecDescription* ulpfec = new JsepVideoCodecDescription(
"123", // payload type
"ulpfec", // codec name
90000 // clock rate (match other video codecs)
);
mSupportedCodecs.values.push_back(ulpfec);
mSupportedCodecs.values.push_back(new JsepApplicationCodecDescription(
"5000",
"webrtc-datachannel",
WEBRTC_DATACHANNEL_STREAMS_DEFAULT
));
// Update the redundant encodings for the RED codec with the supported
// codecs. Note: only uses the video codecs.
red->UpdateRedundantEncodings(mSupportedCodecs.values);
}
void

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

@ -344,13 +344,58 @@ JsepTrack::NegotiateCodecs(
}
}
// Find the (potential) red codec and ulpfec codec
JsepVideoCodecDescription* red = nullptr;
JsepVideoCodecDescription* ulpfec = nullptr;
for (auto codec : *codecs) {
if (codec->mName == "red") {
red = static_cast<JsepVideoCodecDescription*>(codec);
break;
}
if (codec->mName == "ulpfec") {
ulpfec = static_cast<JsepVideoCodecDescription*>(codec);
break;
}
}
// if we have a red codec remove redundant encodings that don't exist
if (red) {
// Since we could have an externally specified redundant endcodings
// list, we shouldn't simply rebuild the redundant encodings list
// based on the current list of codecs.
std::vector<uint8_t> unnegotiatedEncodings;
std::swap(unnegotiatedEncodings, red->mRedundantEncodings);
for (auto redundantPt : unnegotiatedEncodings) {
std::string pt = std::to_string(redundantPt);
for (auto codec : *codecs) {
if (pt == codec->mDefaultPt) {
red->mRedundantEncodings.push_back(redundantPt);
break;
}
}
}
}
// Video FEC is indicated by the existence of the red and ulpfec
// codecs and not an attribute on the particular video codec (like in
// a rtcpfb attr). If we see both red and ulpfec codecs, we enable FEC
// on all the other codecs.
if (red && ulpfec) {
for (auto codec : *codecs) {
if (codec->mName != "red" && codec->mName != "ulpfec") {
JsepVideoCodecDescription* videoCodec =
static_cast<JsepVideoCodecDescription*>(codec);
videoCodec->EnableFec();
}
}
}
// 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()) {
// answer. For now, remove all but the first codec unless the red codec
// exists, and then we include the others per RFC 5109, section 14.2.
if (!codecs->empty() && !red) {
for (size_t i = 1; i < codecs->size(); ++i) {
delete (*codecs)[i];
(*codecs)[i] = nullptr;

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

@ -149,6 +149,7 @@ JsepCodecDescToCodecConfig(const JsepCodecDescription& aCodec,
configRaw->mNackFbTypes = desc.mNackFbTypes;
configRaw->mCcmFbTypes = desc.mCcmFbTypes;
configRaw->mRembFbSet = desc.RtcpFbRembIsSet();
configRaw->mFECFbSet = desc.mFECEnabled;
*aConfig = configRaw;
return NS_OK;

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

@ -886,7 +886,8 @@ class ConfigureCodec {
mVP8MaxFr(0),
mUseTmmbr(false),
mUseRemb(false),
mUseAudioFec(false)
mUseAudioFec(false),
mRedUlpfecEnabled(false)
{
#ifdef MOZ_WEBRTC_OMX
// Check to see if what HW codecs are available (not in use) at this moment.
@ -957,6 +958,9 @@ class ConfigureCodec {
branch->GetBoolPref("media.navigator.video.use_remb", &mUseRemb);
branch->GetBoolPref("media.navigator.audio.use_fec", &mUseAudioFec);
branch->GetBoolPref("media.navigator.video.red_ulpfec_enabled",
&mRedUlpfecEnabled);
}
void operator()(JsepCodecDescription* codec) const
@ -997,6 +1001,10 @@ class ConfigureCodec {
if (mHardwareH264Supported) {
videoCodec.mStronglyPreferred = true;
}
} else if (videoCodec.mName == "red") {
videoCodec.mEnabled = mRedUlpfecEnabled;
} else if (videoCodec.mName == "ulpfec") {
videoCodec.mEnabled = mRedUlpfecEnabled;
} else if (videoCodec.mName == "VP8" || videoCodec.mName == "VP9") {
if (videoCodec.mName == "VP9" && !mVP9Enabled) {
videoCodec.mEnabled = false;
@ -1035,6 +1043,41 @@ class ConfigureCodec {
bool mUseTmmbr;
bool mUseRemb;
bool mUseAudioFec;
bool mRedUlpfecEnabled;
};
class ConfigureRedCodec {
public:
explicit ConfigureRedCodec(nsCOMPtr<nsIPrefBranch>& branch,
std::vector<uint8_t>* redundantEncodings) :
mRedundantEncodings(redundantEncodings)
{
// if we wanted to override or modify which encodings are considered
// for redundant encodings, we'd probably want to handle it here by
// checking prefs modifying the operator() code below
}
void operator()(JsepCodecDescription* codec) const
{
if (codec->mType == SdpMediaSection::kVideo &&
codec->mEnabled == false) {
uint8_t pt = (uint8_t)strtoul(codec->mDefaultPt.c_str(), nullptr, 10);
// don't search for the codec payload type unless we have a valid
// conversion (non-zero)
if (pt != 0) {
std::vector<uint8_t>::iterator it =
std::find(mRedundantEncodings->begin(),
mRedundantEncodings->end(),
pt);
if (it != mRedundantEncodings->end()) {
mRedundantEncodings->erase(it);
}
}
}
}
private:
std::vector<uint8_t>* mRedundantEncodings;
};
nsresult
@ -1059,6 +1102,23 @@ PeerConnectionImpl::ConfigureJsepSessionCodecs() {
ConfigureCodec configurer(branch);
mJsepSession->ForEachCodec(configurer);
// first find the red codec description
std::vector<JsepCodecDescription*>& codecs = mJsepSession->Codecs();
JsepVideoCodecDescription* redCodec = nullptr;
for (auto codec : codecs) {
// we only really care about finding the RED codec if it is
// enabled
if (codec->mName == "red" && codec->mEnabled) {
redCodec = static_cast<JsepVideoCodecDescription*>(codec);
break;
}
}
// if red codec was found, configure it for the other enabled codecs
if (redCodec) {
ConfigureRedCodec configureRed(branch, &(redCodec->mRedundantEncodings));
mJsepSession->ForEachCodec(configureRed);
}
// We use this to sort the list of codecs once everything is configured
CompareCodecPriority comparator;

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

@ -1102,6 +1102,8 @@ ShouldSerializeChannels(SdpRtpmapAttributeList::CodecType type)
case SdpRtpmapAttributeList::kiLBC:
case SdpRtpmapAttributeList::kiSAC:
case SdpRtpmapAttributeList::kH264:
case SdpRtpmapAttributeList::kRed:
case SdpRtpmapAttributeList::kUlpfec:
return false;
case SdpRtpmapAttributeList::kOtherCodec:
return true;

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

@ -1023,6 +1023,8 @@ public:
kiLBC,
kiSAC,
kH264,
kRed,
kUlpfec,
kOtherCodec
};
@ -1102,6 +1104,12 @@ inline std::ostream& operator<<(std::ostream& os,
case SdpRtpmapAttributeList::kH264:
os << "H264";
break;
case SdpRtpmapAttributeList::kRed:
os << "red";
break;
case SdpRtpmapAttributeList::kUlpfec:
os << "ulpfec";
break;
default:
MOZ_ASSERT(false);
os << "?";
@ -1135,6 +1143,32 @@ public:
SdpRtpmapAttributeList::CodecType codec_type;
};
class RedParameters : public Parameters
{
public:
RedParameters()
: Parameters(SdpRtpmapAttributeList::kRed)
{
}
virtual Parameters*
Clone() const override
{
return new RedParameters(*this);
}
virtual void
Serialize(std::ostream& os) const override
{
for(size_t i = 0; i < encodings.size(); ++i) {
os << (i != 0 ? "/" : "")
<< std::to_string(encodings[i]);
}
}
std::vector<uint8_t> encodings;
};
class H264Parameters : public Parameters
{
public:

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

@ -359,6 +359,10 @@ SipccSdpAttributeList::GetCodecType(rtp_ptype type)
return SdpRtpmapAttributeList::kVP8;
case RTP_VP9:
return SdpRtpmapAttributeList::kVP9;
case RTP_RED:
return SdpRtpmapAttributeList::kRed;
case RTP_ULPFEC:
return SdpRtpmapAttributeList::kUlpfec;
case RTP_NONE:
// Happens when sipcc doesn't know how to translate to the enum
case RTP_CELP:
@ -721,6 +725,17 @@ SipccSdpAttributeList::LoadFmtp(sdp_t* sdp, uint16_t level)
parameters.reset(vp8Parameters);
} break;
case RTP_RED: {
SdpFmtpAttributeList::RedParameters* redParameters(
new SdpFmtpAttributeList::RedParameters);
for (int i = 0;
i < SDP_FMTP_MAX_REDUNDANT_ENCODINGS && fmtp->redundant_encodings[i];
++i) {
redParameters->encodings.push_back(fmtp->redundant_encodings[i]);
}
parameters.reset(redParameters);
} break;
case RTP_OPUS: {
SdpFmtpAttributeList::OpusParameters* opusParameters(
new SdpFmtpAttributeList::OpusParameters);

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

@ -40,6 +40,8 @@ typedef enum rtp_ptype_
RTP_OPUS = 109,
RTP_VP8 = 120,
RTP_VP9 = 121,
RTP_RED = 122,
RTP_ULPFEC = 123,
RTP_I420 = 124,
RTP_ISAC = 124
} rtp_ptype;

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

@ -49,6 +49,9 @@
#define SDP_SDESCRIPTIONS_KEY_SIZE_UNKNOWN 0
#define SDP_SRTP_CRYPTO_SELECTION_FLAGS_UNKNOWN 0
/* Max number of fmtp redundant encodings */
#define SDP_FMTP_MAX_REDUNDANT_ENCODINGS 128
/*
* SRTP_CONTEXT_SET_*
* Set a SRTP Context field flag
@ -710,6 +713,9 @@ typedef struct sdp_fmtp {
uint16_t annex_k_val;
uint16_t annex_n_val;
/* RFC 5109 Section 4.2 for specifying redundant encodings */
uint8_t redundant_encodings[SDP_FMTP_MAX_REDUNDANT_ENCODINGS];
/* Annex P can take one or more values in the range 1-4 . e.g P=1,3 */
uint16_t annex_p_val_picture_resize; /* 1 = four; 2 = sixteenth */
uint16_t annex_p_val_warp; /* 3 = half; 4=sixteenth */

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

@ -29,6 +29,8 @@ static const char* logTag = "sdp_access";
#define SIPSDP_ATTR_ENCNAME_L16_256K "L16"
#define SIPSDP_ATTR_ENCNAME_ISAC "ISAC"
#define SIPSDP_ATTR_ENCNAME_OPUS "opus"
#define SIPSDP_ATTR_ENCNAME_RED "red"
#define SIPSDP_ATTR_ENCNAME_ULPFEC "ulpfec"
/* Function: sdp_find_media_level
* Description: Find and return a pointer to the specified media level,
@ -1377,6 +1379,12 @@ rtp_ptype sdp_get_known_payload_type(sdp_t *sdp_p,
if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_VP9) == 0) {
return (RTP_VP9);
}
if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_RED) == 0) {
return (RTP_RED);
}
if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_ULPFEC) == 0) {
return (RTP_ULPFEC);
}
}
}
}

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

@ -1509,7 +1509,7 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
fmtp_p->annex_p_val_picture_resize = 0;
fmtp_p->annex_p_val_warp = 0;
tok = tmp;
tok++; temp=PL_strtok_r(tok, ",", &strtok_state);
tok++; temp = PL_strtok_r(tok, ",", &strtok_state);
if (temp) {
iter=1;
while (temp != NULL) {
@ -1525,7 +1525,7 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
else if (iter == 2)
fmtp_p->annex_p_val_warp = (uint16_t) strtoul_result;
temp=PL_strtok_r(NULL, ",", &strtok_state);
temp = PL_strtok_r(NULL, ",", &strtok_state);
iter++;
}
}
@ -1780,6 +1780,27 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
}
} /* if (temp) */
done = TRUE;
} else if (strchr(tmp, '/')) {
// XXX Note that because RFC 5109 so conveniently specified
// this fmtp with no param names, we hope that nothing else
// has a slash in the string because otherwise we won't know
// how to differentiate.
temp=PL_strtok_r(tmp, "/", &strtok_state);
if (temp) {
iter = 0;
while (temp != NULL) {
errno = 0;
strtoul_result = strtoul(temp, &strtoul_end, 10);
if (errno ||
temp == strtoul_end || strtoul_result > USHRT_MAX) {
continue;
}
fmtp_p->redundant_encodings[iter++] =
(uint8_t)strtoul_result;
temp=PL_strtok_r(NULL, "/", &strtok_state);
}
} /* if (temp) */
} else {
// XXX Note that DTMF fmtp will fall into here:
// a=fmtp:101 0-15 (or 0-15,NN,NN etc)

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

@ -2742,11 +2742,13 @@ TEST_F(JsepSessionTest, ValidateOfferedCodecParams)
auto& video_attrs = video_section.GetAttributeList();
ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection());
ASSERT_EQ(4U, video_section.GetFormats().size());
ASSERT_EQ(6U, video_section.GetFormats().size());
ASSERT_EQ("121", video_section.GetFormats()[0]);
ASSERT_EQ("120", video_section.GetFormats()[1]);
ASSERT_EQ("126", video_section.GetFormats()[2]);
ASSERT_EQ("97", video_section.GetFormats()[3]);
ASSERT_EQ("122", video_section.GetFormats()[4]);
ASSERT_EQ("123", video_section.GetFormats()[5]);
// Validate rtpmap
ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute));
@ -2755,22 +2757,28 @@ TEST_F(JsepSessionTest, ValidateOfferedCodecParams)
ASSERT_TRUE(rtpmaps.HasEntry("121"));
ASSERT_TRUE(rtpmaps.HasEntry("126"));
ASSERT_TRUE(rtpmaps.HasEntry("97"));
ASSERT_TRUE(rtpmaps.HasEntry("122"));
ASSERT_TRUE(rtpmaps.HasEntry("123"));
auto& vp8_entry = rtpmaps.GetEntry("120");
auto& vp9_entry = rtpmaps.GetEntry("121");
auto& h264_1_entry = rtpmaps.GetEntry("126");
auto& h264_0_entry = rtpmaps.GetEntry("97");
auto& red_0_entry = rtpmaps.GetEntry("122");
auto& ulpfec_0_entry = rtpmaps.GetEntry("123");
ASSERT_EQ("VP8", vp8_entry.name);
ASSERT_EQ("VP9", vp9_entry.name);
ASSERT_EQ("H264", h264_1_entry.name);
ASSERT_EQ("H264", h264_0_entry.name);
ASSERT_EQ("red", red_0_entry.name);
ASSERT_EQ("ulpfec", ulpfec_0_entry.name);
// Validate fmtps
ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kFmtpAttribute));
auto& fmtps = video_attrs.GetFmtp().mFmtps;
ASSERT_EQ(4U, fmtps.size());
ASSERT_EQ(5U, fmtps.size());
// VP8
const SdpFmtpAttributeList::Parameters* vp8_params =
@ -2821,11 +2829,30 @@ TEST_F(JsepSessionTest, ValidateOfferedCodecParams)
ASSERT_EQ((uint32_t)0x42e00d, parsed_h264_0_params.profile_level_id);
ASSERT_TRUE(parsed_h264_0_params.level_asymmetry_allowed);
ASSERT_EQ(0U, parsed_h264_0_params.packetization_mode);
// red
const SdpFmtpAttributeList::Parameters* red_params =
video_section.FindFmtp("122");
ASSERT_TRUE(red_params);
ASSERT_EQ(SdpRtpmapAttributeList::kRed, red_params->codec_type);
auto& parsed_red_params =
*static_cast<const SdpFmtpAttributeList::RedParameters*>(red_params);
ASSERT_EQ(5U, parsed_red_params.encodings.size());
ASSERT_EQ(121, parsed_red_params.encodings[0]);
ASSERT_EQ(120, parsed_red_params.encodings[1]);
ASSERT_EQ(126, parsed_red_params.encodings[2]);
ASSERT_EQ(97, parsed_red_params.encodings[3]);
ASSERT_EQ(123, parsed_red_params.encodings[4]);
}
TEST_F(JsepSessionTest, ValidateAnsweredCodecParams)
{
// TODO(bug 1099351): Once fixed, we can allow red in this offer,
// which will also cause multiple codecs in answer. For now,
// red/ulpfec for video are behind a pref to mitigate potential for
// errors.
SetCodecEnabled(mSessionOff, "red", false);
for (auto i = mSessionAns.Codecs().begin(); i != mSessionAns.Codecs().end();
++i) {
auto* codec = *i;

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

@ -32,7 +32,7 @@ class JsepTrackTest : public ::testing::Test
JsepTrackTest() {}
std::vector<JsepCodecDescription*>
MakeCodecs() const
MakeCodecs(bool addFecCodecs = false, bool preferRed = false) const
{
std::vector<JsepCodecDescription*> results;
results.push_back(
@ -40,6 +40,16 @@ class JsepTrackTest : public ::testing::Test
results.push_back(
new JsepAudioCodecDescription("9", "G722", 8000, 1, 320, 64000));
JsepVideoCodecDescription* red = nullptr;
if (addFecCodecs && preferRed) {
red = new JsepVideoCodecDescription(
"122",
"red",
90000
);
results.push_back(red);
}
JsepVideoCodecDescription* vp8 =
new JsepVideoCodecDescription("120", "VP8", 90000);
vp8->mConstraints.maxFs = 12288;
@ -52,6 +62,23 @@ class JsepTrackTest : public ::testing::Test
h264->mProfileLevelId = 0x42E00D;
results.push_back(h264);
if (addFecCodecs) {
if (!preferRed) {
red = new JsepVideoCodecDescription(
"122",
"red",
90000
);
results.push_back(red);
}
JsepVideoCodecDescription* ulpfec = new JsepVideoCodecDescription(
"123",
"ulpfec",
90000
);
results.push_back(ulpfec);
}
results.push_back(
new JsepApplicationCodecDescription(
"5000",
@ -59,6 +86,12 @@ class JsepTrackTest : public ::testing::Test
16
));
// if we're doing something with red, it needs
// to update the redundant encodings list
if (red) {
red->UpdateRedundantEncodings(results);
}
return results;
}
@ -211,7 +244,9 @@ class JsepTrackTest : public ::testing::Test
}
const JsepVideoCodecDescription*
GetVideoCodec(const JsepTrack& track) const
GetVideoCodec(const JsepTrack& track,
size_t expectedSize = 1,
size_t codecIndex = 0) const
{
if (!track.GetNegotiatedDetails() ||
track.GetNegotiatedDetails()->GetEncodingCount() != 1U) {
@ -219,8 +254,8 @@ class JsepTrackTest : public ::testing::Test
}
const std::vector<JsepCodecDescription*>& codecs =
track.GetNegotiatedDetails()->GetEncoding(0).GetCodecs();
if (codecs.size() != 1U ||
codecs[0]->mType != SdpMediaSection::kVideo) {
if (codecs.size() != expectedSize ||
codecs[codecIndex]->mType != SdpMediaSection::kVideo) {
return nullptr;
}
return static_cast<const JsepVideoCodecDescription*>(codecs[0]);
@ -366,6 +401,200 @@ TEST_F(JsepTrackTest, VideoNegotiation)
CheckAnsEncodingCount(1);
}
TEST_F(JsepTrackTest, VideoNegotationOffererFEC)
{
mOffCodecs.values = MakeCodecs(true);
mAnsCodecs.values = MakeCodecs(false);
InitTracks(SdpMediaSection::kVideo);
InitSdp(SdpMediaSection::kVideo);
OfferAnswer();
CheckOffEncodingCount(1);
CheckAnsEncodingCount(1);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
ASSERT_EQ(mAnswer->ToString().find("a=fmtp:122"), std::string::npos);
const JsepVideoCodecDescription* track = nullptr;
ASSERT_TRUE((track = GetVideoCodec(*mSendOff)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvOff)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mSendAns)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvAns)));
ASSERT_EQ("120", track->mDefaultPt);
}
TEST_F(JsepTrackTest, VideoNegotationAnswererFEC)
{
mOffCodecs.values = MakeCodecs(false);
mAnsCodecs.values = MakeCodecs(true);
InitTracks(SdpMediaSection::kVideo);
InitSdp(SdpMediaSection::kVideo);
OfferAnswer();
CheckOffEncodingCount(1);
CheckAnsEncodingCount(1);
ASSERT_EQ(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_EQ(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_EQ(mOffer->ToString().find("a=fmtp:122"), std::string::npos);
ASSERT_EQ(mAnswer->ToString().find("a=fmtp:122"), std::string::npos);
const JsepVideoCodecDescription* track = nullptr;
ASSERT_TRUE((track = GetVideoCodec(*mSendOff)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvOff)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mSendAns)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvAns)));
ASSERT_EQ("120", track->mDefaultPt);
}
TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFEC)
{
mOffCodecs.values = MakeCodecs(true);
mAnsCodecs.values = MakeCodecs(true);
InitTracks(SdpMediaSection::kVideo);
InitSdp(SdpMediaSection::kVideo);
OfferAnswer();
CheckOffEncodingCount(1);
CheckAnsEncodingCount(1);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
const JsepVideoCodecDescription* track = nullptr;
ASSERT_TRUE((track = GetVideoCodec(*mSendOff, 4)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvOff, 4)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mSendAns, 4)));
ASSERT_EQ("120", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvAns, 4)));
ASSERT_EQ("120", track->mDefaultPt);
}
TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECPreferred)
{
mOffCodecs.values = MakeCodecs(true, true);
mAnsCodecs.values = MakeCodecs(true);
InitTracks(SdpMediaSection::kVideo);
InitSdp(SdpMediaSection::kVideo);
OfferAnswer();
CheckOffEncodingCount(1);
CheckAnsEncodingCount(1);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
const JsepVideoCodecDescription* track = nullptr;
ASSERT_TRUE((track = GetVideoCodec(*mSendOff, 4)));
ASSERT_EQ("122", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvOff, 4)));
ASSERT_EQ("122", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mSendAns, 4)));
ASSERT_EQ("122", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvAns, 4)));
ASSERT_EQ("122", track->mDefaultPt);
}
// Make sure we only put the right things in the fmtp:122 120/.... line
TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECMismatch)
{
mOffCodecs.values = MakeCodecs(true, true);
mAnsCodecs.values = MakeCodecs(true);
// remove h264 from answer codecs
ASSERT_EQ("H264", mAnsCodecs.values[3]->mName);
mAnsCodecs.values.erase(mAnsCodecs.values.begin()+3);
InitTracks(SdpMediaSection::kVideo);
InitSdp(SdpMediaSection::kVideo);
OfferAnswer();
CheckOffEncodingCount(1);
CheckAnsEncodingCount(1);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=fmtp:122 120/123"), std::string::npos);
const JsepVideoCodecDescription* track = nullptr;
ASSERT_TRUE((track = GetVideoCodec(*mSendOff, 3)));
ASSERT_EQ("122", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvOff, 3)));
ASSERT_EQ("122", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mSendAns, 3)));
ASSERT_EQ("122", track->mDefaultPt);
ASSERT_TRUE((track = GetVideoCodec(*mRecvAns, 3)));
ASSERT_EQ("122", track->mDefaultPt);
}
TEST_F(JsepTrackTest, VideoNegotationOffererAnswererFECZeroVP9Codec)
{
mOffCodecs.values = MakeCodecs(true);
JsepVideoCodecDescription* vp9 =
new JsepVideoCodecDescription("0", "VP9", 90000);
vp9->mConstraints.maxFs = 12288;
vp9->mConstraints.maxFps = 60;
mOffCodecs.values.push_back(vp9);
ASSERT_EQ(8U, mOffCodecs.values.size());
JsepVideoCodecDescription* red =
static_cast<JsepVideoCodecDescription*>(mOffCodecs.values[4]);
ASSERT_EQ("red", red->mName);
// rebuild the redundant encodings with our newly added "wacky" VP9
red->mRedundantEncodings.clear();
red->UpdateRedundantEncodings(mOffCodecs.values);
mAnsCodecs.values = MakeCodecs(true);
InitTracks(SdpMediaSection::kVideo);
InitSdp(SdpMediaSection::kVideo);
OfferAnswer();
CheckOffEncodingCount(1);
CheckAnsEncodingCount(1);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=rtpmap:122 red"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=rtpmap:123 ulpfec"), std::string::npos);
ASSERT_NE(mOffer->ToString().find("a=fmtp:122 120/126/123/0"), std::string::npos);
ASSERT_NE(mAnswer->ToString().find("a=fmtp:122 120/126/123\r\n"), std::string::npos);
}
TEST_F(JsepTrackTest, VideoNegotiationOfferRemb)
{
InitCodecs();

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

@ -1225,7 +1225,7 @@ const std::string kBasicAudioVideoOffer =
"a=rtcp:62454 IN IP4 162.222.183.171" CRLF
"a=end-of-candidates" CRLF
"a=ssrc:5150" CRLF
"m=video 9 RTP/SAVPF 120 121" CRLF
"m=video 9 RTP/SAVPF 120 121 122 123" CRLF
"c=IN IP6 ::1" CRLF
"a=fingerprint:sha-1 DF:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7" CRLF
"a=mid:second" CRLF
@ -1233,6 +1233,8 @@ const std::string kBasicAudioVideoOffer =
"a=fmtp:120 max-fs=3600;max-fr=30" CRLF
"a=rtpmap:121 VP9/90000" CRLF
"a=fmtp:121 max-fs=3600;max-fr=30" CRLF
"a=rtpmap:122 red/90000" CRLF
"a=rtpmap:123 ulpfec/90000" CRLF
"a=recvonly" CRLF
"a=rtcp-fb:120 nack" CRLF
"a=rtcp-fb:120 nack pli" CRLF
@ -1418,9 +1420,11 @@ TEST_P(NewSdpTest, CheckMlines) {
mSdp->GetMediaSection(1).GetProtocol())
<< "Wrong protocol for video";
auto video_formats = mSdp->GetMediaSection(1).GetFormats();
ASSERT_EQ(2U, video_formats.size()) << "Wrong number of formats for video";
ASSERT_EQ(4U, video_formats.size()) << "Wrong number of formats for video";
ASSERT_EQ("120", video_formats[0]);
ASSERT_EQ("121", video_formats[1]);
ASSERT_EQ("122", video_formats[2]);
ASSERT_EQ("123", video_formats[3]);
ASSERT_EQ(SdpMediaSection::kAudio, mSdp->GetMediaSection(2).GetMediaType())
<< "Wrong type for third media section";
@ -1518,23 +1522,128 @@ TEST_P(NewSdpTest, CheckRtpmap) {
audiosec.GetFormats()[4],
rtpmap);
const SdpMediaSection& videosec1 = mSdp->GetMediaSection(1);
const SdpMediaSection& videosec = mSdp->GetMediaSection(1);
const SdpRtpmapAttributeList videoRtpmap =
videosec.GetAttributeList().GetRtpmap();
ASSERT_EQ(4U, videoRtpmap.mRtpmaps.size())
<< "Wrong number of rtpmap attributes for video";
CheckRtpmap("120",
SdpRtpmapAttributeList::kVP8,
"VP8",
90000,
0,
videosec1.GetFormats()[0],
videosec1.GetAttributeList().GetRtpmap());
videosec.GetFormats()[0],
videoRtpmap);
const SdpMediaSection& videosec2 = mSdp->GetMediaSection(1);
CheckRtpmap("121",
SdpRtpmapAttributeList::kVP9,
"VP9",
90000,
0,
videosec2.GetFormats()[1],
videosec2.GetAttributeList().GetRtpmap());
videosec.GetFormats()[1],
videoRtpmap);
CheckRtpmap("122",
SdpRtpmapAttributeList::kRed,
"red",
90000,
0,
videosec.GetFormats()[2],
videoRtpmap);
CheckRtpmap("123",
SdpRtpmapAttributeList::kUlpfec,
"ulpfec",
90000,
0,
videosec.GetFormats()[3],
videoRtpmap);
}
static const std::string kVideoWithRedAndUlpfecSdp =
"v=0" CRLF
"o=- 4294967296 2 IN IP4 127.0.0.1" CRLF
"s=SIP Call" CRLF
"c=IN IP4 198.51.100.7" CRLF
"t=0 0" CRLF
"m=video 9 RTP/SAVPF 97 120 121 122 123" CRLF
"c=IN IP6 ::1" CRLF
"a=fingerprint:sha-1 DF:FA:FB:08:3B:3C:54:1D:D7:D4:05:77:A0:72:9B:14:08:6D:0F:4C:2E:AC:8A:FD:0A:8E:99:BF:5D:E8:3C:E7" CRLF
"a=rtpmap:97 H264/90000" CRLF
"a=fmtp:97 profile-level-id=42a01e" CRLF
"a=rtpmap:120 VP8/90000" CRLF
"a=fmtp:120 max-fs=3600;max-fr=30" CRLF
"a=rtpmap:121 VP9/90000" CRLF
"a=fmtp:121 max-fs=3600;max-fr=30" CRLF
"a=rtpmap:122 red/90000" CRLF
"a=rtpmap:123 ulpfec/90000" CRLF;
TEST_P(NewSdpTest, CheckRedNoFmtp) {
ParseSdp(kVideoWithRedAndUlpfecSdp);
ASSERT_TRUE(!!mSdp) << "Parse failed: " << GetParseErrors();
ASSERT_EQ(1U, mSdp->GetMediaSectionCount())
<< "Wrong number of media sections";
ASSERT_TRUE(mSdp->GetMediaSection(0).GetAttributeList().HasAttribute(
SdpAttribute::kFmtpAttribute));
auto video_format_params =
mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
ASSERT_EQ(3U, video_format_params.size());
// make sure we don't get a fmtp for codec 122
for (size_t i = 0; i < video_format_params.size(); ++i) {
ASSERT_NE("122", video_format_params[i].format);
}
}
TEST_P(NewSdpTest, CheckRedFmtpWith2Codecs) {
ParseSdp(kVideoWithRedAndUlpfecSdp + "a=fmtp:122 120/121" CRLF);
ASSERT_TRUE(!!mSdp) << "Parse failed: " << GetParseErrors();
ASSERT_EQ(1U, mSdp->GetMediaSectionCount())
<< "Wrong number of media sections";
ASSERT_TRUE(mSdp->GetMediaSection(0).GetAttributeList().HasAttribute(
SdpAttribute::kFmtpAttribute));
auto video_format_params =
mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
ASSERT_EQ(4U, video_format_params.size());
ASSERT_EQ("122", video_format_params[3].format);
ASSERT_TRUE(!!video_format_params[3].parameters);
ASSERT_EQ(SdpRtpmapAttributeList::kRed,
video_format_params[3].parameters->codec_type);
const SdpFmtpAttributeList::RedParameters* red_parameters(
static_cast<SdpFmtpAttributeList::RedParameters*>(
video_format_params[3].parameters.get()));
ASSERT_EQ(2U, red_parameters->encodings.size());
ASSERT_EQ(120U, red_parameters->encodings[0]);
ASSERT_EQ(121U, red_parameters->encodings[1]);
}
TEST_P(NewSdpTest, CheckRedFmtpWith3Codecs) {
ParseSdp(kVideoWithRedAndUlpfecSdp + "a=fmtp:122 120/121/123" CRLF);
ASSERT_TRUE(!!mSdp) << "Parse failed: " << GetParseErrors();
ASSERT_EQ(1U, mSdp->GetMediaSectionCount())
<< "Wrong number of media sections";
ASSERT_TRUE(mSdp->GetMediaSection(0).GetAttributeList().HasAttribute(
SdpAttribute::kFmtpAttribute));
auto video_format_params =
mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
ASSERT_EQ(4U, video_format_params.size());
ASSERT_EQ("122", video_format_params[3].format);
ASSERT_TRUE(!!video_format_params[3].parameters);
ASSERT_EQ(SdpRtpmapAttributeList::kRed,
video_format_params[3].parameters->codec_type);
const SdpFmtpAttributeList::RedParameters* red_parameters(
static_cast<SdpFmtpAttributeList::RedParameters*>(
video_format_params[3].parameters.get()));
ASSERT_EQ(3U, red_parameters->encodings.size());
ASSERT_EQ(120U, red_parameters->encodings[0]);
ASSERT_EQ(121U, red_parameters->encodings[1]);
ASSERT_EQ(123U, red_parameters->encodings[2]);
}
const std::string kH264AudioVideoOffer =

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

@ -403,6 +403,7 @@ pref("media.navigator.video.default_minfps",10);
pref("media.navigator.video.use_remb", true);
pref("media.navigator.video.use_tmmbr", false);
pref("media.navigator.audio.use_fec", true);
pref("media.navigator.video.red_ulpfec_enabled", false);
pref("media.webrtc.debug.trace_mask", 0);
pref("media.webrtc.debug.multi_log", false);