./ct_report/coverage/jingle_to_sdp.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(jingle_to_sdp).
19
20 %% @doc this modules translates jingle stanza to a sdp packet
21 %%
22
23 -export([from_media/1]).
24 -export([parse_transport_element/1]).
25
26 -include("mongoose.hrl").
27 -include("jlib.hrl").
28 -include_lib("nksip/include/nksip.hrl").
29
30 -type rtphdr_ext() :: {ID :: binary(), URI :: binary(), Senders :: binary()}.
31
32 -type source() :: {SSRC :: binary(), [{binary(), binary()}]}.
33
34 -type content() :: #{media := undefined | binary(),
35 name := undefined | binary(),
36 protocol := binary(),
37 description := description(),
38 transport := transport(),
39 senders := binary()}.
40
41 -type description() :: #{codecs := [codec()],
42 rtphdr_ext := [rtphdr_ext()],
43 rtcp_mux := boolean(),
44 sources := [source()]}.
45
46 -type transport() :: #{ufrag := undefined | binary(),
47 pwd := undefined | binary(),
48 fingerprint => fingerprint(),
49 candidates => candidate()}.
50
51 -type codec() :: #{id := binary(),
52 name := binary(),
53 clock_rate := binary(),
54 channels := binary(),
55 params := [param()], %% basic codec parameters.
56 rtcp_fb_params := rtcp_fb_param()}.
57
58 -type param() :: {binary(), binary()}.
59 -type rtcp_fb_param() :: [binary()].
60
61 -type fingerprint() :: {Hash :: undefined | binary(), Setup :: undefined | binary(),
62 Fingerprint :: binary()}.
63
64 -type candidate() :: any().
65
66 -export_type([content/0]).
67
68 -spec from_media(exml:element()) -> content().
69 from_media(#xmlel{name = <<"content">>} = JingleContent) ->
70 4 Content = #{name => exml_query:attr(JingleContent, <<"name">>),
71 protocol => <<"UDP/TLS/RTP/SAVPF">>,
72 senders => decode_senders(JingleContent)},
73 4 parse_content_children(Content, JingleContent#xmlel.children).
74
75 -spec parse_content_children(map(), list()) -> content().
76 parse_content_children(Content, []) ->
77 4 Content;
78 parse_content_children(Content, [Child | Rest]) ->
79 8 NewContent = parse_content_child(Child, Content),
80 8 parse_content_children(NewContent, Rest).
81
82
83 parse_content_child(#xmlel{name = <<"description">>} = DescriptionEl, Content) ->
84 4 Description = #{codecs => [],
85 rtphdr_ext => [],
86 rtcp_mux => false,
87 sources => []},
88 4 Media = exml_query:attr(DescriptionEl, <<"media">>),
89 4 NewDescription = parse_description_children(Description, DescriptionEl#xmlel.children),
90 4 Content#{description => NewDescription, media => Media};
91 parse_content_child(#xmlel{name = <<"transport">>} = TransportEl, Content) ->
92 4 NewTransport = parse_transport_element(TransportEl),
93 4 Content#{transport => NewTransport};
94 parse_content_child(_, Content) ->
95
:-(
Content.
96
97 -spec parse_transport_element(exml:element()) -> transport().
98 parse_transport_element(TransportEl) ->
99 4 Ufrag = exml_query:attr(TransportEl, <<"ufrag">>),
100 4 Pwd = exml_query:attr(TransportEl, <<"pwd">>),
101 4 Transport = #{ufrag => Ufrag,
102 pwd => Pwd,
103 candidates => []},
104 4 parse_transport_children(Transport, TransportEl#xmlel.children).
105
106 -spec parse_description_children(map(), list()) -> description().
107 parse_description_children(Desc, []) ->
108 4 Desc;
109 parse_description_children(Desc, [Child | Rest]) ->
110 36 NewDesc = parse_description_child(Child, Desc),
111 36 parse_description_children(NewDesc, Rest).
112
113 parse_description_child(#xmlel{name = <<"payload-type">>} = Payload, Description) ->
114 30 fill_codec(Description, Payload);
115 parse_description_child(#xmlel{name = <<"rtp-hdrext">>} = RTPHdrExtEl, Description) ->
116 2 RTPHdrExts = maps:get(rtphdr_ext, Description),
117 2 RTPHdrExt = decode_rtp_hdr_ext(RTPHdrExtEl),
118 2 Description#{rtphdr_ext := [RTPHdrExt | RTPHdrExts]};
119 parse_description_child(#xmlel{name = <<"rtcp-mux">>}, Description) ->
120 2 Description#{rtcp_mux := true};
121 parse_description_child(#xmlel{name = <<"source">>} = SourceEl, Description) ->
122 2 fill_source(Description, SourceEl);
123 parse_description_child(_, Content) ->
124
:-(
Content.
125
126 fill_codec(#{codecs := Codecs} = Desc, Payload) ->
127 30 ID = exml_query:attr(Payload, <<"id">>),
128 30 Name = exml_query:attr(Payload, <<"name">>),
129 30 ClockRate = exml_query:attr(Payload, <<"clockrate">>),
130 30 Channels = exml_query:attr(Payload, <<"channels">>, <<"1">>),
131 30 Codec = #{id => ID,
132 name => Name,
133 clock_rate => ClockRate,
134 channels => Channels,
135 params => [],
136 rtcp_fb_params => []},
137 30 FinalCodec = parse_payload_children(Codec, Payload#xmlel.children),
138 30 Desc#{codecs := [FinalCodec | Codecs]}.
139
140 parse_payload_children(Codec, []) ->
141 30 Codec;
142 parse_payload_children(Codec, [Child | Rest]) ->
143 6 UpdatedCodec = parse_payload_child(Child, Codec),
144 6 parse_payload_children(UpdatedCodec, Rest).
145
146 parse_payload_child(#xmlel{name = <<"parameter">>} = BasicParam,
147 #{params := Params} = Codec) ->
148 4 Name = exml_query:attr(BasicParam, <<"name">>),
149 4 Value = exml_query:attr(BasicParam, <<"value">>),
150 4 NewParams = [{Name, Value} | Params],
151 4 Codec#{params := NewParams};
152 parse_payload_child(#xmlel{name = <<"rtcp-fb">>} = RTCPParam,
153 #{rtcp_fb_params := Params} = Codec) ->
154 2 Param = decode_rtcp_fb_param(RTCPParam),
155 2 NewParams = [Param | Params],
156 2 Codec#{rtcp_fb_params := NewParams};
157 parse_payload_child(_, Codec) ->
158
:-(
Codec.
159
160 decode_rtcp_fb_param(RTCPParam) ->
161 2 Type = exml_query:attr(RTCPParam, <<"type">>),
162 2 case exml_query:attr(RTCPParam, <<"subtype">>) of
163 undefined ->
164 2 [Type];
165 SubType ->
166
:-(
[Type, SubType]
167 end.
168
169 decode_rtp_hdr_ext(RTPHdrExtEl) ->
170 2 ID = exml_query:attr(RTPHdrExtEl, <<"id">>),
171 2 URI = exml_query:attr(RTPHdrExtEl, <<"uri">>),
172 2 Senders = exml_query:attr(RTPHdrExtEl, <<"senders">>, <<"both">>),
173 2 {ID, URI, sender_to_sdp_attr(Senders)}.
174
175 fill_source(#{sources := Sources} = Desc, SourceEl) ->
176 2 Params = exml_query:subelements(SourceEl, <<"parameter">>),
177 2 KV = [source_param_to_kv(Param) || Param <- Params],
178 2 ID = exml_query:attr(SourceEl, <<"ssrc">>),
179 2 Desc#{sources := [{ID, KV} | Sources]}.
180
181 source_param_to_kv(El) ->
182 4 Name = exml_query:attr(El, <<"name">>),
183 4 Value = exml_query:attr(El, <<"value">>),
184 4 {Name, Value}.
185
186 decode_senders(ContentEl) ->
187 4 SenderValue = exml_query:attr(ContentEl, <<"senders">>, <<"both">>),
188 4 sender_to_sdp_attr(SenderValue).
189
190 4 sender_to_sdp_attr(<<"both">>) -> <<"sendrecv">>;
191
:-(
sender_to_sdp_attr(<<"initiator">>) -> <<"sendonly">>;
192
:-(
sender_to_sdp_attr(<<"responder">>) -> <<"recvonly">>;
193 2 sender_to_sdp_attr(<<"none">>) -> <<"inactive">>.
194
195 parse_transport_children(Transport, []) ->
196 4 Transport;
197 parse_transport_children(Transport, [Child | Rest]) ->
198 4 NewTransport = parse_transport_child(Child, Transport),
199 4 parse_transport_children(NewTransport, Rest).
200
201 parse_transport_child(#xmlel{name = <<"fingerprint">>} = FingerprintEl, Transport) ->
202 4 Hash = exml_query:attr(FingerprintEl, <<"hash">>),
203 4 Setup = exml_query:attr(FingerprintEl, <<"setup">>),
204 4 Fingerprint = exml_query:cdata(FingerprintEl),
205 4 Transport#{fingerprint => {Hash, Setup, Fingerprint}};
206 parse_transport_child(#xmlel{name = <<"candidate">>, attrs = Attrs},
207 #{candidates := Candidates} = Transport) ->
208
:-(
NewCandidate = lists:foldl(fun parse_candidate_attr/2, #{}, Attrs),
209
:-(
Transport#{candidates := [NewCandidate | Candidates]};
210 parse_transport_child(_, Transport) ->
211
:-(
Transport.
212
213 -spec parse_candidate_attr({binary(), any()}, map()) -> map().
214 parse_candidate_attr({<<"foundation">>, Val}, Map) ->
215
:-(
Map#{foundation => Val};
216 parse_candidate_attr({<<"component">>, Val}, Map) ->
217
:-(
Map#{component => Val};
218 parse_candidate_attr({<<"generation">>, Val}, Map) ->
219
:-(
Map#{generation => Val};
220 parse_candidate_attr({<<"id">>, Val}, Map) ->
221
:-(
Map#{id => Val};
222 parse_candidate_attr({<<"ip">>, Val}, Map) ->
223
:-(
Map#{ip => Val};
224 parse_candidate_attr({<<"network">>, Val}, Map) ->
225
:-(
Map#{network => Val};
226 parse_candidate_attr({<<"port">>, Val}, Map) ->
227
:-(
Map#{port => Val};
228 parse_candidate_attr({<<"priority">>, Val}, Map) ->
229
:-(
Map#{priority => Val};
230 parse_candidate_attr({<<"protocol">>, Val}, Map) ->
231
:-(
Map#{protocol => Val};
232 parse_candidate_attr({<<"rel-addr">>, Val}, Map) ->
233
:-(
Map#{raddr => Val};
234 parse_candidate_attr({<<"rel-port">>, Val}, Map) ->
235
:-(
Map#{rport => Val};
236 parse_candidate_attr({<<"tcptype">>, Val}, Map) ->
237
:-(
Map#{tcptype => Val};
238 parse_candidate_attr({<<"type">>, Val}, Map) ->
239
:-(
Map#{type => Val};
240 parse_candidate_attr(_, Map) ->
241
:-(
Map.
Line Hits Source