./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 60 Content = #{name => exml_query:attr(JingleContent, <<"name">>),
71 protocol => <<"UDP/TLS/RTP/SAVPF">>,
72 senders => decode_senders(JingleContent)},
73 60 parse_content_children(Content, JingleContent#xmlel.children).
74
75 -spec parse_content_children(map(), list()) -> content().
76 parse_content_children(Content, []) ->
77 60 Content;
78 parse_content_children(Content, [Child | Rest]) ->
79 120 NewContent = parse_content_child(Child, Content),
80 120 parse_content_children(NewContent, Rest).
81
82
83 parse_content_child(#xmlel{name = <<"description">>} = DescriptionEl, Content) ->
84 60 Description = #{codecs => [],
85 rtphdr_ext => [],
86 rtcp_mux => false,
87 sources => []},
88 60 Media = exml_query:attr(DescriptionEl, <<"media">>),
89 60 NewDescription = parse_description_children(Description, DescriptionEl#xmlel.children),
90 60 Content#{description => NewDescription, media => Media};
91 parse_content_child(#xmlel{name = <<"transport">>} = TransportEl, Content) ->
92 60 NewTransport = parse_transport_element(TransportEl),
93 60 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 60 Ufrag = exml_query:attr(TransportEl, <<"ufrag">>),
100 60 Pwd = exml_query:attr(TransportEl, <<"pwd">>),
101 60 Transport = #{ufrag => Ufrag,
102 pwd => Pwd,
103 candidates => []},
104 60 parse_transport_children(Transport, TransportEl#xmlel.children).
105
106 -spec parse_description_children(map(), list()) -> description().
107 parse_description_children(Desc, []) ->
108 60 Desc;
109 parse_description_children(Desc, [Child | Rest]) ->
110 756 NewDesc = parse_description_child(Child, Desc),
111 756 parse_description_children(NewDesc, Rest).
112
113 parse_description_child(#xmlel{name = <<"payload-type">>} = Payload, Description) ->
114 540 fill_codec(Description, Payload);
115 parse_description_child(#xmlel{name = <<"rtp-hdrext">>} = RTPHdrExtEl, Description) ->
116 132 RTPHdrExts = maps:get(rtphdr_ext, Description),
117 132 RTPHdrExt = decode_rtp_hdr_ext(RTPHdrExtEl),
118 132 Description#{rtphdr_ext := [RTPHdrExt | RTPHdrExts]};
119 parse_description_child(#xmlel{name = <<"rtcp-mux">>}, Description) ->
120 51 Description#{rtcp_mux := true};
121 parse_description_child(#xmlel{name = <<"source">>} = SourceEl, Description) ->
122 33 fill_source(Description, SourceEl);
123 parse_description_child(_, Content) ->
124
:-(
Content.
125
126 fill_codec(#{codecs := Codecs} = Desc, Payload) ->
127 540 ID = exml_query:attr(Payload, <<"id">>),
128 540 Name = exml_query:attr(Payload, <<"name">>),
129 540 ClockRate = exml_query:attr(Payload, <<"clockrate">>),
130 540 Channels = exml_query:attr(Payload, <<"channels">>, <<"1">>),
131 540 Codec = #{id => ID,
132 name => Name,
133 clock_rate => ClockRate,
134 channels => Channels,
135 params => [],
136 rtcp_fb_params => []},
137 540 FinalCodec = parse_payload_children(Codec, Payload#xmlel.children),
138 540 Desc#{codecs := [FinalCodec | Codecs]}.
139
140 parse_payload_children(Codec, []) ->
141 540 Codec;
142 parse_payload_children(Codec, [Child | Rest]) ->
143 402 UpdatedCodec = parse_payload_child(Child, Codec),
144 402 parse_payload_children(UpdatedCodec, Rest).
145
146 parse_payload_child(#xmlel{name = <<"parameter">>} = BasicParam,
147 #{params := Params} = Codec) ->
148 147 Name = exml_query:attr(BasicParam, <<"name">>),
149 147 Value = exml_query:attr(BasicParam, <<"value">>),
150 147 NewParams = [{Name, Value} | Params],
151 147 Codec#{params := NewParams};
152 parse_payload_child(#xmlel{name = <<"rtcp-fb">>} = RTCPParam,
153 #{rtcp_fb_params := Params} = Codec) ->
154 255 Param = decode_rtcp_fb_param(RTCPParam),
155 255 NewParams = [Param | Params],
156 255 Codec#{rtcp_fb_params := NewParams};
157 parse_payload_child(_, Codec) ->
158
:-(
Codec.
159
160 decode_rtcp_fb_param(RTCPParam) ->
161 255 Type = exml_query:attr(RTCPParam, <<"type">>),
162 255 case exml_query:attr(RTCPParam, <<"subtype">>) of
163 undefined ->
164 159 [Type];
165 SubType ->
166 96 [Type, SubType]
167 end.
168
169 decode_rtp_hdr_ext(RTPHdrExtEl) ->
170 132 ID = exml_query:attr(RTPHdrExtEl, <<"id">>),
171 132 URI = exml_query:attr(RTPHdrExtEl, <<"uri">>),
172 132 Senders = exml_query:attr(RTPHdrExtEl, <<"senders">>, <<"both">>),
173 132 {ID, URI, sender_to_sdp_attr(Senders)}.
174
175 fill_source(#{sources := Sources} = Desc, SourceEl) ->
176 33 Params = exml_query:subelements(SourceEl, <<"parameter">>),
177 33 KV = [source_param_to_kv(Param) || Param <- Params],
178 33 ID = exml_query:attr(SourceEl, <<"ssrc">>),
179 33 Desc#{sources := [{ID, KV} | Sources]}.
180
181 source_param_to_kv(El) ->
182 63 Name = exml_query:attr(El, <<"name">>),
183 63 Value = exml_query:attr(El, <<"value">>),
184 63 {Name, Value}.
185
186 decode_senders(ContentEl) ->
187 60 SenderValue = exml_query:attr(ContentEl, <<"senders">>, <<"both">>),
188 60 sender_to_sdp_attr(SenderValue).
189
190 162 sender_to_sdp_attr(<<"both">>) -> <<"sendrecv">>;
191
:-(
sender_to_sdp_attr(<<"initiator">>) -> <<"sendonly">>;
192 21 sender_to_sdp_attr(<<"responder">>) -> <<"recvonly">>;
193 9 sender_to_sdp_attr(<<"none">>) -> <<"inactive">>.
194
195 parse_transport_children(Transport, []) ->
196 60 Transport;
197 parse_transport_children(Transport, [Child | Rest]) ->
198 60 NewTransport = parse_transport_child(Child, Transport),
199 60 parse_transport_children(NewTransport, Rest).
200
201 parse_transport_child(#xmlel{name = <<"fingerprint">>} = FingerprintEl, Transport) ->
202 60 Hash = exml_query:attr(FingerprintEl, <<"hash">>),
203 60 Setup = exml_query:attr(FingerprintEl, <<"setup">>),
204 60 Fingerprint = exml_query:cdata(FingerprintEl),
205 60 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