./ct_report/coverage/sip_to_jingle.COVER.html

1 %%==============================================================================
2 %% Copyright 2018 Erlang Solutions Ltd.
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15 %%==============================================================================
16 %%
17 %% @author Michal Piotrowski <michal.piotrowski@erlang-solutions.com>
18 -module(sip_to_jingle).
19
20
21 -include("mongoose.hrl").
22 -include("jlib.hrl").
23 -include_lib("nksip/include/nksip.hrl").
24
25 -export([parse_sdp_attributes/1]).
26 -export([sdp_media_to_content_el/2]).
27
28 parse_sdp_attributes(Attrs) ->
29 30 parse_sdp_attributes(Attrs, []).
30
31 parse_sdp_attributes([], Acc) ->
32 30 Acc;
33 parse_sdp_attributes([Attr | Rest], Acc) ->
34 30 NewAcc = parse_sdp_attribute(Attr, Acc),
35 30 parse_sdp_attributes(Rest, NewAcc).
36
37 parse_sdp_attribute({<<"group">>, [Method | Contents]}, Acc) ->
38 27 ContentEls = [sdp_group_content_to_el(Content) || Content <- Contents],
39 27 El = #xmlel{name = <<"group">>,
40 attrs = [{<<"xmlns">>, <<"urn:xmpp:jingle:apps:grouping:0">>},
41 {<<"semantics">>, Method}],
42 children = ContentEls},
43 27 [El | Acc];
44 parse_sdp_attribute(_, Acc) ->
45 3 Acc.
46
47 sdp_group_content_to_el(Content) ->
48 45 #xmlel{name = <<"content">>,
49 attrs = [{<<"name">>, Content}]}.
50
51
52 %% This function assumes that all codecs and their attributes were
53 %% extracted from the attributes list.
54 %% There are still attributes in the list describing other content parameters
55 sdp_media_to_content_el(#sdp_m{media = Media, attributes = Attrs}, CodecMap) ->
56 57 Codecs = maps:get(Media, CodecMap),
57 57 PayloadEls = [codec_to_payload(Codec) || Codec <- Codecs],
58 57 Content = parse_nksip_media_attrs(Attrs),
59 57 AdditionalDescriptionEls = description_elements_from_content(Content),
60
61 57 DescriptionEl = #xmlel{name = <<"description">>,
62 attrs = [{<<"xmlns">>, <<"urn:xmpp:jingle:apps:rtp:1">>},
63 {<<"media">>, Media}],
64 children = PayloadEls ++ AdditionalDescriptionEls},
65 57 TransportEl = make_transport_el(maps:get(transport, Content)),
66
67 57 #xmlel{name = <<"content">>,
68 attrs = [{<<"creator">>, <<"initiator">>},
69 {<<"name">>, maps:get(name, Content, Media)},
70 {<<"senders">>, maps:get(sender, Content)}],
71 children = [DescriptionEl, TransportEl]}.
72
73 codec_to_payload({ID, RTPMap, Attrs}) ->
74 450 Parameters = lists:flatmap(fun codec_attr_to_params/1, Attrs),
75 450 {Name, ClockRate, Channels} = parse_codec_rtpmap(RTPMap),
76 450 #xmlel{name = <<"payload-type">>,
77 attrs = [{<<"id">>, ID},
78 {<<"name">>, Name},
79 {<<"clockrate">>, ClockRate},
80 {<<"channels">>, Channels}],
81 children = Parameters}.
82
83 parse_codec_rtpmap(RTPMap) ->
84 450 case binary:split(RTPMap, <<$/>>, [global]) of
85 [Name, ClockRate] ->
86 423 {Name, ClockRate, <<"1">>};
87 [Name, ClockRate, Channels] ->
88 27 {Name, ClockRate, Channels}
89 end.
90
91 codec_attr_to_params({<<"rtcp-fb">>, Value}) ->
92 438 Attrs = rtcp_fb_value_to_xml_attrs(Value),
93 438 [#xmlel{name = <<"rtcp-fb">>,
94 attrs = [{<<"xmlns">>, <<"urn:xmpp:jingle:apps:rtp:rtcp-fb:0">>} | Attrs]}];
95 codec_attr_to_params({<<"fmtp">>, [Value]}) ->
96 189 Params = binary:split(Value, <<$;>>, [global]),
97 189 [basic_param_to_xml_el(Param) || Param <- Params];
98 codec_attr_to_params(_) ->
99 450 [].
100
101 rtcp_fb_value_to_xml_attrs([Type]) ->
102 264 [{<<"type">>, Type}];
103 rtcp_fb_value_to_xml_attrs([Type, SubType]) ->
104 174 [{<<"type">>, Type}, {<<"subtype">>, SubType}].
105
106 basic_param_to_xml_el(Param) ->
107 333 case binary:split(Param, <<$=>>) of
108 [Name, Value] ->
109 318 #xmlel{name = <<"parameter">>,
110 attrs = [{<<"name">>, Name},
111 {<<"value">>, Value}]};
112 [Value] ->
113 15 #xmlel{name = <<"parameter">>,
114 attrs = [{<<"value">>, Value}]}
115 end.
116
117 parse_nksip_media_attrs(Attrs) ->
118 57 parse_nksip_media_attrs(Attrs, #{}).
119
120 parse_nksip_media_attrs([], Acc) ->
121 57 Acc;
122 parse_nksip_media_attrs([{AttrName, AttrValue} | Rest], Acc) ->
123 660 NewAcc = parse_nksip_media_attr(AttrName, AttrValue, Acc),
124 660 parse_nksip_media_attrs(Rest, NewAcc).
125
126 parse_nksip_media_attr(<<"sendrecv">>, _, Acc) ->
127 30 Acc#{sender => <<"both">>};
128 parse_nksip_media_attr(<<"recvonly">>, _, Acc) ->
129 18 Acc#{sender => <<"responder">>};
130 parse_nksip_media_attr(<<"sendonly">>, _, Acc) ->
131
:-(
Acc#{sender => <<"initiator">>};
132 parse_nksip_media_attr(<<"inactive">>, _, Acc) ->
133 9 Acc#{sender => <<"none">>};
134 parse_nksip_media_attr(<<"candidate">>, Params, Acc) ->
135 93 [Foundation, Component, Protocol, Priority, IP, Port | ExtraArgs] = Params,
136 93 Candidate = #{foundation => Foundation,
137 component => Component,
138 protocol => Protocol,
139 priority => Priority,
140 ip => IP,
141 port => Port,
142 network => <<"0">>, %% no SDP equivalent
143 id => base16:encode(crypto:strong_rand_bytes(5))},
144 93 CompleteCandidate = parse_candidate_extra_args(ExtraArgs, Candidate),
145 93 Transport = maps:get(transport, Acc, #{}),
146 93 Candidates = maps:get(candidates, Transport, []),
147 93 NewTransport = Transport#{candidates => [CompleteCandidate | Candidates]},
148 93 Acc#{transport => NewTransport};
149 parse_nksip_media_attr(<<"ice-ufrag">>, [Value], Acc) ->
150 48 Transport = maps:get(transport, Acc, #{}),
151 48 Acc#{transport => Transport#{ufrag => Value}};
152 parse_nksip_media_attr(<<"ice-pwd">>, [Value], Acc) ->
153 48 Transport = maps:get(transport, Acc, #{}),
154 48 Acc#{transport => Transport#{pwd => Value}};
155 parse_nksip_media_attr(<<"fingerprint">>, [Hash, Fingerprint], Acc) ->
156 57 Transport = maps:get(transport, Acc, #{}),
157 57 Acc#{transport => Transport#{fingerprint => {Hash, Fingerprint}}};
158 parse_nksip_media_attr(<<"setup">>, [Value], Acc) ->
159 48 Transport = maps:get(transport, Acc, #{}),
160 48 Acc#{transport => Transport#{setup => Value}};
161 parse_nksip_media_attr(<<"rtcp-mux">>, _, Acc) ->
162 54 Acc#{rtcp_mux => true};
163 parse_nksip_media_attr(<<"extmap">>, [IDWithSender, URI], Acc) ->
164 144 {ID, Sender} = decode_extmap_id(IDWithSender),
165 144 RTPHdrExts = maps:get(rtphdr_ext, Acc, []),
166 144 Acc#{rtphdr_ext => [{ID, Sender, URI} | RTPHdrExts]};
167 parse_nksip_media_attr(<<"ssrc">>, [ID | Parameter], Acc) ->
168 57 SourceMap = maps:get(source_map, Acc, #{}),
169 57 SourceParams = maps:get(ID, SourceMap, []),
170 57 Param = decode_ssrc_sdp_param(Parameter),
171 57 NewSourceParams = [Param | SourceParams],
172 57 NewSourceMap = SourceMap#{ID => NewSourceParams},
173 57 Acc#{source_map => NewSourceMap};
174 parse_nksip_media_attr(<<"mid">>, [Name], Acc) ->
175 54 Acc#{name => Name};
176 parse_nksip_media_attr(_, _, Acc) ->
177
:-(
Acc.
178
179 decode_extmap_id(IDWithSender) ->
180 144 case binary:split(IDWithSender, <<$/>>) of
181 [ID] ->
182 135 {ID, <<"both">>};
183 [ID, Sender] ->
184 9 {ID, decode_sender(Sender)}
185 end.
186
187
:-(
decode_sender(<<"sendrecv">>) -> <<"both">>;
188 9 decode_sender(<<"recvonly">>) -> <<"responder">>;
189
:-(
decode_sender(<<"sendonly">>) -> <<"initiator">>;
190
:-(
decode_sender(<<"inactive">>) -> <<"none">>.
191
192 description_elements_from_content(Content) ->
193 57 RTPHdrExts = [rtphdr_ext_to_element(Ext) || Ext <- maps:get(rtphdr_ext, Content, [])],
194 57 ElsWithRtcpMux = maybe_add_rtcp_mux(Content, RTPHdrExts),
195 57 maybe_add_sources(Content, ElsWithRtcpMux).
196
197 rtphdr_ext_to_element({ID, Sender, URI}) ->
198 144 #xmlel{name = <<"rtp-hdrext">>,
199 attrs = [{<<"xmlns">>, <<"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0">>},
200 {<<"id">>, ID},
201 {<<"uri">>, URI},
202 {<<"senders">>, Sender}]}.
203
204 maybe_add_rtcp_mux(#{rtcp_mux := true}, Els) ->
205 45 El = #xmlel{name = <<"rtcp-mux">>},
206 45 [El | Els];
207 maybe_add_rtcp_mux(_, Els) ->
208 12 Els.
209
210 parse_candidate_extra_args([], Candidate) ->
211 93 Candidate;
212 parse_candidate_extra_args([<<"typ">>, Value | Rest], Candidate) ->
213 93 NewCandidate = Candidate#{type => Value},
214 93 parse_candidate_extra_args(Rest, NewCandidate);
215 parse_candidate_extra_args([<<"raddr">>, Value | Rest], Candidate) ->
216 87 NewCandidate = Candidate#{raddr => Value},
217 87 parse_candidate_extra_args(Rest, NewCandidate);
218 parse_candidate_extra_args([<<"rport">>, Value | Rest], Candidate) ->
219 87 NewCandidate = Candidate#{rport => Value},
220 87 parse_candidate_extra_args(Rest, NewCandidate);
221 parse_candidate_extra_args([<<"generation">>, Value | Rest], Candidate) ->
222
:-(
NewCandidate = Candidate#{generation => Value},
223
:-(
parse_candidate_extra_args(Rest, NewCandidate);
224 parse_candidate_extra_args([<<"tcptype">>, Value | Rest], Candidate) ->
225
:-(
NewCandidate = Candidate#{tcptype => Value},
226
:-(
parse_candidate_extra_args(Rest, NewCandidate);
227 parse_candidate_extra_args(Rest, Candidate) ->
228
:-(
?LOG_WARNING(#{what => sip_unrecognised_candidate_extra_args,
229 text => <<"Unrecognised candidate extra arg">>,
230
:-(
candidate => Candidate, extra_args => Rest}),
231
:-(
Candidate.
232
233
234 make_transport_el(Transport) ->
235 57 CandidateElements = [make_candidate_el(Candidate) || Candidate <- maps:get(candidates, Transport, [])],
236 57 AttrsWithUfrag = maybe_add_ice_ufrag(maps:get(ufrag, Transport, undefined), []),
237 57 ICEAttrs = maybe_add_ice_pwd(maps:get(pwd, Transport, undefined), AttrsWithUfrag),
238
239 57 El = #xmlel{name = <<"transport">>,
240 attrs = [{<<"xmlns">>, <<"urn:xmpp:jingle:transports:ice-udp:1">>} |
241 ICEAttrs],
242 children = CandidateElements},
243 57 maybe_add_fingerprint_el(Transport, El).
244
245 make_candidate_el(Candidate) ->
246 93 Attrs = maps:fold(fun candidate_kv_to_attr/3, [], Candidate),
247 93 #xmlel{name = <<"candidate">>,
248 attrs = Attrs}.
249
250 candidate_kv_to_attr(raddr, Value, Acc) ->
251 87 [{<<"rel-addr">>, Value} | Acc];
252 candidate_kv_to_attr(rport, Value, Acc) ->
253 87 [{<<"rel-port">>, Value} | Acc];
254 candidate_kv_to_attr(Key, Value, Acc) ->
255 837 [{erlang:atom_to_binary(Key, utf8), Value} | Acc].
256
257 maybe_add_ice_ufrag(undefined, Attrs) ->
258 9 Attrs;
259 maybe_add_ice_ufrag(Ufrag, Attrs) ->
260 48 [{<<"ufrag">>, Ufrag} | Attrs].
261
262 maybe_add_ice_pwd(undefined, Attrs) ->
263 9 Attrs;
264 maybe_add_ice_pwd(Pwd, Attrs) ->
265 48 [{<<"pwd">>, Pwd} | Attrs].
266
267
268 maybe_add_fingerprint_el(#{fingerprint := {Hash, Fingerprint}} = Transport,
269 #xmlel{children = Children} = El) ->
270 57 Attrs = [{<<"xmlns">>, <<"urn:xmpp:jingle:apps:dtls:0">>},
271 {<<"hash">>, Hash}],
272 57 AllAttrs = maybe_add_setup_attr(maps:get(setup, Transport, undefined), Attrs),
273 57 FingerprintEl = #xmlel{name = <<"fingerprint">>,
274 attrs = AllAttrs,
275 children = [#xmlcdata{content = Fingerprint}]},
276 57 El#xmlel{children = [FingerprintEl | Children]}.
277
278 maybe_add_setup_attr(undefined, Attrs) ->
279 9 Attrs;
280 maybe_add_setup_attr(Setup, Attrs) ->
281 48 [{<<"setup">>, Setup} | Attrs].
282
283 decode_ssrc_sdp_param(Parameter) ->
284 57 Bin = list_to_binary(lists:join(" ", Parameter)),
285 57 case binary:split(Bin, <<$:>>) of
286 [Attr] ->
287
:-(
Attr;
288 [Attr, Value] ->
289 57 {Attr, Value}
290 end.
291
292 maybe_add_sources(#{source_map := SourceMap}, Els) ->
293 30 SourceEls = [source_to_el(SSRC, Params) || {SSRC, Params} <- maps:to_list(SourceMap)],
294 30 Els ++ SourceEls;
295 maybe_add_sources(_, Els) ->
296 27 Els.
297
298 source_to_el(SSRC, Params) ->
299 30 Parameters = [ssrc_attr_to_el(Attr) || Attr <- Params],
300 30 #xmlel{name = <<"source">>,
301 attrs = [{<<"xmlns">>, <<"urn:xmpp:jingle:apps:rtp:ssma:0">>},
302 {<<"ssrc">>, SSRC}],
303 children = Parameters}.
304
305 ssrc_attr_to_el(Attr) ->
306 57 Attrs = make_el_attrs_from_ssrc(Attr),
307 57 #xmlel{name = <<"parameter">>,
308 attrs = Attrs}.
309
310 make_el_attrs_from_ssrc({Attr, Value}) ->
311 57 [{<<"name">>, Attr},
312 {<<"value">>, Value}];
313 make_el_attrs_from_ssrc(Attr) ->
314
:-(
[{<<"name">>, Attr}].
315
316
Line Hits Source