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