1 /*
2  * Jicofo, the Jitsi Conference Focus.
3  *
4  * Copyright @ 2015 Atlassian Pty Ltd
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.jitsi.jicofo.util;
19 
20 import org.jitsi.xmpp.extensions.jingle.*;
21 
22 import org.jitsi.service.configuration.*;
23 import org.jitsi.service.neomedia.*;
24 import org.jitsi.service.neomedia.codec.*;
25 import org.jitsi.utils.*;
26 
27 import java.net.*;
28 import java.util.*;
29 
30 /**
31  * Contains factory methods for creating Jingle offer sent in 'session-invite'
32  * by Jitsi Meet conference focus.
33  *
34  * @author Pawel Domas
35  * @author George Politis
36  * @author Boris Grozev
37  * @author Lyubomir Marinov
38  */
39 public class JingleOfferFactory
40 {
41     /**
42      * The property name of the VP8 payload type to include in the Jingle
43      * session-invite.
44      */
45     public static final String VP8_PT_PNAME = "org.jitsi.jicofo.VP8_PT";
46 
47     /**
48      * The name of the property which enables VP8.
49      */
50     public static final String ENABLE_VP8_PNAME
51             = "org.jitsi.jicofo.ENABLE_VP8";
52 
53     /**
54      * The property name of the VP8 RTX payload type to include in the Jingle
55      * session-invite.
56      */
57     public static final String VP8_RTX_PT_PNAME = "org.jitsi.jicofo.VP8_RTX_PT";
58 
59     /**
60      * The property name of the VP9 payload type to include in the Jingle
61      * session-invite.
62      */
63     public static final String VP9_PT_PNAME = "org.jitsi.jicofo.VP9_PT";
64 
65     /**
66      * The name of the property which enables VP9.
67      */
68     public static final String ENABLE_VP9_PNAME
69             = "org.jitsi.jicofo.ENABLE_VP9";
70 
71     /**
72      * The property name of the VP9 RTX payload type to include in the Jingle
73      * session-invite.
74      */
75     public static final String VP9_RTX_PT_PNAME
76         = "org.jitsi.jicofo.VP9_RTX_PT";
77 
78     /**
79      * The property name of the H264 payload type to include in the Jingle
80      * session-invite.
81      */
82     public static final String H264_PT_PNAME = "org.jitsi.jicofo.H264_PT";
83 
84     /**
85      * The name of the property which enables H264.
86      */
87     public static final String ENABLE_H264_PNAME
88             = "org.jitsi.jicofo.ENABLE_H264";
89 
90     /**
91      * The property name of the H264 RTX payload type to include in the Jingle
92      * session-invite.
93      */
94     public static final String H264_RTX_PT_PNAME
95         = "org.jitsi.jicofo.H264_RTX_PT";
96 
97     /**
98      * The name of the property which enables the inclusion of the
99      * framemarking RTP header extension in the offer.
100      */
101     public static final String ENABLE_FRAMEMARKING_PNAME
102         = "org.jitsi.jicofo.ENABLE_FRAMEMARKING";
103 
104     /**
105      * The name of the property which enables the inclusion of the AST RTP
106      * header extension in the offer.
107      */
108     public static final String ENABLE_AST_PNAME = "org.jitsi.jicofo.ENABLE_AST";
109 
110     /**
111      * The name of the property which enables the inclusion of the TOF RTP
112      * header extension in the offer.
113      */
114     public static final String ENABLE_TOF_PNAME = "org.jitsi.jicofo.ENABLE_TOF";
115 
116     /**
117      * The name of the property which enables the inclusion of the video content
118      * type RTP header extension.
119      */
120     public static final String ENABLE_VIDEO_CONTENT_TYPE_PNAME
121         = "org.jitsi.jicofo.ENABLE_VIDEO_CONTENT_TYPE";
122 
123     /**
124      * The name of the property which enables the inclusion of the RID RTP
125      * header extension.
126      */
127     public static final String ENABLE_RID_PNAME = "org.jitsi.jicofo.ENABLE_RID";
128 
129     /**
130      * The ID of the transport-cc header extension.
131      */
132     private static final String TRANSPORT_CC_ID = "5";
133 
134     /**
135      * The VP8 payload type to include in the Jingle session-invite.
136      */
137     private final int VP8_PT;
138 
139     /**
140      * Whether VP8 is enabled.
141      */
142     private final boolean ENABLE_VP8;
143 
144     /**
145      * The VP8 RTX payload type to include in the Jingle session-invite.
146      */
147     private final int VP8_RTX_PT;
148 
149     /**
150      * The VP9 payload type to include in the Jingle session-invite.
151      */
152     private final int VP9_PT;
153 
154     /**
155      * Whether VP9 is enabled.
156      */
157     private final boolean ENABLE_VP9;
158 
159     /**
160      * The VP9 RTX payload type to include in the Jingle session-invite.
161      */
162     private final int VP9_RTX_PT;
163 
164     /**
165      * The H264 payload type to include in the Jingle session-invite.
166      */
167     private final int H264_PT;
168 
169     /**
170      * Whether H264 is enabled.
171      */
172     private final boolean ENABLE_H264;
173 
174     /**
175      * The H264 RTX payload type to include in the Jingle session-invite.
176      */
177     private final int H264_RTX_PT;
178 
179     /**
180      * Whether to enable the framemarking RTP header extension in created
181      * offers.
182      */
183     private final boolean enableFrameMarking;
184 
185     /**
186      * Whether to enable the AST RTP header extension in created offers.
187      */
188     private final boolean enableAst;
189 
190     /**
191      * Whether to enable the TOF RTP header extension in created offers.
192      */
193     private final boolean enableTof;
194 
195     /**
196      * Whether to enable the video content type header extension in created
197      * offers.
198      */
199     private final boolean enableVideoContentType;
200 
201     /**
202      * Whether to enable the RID header extension in created offers.
203      */
204     private final boolean enableRid;
205 
206     /**
207      * Ctor.
208      *
209      * @param cfg the {@link ConfigurationService} to pull config options from.
210      */
JingleOfferFactory(ConfigurationService cfg)211     public JingleOfferFactory(ConfigurationService cfg)
212     {
213         VP8_PT = cfg != null ? cfg.getInt(VP8_PT_PNAME, 100) : 100;
214         ENABLE_VP8 = cfg != null ? cfg.getBoolean(ENABLE_VP8_PNAME, true) : true;
215         VP8_RTX_PT = cfg != null ? cfg.getInt(VP8_RTX_PT_PNAME, 96) : 96;
216         VP9_PT = cfg != null ? cfg.getInt(VP9_PT_PNAME, 101) : 101;
217         ENABLE_VP9 = cfg != null ? cfg.getBoolean(ENABLE_VP9_PNAME, true) : true;
218         VP9_RTX_PT = cfg != null ? cfg.getInt(VP9_RTX_PT_PNAME, 97) : 97;
219         H264_PT = cfg != null ? cfg.getInt(H264_PT_PNAME, 107) : 107;
220         ENABLE_H264 = cfg != null ? cfg.getBoolean(ENABLE_H264_PNAME, true) : true;
221         H264_RTX_PT = cfg != null ? cfg.getInt(H264_RTX_PT_PNAME, 99) : 99;
222         enableFrameMarking
223             = cfg != null && cfg.getBoolean(ENABLE_FRAMEMARKING_PNAME, false);
224 
225         enableAst = cfg != null && cfg.getBoolean(ENABLE_AST_PNAME, true);
226 
227         // TOF is currently disabled, because we don't support it in the bridge
228         // (and currently clients seem to not use it when abs-send-time is
229         // available).
230         enableTof = cfg != null && cfg.getBoolean(ENABLE_TOF_PNAME, false);
231 
232         enableVideoContentType = cfg != null
233             && cfg.getBoolean(ENABLE_VIDEO_CONTENT_TYPE_PNAME, false);
234 
235         enableRid = cfg != null && cfg.getBoolean(ENABLE_RID_PNAME, false);
236     }
237 
238     /**
239      * Creates a {@link ContentPacketExtension} for the audio media type that
240      * will be included in initial conference offer.
241      *
242      * @param disableIce pass <tt>true</tt> if RAW transport instead of ICE
243      * should be indicated in the offer.
244      * @param useDtls whether to add a DTLS element under the transport
245      * elements in the offer.
246      *
247      * @return <tt>ContentPacketExtension</tt> for given media type that will be
248      *         used in initial conference offer.
249      */
createAudioContent( boolean disableIce, boolean useDtls, boolean stereo, boolean enableRemb, boolean enableTcc)250     public ContentPacketExtension createAudioContent(
251         boolean disableIce, boolean useDtls, boolean stereo,
252         boolean enableRemb, boolean enableTcc)
253     {
254         ContentPacketExtension content
255             = createContentPacketExtension(
256                     MediaType.AUDIO, disableIce, useDtls);
257 
258         addAudioToContent(content, stereo, enableRemb, enableTcc);
259 
260         return content;
261     }
262 
263     /**
264      * Creates a {@link ContentPacketExtension} for the data media type that
265      * will be included in initial conference offer.
266      *
267      * @param disableIce pass <tt>true</tt> if RAW transport instead of ICE
268      * should be indicated in the offer.
269      * @param useDtls whether to add a DTLS element under the transport
270      * elements in the offer.
271      *
272      * @return <tt>ContentPacketExtension</tt> for given media type that will be
273      *         used in initial conference offer.
274      */
createDataContent( boolean disableIce, boolean useDtls)275     public ContentPacketExtension createDataContent(
276         boolean disableIce, boolean useDtls)
277     {
278         ContentPacketExtension content
279             = createContentPacketExtension(
280                     MediaType.DATA, disableIce, useDtls);
281 
282         addDataToContent(content);
283 
284         return content;
285     }
286 
287     /**
288      * Creates a {@link ContentPacketExtension} for the video media type that
289      * will be included in initial conference offer.
290      *
291      * @param disableIce pass <tt>true</tt> if RAW transport instead of ICE
292      * should be indicated in the offer.
293      * @param useDtls whether to add a DTLS element under the transport
294      * elements in the offer.
295      * @param useRtx whether RTX should be included in the offer.
296      * @param minBitrate the value to set to the "x-google-min-bitrate" fmtp
297      * line for video, or -1 to not add such a line.
298      * @param startBitrate the value to set to the "x-google-start-bitrate" fmtp
299      * line for video, or -1 to not add such a line.
300      *
301      * @return <tt>ContentPacketExtension</tt> for given media type that will be
302      *         used in initial conference offer.
303      */
createVideoContent( boolean disableIce, boolean useDtls, boolean useRtx, boolean enableRemb, boolean enableTcc, int minBitrate, int startBitrate)304     public ContentPacketExtension createVideoContent(
305             boolean disableIce, boolean useDtls, boolean useRtx,
306             boolean enableRemb, boolean enableTcc,
307             int minBitrate, int startBitrate)
308     {
309         ContentPacketExtension videoContentPe
310             = createContentPacketExtension(
311                     MediaType.VIDEO, disableIce, useDtls);
312 
313         addVideoToContent(videoContentPe, useRtx, enableRemb, enableTcc,
314                 minBitrate, startBitrate);
315 
316         return videoContentPe;
317     }
318 
319     /**
320      * Creates <tt>ContentPacketExtension</tt> initialized with type of
321      * the media and basic transport information based on given parameters.
322      * The creator attribute is set to "initiator" and "senders" to "both".
323      *
324      * @param mediaType the <tt>MediaType</tt> for the content
325      * @param disableIce <tt>true</tt> if ICE transport should be disabled
326      * @param useDtls <tt>true</tt> if DTLS should be used on top of ICE
327      * transport(will have effect only if <tt>disableIce</tt></tt> is
328      * <tt>false</tt>)
329      *
330      * @return new, parametrized instance of <tt>ContentPacketExtension</tt>.
331      */
createContentPacketExtension( MediaType mediaType, boolean disableIce, boolean useDtls)332     private static ContentPacketExtension createContentPacketExtension(
333             MediaType mediaType, boolean disableIce, boolean useDtls)
334     {
335         ContentPacketExtension content
336             = new ContentPacketExtension(
337                     ContentPacketExtension.CreatorEnum.initiator,
338                     mediaType.name().toLowerCase());
339 
340         content.setSenders(ContentPacketExtension.SendersEnum.both);
341 
342         if (!disableIce)
343         {
344             IceUdpTransportPacketExtension iceUdpTransportPacketExtension
345                 = new IceUdpTransportPacketExtension();
346 
347             if (useDtls)
348             {
349                 iceUdpTransportPacketExtension
350                     .addChildExtension(new DtlsFingerprintPacketExtension());
351             }
352 
353             content.addChildExtension(iceUdpTransportPacketExtension);
354         }
355         else
356         {
357             content.addChildExtension(new RawUdpTransportPacketExtension());
358         }
359 
360         return content;
361     }
362 
363     /**
364      * Adds the audio-related extensions for an offer to a
365      * {@link ContentPacketExtension}.
366      * @param content the {@link ContentPacketExtension} to add extensions to.
367      */
addVideoToContent(ContentPacketExtension content, boolean useRtx, boolean enableRemb, boolean enableTcc, int minBitrate, int startBitrate)368     private void addVideoToContent(ContentPacketExtension content,
369                                           boolean useRtx,
370                                           boolean enableRemb,
371                                           boolean enableTcc,
372                                           int minBitrate,
373                                           int startBitrate)
374     {
375         RtpDescriptionPacketExtension rtpDesc
376             = new RtpDescriptionPacketExtension();
377 
378         rtpDesc.setMedia("video");
379 
380         if (enableTof)
381         {
382             // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
383             RTPHdrExtPacketExtension toOffset = new RTPHdrExtPacketExtension();
384             toOffset.setID("2");
385             toOffset.setURI(URI.create(RTPExtension.TOF_URN));
386             rtpDesc.addExtmap(toOffset);
387         }
388 
389         if (enableAst)
390         {
391             // a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
392             RTPHdrExtPacketExtension absSendTime
393                 = new RTPHdrExtPacketExtension();
394             absSendTime.setID("3");
395             absSendTime.setURI(URI.create(RTPExtension.ABS_SEND_TIME_URN));
396             rtpDesc.addExtmap(absSendTime);
397         }
398 
399         if (enableFrameMarking)
400         {
401             // a=extmap:9 urn:ietf:params:rtp-hdrext:framemarking
402             RTPHdrExtPacketExtension framemarking
403                 = new RTPHdrExtPacketExtension();
404             framemarking.setID("9");
405             framemarking.setURI(URI.create(RTPExtension.FRAME_MARKING_URN));
406             rtpDesc.addExtmap(framemarking);
407         }
408 
409         if (enableVideoContentType)
410         {
411             // http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
412             RTPHdrExtPacketExtension videoContentType
413                 = new RTPHdrExtPacketExtension();
414             videoContentType.setID("7");
415             videoContentType.setURI(URI.create(RTPExtension.VIDEO_CONTENT_TYPE_URN));
416             rtpDesc.addExtmap(videoContentType);
417         }
418 
419         if (enableRid)
420         {
421             RTPHdrExtPacketExtension rtpStreamId = new RTPHdrExtPacketExtension();
422             rtpStreamId.setID("4");
423             rtpStreamId.setURI(URI.create(RTPExtension.RTP_STREAM_ID_URN));
424             rtpDesc.addExtmap(rtpStreamId);
425         }
426 
427         if (ENABLE_VP8 && VP8_PT > 0)
428         {
429             // a=rtpmap:XXX VP8/90000
430             PayloadTypePacketExtension vp8
431                     = addPayloadTypeExtension(rtpDesc, VP8_PT, Constants.VP8, 90000);
432 
433             addExtensionsToVideoPayloadType(
434                     vp8, minBitrate, startBitrate, enableRemb, enableTcc);
435         }
436 
437         if (ENABLE_H264 && H264_PT > 0)
438         {
439             // a=rtpmap:XXX H264/90000
440             PayloadTypePacketExtension h264 = addPayloadTypeExtension(
441                     // XXX(gp): older Chrome versions (users have reported 53/55/61)
442                     // fail to enable h264, if the encoding name is in lower case.
443                     rtpDesc, H264_PT, Constants.H264.toUpperCase(), 90000);
444 
445             addExtensionsToVideoPayloadType(
446                     h264, minBitrate, startBitrate, enableRemb, enableTcc);
447             addParameterExtension(
448                 h264,
449                 "profile-level-id",
450                 "42e01f;level-asymmetry-allowed=1;packetization-mode=1;");
451         }
452 
453         if (ENABLE_VP9 && VP9_PT > 0)
454         {
455             // a=rtpmap:XXX VP9/90000
456             PayloadTypePacketExtension vp9
457                     = addPayloadTypeExtension(rtpDesc, VP9_PT, Constants.VP9, 90000);
458 
459             addExtensionsToVideoPayloadType(
460                     vp9, minBitrate, startBitrate, enableRemb, enableTcc);
461         }
462 
463 
464         if (enableTcc)
465         {
466             // a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
467             RTPHdrExtPacketExtension tcc = new RTPHdrExtPacketExtension();
468             tcc.setID(TRANSPORT_CC_ID);
469             tcc.setURI(URI.create(RTPExtension.TRANSPORT_CC_URN));
470             rtpDesc.addExtmap(tcc);
471         }
472 
473 
474         if (useRtx)
475         {
476             if (ENABLE_VP8 && VP8_RTX_PT > 0 && VP8_PT > 0)
477             {
478                 // a=rtpmap:96 rtx/90000
479                 PayloadTypePacketExtension rtx = addPayloadTypeExtension(
480                         rtpDesc, VP8_RTX_PT, Constants.RTX, 90000);
481 
482                 // a=fmtp:96 apt=100
483                 addParameterExtension(rtx, "apt", String.valueOf(VP8_PT));
484 
485                 // Chrome doesn't have these when it creates an offer, but they were
486                 // observed in a hangouts conference. Not sure whether they have any
487                 // effect.
488                 // a=rtcp-fb:96 ccm fir
489                 rtx.addRtcpFeedbackType(createRtcpFbPacketExtension("ccm", "fir"));
490 
491                 // a=rtcp-fb:96 nack
492                 rtx.addRtcpFeedbackType(createRtcpFbPacketExtension("nack", null));
493 
494                 // a=rtcp-fb:96 nack pli
495                 rtx.addRtcpFeedbackType(createRtcpFbPacketExtension("nack", "pli"));
496             }
497 
498             if (ENABLE_VP9 && VP9_RTX_PT > 0 && VP9_PT > 0)
499             {
500                 // a=rtpmap:97 rtx/90000
501                 PayloadTypePacketExtension rtxVP9 = addPayloadTypeExtension(
502                         rtpDesc, VP9_RTX_PT, Constants.RTX, 90000);
503 
504                 // a=fmtp:97 apt=101
505                 addParameterExtension(rtxVP9, "apt", String.valueOf(VP9_PT));
506             }
507 
508             if (ENABLE_H264 && H264_RTX_PT > 0 && H264_PT > 0)
509             {
510                 // a=rtpmap:99 rtx/90000
511                 PayloadTypePacketExtension rtxH264 = addPayloadTypeExtension(
512                         rtpDesc, H264_RTX_PT, Constants.RTX, 90000);
513 
514                 // a=fmtp:99 apt=107
515                 addParameterExtension(rtxH264, "apt", String.valueOf(H264_PT));
516             }
517         }
518 
519         // a=rtpmap:116 red/90000
520         //addPayloadTypeExtension(rtpDesc, 116, Constants.RED, 90000);
521 
522         // a=rtpmap:117 ulpfec/90000
523         //addPayloadTypeExtension(rtpDesc, 117, Constants.ULPFEC, 90000);
524 
525         content.addChildExtension(rtpDesc);
526     }
527 
528     /**
529      * Adds a {@link ParameterPacketExtension} with a given name and value
530      * to a given {@link PayloadTypePacketExtension}.
531      * @param ptExt the extension to add to.
532      * @param name the name of the parameter to add.
533      * @param value the value of the parameter to add.
534      * @return the added extension.
535      */
addParameterExtension( PayloadTypePacketExtension ptExt, String name, String value)536     private static ParameterPacketExtension addParameterExtension(
537         PayloadTypePacketExtension ptExt,
538                                               String name, String value)
539     {
540         ParameterPacketExtension parameterPacketExtension
541             = new ParameterPacketExtension();
542         parameterPacketExtension.setName(name);
543         parameterPacketExtension.setValue(value);
544         ptExt.addParameter(parameterPacketExtension);
545 
546         return parameterPacketExtension;
547     }
548 
addExtensionsToVideoPayloadType( PayloadTypePacketExtension pt, int minBitrate, int startBitrate, boolean enableRemb, boolean enableTcc)549     private static void addExtensionsToVideoPayloadType(
550             PayloadTypePacketExtension pt,
551             int minBitrate,
552             int startBitrate,
553             boolean enableRemb,
554             boolean enableTcc)
555     {
556         // a=rtcp-fb:XXX ccm fir
557         pt.addRtcpFeedbackType(createRtcpFbPacketExtension("ccm", "fir"));
558 
559         // a=rtcp-fb:XXX nack
560         pt.addRtcpFeedbackType(createRtcpFbPacketExtension("nack", null));
561 
562         // a=rtcp-fb:XXX nack pli
563         pt.addRtcpFeedbackType(createRtcpFbPacketExtension("nack", "pli"));
564 
565 
566         if (minBitrate != -1)
567         {
568             addParameterExtension(
569                     pt, "x-google-min-bitrate", String.valueOf(minBitrate));
570         }
571 
572         if (startBitrate != -1)
573         {
574             addParameterExtension(
575                     pt, "x-google-start-bitrate", String.valueOf(startBitrate));
576         }
577 
578         if (enableRemb)
579         {
580             // a=rtcp-fb:XXX goog-remb
581             pt.addRtcpFeedbackType(
582                     createRtcpFbPacketExtension("goog-remb", null));
583         }
584 
585         if (enableTcc)
586         {
587             // a=rtcp-fb:XXX transport-cc
588             pt.addRtcpFeedbackType(
589                     createRtcpFbPacketExtension("transport-cc", null));
590         }
591 
592     }
593 
594     /**
595      * Creates an {@link RtcpFbPacketExtension} with the given type and subtype.
596      * @return the created extension.
597      */
createRtcpFbPacketExtension( String type, String subtype)598     private static RtcpFbPacketExtension createRtcpFbPacketExtension(
599         String type, String subtype)
600     {
601         RtcpFbPacketExtension rtcpFb = new RtcpFbPacketExtension();
602         if (type != null)
603         {
604             rtcpFb.setFeedbackType(type);
605         }
606         if (subtype != null)
607         {
608             rtcpFb.setFeedbackSubtype(subtype);
609         }
610 
611         return rtcpFb;
612     }
613 
614     /**
615      * Adds a {@link PayloadTypePacketExtension} to a
616      * {@link RtpDescriptionPacketExtension}.
617      * @param rtpDesc the {@link RtpDescriptionPacketExtension} to add to.
618      * @param id the ID of the {@link PayloadTypePacketExtension}.
619      * @param name the name of the {@link PayloadTypePacketExtension}.
620      * @param clockRate the clock rate of the {@link PayloadTypePacketExtension}.
621      * @return the added {@link PayloadTypePacketExtension}.
622      */
addPayloadTypeExtension( RtpDescriptionPacketExtension rtpDesc, int id, String name, int clockRate)623     private static PayloadTypePacketExtension addPayloadTypeExtension(
624         RtpDescriptionPacketExtension rtpDesc, int id, String name,
625         int clockRate)
626     {
627         PayloadTypePacketExtension payloadTypePacketExtension
628             = new PayloadTypePacketExtension();
629         payloadTypePacketExtension.setId(id);
630         payloadTypePacketExtension.setName(name);
631         payloadTypePacketExtension.setClockrate(clockRate);
632 
633         rtpDesc.addPayloadType(payloadTypePacketExtension);
634         return payloadTypePacketExtension;
635     }
636 
637     /**
638      * Adds the video-related extensions for an offer to a
639      * {@link ContentPacketExtension}.
640      * @param content the {@link ContentPacketExtension} to add extensions to.
641      * @param stereo Whether to enable stereo for opus.
642      * @param enableRemb Whether to enable REMB.
643      * @param enableTcc Whether to enable transport-cc.
644      */
addAudioToContent(ContentPacketExtension content, boolean stereo, boolean enableRemb, boolean enableTcc)645     private static void addAudioToContent(ContentPacketExtension content,
646                                           boolean stereo, boolean enableRemb,
647                                           boolean enableTcc)
648     {
649         RtpDescriptionPacketExtension rtpDesc
650             = new RtpDescriptionPacketExtension();
651 
652         rtpDesc.setMedia("audio");
653 
654         RTPHdrExtPacketExtension ssrcAudioLevel
655             = new RTPHdrExtPacketExtension();
656         ssrcAudioLevel.setID("1");
657         ssrcAudioLevel.setURI(URI.create(RTPExtension.SSRC_AUDIO_LEVEL_URN));
658         rtpDesc.addExtmap(ssrcAudioLevel);
659 
660         // a=rtpmap:111 opus/48000/2
661         PayloadTypePacketExtension opus
662             = addPayloadTypeExtension(rtpDesc, 111, Constants.OPUS, 48000);
663         opus.setChannels(2);
664 
665         // fmtp:111 minptime=10
666         addParameterExtension(opus, "minptime", "10");
667 
668         if (stereo)
669         {
670             // fmtp: 111 stereo=1
671             addParameterExtension(opus, "stereo", "1");
672         }
673 
674         // fmtp:111 useinbandfec=1
675         addParameterExtension(opus, "useinbandfec", "1");
676 
677         if (enableTcc)
678         {
679             // a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
680             RTPHdrExtPacketExtension tcc = new RTPHdrExtPacketExtension();
681             tcc.setID(TRANSPORT_CC_ID);
682             tcc.setURI(URI.create(RTPExtension.TRANSPORT_CC_URN));
683             rtpDesc.addExtmap(tcc);
684 
685             // a=rtcp-fb:111 transport-cc
686             opus.addRtcpFeedbackType(
687                 createRtcpFbPacketExtension("transport-cc", null));
688         }
689 
690         // a=rtpmap:103 ISAC/16000
691         addPayloadTypeExtension(rtpDesc, 103, "ISAC", 16000);
692 
693         // a=rtpmap:104 ISAC/32000
694         addPayloadTypeExtension(rtpDesc, 104, "ISAC", 32000);
695 
696         // rtpmap:126 telephone-event/8000
697         addPayloadTypeExtension(rtpDesc, 126, Constants.TELEPHONE_EVENT, 8000);
698 
699         // a=maxptime:60
700         rtpDesc.setAttribute("maxptime", "60");
701         content.addChildExtension(rtpDesc);
702     }
703 
704     /**
705      * Adds the data-related extensions for an offer to a
706      * {@link ContentPacketExtension}.
707      * @param content the {@link ContentPacketExtension} to add extensions to.
708      */
addDataToContent(ContentPacketExtension content)709     private static void addDataToContent(ContentPacketExtension content)
710     {
711         //SctpMapExtension sctpMap = new SctpMapExtension();
712         //sctpMap.setPort(5000);
713         //sctpMap.setProtocol(SctpMapExtension.Protocol.WEBRTC_CHANNEL);
714         //sctpMap.setStreams(1024);
715         //content.addChildExtension(sctpMap);
716 
717         RtpDescriptionPacketExtension rdpe
718             = new RtpDescriptionPacketExtension();
719         rdpe.setMedia("application");
720 
721         content.addChildExtension(rdpe);
722     }
723 
724     /**
725      * Check if given offer contains video contents.
726      * @param contents the list of <tt>ContentPacketExtension</tt> describing
727      *        Jingle offer.
728      * @return <tt>true</tt> if given offer has video content.
729      */
containsVideoContent( List<ContentPacketExtension> contents)730     public static boolean containsVideoContent(
731         List<ContentPacketExtension> contents)
732     {
733         for (ContentPacketExtension content : contents)
734         {
735             if (content.getName().equalsIgnoreCase("video"))
736             {
737                 return true;
738             }
739         }
740         return false;
741     }
742 }
743