1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #ifndef _JSEPCODECDESCRIPTION_H_
6 #define _JSEPCODECDESCRIPTION_H_
7 
8 #include <string>
9 #include "signaling/src/sdp/SdpMediaSection.h"
10 #include "signaling/src/sdp/SdpHelper.h"
11 #include "nsCRT.h"
12 #include "mozilla/net/DataChannelProtocol.h"
13 
14 namespace mozilla {
15 
16 #define JSEP_CODEC_CLONE(T) \
17   virtual JsepCodecDescription* Clone() const override { return new T(*this); }
18 
19 // A single entry in our list of known codecs.
20 class JsepCodecDescription {
21  public:
JsepCodecDescription(mozilla::SdpMediaSection::MediaType type,const std::string & defaultPt,const std::string & name,uint32_t clock,uint32_t channels,bool enabled)22   JsepCodecDescription(mozilla::SdpMediaSection::MediaType type,
23                        const std::string& defaultPt, const std::string& name,
24                        uint32_t clock, uint32_t channels, bool enabled)
25       : mType(type),
26         mDefaultPt(defaultPt),
27         mName(name),
28         mClock(clock),
29         mChannels(channels),
30         mEnabled(enabled),
31         mStronglyPreferred(false),
32         mDirection(sdp::kSend) {}
~JsepCodecDescription()33   virtual ~JsepCodecDescription() {}
34 
35   virtual JsepCodecDescription* Clone() const = 0;
36 
GetPtAsInt(uint16_t * ptOutparam)37   bool GetPtAsInt(uint16_t* ptOutparam) const {
38     return SdpHelper::GetPtAsInt(mDefaultPt, ptOutparam);
39   }
40 
Matches(const std::string & fmt,const SdpMediaSection & remoteMsection)41   virtual bool Matches(const std::string& fmt,
42                        const SdpMediaSection& remoteMsection) const {
43     // note: fmt here is remote fmt (to go with remoteMsection)
44     if (mType != remoteMsection.GetMediaType()) {
45       return false;
46     }
47 
48     const SdpRtpmapAttributeList::Rtpmap* entry(remoteMsection.FindRtpmap(fmt));
49 
50     if (entry) {
51       if (!nsCRT::strcasecmp(mName.c_str(), entry->name.c_str()) &&
52           (mClock == entry->clock) && (mChannels == entry->channels)) {
53         return ParametersMatch(fmt, remoteMsection);
54       }
55     } else if (!fmt.compare("9") && mName == "G722") {
56       return true;
57     } else if (!fmt.compare("0") && mName == "PCMU") {
58       return true;
59     } else if (!fmt.compare("8") && mName == "PCMA") {
60       return true;
61     }
62     return false;
63   }
64 
ParametersMatch(const std::string & fmt,const SdpMediaSection & remoteMsection)65   virtual bool ParametersMatch(const std::string& fmt,
66                                const SdpMediaSection& remoteMsection) const {
67     return true;
68   }
69 
Negotiate(const std::string & pt,const SdpMediaSection & remoteMsection)70   virtual bool Negotiate(const std::string& pt,
71                          const SdpMediaSection& remoteMsection) {
72     mDefaultPt = pt;
73     return true;
74   }
75 
AddToMediaSection(SdpMediaSection & msection)76   virtual void AddToMediaSection(SdpMediaSection& msection) const {
77     if (mEnabled && msection.GetMediaType() == mType) {
78       // Both send and recv codec will have the same pt, so don't add twice
79       if (!msection.HasFormat(mDefaultPt)) {
80         msection.AddCodec(mDefaultPt, mName, mClock, mChannels);
81       }
82 
83       AddParametersToMSection(msection);
84     }
85   }
86 
AddParametersToMSection(SdpMediaSection & msection)87   virtual void AddParametersToMSection(SdpMediaSection& msection) const {}
88 
89   mozilla::SdpMediaSection::MediaType mType;
90   std::string mDefaultPt;
91   std::string mName;
92   uint32_t mClock;
93   uint32_t mChannels;
94   bool mEnabled;
95   bool mStronglyPreferred;
96   sdp::Direction mDirection;
97   // Will hold constraints from both fmtp and rid
98   EncodingConstraints mConstraints;
99 };
100 
101 class JsepAudioCodecDescription : public JsepCodecDescription {
102  public:
103   JsepAudioCodecDescription(const std::string& defaultPt,
104                             const std::string& name, uint32_t clock,
105                             uint32_t channels, uint32_t packetSize,
106                             uint32_t bitRate, bool enabled = true)
JsepCodecDescription(mozilla::SdpMediaSection::kAudio,defaultPt,name,clock,channels,enabled)107       : JsepCodecDescription(mozilla::SdpMediaSection::kAudio, defaultPt, name,
108                              clock, channels, enabled),
109         mPacketSize(packetSize),
110         mBitrate(bitRate),
111         mMaxPlaybackRate(0),
112         mForceMono(false),
113         mFECEnabled(false),
114         mDtmfEnabled(false) {}
115 
JSEP_CODEC_CLONE(JsepAudioCodecDescription)116   JSEP_CODEC_CLONE(JsepAudioCodecDescription)
117 
118   SdpFmtpAttributeList::OpusParameters GetOpusParameters(
119       const std::string& pt, const SdpMediaSection& msection) const {
120     // Will contain defaults if nothing else
121     SdpFmtpAttributeList::OpusParameters result;
122     auto* params = msection.FindFmtp(pt);
123 
124     if (params && params->codec_type == SdpRtpmapAttributeList::kOpus) {
125       result =
126           static_cast<const SdpFmtpAttributeList::OpusParameters&>(*params);
127     }
128 
129     return result;
130   }
131 
GetTelephoneEventParameters(const std::string & pt,const SdpMediaSection & msection)132   SdpFmtpAttributeList::TelephoneEventParameters GetTelephoneEventParameters(
133       const std::string& pt, const SdpMediaSection& msection) const {
134     // Will contain defaults if nothing else
135     SdpFmtpAttributeList::TelephoneEventParameters result;
136     auto* params = msection.FindFmtp(pt);
137 
138     if (params &&
139         params->codec_type == SdpRtpmapAttributeList::kTelephoneEvent) {
140       result =
141           static_cast<const SdpFmtpAttributeList::TelephoneEventParameters&>(
142               *params);
143     }
144 
145     return result;
146   }
147 
AddParametersToMSection(SdpMediaSection & msection)148   void AddParametersToMSection(SdpMediaSection& msection) const override {
149     if (mDirection == sdp::kSend) {
150       return;
151     }
152 
153     if (mName == "opus") {
154       SdpFmtpAttributeList::OpusParameters opusParams(
155           GetOpusParameters(mDefaultPt, msection));
156       if (mMaxPlaybackRate) {
157         opusParams.maxplaybackrate = mMaxPlaybackRate;
158       }
159       if (mChannels == 2 && !mForceMono) {
160         // We prefer to receive stereo, if available.
161         opusParams.stereo = 1;
162       }
163       opusParams.useInBandFec = mFECEnabled ? 1 : 0;
164       msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, opusParams));
165     } else if (mName == "telephone-event") {
166       // add the default dtmf tones
167       SdpFmtpAttributeList::TelephoneEventParameters teParams(
168           GetTelephoneEventParameters(mDefaultPt, msection));
169       msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, teParams));
170     }
171   }
172 
Negotiate(const std::string & pt,const SdpMediaSection & remoteMsection)173   bool Negotiate(const std::string& pt,
174                  const SdpMediaSection& remoteMsection) override {
175     JsepCodecDescription::Negotiate(pt, remoteMsection);
176     if (mName == "opus" && mDirection == sdp::kSend) {
177       SdpFmtpAttributeList::OpusParameters opusParams(
178           GetOpusParameters(mDefaultPt, remoteMsection));
179 
180       mMaxPlaybackRate = opusParams.maxplaybackrate;
181       mForceMono = !opusParams.stereo;
182       // draft-ietf-rtcweb-fec-03.txt section 4.2 says support for FEC
183       // at the received side is declarative and can be negotiated
184       // separately for either media direction.
185       mFECEnabled = opusParams.useInBandFec;
186     }
187 
188     return true;
189   }
190 
191   uint32_t mPacketSize;
192   uint32_t mBitrate;
193   uint32_t mMaxPlaybackRate;
194   bool mForceMono;
195   bool mFECEnabled;
196   bool mDtmfEnabled;
197 };
198 
199 class JsepVideoCodecDescription : public JsepCodecDescription {
200  public:
201   JsepVideoCodecDescription(const std::string& defaultPt,
202                             const std::string& name, uint32_t clock,
203                             bool enabled = true)
204       : JsepCodecDescription(mozilla::SdpMediaSection::kVideo, defaultPt, name,
205                              clock, 0, enabled),
206         mTmmbrEnabled(false),
207         mRembEnabled(false),
208         mFECEnabled(false),
209         mPacketizationMode(0) {
210     // Add supported rtcp-fb types
211     mNackFbTypes.push_back("");
212     mNackFbTypes.push_back(SdpRtcpFbAttributeList::pli);
213     mCcmFbTypes.push_back(SdpRtcpFbAttributeList::fir);
214   }
215 
EnableTmmbr()216   virtual void EnableTmmbr() {
217     // EnableTmmbr can be called multiple times due to multiple calls to
218     // PeerConnectionImpl::ConfigureJsepSessionCodecs
219     if (!mTmmbrEnabled) {
220       mTmmbrEnabled = true;
221       mCcmFbTypes.push_back(SdpRtcpFbAttributeList::tmmbr);
222     }
223   }
224 
EnableRemb()225   virtual void EnableRemb() {
226     // EnableRemb can be called multiple times due to multiple calls to
227     // PeerConnectionImpl::ConfigureJsepSessionCodecs
228     if (!mRembEnabled) {
229       mRembEnabled = true;
230       mOtherFbTypes.push_back({"", SdpRtcpFbAttributeList::kRemb, "", ""});
231     }
232   }
233 
EnableFec(std::string redPayloadType,std::string ulpfecPayloadType)234   virtual void EnableFec(std::string redPayloadType,
235                          std::string ulpfecPayloadType) {
236     // Enabling FEC for video works a little differently than enabling
237     // REMB or TMMBR.  Support for FEC is indicated by the presence of
238     // particular codes (red and ulpfec) instead of using rtcpfb
239     // attributes on a given codec.  There is no rtcpfb to push for FEC
240     // as can be seen above when REMB or TMMBR are enabled.
241 
242     // Ensure we have valid payload types. This returns zero on failure, which
243     // is a valid payload type.
244     uint16_t redPt, ulpfecPt;
245     if (!SdpHelper::GetPtAsInt(redPayloadType, &redPt) ||
246         !SdpHelper::GetPtAsInt(ulpfecPayloadType, &ulpfecPt)) {
247       return;
248     }
249 
250     mFECEnabled = true;
251     mREDPayloadType = redPt;
252     mULPFECPayloadType = ulpfecPt;
253   }
254 
AddParametersToMSection(SdpMediaSection & msection)255   void AddParametersToMSection(SdpMediaSection& msection) const override {
256     AddFmtpsToMSection(msection);
257     AddRtcpFbsToMSection(msection);
258   }
259 
AddFmtpsToMSection(SdpMediaSection & msection)260   void AddFmtpsToMSection(SdpMediaSection& msection) const {
261     if (mName == "H264") {
262       SdpFmtpAttributeList::H264Parameters h264Params(
263           GetH264Parameters(mDefaultPt, msection));
264 
265       if (mDirection == sdp::kSend) {
266         if (!h264Params.level_asymmetry_allowed) {
267           // First time the fmtp has been set; set just in case this is for a
268           // sendonly m-line, since even though we aren't receiving the level
269           // negotiation still needs to happen (sigh).
270           h264Params.profile_level_id = mProfileLevelId;
271         }
272       } else {
273         // Parameters that only apply to what we receive
274         h264Params.max_mbps = mConstraints.maxMbps;
275         h264Params.max_fs = mConstraints.maxFs;
276         h264Params.max_cpb = mConstraints.maxCpb;
277         h264Params.max_dpb = mConstraints.maxDpb;
278         h264Params.max_br = mConstraints.maxBr;
279         strncpy(h264Params.sprop_parameter_sets, mSpropParameterSets.c_str(),
280                 sizeof(h264Params.sprop_parameter_sets) - 1);
281         h264Params.profile_level_id = mProfileLevelId;
282       }
283 
284       // Parameters that apply to both the send and recv directions
285       h264Params.packetization_mode = mPacketizationMode;
286       // Hard-coded, may need to change someday?
287       h264Params.level_asymmetry_allowed = true;
288 
289       msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, h264Params));
290     } else if (mName == "red" && !mRedundantEncodings.empty()) {
291       SdpFmtpAttributeList::RedParameters redParams(
292           GetRedParameters(mDefaultPt, msection));
293       redParams.encodings = mRedundantEncodings;
294       msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, redParams));
295     } else if (mName == "VP8" || mName == "VP9") {
296       if (mDirection == sdp::kRecv) {
297         // VP8 and VP9 share the same SDP parameters thus far
298         SdpFmtpAttributeList::VP8Parameters vp8Params(
299             GetVP8Parameters(mDefaultPt, msection));
300 
301         vp8Params.max_fs = mConstraints.maxFs;
302         vp8Params.max_fr = mConstraints.maxFps;
303         msection.SetFmtp(SdpFmtpAttributeList::Fmtp(mDefaultPt, vp8Params));
304       }
305     }
306   }
307 
AddRtcpFbsToMSection(SdpMediaSection & msection)308   void AddRtcpFbsToMSection(SdpMediaSection& msection) const {
309     SdpRtcpFbAttributeList rtcpfbs(msection.GetRtcpFbs());
310     for (const auto& rtcpfb : rtcpfbs.mFeedbacks) {
311       if (rtcpfb.pt == mDefaultPt) {
312         // Already set by the codec for the other direction.
313         return;
314       }
315     }
316 
317     for (const std::string& type : mAckFbTypes) {
318       rtcpfbs.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kAck, type);
319     }
320     for (const std::string& type : mNackFbTypes) {
321       rtcpfbs.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kNack, type);
322     }
323     for (const std::string& type : mCcmFbTypes) {
324       rtcpfbs.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kCcm, type);
325     }
326     for (const auto& fb : mOtherFbTypes) {
327       rtcpfbs.PushEntry(mDefaultPt, fb.type, fb.parameter, fb.extra);
328     }
329 
330     msection.SetRtcpFbs(rtcpfbs);
331   }
332 
GetH264Parameters(const std::string & pt,const SdpMediaSection & msection)333   SdpFmtpAttributeList::H264Parameters GetH264Parameters(
334       const std::string& pt, const SdpMediaSection& msection) const {
335     // Will contain defaults if nothing else
336     SdpFmtpAttributeList::H264Parameters result;
337     auto* params = msection.FindFmtp(pt);
338 
339     if (params && params->codec_type == SdpRtpmapAttributeList::kH264) {
340       result =
341           static_cast<const SdpFmtpAttributeList::H264Parameters&>(*params);
342     }
343 
344     return result;
345   }
346 
GetRedParameters(const std::string & pt,const SdpMediaSection & msection)347   SdpFmtpAttributeList::RedParameters GetRedParameters(
348       const std::string& pt, const SdpMediaSection& msection) const {
349     SdpFmtpAttributeList::RedParameters result;
350     auto* params = msection.FindFmtp(pt);
351 
352     if (params && params->codec_type == SdpRtpmapAttributeList::kRed) {
353       result = static_cast<const SdpFmtpAttributeList::RedParameters&>(*params);
354     }
355 
356     return result;
357   }
358 
GetVP8Parameters(const std::string & pt,const SdpMediaSection & msection)359   SdpFmtpAttributeList::VP8Parameters GetVP8Parameters(
360       const std::string& pt, const SdpMediaSection& msection) const {
361     SdpRtpmapAttributeList::CodecType expectedType(
362         mName == "VP8" ? SdpRtpmapAttributeList::kVP8
363                        : SdpRtpmapAttributeList::kVP9);
364 
365     // Will contain defaults if nothing else
366     SdpFmtpAttributeList::VP8Parameters result(expectedType);
367     auto* params = msection.FindFmtp(pt);
368 
369     if (params && params->codec_type == expectedType) {
370       result = static_cast<const SdpFmtpAttributeList::VP8Parameters&>(*params);
371     }
372 
373     return result;
374   }
375 
NegotiateRtcpFb(const SdpMediaSection & remoteMsection,SdpRtcpFbAttributeList::Type type,std::vector<std::string> * supportedTypes)376   void NegotiateRtcpFb(const SdpMediaSection& remoteMsection,
377                        SdpRtcpFbAttributeList::Type type,
378                        std::vector<std::string>* supportedTypes) {
379     std::vector<std::string> temp;
380     for (auto& subType : *supportedTypes) {
381       if (remoteMsection.HasRtcpFb(mDefaultPt, type, subType)) {
382         temp.push_back(subType);
383       }
384     }
385     *supportedTypes = temp;
386   }
387 
NegotiateRtcpFb(const SdpMediaSection & remoteMsection,std::vector<SdpRtcpFbAttributeList::Feedback> * supportedFbs)388   void NegotiateRtcpFb(
389       const SdpMediaSection& remoteMsection,
390       std::vector<SdpRtcpFbAttributeList::Feedback>* supportedFbs) {
391     std::vector<SdpRtcpFbAttributeList::Feedback> temp;
392     for (auto& fb : *supportedFbs) {
393       if (remoteMsection.HasRtcpFb(mDefaultPt, fb.type, fb.parameter)) {
394         temp.push_back(fb);
395       }
396     }
397     *supportedFbs = temp;
398   }
399 
NegotiateRtcpFb(const SdpMediaSection & remote)400   void NegotiateRtcpFb(const SdpMediaSection& remote) {
401     // Removes rtcp-fb types that the other side doesn't support
402     NegotiateRtcpFb(remote, SdpRtcpFbAttributeList::kAck, &mAckFbTypes);
403     NegotiateRtcpFb(remote, SdpRtcpFbAttributeList::kNack, &mNackFbTypes);
404     NegotiateRtcpFb(remote, SdpRtcpFbAttributeList::kCcm, &mCcmFbTypes);
405     NegotiateRtcpFb(remote, &mOtherFbTypes);
406   }
407 
Negotiate(const std::string & pt,const SdpMediaSection & remoteMsection)408   virtual bool Negotiate(const std::string& pt,
409                          const SdpMediaSection& remoteMsection) override {
410     JsepCodecDescription::Negotiate(pt, remoteMsection);
411     if (mName == "H264") {
412       SdpFmtpAttributeList::H264Parameters h264Params(
413           GetH264Parameters(mDefaultPt, remoteMsection));
414 
415       // Level is negotiated symmetrically if level asymmetry is disallowed
416       if (!h264Params.level_asymmetry_allowed) {
417         SetSaneH264Level(std::min(GetSaneH264Level(h264Params.profile_level_id),
418                                   GetSaneH264Level(mProfileLevelId)),
419                          &mProfileLevelId);
420       }
421 
422       if (mDirection == sdp::kSend) {
423         // Remote values of these apply only to the send codec.
424         mConstraints.maxFs = h264Params.max_fs;
425         mConstraints.maxMbps = h264Params.max_mbps;
426         mConstraints.maxCpb = h264Params.max_cpb;
427         mConstraints.maxDpb = h264Params.max_dpb;
428         mConstraints.maxBr = h264Params.max_br;
429         mSpropParameterSets = h264Params.sprop_parameter_sets;
430         // Only do this if we didn't symmetrically negotiate above
431         if (h264Params.level_asymmetry_allowed) {
432           SetSaneH264Level(GetSaneH264Level(h264Params.profile_level_id),
433                            &mProfileLevelId);
434         }
435       } else {
436         // TODO(bug 1143709): max-recv-level support
437       }
438     } else if (mName == "red") {
439       SdpFmtpAttributeList::RedParameters redParams(
440           GetRedParameters(mDefaultPt, remoteMsection));
441       mRedundantEncodings = redParams.encodings;
442     } else if (mName == "VP8" || mName == "VP9") {
443       if (mDirection == sdp::kSend) {
444         SdpFmtpAttributeList::VP8Parameters vp8Params(
445             GetVP8Parameters(mDefaultPt, remoteMsection));
446 
447         mConstraints.maxFs = vp8Params.max_fs;
448         mConstraints.maxFps = vp8Params.max_fr;
449       }
450     }
451 
452     NegotiateRtcpFb(remoteMsection);
453     return true;
454   }
455 
456   // Maps the not-so-sane encoding of H264 level into something that is
457   // ordered in the way one would expect
458   // 1b is 0xAB, everything else is the level left-shifted one half-byte
459   // (eg; 1.0 is 0xA0, 1.1 is 0xB0, 3.1 is 0x1F0)
GetSaneH264Level(uint32_t profileLevelId)460   static uint32_t GetSaneH264Level(uint32_t profileLevelId) {
461     uint32_t profileIdc = (profileLevelId >> 16);
462 
463     if (profileIdc == 0x42 || profileIdc == 0x4D || profileIdc == 0x58) {
464       if ((profileLevelId & 0x10FF) == 0x100B) {
465         // Level 1b
466         return 0xAB;
467       }
468     }
469 
470     uint32_t level = profileLevelId & 0xFF;
471 
472     if (level == 0x09) {
473       // Another way to encode level 1b
474       return 0xAB;
475     }
476 
477     return level << 4;
478   }
479 
SetSaneH264Level(uint32_t level,uint32_t * profileLevelId)480   static void SetSaneH264Level(uint32_t level, uint32_t* profileLevelId) {
481     uint32_t profileIdc = (*profileLevelId >> 16);
482     uint32_t levelMask = 0xFF;
483 
484     if (profileIdc == 0x42 || profileIdc == 0x4d || profileIdc == 0x58) {
485       levelMask = 0x10FF;
486       if (level == 0xAB) {
487         // Level 1b
488         level = 0x100B;
489       } else {
490         // Not 1b, just shift
491         level = level >> 4;
492       }
493     } else if (level == 0xAB) {
494       // Another way to encode 1b
495       level = 0x09;
496     } else {
497       // Not 1b, just shift
498       level = level >> 4;
499     }
500 
501     *profileLevelId = (*profileLevelId & ~levelMask) | level;
502   }
503 
504   enum Subprofile {
505     kH264ConstrainedBaseline,
506     kH264Baseline,
507     kH264Main,
508     kH264Extended,
509     kH264High,
510     kH264High10,
511     kH264High42,
512     kH264High44,
513     kH264High10I,
514     kH264High42I,
515     kH264High44I,
516     kH264CALVC44,
517     kH264UnknownSubprofile
518   };
519 
GetSubprofile(uint32_t profileLevelId)520   static Subprofile GetSubprofile(uint32_t profileLevelId) {
521     // Based on Table 5 from RFC 6184:
522     //        Profile     profile_idc        profile-iop
523     //                    (hexadecimal)      (binary)
524 
525     //        CB          42 (B)             x1xx0000
526     //           same as: 4D (M)             1xxx0000
527     //           same as: 58 (E)             11xx0000
528     //        B           42 (B)             x0xx0000
529     //           same as: 58 (E)             10xx0000
530     //        M           4D (M)             0x0x0000
531     //        E           58                 00xx0000
532     //        H           64                 00000000
533     //        H10         6E                 00000000
534     //        H42         7A                 00000000
535     //        H44         F4                 00000000
536     //        H10I        6E                 00010000
537     //        H42I        7A                 00010000
538     //        H44I        F4                 00010000
539     //        C44I        2C                 00010000
540 
541     if ((profileLevelId & 0xFF4F00) == 0x424000) {
542       // 01001111 (mask, 0x4F)
543       // x1xx0000 (from table)
544       // 01000000 (expected value, 0x40)
545       return kH264ConstrainedBaseline;
546     }
547 
548     if ((profileLevelId & 0xFF8F00) == 0x4D8000) {
549       // 10001111 (mask, 0x8F)
550       // 1xxx0000 (from table)
551       // 10000000 (expected value, 0x80)
552       return kH264ConstrainedBaseline;
553     }
554 
555     if ((profileLevelId & 0xFFCF00) == 0x58C000) {
556       // 11001111 (mask, 0xCF)
557       // 11xx0000 (from table)
558       // 11000000 (expected value, 0xC0)
559       return kH264ConstrainedBaseline;
560     }
561 
562     if ((profileLevelId & 0xFF4F00) == 0x420000) {
563       // 01001111 (mask, 0x4F)
564       // x0xx0000 (from table)
565       // 00000000 (expected value)
566       return kH264Baseline;
567     }
568 
569     if ((profileLevelId & 0xFFCF00) == 0x588000) {
570       // 11001111 (mask, 0xCF)
571       // 10xx0000 (from table)
572       // 10000000 (expected value, 0x80)
573       return kH264Baseline;
574     }
575 
576     if ((profileLevelId & 0xFFAF00) == 0x4D0000) {
577       // 10101111 (mask, 0xAF)
578       // 0x0x0000 (from table)
579       // 00000000 (expected value)
580       return kH264Main;
581     }
582 
583     if ((profileLevelId & 0xFF0000) == 0x580000) {
584       // 11001111 (mask, 0xCF)
585       // 00xx0000 (from table)
586       // 00000000 (expected value)
587       return kH264Extended;
588     }
589 
590     if ((profileLevelId & 0xFFFF00) == 0x640000) {
591       return kH264High;
592     }
593 
594     if ((profileLevelId & 0xFFFF00) == 0x6E0000) {
595       return kH264High10;
596     }
597 
598     if ((profileLevelId & 0xFFFF00) == 0x7A0000) {
599       return kH264High42;
600     }
601 
602     if ((profileLevelId & 0xFFFF00) == 0xF40000) {
603       return kH264High44;
604     }
605 
606     if ((profileLevelId & 0xFFFF00) == 0x6E1000) {
607       return kH264High10I;
608     }
609 
610     if ((profileLevelId & 0xFFFF00) == 0x7A1000) {
611       return kH264High42I;
612     }
613 
614     if ((profileLevelId & 0xFFFF00) == 0xF41000) {
615       return kH264High44I;
616     }
617 
618     if ((profileLevelId & 0xFFFF00) == 0x2C1000) {
619       return kH264CALVC44;
620     }
621 
622     return kH264UnknownSubprofile;
623   }
624 
ParametersMatch(const std::string & fmt,const SdpMediaSection & remoteMsection)625   virtual bool ParametersMatch(
626       const std::string& fmt,
627       const SdpMediaSection& remoteMsection) const override {
628     if (mName == "H264") {
629       SdpFmtpAttributeList::H264Parameters h264Params(
630           GetH264Parameters(fmt, remoteMsection));
631 
632       if (h264Params.packetization_mode != mPacketizationMode) {
633         return false;
634       }
635 
636       if (GetSubprofile(h264Params.profile_level_id) !=
637           GetSubprofile(mProfileLevelId)) {
638         return false;
639       }
640     }
641 
642     return true;
643   }
644 
RtcpFbRembIsSet()645   virtual bool RtcpFbRembIsSet() const {
646     for (const auto& fb : mOtherFbTypes) {
647       if (fb.type == SdpRtcpFbAttributeList::kRemb) {
648         return true;
649       }
650     }
651     return false;
652   }
653 
UpdateRedundantEncodings(std::vector<JsepCodecDescription * > codecs)654   virtual void UpdateRedundantEncodings(
655       std::vector<JsepCodecDescription*> codecs) {
656     for (const auto codec : codecs) {
657       if (codec->mType == SdpMediaSection::kVideo && codec->mEnabled &&
658           codec->mName != "red") {
659         uint16_t pt;
660         if (!SdpHelper::GetPtAsInt(codec->mDefaultPt, &pt)) {
661           continue;
662         }
663         mRedundantEncodings.push_back(pt);
664       }
665     }
666   }
667 
668   JSEP_CODEC_CLONE(JsepVideoCodecDescription)
669 
670   std::vector<std::string> mAckFbTypes;
671   std::vector<std::string> mNackFbTypes;
672   std::vector<std::string> mCcmFbTypes;
673   std::vector<SdpRtcpFbAttributeList::Feedback> mOtherFbTypes;
674   bool mTmmbrEnabled;
675   bool mRembEnabled;
676   bool mFECEnabled;
677   uint8_t mREDPayloadType;
678   uint8_t mULPFECPayloadType;
679   std::vector<uint8_t> mRedundantEncodings;
680 
681   // H264-specific stuff
682   uint32_t mProfileLevelId;
683   uint32_t mPacketizationMode;
684   std::string mSpropParameterSets;
685 };
686 
687 class JsepApplicationCodecDescription : public JsepCodecDescription {
688   // This is the new draft-21 implementation
689  public:
690   JsepApplicationCodecDescription(const std::string& name, uint16_t channels,
691                                   uint16_t localPort,
692                                   uint32_t localMaxMessageSize,
693                                   bool enabled = true)
694       : JsepCodecDescription(mozilla::SdpMediaSection::kApplication, "", name,
695                              0, channels, enabled),
696         mLocalPort(localPort),
697         mLocalMaxMessageSize(localMaxMessageSize),
698         mRemotePort(0),
699         mRemoteMaxMessageSize(0) {}
700 
JSEP_CODEC_CLONE(JsepApplicationCodecDescription)701   JSEP_CODEC_CLONE(JsepApplicationCodecDescription)
702 
703   // Override, uses sctpport or sctpmap instead of rtpmap
704   virtual bool Matches(const std::string& fmt,
705                        const SdpMediaSection& remoteMsection) const override {
706     if (mType != remoteMsection.GetMediaType()) {
707       return false;
708     }
709 
710     int sctp_port = remoteMsection.GetSctpPort();
711     bool fmt_matches =
712         nsCRT::strcasecmp(mName.c_str(),
713                           remoteMsection.GetFormats()[0].c_str()) == 0;
714     if (sctp_port && fmt_matches) {
715       // New sctp draft 21 format
716       return true;
717     }
718 
719     const SdpSctpmapAttributeList::Sctpmap* sctp_map(
720         remoteMsection.GetSctpmap());
721     if (sctp_map) {
722       // Old sctp draft 05 format
723       return nsCRT::strcasecmp(mName.c_str(), sctp_map->name.c_str()) == 0;
724     }
725 
726     return false;
727   }
728 
AddToMediaSection(SdpMediaSection & msection)729   virtual void AddToMediaSection(SdpMediaSection& msection) const override {
730     if (mEnabled && msection.GetMediaType() == mType) {
731       if (msection.GetFormats().empty()) {
732         msection.AddDataChannel(mName, mLocalPort, mChannels,
733                                 mLocalMaxMessageSize);
734       }
735 
736       AddParametersToMSection(msection);
737     }
738   }
739 
Negotiate(const std::string & pt,const SdpMediaSection & remoteMsection)740   bool Negotiate(const std::string& pt,
741                  const SdpMediaSection& remoteMsection) override {
742     JsepCodecDescription::Negotiate(pt, remoteMsection);
743 
744     uint32_t message_size;
745     mRemoteMMSSet = remoteMsection.GetMaxMessageSize(&message_size);
746     if (mRemoteMMSSet) {
747       mRemoteMaxMessageSize = message_size;
748     } else {
749       mRemoteMaxMessageSize =
750           WEBRTC_DATACHANNEL_MAX_MESSAGE_SIZE_REMOTE_DEFAULT;
751     }
752 
753     int sctp_port = remoteMsection.GetSctpPort();
754     if (sctp_port) {
755       mRemotePort = sctp_port;
756       return true;
757     }
758 
759     const SdpSctpmapAttributeList::Sctpmap* sctp_map(
760         remoteMsection.GetSctpmap());
761     if (sctp_map) {
762       mRemotePort = std::stoi(sctp_map->pt);
763       return true;
764     }
765 
766     return false;
767   }
768 
769   uint16_t mLocalPort;
770   uint32_t mLocalMaxMessageSize;
771   uint16_t mRemotePort;
772   uint32_t mRemoteMaxMessageSize;
773   bool mRemoteMMSSet;
774 };
775 
776 }  // namespace mozilla
777 
778 #endif
779