1 /*
2 * Copyright 2008-2014 Arsen Chaloyan
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 * $Id: mrcp_sdp.c 2136 2014-07-04 06:33:36Z achaloyan@gmail.com $
17 */
18
19 #include <stdlib.h>
20 #include <apr_general.h>
21 #include <sofia-sip/sdp.h>
22 #include "mrcp_sdp.h"
23 #include "mrcp_session_descriptor.h"
24 #include "mrcp_control_descriptor.h"
25 #include "mpf_rtp_attribs.h"
26 #include "mpf_rtp_pt.h"
27 #include "apt_text_stream.h"
28 #include "apt_log.h"
29
30 #if _MSC_VER >= 1900
31 #pragma warning(disable: 4477)
32 // 'snprintf' : format string '%d' requires an argument of type 'int', but variadic argument 1 has type 'apr_size_t' 264
33 // 'snprintf' : format string '%d' requires an argument of type 'int', but variadic argument 1 has type 'const apr_size_t' 198
34 #endif
35
36 static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mpf_rtp_media_descriptor_t *audio_descriptor);
37 static apr_size_t sdp_control_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mrcp_control_descriptor_t *control_media, apt_bool_t offer);
38
39 static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool);
40 static apt_bool_t mrcp_control_media_generate(mrcp_control_descriptor_t *mrcp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool);
41
42 /** Generate SDP string by MRCP descriptor */
sdp_string_generate_by_mrcp_descriptor(char * buffer,apr_size_t size,const mrcp_session_descriptor_t * descriptor,apt_bool_t offer)43 MRCP_DECLARE(apr_size_t) sdp_string_generate_by_mrcp_descriptor(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, apt_bool_t offer)
44 {
45 apr_size_t i;
46 apr_size_t count;
47 apr_size_t audio_index = 0;
48 mpf_rtp_media_descriptor_t *audio_media;
49 apr_size_t video_index = 0;
50 mpf_rtp_media_descriptor_t *video_media;
51 apr_size_t control_index = 0;
52 mrcp_control_descriptor_t *control_media;
53 apr_size_t offset = 0;
54 const char *ip = descriptor->ext_ip.buf ? descriptor->ext_ip.buf : (descriptor->ip.buf ? descriptor->ip.buf : "0.0.0.0");
55 buffer[0] = '\0';
56 offset += snprintf(buffer+offset,size-offset,
57 "v=0\r\n"
58 "o=%s 0 0 IN IP4 %s\r\n"
59 "s=-\r\n"
60 "c=IN IP4 %s\r\n"
61 "t=0 0\r\n",
62 descriptor->origin.buf ? descriptor->origin.buf : "-",
63 ip,
64 ip);
65 count = mrcp_session_media_count_get(descriptor);
66 for(i=0; i<count; i++) {
67 audio_media = mrcp_session_audio_media_get(descriptor,audio_index);
68 if(audio_media && audio_media->id == i) {
69 /* generate audio media */
70 audio_index++;
71 offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,audio_media);
72 continue;
73 }
74 video_media = mrcp_session_video_media_get(descriptor,video_index);
75 if(video_media && video_media->id == i) {
76 /* generate video media */
77 video_index++;
78 offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,video_media);
79 continue;
80 }
81 control_media = mrcp_session_control_media_get(descriptor,control_index);
82 if(control_media && control_media->id == i) {
83 /** generate mrcp control media */
84 control_index++;
85 offset += sdp_control_media_generate(buffer+offset,size-offset,descriptor,control_media,offer);
86 continue;
87 }
88 }
89 return offset;
90 }
91
92 /** Generate MRCP descriptor by SDP session */
mrcp_descriptor_generate_by_sdp_session(mrcp_session_descriptor_t * descriptor,const sdp_session_t * sdp,const char * force_destination_ip,apr_pool_t * pool)93 MRCP_DECLARE(apt_bool_t) mrcp_descriptor_generate_by_sdp_session(mrcp_session_descriptor_t* descriptor, const sdp_session_t *sdp, const char *force_destination_ip, apr_pool_t *pool)
94 {
95 sdp_media_t *sdp_media;
96
97 if(!sdp) {
98 apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid SDP Message");
99 return FALSE;
100 }
101
102 if(force_destination_ip) {
103 apt_string_assign(&descriptor->ip,force_destination_ip,pool);
104 }
105 else if(sdp->sdp_connection) {
106 apt_string_assign(&descriptor->ip,sdp->sdp_connection->c_address,pool);
107 }
108
109 for(sdp_media=sdp->sdp_media; sdp_media; sdp_media=sdp_media->m_next) {
110 switch(sdp_media->m_type) {
111 case sdp_media_audio:
112 {
113 mpf_rtp_media_descriptor_t *media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t));
114 mpf_rtp_media_descriptor_init(media);
115 media->id = mrcp_session_audio_media_add(descriptor,media);
116 mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool);
117 break;
118 }
119 case sdp_media_video:
120 {
121 mpf_rtp_media_descriptor_t *media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t));
122 mpf_rtp_media_descriptor_init(media);
123 media->id = mrcp_session_video_media_add(descriptor,media);
124 mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool);
125 break;
126 }
127 case sdp_media_application:
128 {
129 mrcp_control_descriptor_t *control_media = mrcp_control_descriptor_create(pool);
130 control_media->id = mrcp_session_control_media_add(descriptor,control_media);
131 mrcp_control_media_generate(control_media,sdp_media,&descriptor->ip,pool);
132 break;
133 }
134 default:
135 apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Not Supported SDP Media [%s]", sdp_media->m_type_name);
136 break;
137 }
138 }
139 return TRUE;
140 }
141
142 /** Generate SDP media by RTP media descriptor */
sdp_rtp_media_generate(char * buffer,apr_size_t size,const mrcp_session_descriptor_t * descriptor,const mpf_rtp_media_descriptor_t * audio_media)143 static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mpf_rtp_media_descriptor_t *audio_media)
144 {
145 apr_size_t offset = 0;
146 if(audio_media->state == MPF_MEDIA_ENABLED) {
147 int codec_count = 0;
148 int i;
149 mpf_codec_descriptor_t *codec_descriptor;
150 apr_array_header_t *descriptor_arr = audio_media->codec_list.descriptor_arr;
151 const apt_str_t *direction_str;
152 if(!descriptor_arr) {
153 return 0;
154 }
155
156 offset += snprintf(buffer+offset,size-offset,"m=audio %d RTP/AVP",audio_media->port);
157 for(i=0; i<descriptor_arr->nelts; i++) {
158 codec_descriptor = &APR_ARRAY_IDX(descriptor_arr,i,mpf_codec_descriptor_t);
159 if(codec_descriptor->enabled == TRUE) {
160 offset += snprintf(buffer+offset,size-offset," %d",codec_descriptor->payload_type);
161 codec_count++;
162 }
163 }
164 if(!codec_count){
165 /* SDP m line should have at least one media format listed; use a reserved RTP payload type */
166 offset += snprintf(buffer+offset,size-offset," %d",RTP_PT_RESERVED);
167 }
168 offset += snprintf(buffer+offset,size-offset,"\r\n");
169
170 if(descriptor->ip.length && audio_media->ip.length &&
171 apt_string_compare(&descriptor->ip,&audio_media->ip) != TRUE) {
172 const char *media_ip = audio_media->ext_ip.buf ? audio_media->ext_ip.buf : audio_media->ip.buf;
173 offset += snprintf(buffer+offset,size-offset,"c=IN IP4 %s\r\n",media_ip);
174 }
175
176 for(i=0; i<descriptor_arr->nelts; i++) {
177 codec_descriptor = &APR_ARRAY_IDX(descriptor_arr,i,mpf_codec_descriptor_t);
178 if(codec_descriptor->enabled == TRUE && codec_descriptor->name.buf) {
179 offset += snprintf(buffer+offset,size-offset,"a=rtpmap:%d %s/%d\r\n",
180 codec_descriptor->payload_type,
181 codec_descriptor->name.buf,
182 codec_descriptor->sampling_rate);
183 if(codec_descriptor->format.buf) {
184 offset += snprintf(buffer+offset,size-offset,"a=fmtp:%d %s\r\n",
185 codec_descriptor->payload_type,
186 codec_descriptor->format.buf);
187 }
188 }
189 }
190
191 direction_str = mpf_rtp_direction_str_get(audio_media->direction);
192 if(direction_str) {
193 offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",direction_str->buf);
194 }
195
196 if(audio_media->ptime) {
197 offset += snprintf(buffer+offset,size-offset,"a=ptime:%hu\r\n",audio_media->ptime);
198 }
199 }
200 else {
201 offset += snprintf(buffer+offset,size-offset,"m=audio 0 RTP/AVP %d\r\n",RTP_PT_RESERVED);
202 }
203
204 offset += snprintf(buffer+offset,size-offset,"a=mid:%"APR_SIZE_T_FMT"\r\n",audio_media->mid);
205 return offset;
206 }
207
208 /** Generate SDP media by MRCP control media descriptor */
sdp_control_media_generate(char * buffer,apr_size_t size,const mrcp_session_descriptor_t * descriptor,const mrcp_control_descriptor_t * control_media,apt_bool_t offer)209 static apr_size_t sdp_control_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mrcp_control_descriptor_t *control_media, apt_bool_t offer)
210 {
211 int i;
212 apr_size_t offset = 0;
213 const apt_str_t *proto;
214 const apt_str_t *setup_type;
215 const apt_str_t *connection_type;
216 proto = mrcp_proto_get(control_media->proto);
217 setup_type = mrcp_setup_type_get(control_media->setup_type);
218 connection_type = mrcp_connection_type_get(control_media->connection_type);
219 if(offer == TRUE) { /* offer */
220 if(control_media->port) {
221 offset += snprintf(buffer+offset,size-offset,
222 "m=application %d %s 1\r\n"
223 "a=setup:%s\r\n"
224 "a=connection:%s\r\n"
225 "a=resource:%s\r\n",
226 control_media->port,
227 proto ? proto->buf : "",
228 setup_type ? setup_type->buf : "",
229 connection_type ? connection_type->buf : "",
230 control_media->resource_name.buf);
231
232 }
233 else {
234 offset += snprintf(buffer+offset,size-offset,
235 "m=application %d %s 1\r\n"
236 "a=resource:%s\r\n",
237 control_media->port,
238 proto ? proto->buf : "",
239 control_media->resource_name.buf);
240 }
241 }
242 else { /* answer */
243 if(control_media->port) {
244 offset += snprintf(buffer+offset,size-offset,
245 "m=application %d %s 1\r\n"
246 "a=setup:%s\r\n"
247 "a=connection:%s\r\n"
248 "a=channel:%s@%s\r\n",
249 control_media->port,
250 proto ? proto->buf : "",
251 setup_type ? setup_type->buf : "",
252 connection_type ? connection_type->buf : "",
253 control_media->session_id.buf,
254 control_media->resource_name.buf);
255 }
256 else {
257 offset += snprintf(buffer+offset,size-offset,
258 "m=application %d %s 1\r\n"
259 "a=channel:%s@%s\r\n",
260 control_media->port,
261 proto ? proto->buf : "",
262 control_media->session_id.buf,
263 control_media->resource_name.buf);
264 }
265 }
266
267 for(i=0; i<control_media->cmid_arr->nelts; i++) {
268 offset += snprintf(buffer+offset,size-offset,
269 "a=cmid:%"APR_SIZE_T_FMT"\r\n",
270 APR_ARRAY_IDX(control_media->cmid_arr,i,apr_size_t));
271 }
272
273 return offset;
274 }
275
276 /** Generate RTP media descriptor by SDP media */
mpf_rtp_media_generate(mpf_rtp_media_descriptor_t * rtp_media,const sdp_media_t * sdp_media,const apt_str_t * ip,apr_pool_t * pool)277 static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool)
278 {
279 mpf_rtp_attrib_e id;
280 apt_str_t name;
281 sdp_attribute_t *attrib = NULL;
282 sdp_rtpmap_t *map;
283 mpf_codec_descriptor_t *codec;
284 for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) {
285 apt_string_set(&name,attrib->a_name);
286 id = mpf_rtp_attrib_id_find(&name);
287 switch(id) {
288 case RTP_ATTRIB_MID:
289 rtp_media->mid = atoi(attrib->a_value);
290 break;
291 case RTP_ATTRIB_PTIME:
292 rtp_media->ptime = (apr_uint16_t)atoi(attrib->a_value);
293 break;
294 default:
295 break;
296 }
297 }
298
299 mpf_codec_list_init(&rtp_media->codec_list,5,pool);
300 for(map = sdp_media->m_rtpmaps; map; map = map->rm_next) {
301 codec = mpf_codec_list_add(&rtp_media->codec_list);
302 if(codec) {
303 codec->payload_type = (apr_byte_t)map->rm_pt;
304 apt_string_assign(&codec->name,map->rm_encoding,pool);
305 codec->sampling_rate = (apr_uint16_t)map->rm_rate;
306 codec->channel_count = 1;
307 }
308 }
309
310 switch(sdp_media->m_mode) {
311 case sdp_inactive:
312 rtp_media->direction = STREAM_DIRECTION_NONE;
313 break;
314 case sdp_sendonly:
315 rtp_media->direction = STREAM_DIRECTION_SEND;
316 break;
317 case sdp_recvonly:
318 rtp_media->direction = STREAM_DIRECTION_RECEIVE;
319 break;
320 case sdp_sendrecv:
321 rtp_media->direction = STREAM_DIRECTION_DUPLEX;
322 break;
323 }
324
325 if(sdp_media->m_connections) {
326 apt_string_assign(&rtp_media->ip,sdp_media->m_connections->c_address,pool);
327 }
328 else {
329 rtp_media->ip = *ip;
330 }
331 if(sdp_media->m_port) {
332 rtp_media->port = (apr_port_t)sdp_media->m_port;
333 rtp_media->state = MPF_MEDIA_ENABLED;
334 }
335 else {
336 rtp_media->state = MPF_MEDIA_DISABLED;
337 }
338 return TRUE;
339 }
340
341 /** Generate MRCP control media by SDP media */
mrcp_control_media_generate(mrcp_control_descriptor_t * control_media,const sdp_media_t * sdp_media,const apt_str_t * ip,apr_pool_t * pool)342 static apt_bool_t mrcp_control_media_generate(mrcp_control_descriptor_t *control_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool)
343 {
344 mrcp_attrib_e id;
345 apt_str_t name;
346 apt_str_t value;
347 sdp_attribute_t *attrib = NULL;
348 apt_string_set(&name,sdp_media->m_proto_name);
349 control_media->proto = mrcp_proto_find(&name);
350 if(control_media->proto != MRCP_PROTO_TCP) {
351 apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Not supported SDP Proto [%s], expected [%s]",sdp_media->m_proto_name,mrcp_proto_get(MRCP_PROTO_TCP)->buf);
352 return FALSE;
353 }
354
355 for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) {
356 apt_string_set(&name,attrib->a_name);
357 id = mrcp_attrib_id_find(&name);
358 switch(id) {
359 case MRCP_ATTRIB_SETUP:
360 apt_string_set(&value,attrib->a_value);
361 control_media->setup_type = mrcp_setup_type_find(&value);
362 break;
363 case MRCP_ATTRIB_CONNECTION:
364 apt_string_set(&value,attrib->a_value);
365 control_media->connection_type = mrcp_connection_type_find(&value);
366 break;
367 case MRCP_ATTRIB_RESOURCE:
368 apt_string_assign(&control_media->resource_name,attrib->a_value,pool);
369 break;
370 case MRCP_ATTRIB_CHANNEL:
371 apt_string_set(&value,attrib->a_value);
372 apt_id_resource_parse(&value,'@',&control_media->session_id,&control_media->resource_name,pool);
373 break;
374 case MRCP_ATTRIB_CMID:
375 mrcp_cmid_add(control_media->cmid_arr,atoi(attrib->a_value));
376 break;
377 default:
378 break;
379 }
380 }
381
382 if(sdp_media->m_connections) {
383 apt_string_assign(&control_media->ip,sdp_media->m_connections->c_address,pool);
384 }
385 else {
386 control_media->ip = *ip;
387 }
388 control_media->port = (apr_port_t)sdp_media->m_port;
389 return TRUE;
390 }
391
392 /** Generate SDP resource discovery string */
sdp_resource_discovery_string_generate(const char * ip,const char * origin,char * buffer,apr_size_t size)393 MRCP_DECLARE(apr_size_t) sdp_resource_discovery_string_generate(const char *ip, const char *origin, char *buffer, apr_size_t size)
394 {
395 apr_size_t offset = 0;
396 if(!ip) {
397 ip = "0.0.0.0";
398 }
399 if(!origin) {
400 origin = "-";
401 }
402 buffer[0] = '\0';
403 offset += snprintf(buffer+offset,size-offset,
404 "v=0\r\n"
405 "o=%s 0 0 IN IP4 %s\r\n"
406 "s=-\r\n"
407 "c=IN IP4 %s\r\n"
408 "t=0 0\r\n"
409 "m=application 0 TCP/MRCPv2 1\r\n"
410 "a=resource:speechsynth\r\n"
411 "a=resource:speechrecog\r\n"
412 "m=audio 0 RTP/AVP 0 8 96 101\r\n"
413 "a=rtpmap:0 PCMU/8000\r\n"
414 "a=rtpmap:8 PCMA/8000\r\n"
415 "a=rtpmap:96 L16/8000\r\n"
416 "a=rtpmap:101 telephone-event/8000\r\n",
417 origin,
418 ip,
419 ip);
420 return offset;
421 }
422