--- a/media/webrtc/signaling/src/jsep/JsepCodecDescription.h
+++ b/media/webrtc/signaling/src/jsep/JsepCodecDescription.h
@@ -129,17 +129,18 @@ class JsepAudioCodecDescription : public
uint32_t bitRate,
bool enabled = true)
: JsepCodecDescription(mozilla::SdpMediaSection::kAudio, defaultPt, name,
clock, channels, enabled),
mPacketSize(packetSize),
mBitrate(bitRate),
mMaxPlaybackRate(0),
mForceMono(false),
- mFECEnabled(false)
+ mFECEnabled(false),
+ mDtmfEnabled(false)
{
}
JSEP_CODEC_CLONE(JsepAudioCodecDescription)
SdpFmtpAttributeList::OpusParameters
GetOpusParameters(const std::string& pt,
const SdpMediaSection& msection) const
@@ -151,16 +152,33 @@ class JsepAudioCodecDescription : public
if (params && params->codec_type == SdpRtpmapAttributeList::kOpus) {
result =
static_cast<const SdpFmtpAttributeList::OpusParameters&>(*params);
}
return result;
}
+ SdpFmtpAttributeList::TelephoneEventParameters
+ GetTelephoneEventParameters(const std::string& pt,
+ const SdpMediaSection& msection) const
+ {
+ // Will contain defaults if nothing else
+ SdpFmtpAttributeList::TelephoneEventParameters result;
+ auto* params = msection.FindFmtp(pt);
+
+ if (params && params->codec_type == SdpRtpmapAttributeList::kAvt) {
+ result =
+ static_cast<const SdpFmtpAttributeList::TelephoneEventParameters&>
+ (*params);
+ }
+
+ return result;
+ }
+
void
AddParametersToMSection(SdpMediaSection& msection) const override
{
if (mDirection == sdp::kSend) {
return;
}
if (mName == "opus") {
@@ -170,16 +188,21 @@ class JsepAudioCodecDescription : public
opusParams.maxplaybackrate = mMaxPlaybackRate;
}
if (mChannels == 2 && !mForceMono) {
// We prefer to receive stereo, if available.
opusParams.stereo = 1;
}
opusParams.useInBandFec = mFECEnabled ? 1 : 0;
msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, opusParams));
+ } else if (mName == "telephone-event") {
+ // add the default dtmf tones
+ SdpFmtpAttributeList::TelephoneEventParameters teParams(
+ GetTelephoneEventParameters(mDefaultPt, msection));
+ msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, teParams));
}
}
bool
Negotiate(const std::string& pt,
const SdpMediaSection& remoteMsection) override
{
JsepCodecDescription::Negotiate(pt, remoteMsection);
@@ -198,16 +221,17 @@ class JsepAudioCodecDescription : public
return true;
}
uint32_t mPacketSize;
uint32_t mBitrate;
uint32_t mMaxPlaybackRate;
bool mForceMono;
bool mFECEnabled;
+ bool mDtmfEnabled;
};
class JsepVideoCodecDescription : public JsepCodecDescription {
public:
JsepVideoCodecDescription(const std::string& defaultPt,
const std::string& name,
uint32_t clock,
bool enabled = true)
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -2135,16 +2135,28 @@ JsepSessionImpl::SetupDefaultCodecs()
new JsepAudioCodecDescription("8",
"PCMA",
8000,
1,
8000 / 50, // frequency / 50
8 * 8000 * 1 // 8 * frequency * channels
));
+ // note: because telephone-event is effectively a marker codec that indicates
+ // that dtmf rtp packets may be passed, the packetSize and bitRate fields
+ // don't make sense here. For now, use zero. (mjf)
+ mSupportedCodecs.values.push_back(
+ new JsepAudioCodecDescription("101",
+ "telephone-event",
+ 8000,
+ 1,
+ 0, // packetSize doesn't make sense here
+ 0 // bitRate doesn't make sense here
+ ));
+
// Supported video codecs.
// Note: order here implies priority for building offers!
JsepVideoCodecDescription* vp9 = new JsepVideoCodecDescription(
"121",
"VP9",
90000
);
// Defaults for mandatory params
--- a/media/webrtc/signaling/src/jsep/JsepTrack.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepTrack.cpp
@@ -339,26 +339,30 @@ JsepTrack::NegotiateCodecs(
if (formatChanges) {
(*formatChanges)[originalFormat] = codec->mDefaultPt;
}
break;
}
}
}
- // Find the (potential) red codec and ulpfec codec
+ // Find the (potential) red codec and ulpfec codec or telephone-event
JsepVideoCodecDescription* red = nullptr;
JsepVideoCodecDescription* ulpfec = nullptr;
+ JsepAudioCodecDescription* dtmf = nullptr;
for (auto codec : *codecs) {
if (codec->mName == "red") {
red = static_cast<JsepVideoCodecDescription*>(codec);
}
else if (codec->mName == "ulpfec") {
ulpfec = static_cast<JsepVideoCodecDescription*>(codec);
}
+ else if (codec->mName == "telephone-event") {
+ dtmf = static_cast<JsepAudioCodecDescription*>(codec);
+ }
}
// 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);
@@ -381,29 +385,52 @@ JsepTrack::NegotiateCodecs(
if (codec->mName != "red" && codec->mName != "ulpfec") {
JsepVideoCodecDescription* videoCodec =
static_cast<JsepVideoCodecDescription*>(codec);
videoCodec->EnableFec();
}
}
}
+ // Dtmf support is indicated by the existence of the telephone-event
+ // codec, and not an attribute on the particular audio codec (like in a
+ // rtcpfb attr). If we see the telephone-event codec, we enabled dtmf
+ // support on all the other audio codecs.
+ if (dtmf) {
+ for (auto codec : *codecs) {
+ if (codec->mType == SdpMediaSection::kAudio
+ && codec->mName != "telephone-event") {
+ JsepAudioCodecDescription* audioCodec =
+ static_cast<JsepAudioCodecDescription*>(codec);
+ audioCodec->mDtmfEnabled = true;
+ }
+ }
+ }
+
// 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. 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.
+ // Note: now allows keeping the telephone-event codec, if it appears, as the
+ // last codec in the list.
if (!codecs->empty() && !red) {
+ int newSize = 1 + (dtmf ? 1 : 0);
for (size_t i = 1; i < codecs->size(); ++i) {
- delete (*codecs)[i];
- (*codecs)[i] = nullptr;
+ if (!dtmf || dtmf != (*codecs)[i]) {
+ delete (*codecs)[i];
+ (*codecs)[i] = nullptr;
+ }
}
- codecs->resize(1);
+ if (dtmf) {
+ (*codecs)[newSize-1] = dtmf;
+ }
+ codecs->resize(newSize);
}
}
void
JsepTrack::Negotiate(const SdpMediaSection& answer,
const SdpMediaSection& remote)
{
PtrVector<JsepCodecDescription> negotiatedCodecs;
--- a/media/webrtc/signaling/src/media-conduit/CodecConfig.h
+++ b/media/webrtc/signaling/src/media-conduit/CodecConfig.h
@@ -25,16 +25,17 @@ struct AudioCodecConfig
int mType;
std::string mName;
int mFreq;
int mPacSize;
int mChannels;
int mRate;
bool mFECEnabled;
+ bool mDtmfEnabled;
// OPUS-specific
int mMaxPlaybackRate;
/* Default constructor is not provided since as a consumer, we
* can't decide the default configuration for the codec
*/
explicit AudioCodecConfig(int type, std::string name,
@@ -42,16 +43,17 @@ struct AudioCodecConfig
int channels, int rate, bool FECEnabled)
: mType(type),
mName(name),
mFreq(freq),
mPacSize(pacSize),
mChannels(channels),
mRate(rate),
mFECEnabled(FECEnabled),
+ mDtmfEnabled(false),
mMaxPlaybackRate(0)
{
}
};
/*
* Minimalistic video codec configuration
* More to be added later depending on the use-case
--- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ -72,16 +72,17 @@ JsepCodecDescToCodecConfig(const JsepCod
*aConfig = new AudioCodecConfig(pt,
desc.mName,
desc.mClock,
desc.mPacketSize,
desc.mForceMono ? 1 : desc.mChannels,
desc.mBitrate,
desc.mFECEnabled);
(*aConfig)->mMaxPlaybackRate = desc.mMaxPlaybackRate;
+ (*aConfig)->mDtmfEnabled = desc.mDtmfEnabled;
return NS_OK;
}
static std::vector<JsepCodecDescription*>
GetCodecs(const JsepTrackNegotiatedDetails& aDetails)
{
// We do not try to handle cases where a codec is not used on the primary
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -887,17 +887,18 @@ class ConfigureCodec {
mH264Level(13), // minimum suggested for WebRTC spec
mH264MaxBr(0), // Unlimited
mH264MaxMbps(0), // Unlimited
mVP8MaxFs(0),
mVP8MaxFr(0),
mUseTmmbr(false),
mUseRemb(false),
mUseAudioFec(false),
- mRedUlpfecEnabled(false)
+ mRedUlpfecEnabled(false),
+ mDtmfEnabled(false)
{
#ifdef MOZ_WEBRTC_OMX
// Check to see if what HW codecs are available (not in use) at this moment.
// Note that streaming video decode can reserve a decoder
// XXX See bug 1018791 Implement W3 codec reservation policy
// Note that currently, OMXCodecReservation needs to be held by an sp<> because it puts
// 'this' into an sp<EventListener> to talk to the resource reservation code
@@ -961,27 +962,31 @@ class ConfigureCodec {
// REMB is enabled by default, but can be disabled from about:config
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);
+
+ branch->GetBoolPref("media.navigator.audio.dtmf_enabled", &mDtmfEnabled);
}
void operator()(JsepCodecDescription* codec) const
{
switch (codec->mType) {
case SdpMediaSection::kAudio:
{
JsepAudioCodecDescription& audioCodec =
static_cast<JsepAudioCodecDescription&>(*codec);
if (audioCodec.mName == "opus") {
audioCodec.mFECEnabled = mUseAudioFec;
+ } else if (audioCodec.mName == "telephone-event") {
+ audioCodec.mEnabled = mDtmfEnabled;
}
}
break;
case SdpMediaSection::kVideo:
{
JsepVideoCodecDescription& videoCodec =
static_cast<JsepVideoCodecDescription&>(*codec);
@@ -1044,16 +1049,17 @@ class ConfigureCodec {
int32_t mH264MaxBr;
int32_t mH264MaxMbps;
int32_t mVP8MaxFs;
int32_t mVP8MaxFr;
bool mUseTmmbr;
bool mUseRemb;
bool mUseAudioFec;
bool mRedUlpfecEnabled;
+ bool mDtmfEnabled;
};
class ConfigureRedCodec {
public:
explicit ConfigureRedCodec(nsCOMPtr<nsIPrefBranch>& branch,
std::vector<uint8_t>* redundantEncodings) :
mRedundantEncodings(redundantEncodings)
{
--- a/media/webrtc/signaling/src/sdp/SdpAttribute.cpp
+++ b/media/webrtc/signaling/src/sdp/SdpAttribute.cpp
@@ -1126,16 +1126,17 @@ ShouldSerializeChannels(SdpRtpmapAttribu
case SdpRtpmapAttributeList::kPCMA:
case SdpRtpmapAttributeList::kVP8:
case SdpRtpmapAttributeList::kVP9:
case SdpRtpmapAttributeList::kiLBC:
case SdpRtpmapAttributeList::kiSAC:
case SdpRtpmapAttributeList::kH264:
case SdpRtpmapAttributeList::kRed:
case SdpRtpmapAttributeList::kUlpfec:
+ case SdpRtpmapAttributeList::kAvt:
return false;
case SdpRtpmapAttributeList::kOtherCodec:
return true;
}
MOZ_CRASH();
}
void
--- a/media/webrtc/signaling/src/sdp/SdpAttribute.h
+++ b/media/webrtc/signaling/src/sdp/SdpAttribute.h
@@ -1082,16 +1082,17 @@ public:
kPCMA,
kVP8,
kVP9,
kiLBC,
kiSAC,
kH264,
kRed,
kUlpfec,
+ kAvt, // telephone-event
kOtherCodec
};
struct Rtpmap {
std::string pt;
CodecType codec;
std::string name;
uint32_t clock;
@@ -1167,16 +1168,19 @@ inline std::ostream& operator<<(std::ost
os << "H264";
break;
case SdpRtpmapAttributeList::kRed:
os << "red";
break;
case SdpRtpmapAttributeList::kUlpfec:
os << "ulpfec";
break;
+ case SdpRtpmapAttributeList::kAvt:
+ os << "telephone-event";
+ break;
default:
MOZ_ASSERT(false);
os << "?";
}
return os;
}
///////////////////////////////////////////////////////////////////////////
@@ -1362,16 +1366,39 @@ public:
<< ";useinbandfec=" << useInBandFec;
}
unsigned int maxplaybackrate;
unsigned int stereo;
unsigned int useInBandFec;
};
+ class TelephoneEventParameters : public Parameters
+ {
+ public:
+ TelephoneEventParameters() :
+ Parameters(SdpRtpmapAttributeList::kAvt),
+ dtmfTones("0-15")
+ {}
+
+ virtual Parameters*
+ Clone() const override
+ {
+ return new TelephoneEventParameters(*this);
+ }
+
+ void
+ Serialize(std::ostream& os) const override
+ {
+ os << dtmfTones;
+ }
+
+ std::string dtmfTones;
+ };
+
class Fmtp
{
public:
Fmtp(const std::string& aFormat, UniquePtr<Parameters> aParameters)
: format(aFormat),
parameters(Move(aParameters))
{
}
--- a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp
+++ b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp
@@ -358,31 +358,32 @@ SipccSdpAttributeList::GetCodecType(rtp_
case RTP_VP8:
return SdpRtpmapAttributeList::kVP8;
case RTP_VP9:
return SdpRtpmapAttributeList::kVP9;
case RTP_RED:
return SdpRtpmapAttributeList::kRed;
case RTP_ULPFEC:
return SdpRtpmapAttributeList::kUlpfec;
+ case RTP_AVT:
+ return SdpRtpmapAttributeList::kAvt;
case RTP_NONE:
// Happens when sipcc doesn't know how to translate to the enum
case RTP_CELP:
case RTP_G726:
case RTP_GSM:
case RTP_G723:
case RTP_DVI4:
case RTP_DVI4_II:
case RTP_LPC:
case RTP_G728:
case RTP_G729:
case RTP_JPEG:
case RTP_NV:
case RTP_H261:
- case RTP_AVT:
case RTP_L16:
case RTP_H263:
case RTP_ILBC:
case RTP_I420:
return SdpRtpmapAttributeList::kOtherCodec;
}
MOZ_CRASH("Invalid codec type from sipcc. Probably corruption.");
}
@@ -752,16 +753,24 @@ SipccSdpAttributeList::LoadFmtp(sdp_t* s
case RTP_OPUS: {
SdpFmtpAttributeList::OpusParameters* opusParameters(
new SdpFmtpAttributeList::OpusParameters);
opusParameters->maxplaybackrate = fmtp->maxplaybackrate;
opusParameters->stereo = fmtp->stereo;
opusParameters->useInBandFec = fmtp->useinbandfec;
parameters.reset(opusParameters);
} break;
+ case RTP_AVT: { // telephone-event
+ SdpFmtpAttributeList::TelephoneEventParameters* teParameters(
+ new SdpFmtpAttributeList::TelephoneEventParameters);
+ if (strlen(fmtp->dtmf_tones) > 0) {
+ teParameters->dtmfTones = fmtp->dtmf_tones;
+ }
+ parameters.reset(teParameters);
+ } break;
default: {
}
}
fmtps->PushEntry(osPayloadType.str(), Move(parameters));
}
if (!fmtps->mFmtps.empty()) {
--- a/media/webrtc/signaling/src/sdp/sipcc/sdp.h
+++ b/media/webrtc/signaling/src/sdp/sipcc/sdp.h
@@ -711,16 +711,25 @@ typedef struct sdp_fmtp {
/* H.263 codec requires annex K,N and P to have values */
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];
+ /* RFC 2833 Section 3.9 (4733) for specifying support DTMF tones:
+ The list of values consists of comma-separated elements, which
+ can be either a single decimal number or two decimal numbers
+ separated by a hyphen (dash), where the second number is larger
+ than the first. No whitespace is allowed between numbers or
+ hyphens. The list does not have to be sorted.
+ */
+ char dtmf_tones[SDP_MAX_STRING_LEN+1];
+
/* 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 */
uint8_t flag;
/* END - All Video related FMTP parameters */
--- a/media/webrtc/signaling/src/sdp/sipcc/sdp_access.c
+++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_access.c
@@ -26,16 +26,17 @@ static const char* logTag = "sdp_access"
#define SIPSDP_ATTR_ENCNAME_H264 "H264"
#define SIPSDP_ATTR_ENCNAME_VP8 "VP8"
#define SIPSDP_ATTR_ENCNAME_VP9 "VP9"
#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"
+#define SIPSDP_ATTR_ENCNAME_AVT "telephone-event"
/* Function: sdp_find_media_level
* Description: Find and return a pointer to the specified media level,
* if it exists.
* Note: This is not an API for the application but an internal
* routine used by the SDP library.
* Parameters: sdp_p The SDP handle returned by sdp_init_description.
* level The media level to find.
@@ -1380,16 +1381,19 @@ rtp_ptype sdp_get_known_payload_type(sdp
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);
}
+ if (cpr_strcasecmp(encname, SIPSDP_ATTR_ENCNAME_AVT) == 0) {
+ return (RTP_AVT); // telephone-event
+ }
}
}
}
return (RTP_NONE);
}
/* Function: sdp_get_media_payload_type
--- a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c
+++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr.c
@@ -1796,20 +1796,25 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t
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 {
+ } else if (strspn(tmp, "0123456789,-") == strlen(tmp)
+ && ('0' <= tmp[0] && tmp[0] <= '9')
+ && (('0' <= tmp[1] && tmp[1] <= '9')
+ || tmp[1] == '-' || tmp[1] == ',')
+ ) {
// XXX Note that DTMF fmtp will fall into here:
// a=fmtp:101 0-15 (or 0-15,NN,NN etc)
-
+ sstrncpy(fmtp_p->dtmf_tones , tmp, sizeof(fmtp_p->dtmf_tones));
+ } else {
// unknown parameter - eat chars until ';'
CSFLogDebug(logTag, "%s Unknown fmtp type (%s) - ignoring", __FUNCTION__,
tmp);
fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), "; \t",
&result1);
if (result1 != SDP_SUCCESS) {
fmtp_ptr = sdp_getnextstrtok(fmtp_ptr, tmp, sizeof(tmp), " \t", &result1);
if (result1 != SDP_SUCCESS) {
--- a/media/webrtc/signaling/test/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp
@@ -2714,17 +2714,17 @@ TEST_F(JsepSessionTest, CreateOfferNoDat
ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
ASSERT_EQ(SdpMediaSection::kAudio,
outputSdp->GetMediaSection(0).GetMediaType());
ASSERT_EQ(SdpMediaSection::kVideo,
outputSdp->GetMediaSection(1).GetMediaType());
}
-TEST_F(JsepSessionTest, ValidateOfferedCodecParams)
+TEST_F(JsepSessionTest, ValidateOfferedVideoCodecParams)
{
types.push_back(SdpMediaSection::kAudio);
types.push_back(SdpMediaSection::kVideo);
RefPtr<JsepTrack> msta(
new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
mSessionOff.AddTrack(msta);
RefPtr<JsepTrack> mstv1(
@@ -2841,16 +2841,98 @@ TEST_F(JsepSessionTest, ValidateOfferedC
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, ValidateOfferedAudioCodecParams)
+{
+ types.push_back(SdpMediaSection::kAudio);
+ types.push_back(SdpMediaSection::kVideo);
+
+ RefPtr<JsepTrack> msta(
+ new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
+ mSessionOff.AddTrack(msta);
+ RefPtr<JsepTrack> mstv1(
+ new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v2"));
+ mSessionOff.AddTrack(mstv1);
+
+ std::string offer = CreateOffer();
+
+ UniquePtr<Sdp> outputSdp(Parse(offer));
+ ASSERT_TRUE(!!outputSdp);
+
+ ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
+ auto& audio_section = outputSdp->GetMediaSection(0);
+ ASSERT_EQ(SdpMediaSection::kAudio, audio_section.GetMediaType());
+ auto& audio_attrs = audio_section.GetAttributeList();
+ ASSERT_EQ(SdpDirectionAttribute::kSendrecv, audio_attrs.GetDirection());
+ ASSERT_EQ(5U, audio_section.GetFormats().size());
+ ASSERT_EQ("109", audio_section.GetFormats()[0]);
+ ASSERT_EQ("9", audio_section.GetFormats()[1]);
+ ASSERT_EQ("0", audio_section.GetFormats()[2]);
+ ASSERT_EQ("8", audio_section.GetFormats()[3]);
+ ASSERT_EQ("101", audio_section.GetFormats()[4]);
+
+ // Validate rtpmap
+ ASSERT_TRUE(audio_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute));
+ auto& rtpmaps = audio_attrs.GetRtpmap();
+ ASSERT_TRUE(rtpmaps.HasEntry("109"));
+ ASSERT_TRUE(rtpmaps.HasEntry("9"));
+ ASSERT_TRUE(rtpmaps.HasEntry("0"));
+ ASSERT_TRUE(rtpmaps.HasEntry("8"));
+ ASSERT_TRUE(rtpmaps.HasEntry("101"));
+
+ auto& opus_entry = rtpmaps.GetEntry("109");
+ auto& g722_entry = rtpmaps.GetEntry("9");
+ auto& pcmu_entry = rtpmaps.GetEntry("0");
+ auto& pcma_entry = rtpmaps.GetEntry("8");
+ auto& telephone_event_entry = rtpmaps.GetEntry("101");
+
+ ASSERT_EQ("opus", opus_entry.name);
+ ASSERT_EQ("G722", g722_entry.name);
+ ASSERT_EQ("PCMU", pcmu_entry.name);
+ ASSERT_EQ("PCMA", pcma_entry.name);
+ ASSERT_EQ("telephone-event", telephone_event_entry.name);
+
+ // Validate fmtps
+ ASSERT_TRUE(audio_attrs.HasAttribute(SdpAttribute::kFmtpAttribute));
+ auto& fmtps = audio_attrs.GetFmtp().mFmtps;
+
+ ASSERT_EQ(2U, fmtps.size());
+
+ // opus
+ const SdpFmtpAttributeList::Parameters* opus_params =
+ audio_section.FindFmtp("109");
+ ASSERT_TRUE(opus_params);
+ ASSERT_EQ(SdpRtpmapAttributeList::kOpus, opus_params->codec_type);
+
+ auto& parsed_opus_params =
+ *static_cast<const SdpFmtpAttributeList::OpusParameters*>(opus_params);
+
+ ASSERT_EQ((uint32_t)48000, parsed_opus_params.maxplaybackrate);
+ ASSERT_EQ((uint32_t)1, parsed_opus_params.stereo);
+ ASSERT_EQ((uint32_t)0, parsed_opus_params.useInBandFec);
+
+ // dtmf
+ const SdpFmtpAttributeList::Parameters* dtmf_params =
+ audio_section.FindFmtp("101");
+ ASSERT_TRUE(dtmf_params);
+ ASSERT_EQ(SdpRtpmapAttributeList::kAvt, dtmf_params->codec_type);
+
+ auto& parsed_dtmf_params =
+ *static_cast<const SdpFmtpAttributeList::TelephoneEventParameters*>
+ (dtmf_params);
+
+ ASSERT_EQ("0-15", parsed_dtmf_params.dtmfTones);
+}
+
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();
--- a/media/webrtc/signaling/test/jsep_track_unittest.cpp
+++ b/media/webrtc/signaling/test/jsep_track_unittest.cpp
@@ -27,23 +27,30 @@
namespace mozilla {
class JsepTrackTest : public ::testing::Test
{
public:
JsepTrackTest() {}
std::vector<JsepCodecDescription*>
- MakeCodecs(bool addFecCodecs = false, bool preferRed = false) const
+ MakeCodecs(bool addFecCodecs = false,
+ bool preferRed = false,
+ bool addDtmfCodec = false) 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));
+ if (addDtmfCodec) {
+ results.push_back(
+ new JsepAudioCodecDescription("101", "telephone-event",
+ 8000, 1, 0, 0));
+ }
JsepVideoCodecDescription* red = nullptr;
if (addFecCodecs && preferRed) {
red = new JsepVideoCodecDescription(
"122",
"red",
90000
);
@@ -238,32 +245,50 @@ class JsepTrackTest : public ::testing::
CheckEncodingCount(expected, mSendOff, mRecvAns);
}
void CheckAnsEncodingCount(size_t expected) const
{
CheckEncodingCount(expected, mSendAns, mRecvOff);
}
- const JsepVideoCodecDescription*
- GetVideoCodec(const JsepTrack& track,
- size_t expectedSize = 1,
- size_t codecIndex = 0) const
+ const JsepCodecDescription* GetCodec(const JsepTrack& track,
+ SdpMediaSection::MediaType type,
+ size_t expectedSize,
+ size_t codecIndex) const
{
if (!track.GetNegotiatedDetails() ||
track.GetNegotiatedDetails()->GetEncodingCount() != 1U) {
return nullptr;
}
const std::vector<JsepCodecDescription*>& codecs =
track.GetNegotiatedDetails()->GetEncoding(0).GetCodecs();
- if (codecs.size() != expectedSize ||
- codecs[codecIndex]->mType != SdpMediaSection::kVideo) {
+ if (codecs.size() != expectedSize || codecIndex >= expectedSize ||
+ codecs[codecIndex]->mType != type) {
return nullptr;
}
- return static_cast<const JsepVideoCodecDescription*>(codecs[0]);
+ return codecs[codecIndex];
+ }
+
+ const JsepVideoCodecDescription*
+ GetVideoCodec(const JsepTrack& track,
+ size_t expectedSize = 1,
+ size_t codecIndex = 0) const
+ {
+ return static_cast<const JsepVideoCodecDescription*>
+ (GetCodec(track, SdpMediaSection::kVideo, expectedSize, codecIndex));
+ }
+
+ const JsepAudioCodecDescription*
+ GetAudioCodec(const JsepTrack& track,
+ size_t expectedSize = 1,
+ size_t codecIndex = 0) const
+ {
+ return static_cast<const JsepAudioCodecDescription*>
+ (GetCodec(track, SdpMediaSection::kAudio, expectedSize, codecIndex));
}
void CheckOtherFbsSize(const JsepTrack& track, size_t expected) const
{
const JsepVideoCodecDescription* videoCodec = GetVideoCodec(track);
ASSERT_NE(videoCodec, nullptr);
ASSERT_EQ(videoCodec->mOtherFbTypes.size(), expected);
}
@@ -364,16 +389,33 @@ class JsepTrackTest : public ::testing::
if (mSendOff && mRecvAns) {
SanityCheckTracks(*mSendOff, *mRecvAns);
}
if (mRecvOff && mSendAns) {
SanityCheckTracks(*mRecvOff, *mSendAns);
}
}
+ void RemoveFmtp(SdpMediaSection& mediaSection, const char* pt)
+ {
+ UniquePtr<SdpFmtpAttributeList> fmtps(new SdpFmtpAttributeList);
+
+ SdpAttributeList& attrList = mediaSection.GetAttributeList();
+ if (attrList.HasAttribute(SdpAttribute::kFmtpAttribute)) {
+ *fmtps = attrList.GetFmtp();
+ }
+ for (size_t i=0; i<fmtps->mFmtps.size(); ++i) {
+ if (pt == fmtps->mFmtps[i].format) {
+ fmtps->mFmtps.erase(fmtps->mFmtps.begin()+i);
+ break;
+ }
+ }
+ attrList.SetAttribute(fmtps.release());
+ }
+
protected:
RefPtr<JsepTrack> mSendOff;
RefPtr<JsepTrack> mRecvOff;
RefPtr<JsepTrack> mSendAns;
RefPtr<JsepTrack> mRecvAns;
PtrVector<JsepCodecDescription> mOffCodecs;
PtrVector<JsepCodecDescription> mAnsCodecs;
UniquePtr<Sdp> mOffer;
@@ -396,16 +438,349 @@ TEST_F(JsepTrackTest, AudioNegotiation)
TEST_F(JsepTrackTest, VideoNegotiation)
{
Init(SdpMediaSection::kVideo);
OfferAnswer();
CheckOffEncodingCount(1);
CheckAnsEncodingCount(1);
}
+class CheckForCodecType
+{
+public:
+ explicit CheckForCodecType(SdpMediaSection::MediaType type,
+ bool *result) :
+ mResult(result),
+ mType(type) {}
+
+ void operator()(JsepCodecDescription* codec) {
+ if (codec->mType == mType) {
+ *mResult = true;
+ }
+ }
+
+private:
+ bool *mResult;
+ SdpMediaSection::MediaType mType;
+};
+
+TEST_F(JsepTrackTest, CheckForMismatchedAudioCodecAndVideoTrack)
+{
+ PtrVector<JsepCodecDescription> offerCodecs;
+
+ // make codecs including telephone-event (an audio codec)
+ offerCodecs.values = MakeCodecs(false, false, true);
+ RefPtr<JsepTrack> videoTrack = new JsepTrack(SdpMediaSection::kVideo,
+ "stream_id",
+ "track_id",
+ sdp::kSend);
+ // populate codecs and then make sure we don't have any audio codecs
+ // in the video track
+ videoTrack->PopulateCodecs(offerCodecs.values);
+
+ bool found = false;
+ videoTrack->ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+
+ found = false;
+ videoTrack->ForEachCodec(CheckForCodecType(SdpMediaSection::kVideo, &found));
+ ASSERT_TRUE(found); // for sanity, make sure we did find video codecs
+}
+
+TEST_F(JsepTrackTest, CheckVideoTrackWithHackedDtmfSdp)
+{
+ Init(SdpMediaSection::kVideo);
+ CreateOffer();
+ // make sure we don't find sdp containing telephone-event in video track
+ ASSERT_EQ(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ // force audio codec telephone-event into video m= section of offer
+ GetOffer().AddCodec("101", "telephone-event", 8000, 1);
+ // make sure we _do_ find sdp containing telephone-event in video track
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ CreateAnswer();
+ // make sure we don't find sdp containing telephone-event in video track
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ // force audio codec telephone-event into video m= section of answer
+ GetAnswer().AddCodec("101", "telephone-event", 8000, 1);
+ // make sure we _do_ find sdp containing telephone-event in video track
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ Negotiate();
+ SanityCheck();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_TRUE(mSendOff.get());
+ ASSERT_TRUE(mRecvOff.get());
+ ASSERT_TRUE(mSendAns.get());
+ ASSERT_TRUE(mRecvAns.get());
+
+ // make sure we still don't find any audio codecs in the video track after
+ // hacking the sdp
+ bool found = false;
+ mSendOff->ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+ mRecvOff->ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+ mSendAns->ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+ mRecvAns->ForEachCodec(CheckForCodecType(SdpMediaSection::kAudio, &found));
+ ASSERT_FALSE(found);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationOffererDtmf)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = MakeCodecs(false, false, false);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns)));
+ ASSERT_EQ("1", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationAnswererDtmf)
+{
+ mOffCodecs.values = MakeCodecs(false, false, false);
+ mAnsCodecs.values = MakeCodecs(false, false, true);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_EQ(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_EQ(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns)));
+ ASSERT_EQ("1", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationOffererAnswererDtmf)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = MakeCodecs(false, false, true);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+ OfferAnswer();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererNoFmtpAnswererFmtp)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = MakeCodecs(false, false, true);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+
+ CreateOffer();
+ RemoveFmtp(GetOffer(), "101");
+
+ CreateAnswer();
+
+ Negotiate();
+ SanityCheck();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_EQ(mOffer->ToString().find("a=fmtp:101"), std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererFmtpAnswererNoFmtp)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = MakeCodecs(false, false, true);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+
+ CreateOffer();
+
+ CreateAnswer();
+ RemoveFmtp(GetAnswer(), "101");
+
+ Negotiate();
+ SanityCheck();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_NE(mOffer->ToString().find("a=fmtp:101 0-15"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
+TEST_F(JsepTrackTest, AudioNegotiationDtmfOffererNoFmtpAnswererNoFmtp)
+{
+ mOffCodecs.values = MakeCodecs(false, false, true);
+ mAnsCodecs.values = MakeCodecs(false, false, true);
+
+ InitTracks(SdpMediaSection::kAudio);
+ InitSdp(SdpMediaSection::kAudio);
+
+ CreateOffer();
+ RemoveFmtp(GetOffer(), "101");
+
+ CreateAnswer();
+ RemoveFmtp(GetAnswer(), "101");
+
+ Negotiate();
+ SanityCheck();
+
+ CheckOffEncodingCount(1);
+ CheckAnsEncodingCount(1);
+
+ ASSERT_NE(mOffer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+ ASSERT_NE(mAnswer->ToString().find("a=rtpmap:101 telephone-event"),
+ std::string::npos);
+
+ ASSERT_EQ(mOffer->ToString().find("a=fmtp:101"), std::string::npos);
+ ASSERT_EQ(mAnswer->ToString().find("a=fmtp:101"), std::string::npos);
+
+ const JsepAudioCodecDescription* track = nullptr;
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2)));
+ ASSERT_EQ("1", track->mDefaultPt);
+
+ ASSERT_TRUE((track = GetAudioCodec(*mSendOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvOff, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mSendAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+ ASSERT_TRUE((track = GetAudioCodec(*mRecvAns, 2, 1)));
+ ASSERT_EQ("101", track->mDefaultPt);
+}
+
TEST_F(JsepTrackTest, VideoNegotationOffererFEC)
{
mOffCodecs.values = MakeCodecs(true);
mAnsCodecs.values = MakeCodecs(false);
InitTracks(SdpMediaSection::kVideo);
InitSdp(SdpMediaSection::kVideo);
OfferAnswer();
--- a/media/webrtc/signaling/test/sdp_unittests.cpp
+++ b/media/webrtc/signaling/test/sdp_unittests.cpp
@@ -1206,17 +1206,17 @@ const std::string kBasicAudioVideoOffer
"a=rtpmap:109 opus/48000/2" CRLF
"a=fmtp:109 maxplaybackrate=32000;stereo=1" CRLF
"a=ptime:20" CRLF
"a=maxptime:20" CRLF
"a=rtpmap:9 G722/8000" CRLF
"a=rtpmap:0 PCMU/8000" CRLF
"a=rtpmap:8 PCMA/8000" CRLF
"a=rtpmap:101 telephone-event/8000" CRLF
-"a=fmtp:101 0-15" CRLF
+"a=fmtp:101 0-15,66,32-34,67" CRLF
"a=ice-ufrag:00000000" CRLF
"a=ice-pwd:0000000000000000000000000000000" CRLF
"a=sendonly" CRLF
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF
"a=setup:actpass" CRLF
"a=rtcp-mux" CRLF
"a=msid:stream track" CRLF
"a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF
@@ -1527,17 +1527,17 @@ TEST_P(NewSdpTest, CheckRtpmap) {
SdpRtpmapAttributeList::kPCMA,
"PCMA",
8000,
1,
audiosec.GetFormats()[3],
rtpmap);
CheckRtpmap("101",
- SdpRtpmapAttributeList::kOtherCodec,
+ SdpRtpmapAttributeList::kAvt,
"telephone-event",
8000,
1,
audiosec.GetFormats()[4],
rtpmap);
const SdpMediaSection& videosec = mSdp->GetMediaSection(1);
const SdpRtpmapAttributeList videoRtpmap =
@@ -1573,16 +1573,163 @@ TEST_P(NewSdpTest, CheckRtpmap) {
SdpRtpmapAttributeList::kUlpfec,
"ulpfec",
90000,
0,
videosec.GetFormats()[3],
videoRtpmap);
}
+static const std::string kAudioWithTelephoneEvent =
+ "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=audio 9 RTP/SAVPF 109 9 0 8 101" CRLF
+ "c=IN IP4 0.0.0.0" CRLF
+ "a=mid:first" CRLF
+ "a=rtpmap:109 opus/48000/2" CRLF
+ "a=fmtp:109 maxplaybackrate=32000;stereo=1" CRLF
+ "a=ptime:20" CRLF
+ "a=maxptime:20" CRLF
+ "a=rtpmap:9 G722/8000" CRLF
+ "a=rtpmap:0 PCMU/8000" CRLF
+ "a=rtpmap:8 PCMA/8000" CRLF
+ "a=rtpmap:101 telephone-event/8000" CRLF;
+
+TEST_P(NewSdpTest, CheckTelephoneEventNoFmtp) {
+ ParseSdp(kAudioWithTelephoneEvent);
+ 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 audio_format_params =
+ mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
+ ASSERT_EQ(1U, audio_format_params.size());
+
+ // make sure we don't get a fmtp for codec 101
+ for (size_t i = 0; i < audio_format_params.size(); ++i) {
+ ASSERT_NE("101", audio_format_params[i].format);
+ }
+}
+
+TEST_P(NewSdpTest, CheckTelephoneEventWithDefaultEvents) {
+ ParseSdp(kAudioWithTelephoneEvent
+ + "a=fmtp:101 0-15" 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 audio_format_params =
+ mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
+ ASSERT_EQ(2U, audio_format_params.size());
+
+ ASSERT_EQ("101", audio_format_params[1].format);
+ ASSERT_TRUE(!!audio_format_params[1].parameters);
+ const SdpFmtpAttributeList::TelephoneEventParameters* te_parameters =
+ static_cast<SdpFmtpAttributeList::TelephoneEventParameters*>(
+ audio_format_params[1].parameters.get());
+ ASSERT_NE(0U, te_parameters->dtmfTones.size());
+ ASSERT_EQ("0-15", te_parameters->dtmfTones);
+}
+
+TEST_P(NewSdpTest, CheckTelephoneEventWithBadCharacter) {
+ ParseSdp(kAudioWithTelephoneEvent
+ + "a=fmtp:101 0-5." 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 audio_format_params =
+ mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
+ ASSERT_EQ(2U, audio_format_params.size());
+
+ ASSERT_EQ("101", audio_format_params[1].format);
+ ASSERT_TRUE(!!audio_format_params[1].parameters);
+ const SdpFmtpAttributeList::TelephoneEventParameters* te_parameters =
+ static_cast<SdpFmtpAttributeList::TelephoneEventParameters*>(
+ audio_format_params[1].parameters.get());
+ ASSERT_NE(0U, te_parameters->dtmfTones.size());
+ // check for the default dtmf tones
+ ASSERT_EQ("0-15", te_parameters->dtmfTones);
+}
+
+TEST_P(NewSdpTest, CheckTelephoneEventIncludingCommas) {
+ ParseSdp(kAudioWithTelephoneEvent
+ + "a=fmtp:101 0-15,66,67" 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 audio_format_params =
+ mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
+ ASSERT_EQ(2U, audio_format_params.size());
+
+ ASSERT_EQ("101", audio_format_params[1].format);
+ ASSERT_TRUE(!!audio_format_params[1].parameters);
+ const SdpFmtpAttributeList::TelephoneEventParameters* te_parameters =
+ static_cast<SdpFmtpAttributeList::TelephoneEventParameters*>(
+ audio_format_params[1].parameters.get());
+ ASSERT_NE(0U, te_parameters->dtmfTones.size());
+ ASSERT_EQ("0-15,66,67", te_parameters->dtmfTones);
+}
+
+TEST_P(NewSdpTest, CheckTelephoneEventComplexEvents) {
+ ParseSdp(kAudioWithTelephoneEvent
+ + "a=fmtp:101 0,1,2-4,5-15,66,67" 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 audio_format_params =
+ mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
+ ASSERT_EQ(2U, audio_format_params.size());
+
+ ASSERT_EQ("101", audio_format_params[1].format);
+ ASSERT_TRUE(!!audio_format_params[1].parameters);
+ const SdpFmtpAttributeList::TelephoneEventParameters* te_parameters =
+ static_cast<SdpFmtpAttributeList::TelephoneEventParameters*>(
+ audio_format_params[1].parameters.get());
+ ASSERT_NE(0U, te_parameters->dtmfTones.size());
+ ASSERT_EQ("0,1,2-4,5-15,66,67", te_parameters->dtmfTones);
+}
+
+TEST_P(NewSdpTest, CheckTelephoneEventNoHyphen) {
+ ParseSdp(kAudioWithTelephoneEvent
+ + "a=fmtp:101 5,6,7" 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 audio_format_params =
+ mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
+ ASSERT_EQ(2U, audio_format_params.size());
+
+ ASSERT_EQ("101", audio_format_params[1].format);
+ ASSERT_TRUE(!!audio_format_params[1].parameters);
+ const SdpFmtpAttributeList::TelephoneEventParameters* te_parameters =
+ static_cast<SdpFmtpAttributeList::TelephoneEventParameters*>(
+ audio_format_params[1].parameters.get());
+ ASSERT_NE(0U, te_parameters->dtmfTones.size());
+ ASSERT_EQ("5,6,7", te_parameters->dtmfTones);
+}
+
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
@@ -1683,16 +1830,17 @@ const std::string kH264AudioVideoOffer =
"a=rtpmap:109 opus/48000/2" CRLF
"a=ptime:20" CRLF
"a=maxptime:20" CRLF
"a=rtpmap:9 G722/8000" CRLF
"a=rtpmap:0 PCMU/8000" CRLF
"a=rtpmap:8 PCMA/8000" CRLF
"a=rtpmap:101 telephone-event/8000" CRLF
"a=fmtp:109 maxplaybackrate=32000;stereo=1;useinbandfec=1" CRLF
+"a=fmtp:101 0-15,66,32-34,67" CRLF
"a=ice-ufrag:00000000" CRLF
"a=ice-pwd:0000000000000000000000000000000" CRLF
"a=sendonly" CRLF
"a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level" CRLF
"a=setup:actpass" CRLF
"a=rtcp-mux" CRLF
"a=msid:stream track" CRLF
"a=candidate:0 1 UDP 2130379007 10.0.0.36 62453 typ host" CRLF
@@ -1736,25 +1884,32 @@ TEST_P(NewSdpTest, CheckFormatParameters
ASSERT_TRUE(!!mSdp) << "Parse failed: " << GetParseErrors();
ASSERT_EQ(3U, mSdp->GetMediaSectionCount())
<< "Wrong number of media sections";
ASSERT_TRUE(mSdp->GetMediaSection(0).GetAttributeList().HasAttribute(
SdpAttribute::kFmtpAttribute));
auto audio_format_params =
mSdp->GetMediaSection(0).GetAttributeList().GetFmtp().mFmtps;
- ASSERT_EQ(1U, audio_format_params.size());
+ ASSERT_EQ(2U, audio_format_params.size());
ASSERT_EQ("109", audio_format_params[0].format);
ASSERT_TRUE(!!audio_format_params[0].parameters);
const SdpFmtpAttributeList::OpusParameters* opus_parameters =
static_cast<SdpFmtpAttributeList::OpusParameters*>(
audio_format_params[0].parameters.get());
ASSERT_EQ(32000U, opus_parameters->maxplaybackrate);
ASSERT_EQ(1U, opus_parameters->stereo);
ASSERT_EQ(1U, opus_parameters->useInBandFec);
+ ASSERT_EQ("101", audio_format_params[1].format);
+ ASSERT_TRUE(!!audio_format_params[1].parameters);
+ const SdpFmtpAttributeList::TelephoneEventParameters* te_parameters =
+ static_cast<SdpFmtpAttributeList::TelephoneEventParameters*>(
+ audio_format_params[1].parameters.get());
+ ASSERT_NE(0U, te_parameters->dtmfTones.size());
+ ASSERT_EQ("0-15,66,32-34,67", te_parameters->dtmfTones);
ASSERT_TRUE(mSdp->GetMediaSection(1).GetAttributeList().HasAttribute(
SdpAttribute::kFmtpAttribute));
auto video_format_params =
mSdp->GetMediaSection(1).GetAttributeList().GetFmtp().mFmtps;
ASSERT_EQ(3U, video_format_params.size());
ASSERT_EQ("97", video_format_params[0].format);
ASSERT_TRUE(!!video_format_params[0].parameters);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -408,16 +408,17 @@ pref("media.navigator.load_adapt.measure
pref("media.navigator.load_adapt.avg_seconds",3);
pref("media.navigator.load_adapt.high_load","0.90");
pref("media.navigator.load_adapt.low_load","0.40");
pref("media.navigator.video.default_fps",30);
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.audio.dtmf_enabled", false);
pref("media.navigator.video.red_ulpfec_enabled", false);
pref("media.webrtc.debug.trace_mask", 0);
pref("media.webrtc.debug.multi_log", false);
pref("media.webrtc.debug.aec_log_dir", "");
pref("media.webrtc.debug.log_file", "");
pref("media.webrtc.debug.aec_dump_max_size", 4194304); // 4MB