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 |
|
|