mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1192390 - Part 2: Simulcast and RID negotiation. r=mt
--HG-- extra : transplant_source : %D1%CAj%05%C7%7B%92%D9%CDV%24j%FF%CB%24B%D4%03%92%5E
This commit is contained in:
parent
dac8417d25
commit
bc30932b74
@ -103,6 +103,9 @@ void
|
||||
JsepTrack::AddToOffer(SdpMediaSection* offer) const
|
||||
{
|
||||
AddToMsection(mPrototypeCodecs.values, offer);
|
||||
if (mDirection == sdp::kSend) {
|
||||
AddToMsection(mJsEncodeConstraints, sdp::kSend, offer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -119,6 +122,14 @@ JsepTrack::AddToAnswer(const SdpMediaSection& offer,
|
||||
}
|
||||
|
||||
AddToMsection(codecs.values, answer);
|
||||
|
||||
if (mDirection == sdp::kSend) {
|
||||
std::vector<JsConstraints> constraints;
|
||||
std::vector<SdpRidAttributeList::Rid> rids;
|
||||
GetRids(offer, sdp::kRecv, &rids);
|
||||
NegotiateRids(rids, &constraints);
|
||||
AddToMsection(constraints, sdp::kSend, answer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -143,59 +154,143 @@ JsepTrack::AddToMsection(const std::vector<JsepCodecDescription*>& codecs,
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the |id| values in |constraintsList| with the rid values in |rids|,
|
||||
// where necessary.
|
||||
void
|
||||
JsepTrack::GetRids(const SdpMediaSection& msection,
|
||||
std::vector<SdpRidAttributeList::Rid>* rids) const
|
||||
JsepTrack::NegotiateRids(const std::vector<SdpRidAttributeList::Rid>& rids,
|
||||
std::vector<JsConstraints>* constraintsList) const
|
||||
{
|
||||
// TODO(bug 1192390): Get list of rids from |answer|; first rid from each
|
||||
// simulcast version.
|
||||
for (const SdpRidAttributeList::Rid& rid : rids) {
|
||||
if (!FindConstraints(rid.id, *constraintsList)) {
|
||||
// Pair up the first JsConstraints with an empty id, if it exists.
|
||||
JsConstraints* constraints = FindConstraints("", *constraintsList);
|
||||
if (constraints) {
|
||||
constraints->rid = rid.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
JsepTrack::AddToMsection(const std::vector<JsConstraints>& constraintsList,
|
||||
sdp::Direction direction,
|
||||
SdpMediaSection* msection)
|
||||
{
|
||||
UniquePtr<SdpSimulcastAttribute> simulcast(new SdpSimulcastAttribute);
|
||||
UniquePtr<SdpRidAttributeList> rids(new SdpRidAttributeList);
|
||||
for (const JsConstraints& constraints : constraintsList) {
|
||||
if (!constraints.rid.empty()) {
|
||||
SdpRidAttributeList::Rid rid;
|
||||
rid.id = constraints.rid;
|
||||
rid.direction = direction;
|
||||
rids->mRids.push_back(rid);
|
||||
|
||||
SdpSimulcastAttribute::Version version;
|
||||
version.choices.push_back(constraints.rid);
|
||||
if (direction == sdp::kSend) {
|
||||
simulcast->sendVersions.push_back(version);
|
||||
} else {
|
||||
simulcast->recvVersions.push_back(version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!rids->mRids.empty()) {
|
||||
msection->GetAttributeList().SetAttribute(simulcast.release());
|
||||
msection->GetAttributeList().SetAttribute(rids.release());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JsepTrack::UpdateRidsFromAnswer(
|
||||
const std::vector<SdpRidAttributeList::Rid>& rids)
|
||||
JsepTrack::GetRids(const SdpMediaSection& msection,
|
||||
sdp::Direction direction,
|
||||
std::vector<SdpRidAttributeList::Rid>* rids) const
|
||||
{
|
||||
// TODO(bug 1192390): For each rid, try to pair it with something in
|
||||
// mEncodingParameters (either by matching rid, or just matching by index).
|
||||
// Once these are paired up, update the ids in mEncodingParameters to match.
|
||||
rids->clear();
|
||||
if (!msection.GetAttributeList().HasAttribute(
|
||||
SdpAttribute::kSimulcastAttribute)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const SdpSimulcastAttribute& simulcast(
|
||||
msection.GetAttributeList().GetSimulcast());
|
||||
|
||||
const SdpSimulcastAttribute::Versions* versions = nullptr;
|
||||
switch (direction) {
|
||||
case sdp::kSend:
|
||||
versions = &simulcast.sendVersions;
|
||||
break;
|
||||
case sdp::kRecv:
|
||||
versions = &simulcast.recvVersions;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!versions->IsSet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (versions->type != SdpSimulcastAttribute::Versions::kRid) {
|
||||
// No support for PT-based simulcast, yet.
|
||||
return;
|
||||
}
|
||||
|
||||
for (const SdpSimulcastAttribute::Version& version : *versions) {
|
||||
if (!version.choices.empty()) {
|
||||
// We validate that rids are present (and sane) elsewhere.
|
||||
rids->push_back(*msection.FindRid(version.choices[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JsepTrack::JsConstraints*
|
||||
JsepTrack::FindConstraints(const std::string& id,
|
||||
std::vector<JsConstraints>& constraintsList) const
|
||||
{
|
||||
for (JsConstraints& constraints : constraintsList) {
|
||||
if (constraints.rid == id) {
|
||||
return &constraints;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
JsepTrack::CreateEncodings(
|
||||
const SdpMediaSection& answer,
|
||||
const SdpMediaSection& remote,
|
||||
const std::vector<JsepCodecDescription*>& negotiatedCodecs,
|
||||
JsepTrackNegotiatedDetails* negotiatedDetails)
|
||||
{
|
||||
std::vector<SdpRidAttributeList::Rid> answerRids;
|
||||
GetRids(answer, &answerRids);
|
||||
UpdateRidsFromAnswer(answerRids);
|
||||
if (answerRids.empty()) {
|
||||
std::vector<SdpRidAttributeList::Rid> rids;
|
||||
GetRids(remote, sdp::kRecv, &rids); // Get rids we will send
|
||||
NegotiateRids(rids, &mJsEncodeConstraints);
|
||||
if (rids.empty()) {
|
||||
// Add dummy value with an empty id to make sure we get a single unicast
|
||||
// stream.
|
||||
answerRids.push_back(SdpRidAttributeList::Rid());
|
||||
rids.push_back(SdpRidAttributeList::Rid());
|
||||
}
|
||||
|
||||
// For each rid in the answer, make sure we have an encoding, and configure
|
||||
// For each rid in the remote, make sure we have an encoding, and configure
|
||||
// that encoding appropriately.
|
||||
for (size_t i = 0; i < answerRids.size(); ++i) {
|
||||
if (i >= negotiatedDetails->mEncodings.values.size()) {
|
||||
for (size_t i = 0; i < rids.size(); ++i) {
|
||||
if (i == negotiatedDetails->mEncodings.values.size()) {
|
||||
negotiatedDetails->mEncodings.values.push_back(new JsepTrackEncoding);
|
||||
}
|
||||
|
||||
JsepTrackEncoding* encoding = negotiatedDetails->mEncodings.values[i];
|
||||
|
||||
for (const JsepCodecDescription* codec : negotiatedCodecs) {
|
||||
if (answerRids[i].HasFormat(codec->mDefaultPt)) {
|
||||
if (rids[i].HasFormat(codec->mDefaultPt)) {
|
||||
encoding->AddCodec(*codec);
|
||||
}
|
||||
}
|
||||
|
||||
encoding->mRid = answerRids[i].id;
|
||||
encoding->mRid = rids[i].id;
|
||||
// If we end up supporting params for rid, we would handle that here.
|
||||
|
||||
// Incorporate the corresponding JS encoding constraints, if they exist
|
||||
for (const JsConstraints& jsConstraints : mJsEncodeConstraints) {
|
||||
if (jsConstraints.id == answerRids[i].id) {
|
||||
if (jsConstraints.rid == rids[i].id) {
|
||||
encoding->mConstraints = jsConstraints.constraints;
|
||||
}
|
||||
}
|
||||
@ -304,7 +399,7 @@ JsepTrack::Negotiate(const SdpMediaSection& answer,
|
||||
UniquePtr<JsepTrackNegotiatedDetails> negotiatedDetails =
|
||||
MakeUnique<JsepTrackNegotiatedDetails>();
|
||||
|
||||
CreateEncodings(answer, negotiatedCodecs.values, negotiatedDetails.get());
|
||||
CreateEncodings(remote, negotiatedCodecs.values, negotiatedDetails.get());
|
||||
|
||||
if (answer.GetAttributeList().HasAttribute(SdpAttribute::kExtmapAttribute)) {
|
||||
for (auto& extmapAttr : answer.GetAttributeList().GetExtmap().mExtmaps) {
|
||||
|
@ -174,6 +174,21 @@ public:
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JsepTrack);
|
||||
|
||||
struct JsConstraints
|
||||
{
|
||||
std::string rid;
|
||||
EncodingConstraints constraints;
|
||||
};
|
||||
|
||||
void SetJsConstraints(const std::vector<JsConstraints>& constraintsList)
|
||||
{
|
||||
mJsEncodeConstraints = constraintsList;
|
||||
}
|
||||
|
||||
static void AddToMsection(const std::vector<JsConstraints>& constraintsList,
|
||||
sdp::Direction direction,
|
||||
SdpMediaSection* msection);
|
||||
|
||||
protected:
|
||||
virtual ~JsepTrack() {}
|
||||
|
||||
@ -189,10 +204,10 @@ private:
|
||||
void AddToMsection(const std::vector<JsepCodecDescription*>& codecs,
|
||||
SdpMediaSection* msection) const;
|
||||
void GetRids(const SdpMediaSection& msection,
|
||||
sdp::Direction direction,
|
||||
std::vector<SdpRidAttributeList::Rid>* rids) const;
|
||||
void UpdateRidsFromAnswer(const std::vector<SdpRidAttributeList::Rid>& rids);
|
||||
void CreateEncodings(
|
||||
const SdpMediaSection& answer,
|
||||
const SdpMediaSection& remote,
|
||||
const std::vector<JsepCodecDescription*>& negotiatedCodecs,
|
||||
JsepTrackNegotiatedDetails* details);
|
||||
|
||||
@ -207,17 +222,18 @@ private:
|
||||
const SdpMediaSection* answer = nullptr,
|
||||
std::map<std::string, std::string>* formatChanges = nullptr) const;
|
||||
|
||||
JsConstraints* FindConstraints(
|
||||
const std::string& rid,
|
||||
std::vector<JsConstraints>& constraintsList) const;
|
||||
void NegotiateRids(const std::vector<SdpRidAttributeList::Rid>& rids,
|
||||
std::vector<JsConstraints>* constraints) const;
|
||||
|
||||
const mozilla::SdpMediaSection::MediaType mType;
|
||||
std::string mStreamId;
|
||||
std::string mTrackId;
|
||||
std::string mCNAME;
|
||||
const sdp::Direction mDirection;
|
||||
PtrVector<JsepCodecDescription> mPrototypeCodecs;
|
||||
struct JsConstraints
|
||||
{
|
||||
std::string id;
|
||||
EncodingConstraints constraints;
|
||||
};
|
||||
// Holds encoding params/constraints from JS. Simulcast happens when there are
|
||||
// multiple of these. If there are none, we assume unconstrained unicast with
|
||||
// no rid.
|
||||
|
@ -104,7 +104,7 @@ class SdpHelper {
|
||||
nsresult CopyStickyParams(const SdpMediaSection& source,
|
||||
SdpMediaSection* dest);
|
||||
bool HasRtcp(SdpMediaSection::Protocol proto) const;
|
||||
SdpMediaSection::Protocol GetProtocolForMediaType(
|
||||
static SdpMediaSection::Protocol GetProtocolForMediaType(
|
||||
SdpMediaSection::MediaType type);
|
||||
void appendSdpParseErrors(
|
||||
const std::vector<std::pair<size_t, std::string> >& aErrors,
|
||||
|
@ -156,5 +156,21 @@ SdpMediaSection::AddMsid(const std::string& id, const std::string& appdata)
|
||||
GetAttributeList().SetAttribute(msids.release());
|
||||
}
|
||||
|
||||
const SdpRidAttributeList::Rid*
|
||||
SdpMediaSection::FindRid(const std::string& id) const
|
||||
{
|
||||
if (!GetAttributeList().HasAttribute(SdpAttribute::kRidAttribute)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const auto& rid : GetAttributeList().GetRid().mRids) {
|
||||
if (rid.id == id) {
|
||||
return &rid;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -169,6 +169,7 @@ public:
|
||||
void SetSsrcs(const std::vector<uint32_t>& ssrcs,
|
||||
const std::string& cname);
|
||||
void AddMsid(const std::string& id, const std::string& appdata);
|
||||
const SdpRidAttributeList::Rid* FindRid(const std::string& id) const;
|
||||
|
||||
private:
|
||||
size_t mLevel;
|
||||
|
@ -136,6 +136,10 @@ SipccSdpMediaSection::Load(sdp_t* sdp, uint16_t level,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidateSimulcast(sdp, level, errorHolder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mBandwidths.Load(sdp, level, errorHolder)) {
|
||||
return false;
|
||||
}
|
||||
@ -223,6 +227,68 @@ SipccSdpMediaSection::LoadFormats(sdp_t* sdp,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SipccSdpMediaSection::ValidateSimulcast(sdp_t* sdp, uint16_t level,
|
||||
SdpErrorHolder& errorHolder) const
|
||||
{
|
||||
if (!GetAttributeList().HasAttribute(SdpAttribute::kSimulcastAttribute)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const SdpSimulcastAttribute& simulcast(GetAttributeList().GetSimulcast());
|
||||
if (!ValidateSimulcastVersions(
|
||||
sdp, level, simulcast.sendVersions, sdp::kSend, errorHolder)) {
|
||||
return false;
|
||||
}
|
||||
if (!ValidateSimulcastVersions(
|
||||
sdp, level, simulcast.recvVersions, sdp::kRecv, errorHolder)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SipccSdpMediaSection::ValidateSimulcastVersions(
|
||||
sdp_t* sdp,
|
||||
uint16_t level,
|
||||
const SdpSimulcastAttribute::Versions& versions,
|
||||
sdp::Direction direction,
|
||||
SdpErrorHolder& errorHolder) const
|
||||
{
|
||||
if (versions.IsSet() && !(direction & GetDirectionAttribute().mValue)) {
|
||||
errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
|
||||
"simulcast attribute has a direction that is "
|
||||
"inconsistent with the direction of this media "
|
||||
"section.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const SdpSimulcastAttribute::Version& version : versions) {
|
||||
for (const std::string& id : version.choices) {
|
||||
if (versions.type == SdpSimulcastAttribute::Versions::kRid) {
|
||||
const SdpRidAttributeList::Rid* ridAttr = FindRid(id);
|
||||
if (!ridAttr || (ridAttr->direction != direction)) {
|
||||
std::ostringstream os;
|
||||
os << "No rid attribute for \'" << id << "\'";
|
||||
errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
|
||||
os.str());
|
||||
return false;
|
||||
}
|
||||
} else if (versions.type == SdpSimulcastAttribute::Versions::kPt) {
|
||||
if (std::find(mFormats.begin(), mFormats.end(), id)
|
||||
== mFormats.end()) {
|
||||
std::ostringstream os;
|
||||
os << "No pt for \'" << id << "\'";
|
||||
errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
|
||||
os.str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SipccSdpMediaSection::LoadConnection(sdp_t* sdp, uint16_t level,
|
||||
SdpErrorHolder& errorHolder)
|
||||
|
@ -76,6 +76,14 @@ private:
|
||||
bool LoadConnection(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder);
|
||||
bool LoadProtocol(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder);
|
||||
bool LoadFormats(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder);
|
||||
bool ValidateSimulcast(sdp_t* sdp, uint16_t level,
|
||||
SdpErrorHolder& errorHolder) const;
|
||||
bool ValidateSimulcastVersions(
|
||||
sdp_t* sdp,
|
||||
uint16_t level,
|
||||
const SdpSimulcastAttribute::Versions& versions,
|
||||
sdp::Direction direction,
|
||||
SdpErrorHolder& errorHolder) const;
|
||||
|
||||
// the following values are cached on first get
|
||||
MediaType mMediaType;
|
||||
|
475
media/webrtc/signaling/test/jsep_track_unittest.cpp
Normal file
475
media/webrtc/signaling/test/jsep_track_unittest.cpp
Normal file
@ -0,0 +1,475 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#define GTEST_HAS_RTTI 0
|
||||
#include "gtest/gtest.h"
|
||||
#include "gtest_utils.h"
|
||||
|
||||
// Magic linker includes :(
|
||||
#include "FakeMediaStreams.h"
|
||||
#include "FakeMediaStreamsImpl.h"
|
||||
|
||||
#include "signaling/src/jsep/JsepTrack.h"
|
||||
#include "signaling/src/sdp/SipccSdp.h"
|
||||
#include "signaling/src/sdp/SdpHelper.h"
|
||||
|
||||
#include "mtransport_test_utils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class JsepTrackTest : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
JsepTrackTest() {}
|
||||
|
||||
std::vector<JsepCodecDescription*>
|
||||
MakeCodecs() const
|
||||
{
|
||||
std::vector<JsepCodecDescription*> results;
|
||||
results.push_back(
|
||||
new JsepAudioCodecDescription("1", "opus", 48000, 2, 960, 40000));
|
||||
results.push_back(
|
||||
new JsepAudioCodecDescription("9", "G722", 8000, 1, 320, 64000));
|
||||
|
||||
JsepVideoCodecDescription* vp8 =
|
||||
new JsepVideoCodecDescription("120", "VP8", 90000);
|
||||
vp8->mConstraints.maxFs = 12288;
|
||||
vp8->mConstraints.maxFps = 60;
|
||||
results.push_back(vp8);
|
||||
|
||||
JsepVideoCodecDescription* h264 =
|
||||
new JsepVideoCodecDescription("126", "H264", 90000);
|
||||
h264->mPacketizationMode = 1;
|
||||
h264->mProfileLevelId = 0x42E00D;
|
||||
results.push_back(h264);
|
||||
|
||||
results.push_back(
|
||||
new JsepApplicationCodecDescription(
|
||||
"5000",
|
||||
"webrtc-datachannel",
|
||||
16
|
||||
));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
void Init(SdpMediaSection::MediaType type) {
|
||||
InitCodecs();
|
||||
InitTracks(type);
|
||||
InitSdp(type);
|
||||
}
|
||||
|
||||
void InitCodecs() {
|
||||
mOffCodecs.values = MakeCodecs();
|
||||
mAnsCodecs.values = MakeCodecs();
|
||||
}
|
||||
|
||||
void InitTracks(SdpMediaSection::MediaType type)
|
||||
{
|
||||
mSendOff = new JsepTrack(type, "stream_id", "track_id", sdp::kSend);
|
||||
mRecvOff = new JsepTrack(type, "stream_id", "track_id", sdp::kRecv);
|
||||
mSendOff->PopulateCodecs(mOffCodecs.values);
|
||||
mRecvOff->PopulateCodecs(mOffCodecs.values);
|
||||
|
||||
mSendAns = new JsepTrack(type, "stream_id", "track_id", sdp::kSend);
|
||||
mRecvAns = new JsepTrack(type, "stream_id", "track_id", sdp::kRecv);
|
||||
mSendAns->PopulateCodecs(mAnsCodecs.values);
|
||||
mRecvAns->PopulateCodecs(mAnsCodecs.values);
|
||||
}
|
||||
|
||||
void InitSdp(SdpMediaSection::MediaType type)
|
||||
{
|
||||
mOffer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
|
||||
mOffer->AddMediaSection(
|
||||
type,
|
||||
SdpDirectionAttribute::kInactive,
|
||||
0,
|
||||
SdpHelper::GetProtocolForMediaType(type),
|
||||
sdp::kIPv4,
|
||||
"0.0.0.0");
|
||||
mAnswer.reset(new SipccSdp(SdpOrigin("", 0, 0, sdp::kIPv4, "")));
|
||||
mAnswer->AddMediaSection(
|
||||
type,
|
||||
SdpDirectionAttribute::kInactive,
|
||||
0,
|
||||
SdpHelper::GetProtocolForMediaType(type),
|
||||
sdp::kIPv4,
|
||||
"0.0.0.0");
|
||||
}
|
||||
|
||||
SdpMediaSection& GetOffer()
|
||||
{
|
||||
return mOffer->GetMediaSection(0);
|
||||
}
|
||||
|
||||
SdpMediaSection& GetAnswer()
|
||||
{
|
||||
return mAnswer->GetMediaSection(0);
|
||||
}
|
||||
|
||||
void CreateOffer()
|
||||
{
|
||||
if (mSendOff) {
|
||||
mSendOff->AddToOffer(&GetOffer());
|
||||
}
|
||||
|
||||
if (mRecvOff) {
|
||||
mRecvOff->AddToOffer(&GetOffer());
|
||||
}
|
||||
}
|
||||
|
||||
void CreateAnswer()
|
||||
{
|
||||
if (mSendAns && GetOffer().IsReceiving()) {
|
||||
mSendAns->AddToAnswer(GetOffer(), &GetAnswer());
|
||||
}
|
||||
|
||||
if (mRecvAns && GetOffer().IsSending()) {
|
||||
mRecvAns->AddToAnswer(GetOffer(), &GetAnswer());
|
||||
}
|
||||
}
|
||||
|
||||
void Negotiate()
|
||||
{
|
||||
std::cerr << "Offer SDP: " << std::endl;
|
||||
mOffer->Serialize(std::cerr);
|
||||
|
||||
std::cerr << "Answer SDP: " << std::endl;
|
||||
mAnswer->Serialize(std::cerr);
|
||||
|
||||
if (mSendAns && GetAnswer().IsSending()) {
|
||||
mSendAns->Negotiate(GetAnswer(), GetOffer());
|
||||
}
|
||||
|
||||
if (mRecvAns && GetAnswer().IsReceiving()) {
|
||||
mRecvAns->Negotiate(GetAnswer(), GetOffer());
|
||||
}
|
||||
|
||||
if (mSendOff && GetAnswer().IsReceiving()) {
|
||||
mSendOff->Negotiate(GetAnswer(), GetAnswer());
|
||||
}
|
||||
|
||||
if (mRecvOff && GetAnswer().IsSending()) {
|
||||
mRecvOff->Negotiate(GetAnswer(), GetAnswer());
|
||||
}
|
||||
}
|
||||
|
||||
void OfferAnswer()
|
||||
{
|
||||
CreateOffer();
|
||||
CreateAnswer();
|
||||
Negotiate();
|
||||
SanityCheck();
|
||||
}
|
||||
|
||||
static size_t EncodingCount(const RefPtr<JsepTrack>& track)
|
||||
{
|
||||
return track->GetNegotiatedDetails()->GetEncodingCount();
|
||||
}
|
||||
|
||||
// TODO: Look into writing a macro that wraps an ASSERT_ and returns false
|
||||
// if it fails (probably requires writing a bool-returning function that
|
||||
// takes a void-returning lambda with a bool outparam, which will in turn
|
||||
// invokes the ASSERT_)
|
||||
static void CheckEncodingCount(size_t expected,
|
||||
const RefPtr<JsepTrack>& send,
|
||||
const RefPtr<JsepTrack>& recv)
|
||||
{
|
||||
if (expected) {
|
||||
ASSERT_TRUE(!!send);
|
||||
ASSERT_TRUE(send->GetNegotiatedDetails());
|
||||
ASSERT_TRUE(!!recv);
|
||||
ASSERT_TRUE(recv->GetNegotiatedDetails());
|
||||
}
|
||||
|
||||
if (send && send->GetNegotiatedDetails()) {
|
||||
ASSERT_EQ(expected, send->GetNegotiatedDetails()->GetEncodingCount());
|
||||
}
|
||||
|
||||
if (recv && recv->GetNegotiatedDetails()) {
|
||||
ASSERT_EQ(expected, recv->GetNegotiatedDetails()->GetEncodingCount());
|
||||
}
|
||||
}
|
||||
|
||||
void CheckOffEncodingCount(size_t expected) const
|
||||
{
|
||||
CheckEncodingCount(expected, mSendOff, mRecvAns);
|
||||
}
|
||||
|
||||
void CheckAnsEncodingCount(size_t expected) const
|
||||
{
|
||||
CheckEncodingCount(expected, mSendAns, mRecvOff);
|
||||
}
|
||||
|
||||
void SanityCheckCodecs(const JsepCodecDescription& a,
|
||||
const JsepCodecDescription& b) const
|
||||
{
|
||||
ASSERT_EQ(a.mType, b.mType);
|
||||
ASSERT_EQ(a.mDefaultPt, b.mDefaultPt);
|
||||
ASSERT_EQ(a.mName, b.mName);
|
||||
ASSERT_EQ(a.mClock, b.mClock);
|
||||
ASSERT_EQ(a.mChannels, b.mChannels);
|
||||
ASSERT_NE(a.mDirection, b.mDirection);
|
||||
// These constraints are for fmtp and rid, which _are_ signaled
|
||||
ASSERT_EQ(a.mConstraints, b.mConstraints);
|
||||
}
|
||||
|
||||
void SanityCheckEncodings(const JsepTrackEncoding& a,
|
||||
const JsepTrackEncoding& b) const
|
||||
{
|
||||
ASSERT_EQ(a.GetCodecs().size(), b.GetCodecs().size());
|
||||
for (size_t i = 0; i < a.GetCodecs().size(); ++i) {
|
||||
SanityCheckCodecs(*a.GetCodecs()[i], *b.GetCodecs()[i]);
|
||||
}
|
||||
|
||||
ASSERT_EQ(a.mRid, b.mRid);
|
||||
// mConstraints will probably differ, since they are not signaled to the
|
||||
// other side.
|
||||
}
|
||||
|
||||
void SanityCheckNegotiatedDetails(const JsepTrackNegotiatedDetails& a,
|
||||
const JsepTrackNegotiatedDetails& b) const
|
||||
{
|
||||
ASSERT_EQ(a.GetEncodingCount(), b.GetEncodingCount());
|
||||
for (size_t i = 0; i < a.GetEncodingCount(); ++i) {
|
||||
SanityCheckEncodings(a.GetEncoding(i), b.GetEncoding(i));
|
||||
}
|
||||
|
||||
ASSERT_EQ(a.GetUniquePayloadTypes().size(),
|
||||
b.GetUniquePayloadTypes().size());
|
||||
for (size_t i = 0; i < a.GetUniquePayloadTypes().size(); ++i) {
|
||||
ASSERT_EQ(a.GetUniquePayloadTypes()[i], b.GetUniquePayloadTypes()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void SanityCheckTracks(const JsepTrack& a, const JsepTrack& b) const
|
||||
{
|
||||
if (!a.GetNegotiatedDetails()) {
|
||||
ASSERT_FALSE(!!b.GetNegotiatedDetails());
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(!!a.GetNegotiatedDetails());
|
||||
ASSERT_TRUE(!!b.GetNegotiatedDetails());
|
||||
ASSERT_EQ(a.GetMediaType(), b.GetMediaType());
|
||||
ASSERT_EQ(a.GetStreamId(), b.GetStreamId());
|
||||
ASSERT_EQ(a.GetTrackId(), b.GetTrackId());
|
||||
ASSERT_EQ(a.GetCNAME(), b.GetCNAME());
|
||||
ASSERT_NE(a.GetDirection(), b.GetDirection());
|
||||
ASSERT_EQ(a.GetSsrcs().size(), b.GetSsrcs().size());
|
||||
for (size_t i = 0; i < a.GetSsrcs().size(); ++i) {
|
||||
ASSERT_EQ(a.GetSsrcs()[i], b.GetSsrcs()[i]);
|
||||
}
|
||||
|
||||
SanityCheckNegotiatedDetails(*a.GetNegotiatedDetails(),
|
||||
*b.GetNegotiatedDetails());
|
||||
}
|
||||
|
||||
void SanityCheck() const
|
||||
{
|
||||
if (mSendOff && mRecvAns) {
|
||||
SanityCheckTracks(*mSendOff, *mRecvAns);
|
||||
}
|
||||
if (mRecvOff && mSendAns) {
|
||||
SanityCheckTracks(*mRecvOff, *mSendAns);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
RefPtr<JsepTrack> mSendOff;
|
||||
RefPtr<JsepTrack> mRecvOff;
|
||||
RefPtr<JsepTrack> mSendAns;
|
||||
RefPtr<JsepTrack> mRecvAns;
|
||||
PtrVector<JsepCodecDescription> mOffCodecs;
|
||||
PtrVector<JsepCodecDescription> mAnsCodecs;
|
||||
UniquePtr<Sdp> mOffer;
|
||||
UniquePtr<Sdp> mAnswer;
|
||||
};
|
||||
|
||||
TEST_F(JsepTrackTest, CreateDestroy)
|
||||
{
|
||||
Init(SdpMediaSection::kAudio);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, AudioNegotiation)
|
||||
{
|
||||
Init(SdpMediaSection::kAudio);
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(1);
|
||||
CheckAnsEncodingCount(1);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, VideoNegotiation)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(1);
|
||||
CheckAnsEncodingCount(1);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, AudioOffSendonlyAnsRecvonly)
|
||||
{
|
||||
Init(SdpMediaSection::kAudio);
|
||||
mRecvOff = nullptr;
|
||||
mSendAns = nullptr;
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(1);
|
||||
CheckAnsEncodingCount(0);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, VideoOffSendonlyAnsRecvonly)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
mRecvOff = nullptr;
|
||||
mSendAns = nullptr;
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(1);
|
||||
CheckAnsEncodingCount(0);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, AudioOffSendrecvAnsRecvonly)
|
||||
{
|
||||
Init(SdpMediaSection::kAudio);
|
||||
mSendAns = nullptr;
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(1);
|
||||
CheckAnsEncodingCount(0);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, VideoOffSendrecvAnsRecvonly)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
mSendAns = nullptr;
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(1);
|
||||
CheckAnsEncodingCount(0);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, AudioOffRecvonlyAnsSendrecv)
|
||||
{
|
||||
Init(SdpMediaSection::kAudio);
|
||||
mSendOff = nullptr;
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(0);
|
||||
CheckAnsEncodingCount(1);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, VideoOffRecvonlyAnsSendrecv)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
mSendOff = nullptr;
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(0);
|
||||
CheckAnsEncodingCount(1);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, AudioOffSendrecvAnsSendonly)
|
||||
{
|
||||
Init(SdpMediaSection::kAudio);
|
||||
mRecvAns = nullptr;
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(0);
|
||||
CheckAnsEncodingCount(1);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, VideoOffSendrecvAnsSendonly)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
mRecvAns = nullptr;
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(0);
|
||||
CheckAnsEncodingCount(1);
|
||||
}
|
||||
|
||||
static JsepTrack::JsConstraints
|
||||
MakeConstraints(const std::string& rid, uint32_t maxBitrate)
|
||||
{
|
||||
JsepTrack::JsConstraints constraints;
|
||||
constraints.rid = rid;
|
||||
constraints.constraints.maxBr = maxBitrate;
|
||||
return constraints;
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, SimulcastRejected)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
std::vector<JsepTrack::JsConstraints> constraints;
|
||||
constraints.push_back(MakeConstraints("foo", 40000));
|
||||
constraints.push_back(MakeConstraints("bar", 10000));
|
||||
mSendOff->SetJsConstraints(constraints);
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(1);
|
||||
CheckAnsEncodingCount(1);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, SimulcastPrevented)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
std::vector<JsepTrack::JsConstraints> constraints;
|
||||
constraints.push_back(MakeConstraints("foo", 40000));
|
||||
constraints.push_back(MakeConstraints("bar", 10000));
|
||||
mSendAns->SetJsConstraints(constraints);
|
||||
OfferAnswer();
|
||||
CheckOffEncodingCount(1);
|
||||
CheckAnsEncodingCount(1);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, SimulcastOfferer)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
std::vector<JsepTrack::JsConstraints> constraints;
|
||||
constraints.push_back(MakeConstraints("foo", 40000));
|
||||
constraints.push_back(MakeConstraints("bar", 10000));
|
||||
mSendOff->SetJsConstraints(constraints);
|
||||
CreateOffer();
|
||||
CreateAnswer();
|
||||
// Add simulcast/rid to answer
|
||||
JsepTrack::AddToMsection(constraints, sdp::kRecv, &GetAnswer());
|
||||
Negotiate();
|
||||
ASSERT_TRUE(mSendOff->GetNegotiatedDetails());
|
||||
ASSERT_EQ(2U, mSendOff->GetNegotiatedDetails()->GetEncodingCount());
|
||||
ASSERT_EQ("foo", mSendOff->GetNegotiatedDetails()->GetEncoding(0).mRid);
|
||||
ASSERT_EQ(40000U,
|
||||
mSendOff->GetNegotiatedDetails()->GetEncoding(0).mConstraints.maxBr);
|
||||
ASSERT_EQ("bar", mSendOff->GetNegotiatedDetails()->GetEncoding(1).mRid);
|
||||
ASSERT_EQ(10000U,
|
||||
mSendOff->GetNegotiatedDetails()->GetEncoding(1).mConstraints.maxBr);
|
||||
}
|
||||
|
||||
TEST_F(JsepTrackTest, SimulcastAnswerer)
|
||||
{
|
||||
Init(SdpMediaSection::kVideo);
|
||||
std::vector<JsepTrack::JsConstraints> constraints;
|
||||
constraints.push_back(MakeConstraints("foo", 40000));
|
||||
constraints.push_back(MakeConstraints("bar", 10000));
|
||||
mSendAns->SetJsConstraints(constraints);
|
||||
CreateOffer();
|
||||
// Add simulcast/rid to offer
|
||||
JsepTrack::AddToMsection(constraints, sdp::kRecv, &GetOffer());
|
||||
CreateAnswer();
|
||||
Negotiate();
|
||||
ASSERT_TRUE(mSendAns->GetNegotiatedDetails());
|
||||
ASSERT_EQ(2U, mSendAns->GetNegotiatedDetails()->GetEncodingCount());
|
||||
ASSERT_EQ("foo", mSendAns->GetNegotiatedDetails()->GetEncoding(0).mRid);
|
||||
ASSERT_EQ(40000U,
|
||||
mSendAns->GetNegotiatedDetails()->GetEncoding(0).mConstraints.maxBr);
|
||||
ASSERT_EQ("bar", mSendAns->GetNegotiatedDetails()->GetEncoding(1).mRid);
|
||||
ASSERT_EQ(10000U,
|
||||
mSendAns->GetNegotiatedDetails()->GetEncoding(1).mConstraints.maxBr);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
// Prevents some log spew
|
||||
ScopedXPCOM xpcom("jsep_track_unittest");
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'uikit':
|
||||
GeckoCppUnitTests([
|
||||
'jsep_session_unittest',
|
||||
'jsep_track_unittest',
|
||||
'mediaconduit_unittests',
|
||||
'mediapipeline_unittest',
|
||||
'sdp_file_parser',
|
||||
|
@ -1195,8 +1195,7 @@ const std::string kBasicAudioVideoOffer =
|
||||
"a=ssrc:1111 foo:bar" CRLF
|
||||
"a=imageattr:120 send * recv *" CRLF
|
||||
"a=imageattr:121 send [x=640,y=480] recv [x=640,y=480]" CRLF
|
||||
"a=simulcast:send pt=120;121" CRLF
|
||||
"a=rid:foo send" CRLF
|
||||
"a=simulcast:recv pt=120;121" CRLF
|
||||
"a=rid:bar recv pt=96;max-width=800;max-height=600" CRLF
|
||||
"m=audio 9 RTP/SAVPF 0" CRLF
|
||||
"a=mid:third" CRLF
|
||||
@ -1778,16 +1777,13 @@ TEST_P(NewSdpTest, CheckRid)
|
||||
const SdpRidAttributeList& rids =
|
||||
mSdp->GetMediaSection(1).GetAttributeList().GetRid();
|
||||
|
||||
ASSERT_EQ(2U, rids.mRids.size());
|
||||
ASSERT_EQ("foo", rids.mRids[0].id);
|
||||
ASSERT_EQ(sdp::kSend, rids.mRids[0].direction);
|
||||
ASSERT_EQ(0U, rids.mRids[0].formats.size());
|
||||
ASSERT_EQ("bar", rids.mRids[1].id);
|
||||
ASSERT_EQ(sdp::kRecv, rids.mRids[1].direction);
|
||||
ASSERT_EQ(1U, rids.mRids[1].formats.size());
|
||||
ASSERT_EQ(96U, rids.mRids[1].formats[0]);
|
||||
ASSERT_EQ(800U, rids.mRids[1].constraints.maxWidth);
|
||||
ASSERT_EQ(600U, rids.mRids[1].constraints.maxHeight);
|
||||
ASSERT_EQ(1U, rids.mRids.size());
|
||||
ASSERT_EQ("bar", rids.mRids[0].id);
|
||||
ASSERT_EQ(sdp::kRecv, rids.mRids[0].direction);
|
||||
ASSERT_EQ(1U, rids.mRids[0].formats.size());
|
||||
ASSERT_EQ(96U, rids.mRids[0].formats[0]);
|
||||
ASSERT_EQ(800U, rids.mRids[0].constraints.maxWidth);
|
||||
ASSERT_EQ(600U, rids.mRids[0].constraints.maxHeight);
|
||||
}
|
||||
|
||||
TEST_P(NewSdpTest, CheckMediaLevelIceUfrag) {
|
||||
@ -2099,14 +2095,14 @@ TEST_P(NewSdpTest, CheckSimulcast)
|
||||
const SdpSimulcastAttribute& simulcast =
|
||||
mSdp->GetMediaSection(1).GetAttributeList().GetSimulcast();
|
||||
|
||||
ASSERT_EQ(0U, simulcast.recvVersions.size());
|
||||
ASSERT_EQ(2U, simulcast.sendVersions.size());
|
||||
ASSERT_EQ(1U, simulcast.sendVersions[0].choices.size());
|
||||
ASSERT_EQ("120", simulcast.sendVersions[0].choices[0]);
|
||||
ASSERT_EQ(1U, simulcast.sendVersions[1].choices.size());
|
||||
ASSERT_EQ("121", simulcast.sendVersions[1].choices[0]);
|
||||
ASSERT_EQ(2U, simulcast.recvVersions.size());
|
||||
ASSERT_EQ(0U, simulcast.sendVersions.size());
|
||||
ASSERT_EQ(1U, simulcast.recvVersions[0].choices.size());
|
||||
ASSERT_EQ("120", simulcast.recvVersions[0].choices[0]);
|
||||
ASSERT_EQ(1U, simulcast.recvVersions[1].choices.size());
|
||||
ASSERT_EQ("121", simulcast.recvVersions[1].choices[0]);
|
||||
ASSERT_EQ(SdpSimulcastAttribute::Versions::kPt,
|
||||
simulcast.sendVersions.type);
|
||||
simulcast.recvVersions.type);
|
||||
}
|
||||
|
||||
TEST_P(NewSdpTest, CheckSctpmap) {
|
||||
@ -2613,6 +2609,81 @@ TEST_P(NewSdpTest, CheckMalformedImageattr)
|
||||
ASSERT_NE("", GetParseErrors());
|
||||
}
|
||||
|
||||
TEST_P(NewSdpTest, ParseInvalidSimulcastNoSuchSendRid) {
|
||||
ParseSdp("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
|
||||
"b=CT:5000" CRLF
|
||||
"t=0 0" CRLF
|
||||
"m=video 56436 RTP/SAVPF 120" CRLF
|
||||
"a=rtpmap:120 VP8/90000" CRLF
|
||||
"a=sendrecv" CRLF
|
||||
"a=simulcast: send rid=9" CRLF,
|
||||
false);
|
||||
ASSERT_NE("", GetParseErrors());
|
||||
}
|
||||
|
||||
TEST_P(NewSdpTest, ParseInvalidSimulcastNoSuchRecvRid) {
|
||||
ParseSdp("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
|
||||
"b=CT:5000" CRLF
|
||||
"t=0 0" CRLF
|
||||
"m=video 56436 RTP/SAVPF 120" CRLF
|
||||
"a=rtpmap:120 VP8/90000" CRLF
|
||||
"a=sendrecv" CRLF
|
||||
"a=simulcast: recv rid=9" CRLF,
|
||||
false);
|
||||
ASSERT_NE("", GetParseErrors());
|
||||
}
|
||||
|
||||
TEST_P(NewSdpTest, ParseInvalidSimulcastNoSuchPt) {
|
||||
ParseSdp("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
|
||||
"b=CT:5000" CRLF
|
||||
"t=0 0" CRLF
|
||||
"m=video 56436 RTP/SAVPF 120" CRLF
|
||||
"a=rtpmap:120 VP8/90000" CRLF
|
||||
"a=sendrecv" CRLF
|
||||
"a=simulcast: send pt=9" CRLF,
|
||||
false);
|
||||
ASSERT_NE("", GetParseErrors());
|
||||
}
|
||||
|
||||
TEST_P(NewSdpTest, ParseInvalidSimulcastNotSending) {
|
||||
ParseSdp("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
|
||||
"b=CT:5000" CRLF
|
||||
"t=0 0" CRLF
|
||||
"m=video 56436 RTP/SAVPF 120" CRLF
|
||||
"a=rtpmap:120 VP8/90000" CRLF
|
||||
"a=recvonly" CRLF
|
||||
"a=simulcast: send pt=120" CRLF,
|
||||
false);
|
||||
ASSERT_NE("", GetParseErrors());
|
||||
}
|
||||
|
||||
TEST_P(NewSdpTest, ParseInvalidSimulcastNotReceiving) {
|
||||
ParseSdp("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
|
||||
"b=CT:5000" CRLF
|
||||
"t=0 0" CRLF
|
||||
"m=video 56436 RTP/SAVPF 120" CRLF
|
||||
"a=rtpmap:120 VP8/90000" CRLF
|
||||
"a=sendonly" CRLF
|
||||
"a=simulcast: recv pt=120" CRLF,
|
||||
false);
|
||||
ASSERT_NE("", GetParseErrors());
|
||||
}
|
||||
|
||||
const std::string kNoAttributes =
|
||||
"v=0" CRLF
|
||||
"o=Mozilla-SIPUA-35.0a1 5184 0 IN IP4 0.0.0.0" CRLF
|
||||
|
@ -101,6 +101,7 @@ skip-if = os == 'b2g' || os == 'android' # Bug 919646
|
||||
[rlogringbuffer_unittest]
|
||||
[runnable_utils_unittest]
|
||||
[sctp_unittest]
|
||||
[jsep_track_unittest]
|
||||
[jsep_session_unittest]
|
||||
skip-if = os == 'android' # Bug 1147631
|
||||
[sdp_unittests]
|
||||
|
Loading…
Reference in New Issue
Block a user