blob: bb572cc92b321b6bb11055df915a811476e15d38 [file] [log] [blame]
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua-lib/pjsua.h>
20#include <pjsua-lib/pjsua_internal.h>
21
22
23#define THIS_FILE "pjsua_media.c"
24
Benny Prijono80eee892007-11-03 22:43:23 +000025#define DEFAULT_RTP_PORT 4000
26
27#ifndef PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
28# define PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT 0
29#endif
30
Benny Prijonoeebe9af2006-06-13 22:57:13 +000031
Benny Prijonode479562007-03-15 10:23:55 +000032/* Next RTP port to be used */
33static pj_uint16_t next_rtp_port;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000034
35/* Close existing sound device */
36static void close_snd_dev(void);
37
38
39/**
40 * Init media subsystems.
41 */
42pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg)
43{
Benny Prijonoba5926a2007-05-02 11:29:37 +000044 pj_str_t codec_id = {NULL, 0};
Benny Prijono0498d902006-06-19 14:49:14 +000045 unsigned opt;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000046 pj_status_t status;
47
Benny Prijonofc24e692007-01-27 18:31:51 +000048 /* To suppress warning about unused var when all codecs are disabled */
49 PJ_UNUSED_ARG(codec_id);
50
Benny Prijonoeebe9af2006-06-13 22:57:13 +000051 /* Copy configuration */
52 pj_memcpy(&pjsua_var.media_cfg, cfg, sizeof(*cfg));
53
54 /* Normalize configuration */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000055
56 if (pjsua_var.media_cfg.has_ioqueue &&
57 pjsua_var.media_cfg.thread_cnt == 0)
58 {
59 pjsua_var.media_cfg.thread_cnt = 1;
60 }
61
62 if (pjsua_var.media_cfg.max_media_ports < pjsua_var.ua_cfg.max_calls) {
63 pjsua_var.media_cfg.max_media_ports = pjsua_var.ua_cfg.max_calls + 2;
64 }
65
66 /* Create media endpoint. */
67 status = pjmedia_endpt_create(&pjsua_var.cp.factory,
68 pjsua_var.media_cfg.has_ioqueue? NULL :
69 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
70 pjsua_var.media_cfg.thread_cnt,
71 &pjsua_var.med_endpt);
72 if (status != PJ_SUCCESS) {
73 pjsua_perror(THIS_FILE,
74 "Media stack initialization has returned error",
75 status);
76 return status;
77 }
78
79 /* Register all codecs */
80#if PJMEDIA_HAS_SPEEX_CODEC
81 /* Register speex. */
82 status = pjmedia_codec_speex_init(pjsua_var.med_endpt,
Benny Prijono7ca96da2006-08-07 12:11:40 +000083 0,
Benny Prijono0498d902006-06-19 14:49:14 +000084 pjsua_var.media_cfg.quality,
85 pjsua_var.media_cfg.quality);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000086 if (status != PJ_SUCCESS) {
87 pjsua_perror(THIS_FILE, "Error initializing Speex codec",
88 status);
89 return status;
90 }
Benny Prijono7ca96da2006-08-07 12:11:40 +000091
92 /* Set speex/16000 to higher priority*/
93 codec_id = pj_str("speex/16000");
94 pjmedia_codec_mgr_set_codec_priority(
95 pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt),
96 &codec_id, PJMEDIA_CODEC_PRIO_NORMAL+2);
97
98 /* Set speex/8000 to next higher priority*/
99 codec_id = pj_str("speex/8000");
100 pjmedia_codec_mgr_set_codec_priority(
101 pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt),
102 &codec_id, PJMEDIA_CODEC_PRIO_NORMAL+1);
103
104
105
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106#endif /* PJMEDIA_HAS_SPEEX_CODEC */
107
Benny Prijono00cae612006-07-31 15:19:36 +0000108#if PJMEDIA_HAS_ILBC_CODEC
109 /* Register iLBC. */
110 status = pjmedia_codec_ilbc_init( pjsua_var.med_endpt,
111 pjsua_var.media_cfg.ilbc_mode);
112 if (status != PJ_SUCCESS) {
113 pjsua_perror(THIS_FILE, "Error initializing iLBC codec",
114 status);
115 return status;
116 }
117#endif /* PJMEDIA_HAS_ILBC_CODEC */
118
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000119#if PJMEDIA_HAS_GSM_CODEC
120 /* Register GSM */
121 status = pjmedia_codec_gsm_init(pjsua_var.med_endpt);
122 if (status != PJ_SUCCESS) {
123 pjsua_perror(THIS_FILE, "Error initializing GSM codec",
124 status);
125 return status;
126 }
127#endif /* PJMEDIA_HAS_GSM_CODEC */
128
129#if PJMEDIA_HAS_G711_CODEC
130 /* Register PCMA and PCMU */
131 status = pjmedia_codec_g711_init(pjsua_var.med_endpt);
132 if (status != PJ_SUCCESS) {
133 pjsua_perror(THIS_FILE, "Error initializing G711 codec",
134 status);
135 return status;
136 }
137#endif /* PJMEDIA_HAS_G711_CODEC */
138
139#if PJMEDIA_HAS_L16_CODEC
140 /* Register L16 family codecs, but disable all */
141 status = pjmedia_codec_l16_init(pjsua_var.med_endpt, 0);
142 if (status != PJ_SUCCESS) {
143 pjsua_perror(THIS_FILE, "Error initializing L16 codecs",
144 status);
145 return status;
146 }
147
148 /* Disable ALL L16 codecs */
149 codec_id = pj_str("L16");
150 pjmedia_codec_mgr_set_codec_priority(
151 pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt),
152 &codec_id, PJMEDIA_CODEC_PRIO_DISABLED);
153
154#endif /* PJMEDIA_HAS_L16_CODEC */
155
156
157 /* Save additional conference bridge parameters for future
158 * reference.
159 */
160 pjsua_var.mconf_cfg.samples_per_frame = pjsua_var.media_cfg.clock_rate *
Benny Prijonocf0b4b22007-10-06 17:31:09 +0000161 pjsua_var.media_cfg.audio_frame_ptime /
162 1000;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000163 pjsua_var.mconf_cfg.channel_count = 1;
164 pjsua_var.mconf_cfg.bits_per_sample = 16;
165
Benny Prijono0498d902006-06-19 14:49:14 +0000166 /* Init options for conference bridge. */
167 opt = PJMEDIA_CONF_NO_DEVICE;
168 if (pjsua_var.media_cfg.quality >= 3 &&
Benny Prijono7ca96da2006-08-07 12:11:40 +0000169 pjsua_var.media_cfg.quality <= 4)
Benny Prijono0498d902006-06-19 14:49:14 +0000170 {
171 opt |= PJMEDIA_CONF_SMALL_FILTER;
172 }
173 else if (pjsua_var.media_cfg.quality < 3) {
174 opt |= PJMEDIA_CONF_USE_LINEAR;
175 }
176
177
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178 /* Init conference bridge. */
179 status = pjmedia_conf_create(pjsua_var.pool,
180 pjsua_var.media_cfg.max_media_ports,
181 pjsua_var.media_cfg.clock_rate,
182 pjsua_var.mconf_cfg.channel_count,
183 pjsua_var.mconf_cfg.samples_per_frame,
184 pjsua_var.mconf_cfg.bits_per_sample,
Benny Prijono0498d902006-06-19 14:49:14 +0000185 opt, &pjsua_var.mconf);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000186 if (status != PJ_SUCCESS) {
187 pjsua_perror(THIS_FILE,
188 "Media stack initialization has returned error",
189 status);
190 return status;
191 }
192
193 /* Create null port just in case user wants to use null sound. */
194 status = pjmedia_null_port_create(pjsua_var.pool,
195 pjsua_var.media_cfg.clock_rate,
196 pjsua_var.mconf_cfg.channel_count,
197 pjsua_var.mconf_cfg.samples_per_frame,
198 pjsua_var.mconf_cfg.bits_per_sample,
199 &pjsua_var.null_port);
200 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
201
Benny Prijono6ba8c542007-10-16 01:34:14 +0000202 /* Perform NAT detection */
203 pjsua_detect_nat_type();
204
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000205 return PJ_SUCCESS;
206}
207
208
209/*
210 * Create RTP and RTCP socket pair, and possibly resolve their public
211 * address via STUN.
212 */
213static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
214 pjmedia_sock_info *skinfo)
215{
216 enum {
217 RTP_RETRY = 100
218 };
219 int i;
Benny Prijono0a5cad82006-09-26 13:21:02 +0000220 pj_sockaddr_in bound_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 pj_sockaddr_in mapped_addr[2];
222 pj_status_t status = PJ_SUCCESS;
Benny Prijono38fb3ea2008-01-02 08:27:03 +0000223 char addr_buf[PJ_INET6_ADDRSTRLEN+2];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000224 pj_sock_t sock[2];
225
Benny Prijonoc97608e2007-03-23 16:34:20 +0000226 /* Make sure STUN server resolution has completed */
227 status = pjsua_resolve_stun_server(PJ_TRUE);
228 if (status != PJ_SUCCESS) {
229 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
230 return status;
231 }
232
Benny Prijonode479562007-03-15 10:23:55 +0000233 if (next_rtp_port == 0)
234 next_rtp_port = (pj_uint16_t)cfg->port;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000235
236 for (i=0; i<2; ++i)
237 sock[i] = PJ_INVALID_SOCKET;
238
Benny Prijono0a5cad82006-09-26 13:21:02 +0000239 bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
240 if (cfg->bound_addr.slen) {
241 status = pj_sockaddr_in_set_str_addr(&bound_addr, &cfg->bound_addr);
242 if (status != PJ_SUCCESS) {
243 pjsua_perror(THIS_FILE, "Unable to resolve transport bind address",
244 status);
245 return status;
246 }
247 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000248
249 /* Loop retry to bind RTP and RTCP sockets. */
Benny Prijonode479562007-03-15 10:23:55 +0000250 for (i=0; i<RTP_RETRY; ++i, next_rtp_port += 2) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000251
252 /* Create and bind RTP socket. */
Benny Prijono8ab968f2007-07-20 08:08:30 +0000253 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[0]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000254 if (status != PJ_SUCCESS) {
255 pjsua_perror(THIS_FILE, "socket() error", status);
256 return status;
257 }
258
Benny Prijono0cdcd3f2007-12-09 14:14:11 +0000259 status=pj_sock_bind_in(sock[0], pj_ntohl(bound_addr.sin_addr.s_addr),
260 next_rtp_port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000261 if (status != PJ_SUCCESS) {
262 pj_sock_close(sock[0]);
263 sock[0] = PJ_INVALID_SOCKET;
264 continue;
265 }
266
267 /* Create and bind RTCP socket. */
Benny Prijono8ab968f2007-07-20 08:08:30 +0000268 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[1]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000269 if (status != PJ_SUCCESS) {
270 pjsua_perror(THIS_FILE, "socket() error", status);
271 pj_sock_close(sock[0]);
272 return status;
273 }
274
Benny Prijono0cdcd3f2007-12-09 14:14:11 +0000275 status=pj_sock_bind_in(sock[1], pj_ntohl(bound_addr.sin_addr.s_addr),
276 (pj_uint16_t)(next_rtp_port+1));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000277 if (status != PJ_SUCCESS) {
278 pj_sock_close(sock[0]);
279 sock[0] = PJ_INVALID_SOCKET;
280
281 pj_sock_close(sock[1]);
282 sock[1] = PJ_INVALID_SOCKET;
283 continue;
284 }
285
286 /*
287 * If we're configured to use STUN, then find out the mapped address,
288 * and make sure that the mapped RTCP port is adjacent with the RTP.
289 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000290 if (pjsua_var.stun_srv.addr.sa_family != 0) {
291 char ip_addr[32];
292 pj_str_t stun_srv;
293
294 pj_ansi_strcpy(ip_addr,
295 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
296 stun_srv = pj_str(ip_addr);
297
Benny Prijono14c2b862007-02-21 00:40:05 +0000298 status=pjstun_get_mapped_addr(&pjsua_var.cp.factory, 2, sock,
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000299 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
300 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000301 mapped_addr);
302 if (status != PJ_SUCCESS) {
303 pjsua_perror(THIS_FILE, "STUN resolve error", status);
304 goto on_error;
305 }
306
Benny Prijono80eee892007-11-03 22:43:23 +0000307#if PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000308 if (pj_ntohs(mapped_addr[1].sin_port) ==
309 pj_ntohs(mapped_addr[0].sin_port)+1)
310 {
311 /* Success! */
312 break;
313 }
314
315 pj_sock_close(sock[0]);
316 sock[0] = PJ_INVALID_SOCKET;
317
318 pj_sock_close(sock[1]);
319 sock[1] = PJ_INVALID_SOCKET;
Benny Prijono80eee892007-11-03 22:43:23 +0000320#else
321 if (pj_ntohs(mapped_addr[1].sin_port) !=
322 pj_ntohs(mapped_addr[0].sin_port)+1)
323 {
324 PJ_LOG(4,(THIS_FILE,
325 "Note: STUN mapped RTCP port %d is not adjacent"
326 " to RTP port %d",
327 pj_ntohs(mapped_addr[1].sin_port),
328 pj_ntohs(mapped_addr[0].sin_port)));
329 }
330 /* Success! */
331 break;
332#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000333
Benny Prijono0a5cad82006-09-26 13:21:02 +0000334 } else if (cfg->public_addr.slen) {
335
336 status = pj_sockaddr_in_init(&mapped_addr[0], &cfg->public_addr,
Benny Prijonode479562007-03-15 10:23:55 +0000337 (pj_uint16_t)next_rtp_port);
Benny Prijono0a5cad82006-09-26 13:21:02 +0000338 if (status != PJ_SUCCESS)
339 goto on_error;
340
341 status = pj_sockaddr_in_init(&mapped_addr[1], &cfg->public_addr,
Benny Prijonode479562007-03-15 10:23:55 +0000342 (pj_uint16_t)(next_rtp_port+1));
Benny Prijono0a5cad82006-09-26 13:21:02 +0000343 if (status != PJ_SUCCESS)
344 goto on_error;
345
346 break;
347
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000348 } else {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349
Benny Prijono42d08d22007-12-20 11:23:07 +0000350 if (bound_addr.sin_addr.s_addr == 0) {
351 pj_sockaddr addr;
352
353 /* Get local IP address. */
354 status = pj_gethostip(pj_AF_INET(), &addr);
355 if (status != PJ_SUCCESS)
356 goto on_error;
357
358 bound_addr.sin_addr.s_addr = addr.ipv4.sin_addr.s_addr;
359 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000360
Benny Prijonofe0b1d62007-12-05 04:07:37 +0000361 for (i=0; i<2; ++i) {
362 pj_sockaddr_in_init(&mapped_addr[i], NULL, 0);
Benny Prijono42d08d22007-12-20 11:23:07 +0000363 mapped_addr[i].sin_addr.s_addr = bound_addr.sin_addr.s_addr;
Benny Prijonofe0b1d62007-12-05 04:07:37 +0000364 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000365
Benny Prijonode479562007-03-15 10:23:55 +0000366 mapped_addr[0].sin_port=pj_htons((pj_uint16_t)next_rtp_port);
367 mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(next_rtp_port+1));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000368 break;
369 }
370 }
371
372 if (sock[0] == PJ_INVALID_SOCKET) {
373 PJ_LOG(1,(THIS_FILE,
374 "Unable to find appropriate RTP/RTCP ports combination"));
375 goto on_error;
376 }
377
378
379 skinfo->rtp_sock = sock[0];
380 pj_memcpy(&skinfo->rtp_addr_name,
381 &mapped_addr[0], sizeof(pj_sockaddr_in));
382
383 skinfo->rtcp_sock = sock[1];
384 pj_memcpy(&skinfo->rtcp_addr_name,
385 &mapped_addr[1], sizeof(pj_sockaddr_in));
386
Benny Prijono8b22ce12008-02-08 12:57:55 +0000387 PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s",
Benny Prijono5186eae2007-12-03 14:38:25 +0000388 pj_sockaddr_print(&skinfo->rtp_addr_name, addr_buf,
389 sizeof(addr_buf), 3)));
Benny Prijono8b22ce12008-02-08 12:57:55 +0000390 PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s",
Benny Prijono5186eae2007-12-03 14:38:25 +0000391 pj_sockaddr_print(&skinfo->rtcp_addr_name, addr_buf,
392 sizeof(addr_buf), 3)));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000393
Benny Prijonode479562007-03-15 10:23:55 +0000394 next_rtp_port += 2;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000395 return PJ_SUCCESS;
396
397on_error:
398 for (i=0; i<2; ++i) {
399 if (sock[i] != PJ_INVALID_SOCKET)
400 pj_sock_close(sock[i]);
401 }
402 return status;
403}
404
405
406/*
407 * Start pjsua media subsystem.
408 */
409pj_status_t pjsua_media_subsys_start(void)
410{
411 pj_status_t status;
412
413 /* Create media for calls, if none is specified */
414 if (pjsua_var.calls[0].med_tp == NULL) {
415 pjsua_transport_config transport_cfg;
416
417 /* Create default transport config */
418 pjsua_transport_config_default(&transport_cfg);
419 transport_cfg.port = DEFAULT_RTP_PORT;
420
421 status = pjsua_media_transports_create(&transport_cfg);
422 if (status != PJ_SUCCESS)
423 return status;
424 }
425
426 /* Create sound port if none is created yet */
Benny Prijonoe909eac2006-07-27 22:04:56 +0000427 if (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&
428 !pjsua_var.no_snd)
429 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000430 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
431 if (status != PJ_SUCCESS) {
Benny Prijonod639efa2007-04-05 22:45:06 +0000432 pjsua_perror(THIS_FILE, "Error opening sound device", status);
433 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000434 }
435 }
436
437 return PJ_SUCCESS;
438}
439
440
441/*
442 * Destroy pjsua media subsystem.
443 */
444pj_status_t pjsua_media_subsys_destroy(void)
445{
446 unsigned i;
447
448 close_snd_dev();
449
450 if (pjsua_var.mconf) {
451 pjmedia_conf_destroy(pjsua_var.mconf);
452 pjsua_var.mconf = NULL;
453 }
454
455 if (pjsua_var.null_port) {
456 pjmedia_port_destroy(pjsua_var.null_port);
457 pjsua_var.null_port = NULL;
458 }
459
460 /* Destroy file players */
461 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.player); ++i) {
462 if (pjsua_var.player[i].port) {
463 pjmedia_port_destroy(pjsua_var.player[i].port);
464 pjsua_var.player[i].port = NULL;
465 }
466 }
467
468 /* Destroy file recorders */
469 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.recorder); ++i) {
470 if (pjsua_var.recorder[i].port) {
471 pjmedia_port_destroy(pjsua_var.recorder[i].port);
472 pjsua_var.recorder[i].port = NULL;
473 }
474 }
475
476 /* Close media transports */
Benny Prijono0875ae82006-12-26 00:11:48 +0000477 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000478 if (pjsua_var.calls[i].med_tp) {
479 (*pjsua_var.calls[i].med_tp->op->destroy)(pjsua_var.calls[i].med_tp);
480 pjsua_var.calls[i].med_tp = NULL;
481 }
482 }
483
484 /* Destroy media endpoint. */
485 if (pjsua_var.med_endpt) {
486
487 /* Shutdown all codecs: */
488# if PJMEDIA_HAS_SPEEX_CODEC
489 pjmedia_codec_speex_deinit();
490# endif /* PJMEDIA_HAS_SPEEX_CODEC */
491
492# if PJMEDIA_HAS_GSM_CODEC
493 pjmedia_codec_gsm_deinit();
494# endif /* PJMEDIA_HAS_GSM_CODEC */
495
496# if PJMEDIA_HAS_G711_CODEC
497 pjmedia_codec_g711_deinit();
498# endif /* PJMEDIA_HAS_G711_CODEC */
499
500# if PJMEDIA_HAS_L16_CODEC
501 pjmedia_codec_l16_deinit();
502# endif /* PJMEDIA_HAS_L16_CODEC */
503
504
505 pjmedia_endpt_destroy(pjsua_var.med_endpt);
506 pjsua_var.med_endpt = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000507
Benny Prijonoad2e0ca2007-04-29 12:31:51 +0000508 /* Deinitialize sound subsystem */
Benny Prijonoa41d66e2007-10-01 12:17:23 +0000509 // Not necessary, as pjmedia_snd_deinit() should have been called
510 // in pjmedia_endpt_destroy().
511 //pjmedia_snd_deinit();
Benny Prijonoad2e0ca2007-04-29 12:31:51 +0000512 }
Benny Prijonoaf1bb1e2006-11-21 12:39:31 +0000513
Benny Prijonode479562007-03-15 10:23:55 +0000514 /* Reset RTP port */
515 next_rtp_port = 0;
516
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000517 return PJ_SUCCESS;
518}
519
520
Benny Prijonoc97608e2007-03-23 16:34:20 +0000521/* Create normal UDP media transports */
522static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000523{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000524 unsigned i;
Benny Prijono617c5bc2007-04-02 19:51:21 +0000525 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000526 pj_status_t status;
527
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000528 /* Create each media transport */
529 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
530
Benny Prijono617c5bc2007-04-02 19:51:21 +0000531 status = create_rtp_rtcp_sock(cfg, &skinfo);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000532 if (status != PJ_SUCCESS) {
533 pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket",
534 status);
535 goto on_error;
536 }
Benny Prijonod8179652008-01-23 20:39:07 +0000537
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000538 status = pjmedia_transport_udp_attach(pjsua_var.med_endpt, NULL,
Benny Prijono617c5bc2007-04-02 19:51:21 +0000539 &skinfo, 0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000540 &pjsua_var.calls[i].med_tp);
541 if (status != PJ_SUCCESS) {
542 pjsua_perror(THIS_FILE, "Unable to create media transport",
543 status);
544 goto on_error;
545 }
Benny Prijono00cae612006-07-31 15:19:36 +0000546
Benny Prijonod8179652008-01-23 20:39:07 +0000547 pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp,
548 PJMEDIA_DIR_ENCODING,
549 pjsua_var.media_cfg.tx_drop_pct);
Benny Prijono00cae612006-07-31 15:19:36 +0000550
Benny Prijonod8179652008-01-23 20:39:07 +0000551 pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp,
552 PJMEDIA_DIR_DECODING,
553 pjsua_var.media_cfg.rx_drop_pct);
Benny Prijono00cae612006-07-31 15:19:36 +0000554
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000555 }
556
Benny Prijonoc97608e2007-03-23 16:34:20 +0000557 return PJ_SUCCESS;
558
559on_error:
560 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
561 if (pjsua_var.calls[i].med_tp != NULL) {
562 pjmedia_transport_close(pjsua_var.calls[i].med_tp);
563 pjsua_var.calls[i].med_tp = NULL;
564 }
565 }
566
567 return status;
568}
569
570
Benny Prijono096c56c2007-09-15 08:30:16 +0000571/* This callback is called when ICE negotiation completes */
572static void on_ice_complete(pjmedia_transport *tp, pj_status_t result)
573{
574 unsigned id, c;
575 pj_bool_t found = PJ_FALSE;
576
577 /* We're only interested with failure case */
578 if (result == PJ_SUCCESS)
579 return;
580
581 /* Find call which has this media transport */
582
583 PJSUA_LOCK();
584
585 for (id=0, c=0; id<PJSUA_MAX_CALLS && c<pjsua_var.call_cnt; ++id) {
586 pjsua_call *call = &pjsua_var.calls[id];
587 if (call->inv) {
588 ++c;
589
590 if (call->med_tp == tp) {
591 call->media_st = PJSUA_CALL_MEDIA_ERROR;
592 call->media_dir = PJMEDIA_DIR_NONE;
593 found = PJ_TRUE;
594 break;
595 }
596 }
597 }
598
599 PJSUA_UNLOCK();
600
601 if (found && pjsua_var.ua_cfg.cb.on_call_media_state) {
602 pjsua_var.ua_cfg.cb.on_call_media_state(id);
603 }
604}
605
606
Benny Prijonoc97608e2007-03-23 16:34:20 +0000607/* Create ICE media transports (when ice is enabled) */
608static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
609{
610 unsigned i;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000611 pj_sockaddr_in addr;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000612 pj_status_t status;
613
Benny Prijonoda9785b2007-04-02 20:43:06 +0000614 /* Make sure STUN server resolution has completed */
615 status = pjsua_resolve_stun_server(PJ_TRUE);
616 if (status != PJ_SUCCESS) {
617 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
618 return status;
619 }
620
Benny Prijonob681a2f2007-03-25 18:44:51 +0000621 pj_sockaddr_in_init(&addr, 0, (pj_uint16_t)cfg->port);
622
Benny Prijonoc97608e2007-03-23 16:34:20 +0000623 /* Create each media transport */
624 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijonoa6bd7582007-03-28 15:49:48 +0000625 pj_ice_strans_comp comp;
Benny Prijono096c56c2007-09-15 08:30:16 +0000626 pjmedia_ice_cb ice_cb;
Benny Prijonob681a2f2007-03-25 18:44:51 +0000627 int next_port;
Benny Prijono38fb3ea2008-01-02 08:27:03 +0000628 char name[32];
Benny Prijonodc1fe222007-06-26 10:13:13 +0000629#if PJMEDIA_ADVERTISE_RTCP
630 enum { COMP_CNT=2 };
631#else
632 enum { COMP_CNT=1 };
633#endif
Benny Prijonoc97608e2007-03-23 16:34:20 +0000634
Benny Prijono096c56c2007-09-15 08:30:16 +0000635 pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
636 ice_cb.on_ice_complete = &on_ice_complete;
637
Benny Prijono38fb3ea2008-01-02 08:27:03 +0000638 pj_ansi_snprintf(name, sizeof(name), "icetp%02d", i);
639
640 status = pjmedia_ice_create(pjsua_var.med_endpt, name, COMP_CNT,
Benny Prijono096c56c2007-09-15 08:30:16 +0000641 &pjsua_var.stun_cfg, &ice_cb,
Benny Prijonoc97608e2007-03-23 16:34:20 +0000642 &pjsua_var.calls[i].med_tp);
643 if (status != PJ_SUCCESS) {
644 pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
645 status);
646 goto on_error;
647 }
648
Benny Prijonod8179652008-01-23 20:39:07 +0000649 pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp,
650 PJMEDIA_DIR_ENCODING,
651 pjsua_var.media_cfg.tx_drop_pct);
Benny Prijono11da9bc2007-09-15 08:55:00 +0000652
Benny Prijonod8179652008-01-23 20:39:07 +0000653 pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp,
654 PJMEDIA_DIR_DECODING,
655 pjsua_var.media_cfg.rx_drop_pct);
Benny Prijono11da9bc2007-09-15 08:55:00 +0000656
Benny Prijonob681a2f2007-03-25 18:44:51 +0000657 status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, 0, &addr,
658 &pjsua_var.stun_srv.ipv4, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000659 if (status != PJ_SUCCESS) {
Benny Prijonob681a2f2007-03-25 18:44:51 +0000660 pjsua_perror(THIS_FILE, "Error starting ICE transport",
Benny Prijonoc97608e2007-03-23 16:34:20 +0000661 status);
662 goto on_error;
663 }
664
Benny Prijonob681a2f2007-03-25 18:44:51 +0000665 pjmedia_ice_get_comp(pjsua_var.calls[i].med_tp, 1, &comp);
666 next_port = pj_ntohs(comp.local_addr.ipv4.sin_port);
667 next_port += 2;
668 addr.sin_port = pj_htons((pj_uint16_t)next_port);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000669 }
670
671 /* Wait until all ICE transports are ready */
672 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijonoc97608e2007-03-23 16:34:20 +0000673
674 /* Wait until interface status is PJ_SUCCESS */
675 for (;;) {
Benny Prijonob681a2f2007-03-25 18:44:51 +0000676 status = pjmedia_ice_get_init_status(pjsua_var.calls[i].med_tp);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000677 if (status == PJ_EPENDING)
678 pjsua_handle_events(100);
679 else
680 break;
681 }
682
683 if (status != PJ_SUCCESS) {
684 pjsua_perror(THIS_FILE,
685 "Error adding STUN address to ICE media transport",
686 status);
687 goto on_error;
688 }
689
Benny Prijonoc97608e2007-03-23 16:34:20 +0000690 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000691
692 return PJ_SUCCESS;
693
694on_error:
695 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
696 if (pjsua_var.calls[i].med_tp != NULL) {
Benny Prijonoc97608e2007-03-23 16:34:20 +0000697 pjmedia_transport_close(pjsua_var.calls[i].med_tp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000698 pjsua_var.calls[i].med_tp = NULL;
699 }
700 }
701
Benny Prijonoc97608e2007-03-23 16:34:20 +0000702 return status;
703}
704
705
706/*
707 * Create UDP media transports for all the calls. This function creates
708 * one UDP media transport for each call.
709 */
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000710PJ_DEF(pj_status_t) pjsua_media_transports_create(
711 const pjsua_transport_config *app_cfg)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000712{
713 pjsua_transport_config cfg;
714 unsigned i;
715 pj_status_t status;
716
717
718 /* Make sure pjsua_init() has been called */
719 PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
720
721 PJSUA_LOCK();
722
723 /* Delete existing media transports */
724 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
725 if (pjsua_var.calls[i].med_tp != NULL) {
726 pjmedia_transport_close(pjsua_var.calls[i].med_tp);
727 pjsua_var.calls[i].med_tp = NULL;
728 }
729 }
730
731 /* Copy config */
732 pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
733
734 if (pjsua_var.media_cfg.enable_ice) {
735 status = create_ice_media_transports(&cfg);
736 } else {
737 status = create_udp_media_transports(&cfg);
738 }
739
740
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000741 PJSUA_UNLOCK();
742
743 return status;
744}
745
746
Benny Prijonoc97608e2007-03-23 16:34:20 +0000747pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
Benny Prijonod8179652008-01-23 20:39:07 +0000748 pjsip_role_e role,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000749 int security_level,
750 int *sip_err_code)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000751{
752 pjsua_call *call = &pjsua_var.calls[call_id];
753
Benny Prijonod8179652008-01-23 20:39:07 +0000754#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
755 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
756 pjmedia_srtp_setting srtp_opt;
757 pjmedia_transport *srtp;
758 pj_status_t status;
759#endif
Benny Prijonoc97608e2007-03-23 16:34:20 +0000760
Benny Prijonod8179652008-01-23 20:39:07 +0000761 PJ_UNUSED_ARG(role);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000762
Benny Prijonod8179652008-01-23 20:39:07 +0000763 /* Return error if media transport has not been created yet
764 * (e.g. application is starting)
765 */
766 if (call->med_tp == NULL) {
767 return PJ_EBUSY;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000768 }
769
Benny Prijonod8179652008-01-23 20:39:07 +0000770 /* Stop media transport (for good measure!) */
771 pjmedia_transport_media_stop(call->med_tp);
772
773#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
774 /* Check if SRTP requires secure signaling */
775 if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
776 if (security_level < acc->cfg.srtp_secure_signaling) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000777 if (sip_err_code)
778 *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
Benny Prijonod8179652008-01-23 20:39:07 +0000779 return PJSIP_ESESSIONINSECURE;
780 }
781 }
782
783 /* Always create SRTP adapter */
784 pjmedia_srtp_setting_default(&srtp_opt);
785 srtp_opt.close_member_tp = PJ_FALSE;
786 srtp_opt.use = acc->cfg.use_srtp;
787 status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
788 call->med_tp,
789 &srtp_opt, &srtp);
Benny Prijono25b2ea12008-01-24 19:20:54 +0000790 if (status != PJ_SUCCESS) {
791 if (sip_err_code)
792 *sip_err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
Benny Prijonod8179652008-01-23 20:39:07 +0000793 return status;
Benny Prijono25b2ea12008-01-24 19:20:54 +0000794 }
Benny Prijonod8179652008-01-23 20:39:07 +0000795
796 /* Set SRTP as current media transport */
797 call->med_orig = call->med_tp;
798 call->med_tp = srtp;
799#else
800 call->med_orig = call->med_tp;
801 PJ_UNUSED_ARG(security_level);
802#endif
803
Benny Prijonoc97608e2007-03-23 16:34:20 +0000804 return PJ_SUCCESS;
805}
806
807pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
808 pj_pool_t *pool,
Benny Prijonod8179652008-01-23 20:39:07 +0000809 const pjmedia_sdp_session *rem_sdp,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000810 pjmedia_sdp_session **p_sdp,
811 int *sip_status_code)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000812{
Benny Prijonod8179652008-01-23 20:39:07 +0000813 enum { MAX_MEDIA = 1, MEDIA_IDX = 0 };
Benny Prijonoc97608e2007-03-23 16:34:20 +0000814 pjmedia_sdp_session *sdp;
Benny Prijono617c5bc2007-04-02 19:51:21 +0000815 pjmedia_sock_info skinfo;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000816 pjsua_call *call = &pjsua_var.calls[call_id];
817 pj_status_t status;
818
Benny Prijono55e82352007-05-10 20:49:08 +0000819 /* Return error if media transport has not been created yet
820 * (e.g. application is starting)
821 */
822 if (call->med_tp == NULL) {
823 return PJ_EBUSY;
824 }
825
Benny Prijono617c5bc2007-04-02 19:51:21 +0000826 /* Get media socket info */
827 pjmedia_transport_get_info(call->med_tp, &skinfo);
828
829 /* Create SDP */
Benny Prijonod8179652008-01-23 20:39:07 +0000830 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pool, MAX_MEDIA,
Benny Prijono617c5bc2007-04-02 19:51:21 +0000831 &skinfo, &sdp);
Benny Prijono25b2ea12008-01-24 19:20:54 +0000832 if (status != PJ_SUCCESS) {
833 if (sip_status_code) *sip_status_code = 500;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000834 goto on_error;
Benny Prijono25b2ea12008-01-24 19:20:54 +0000835 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000836
Benny Prijono6ba8c542007-10-16 01:34:14 +0000837 /* Add NAT info in the SDP */
838 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
839 pjmedia_sdp_attr *a;
840 pj_str_t value;
841 char nat_info[80];
842
843 value.ptr = nat_info;
844 if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) {
845 value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
846 "%d", pjsua_var.nat_type);
847 } else {
848 const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type);
849 value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
850 "%d %s",
851 pjsua_var.nat_type,
852 type_name);
853 }
854
855 a = pjmedia_sdp_attr_create(pool, "X-nat", &value);
856
857 pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
858
859 }
860
Benny Prijonod8179652008-01-23 20:39:07 +0000861 /* Give the SDP to media transport */
Benny Prijono2dbed822008-02-21 10:08:27 +0000862 status = pjmedia_transport_media_create(call->med_tp, pool, 0,
Benny Prijonod8179652008-01-23 20:39:07 +0000863 sdp, rem_sdp, MEDIA_IDX);
Benny Prijono25b2ea12008-01-24 19:20:54 +0000864 if (status != PJ_SUCCESS) {
865 if (sip_status_code) *sip_status_code = PJSIP_SC_NOT_ACCEPTABLE;
Benny Prijonod8179652008-01-23 20:39:07 +0000866 goto on_error;
Benny Prijono25b2ea12008-01-24 19:20:54 +0000867 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000868
869 *p_sdp = sdp;
870 return PJ_SUCCESS;
871
872on_error:
873 pjsua_media_channel_deinit(call_id);
874 return status;
875
876}
877
878
879static void stop_media_session(pjsua_call_id call_id)
880{
881 pjsua_call *call = &pjsua_var.calls[call_id];
882
883 if (call->conf_slot != PJSUA_INVALID_ID) {
884 pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
885 call->conf_slot = PJSUA_INVALID_ID;
886 }
887
888 if (call->session) {
Benny Prijonofc13bf62008-02-20 08:56:15 +0000889 if (pjsua_var.ua_cfg.cb.on_stream_destroyed) {
890 pjsua_var.ua_cfg.cb.on_stream_destroyed(call_id, call->session, 0);
891 }
892
Benny Prijonoc97608e2007-03-23 16:34:20 +0000893 pjmedia_session_destroy(call->session);
894 call->session = NULL;
895
896 PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
897 call_id));
898
899 }
900
901 call->media_st = PJSUA_CALL_MEDIA_NONE;
902}
903
904pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
905{
906 pjsua_call *call = &pjsua_var.calls[call_id];
907
908 stop_media_session(call_id);
909
Benny Prijonod8179652008-01-23 20:39:07 +0000910 pjmedia_transport_media_stop(call->med_tp);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000911
Benny Prijonod8179652008-01-23 20:39:07 +0000912 if (call->med_tp != call->med_orig) {
913 pjmedia_transport_close(call->med_tp);
914 call->med_tp = call->med_orig;
915 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000916 return PJ_SUCCESS;
917}
918
919
920/*
921 * DTMF callback from the stream.
922 */
923static void dtmf_callback(pjmedia_stream *strm, void *user_data,
924 int digit)
925{
926 PJ_UNUSED_ARG(strm);
927
Benny Prijono0c068262008-02-14 14:38:52 +0000928 /* For discussions about call mutex protection related to this
929 * callback, please see ticket #460:
930 * http://trac.pjsip.org/repos/ticket/460#comment:4
931 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000932 if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
933 pjsua_call_id call_id;
934
Benny Prijonod8179652008-01-23 20:39:07 +0000935 call_id = (pjsua_call_id)(long)user_data;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000936 pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit);
937 }
938}
939
940
941pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
Benny Prijonod8179652008-01-23 20:39:07 +0000942 pjmedia_sdp_session *local_sdp,
Benny Prijonodbce2cf2007-03-28 16:24:00 +0000943 const pjmedia_sdp_session *remote_sdp)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000944{
Benny Prijono91e567e2007-12-28 08:51:58 +0000945 unsigned i;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000946 int prev_media_st = 0;
947 pjsua_call *call = &pjsua_var.calls[call_id];
948 pjmedia_session_info sess_info;
Benny Prijono91e567e2007-12-28 08:51:58 +0000949 pjmedia_stream_info *si = NULL;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000950 pjmedia_port *media_port;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000951 pj_status_t status;
952
953 /* Destroy existing media session, if any. */
954 prev_media_st = call->media_st;
955 stop_media_session(call->index);
956
957 /* Create media session info based on SDP parameters.
Benny Prijonoc97608e2007-03-23 16:34:20 +0000958 */
959 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
960 pjsua_var.med_endpt,
Benny Prijono91e567e2007-12-28 08:51:58 +0000961 PJMEDIA_MAX_SDP_MEDIA, &sess_info,
Benny Prijonoc97608e2007-03-23 16:34:20 +0000962 local_sdp, remote_sdp);
963 if (status != PJ_SUCCESS)
964 return status;
965
Benny Prijono91e567e2007-12-28 08:51:58 +0000966 /* Find which session is audio (we only support audio for now) */
967 for (i=0; i < sess_info.stream_cnt; ++i) {
968 if (sess_info.stream_info[i].type == PJMEDIA_TYPE_AUDIO &&
Benny Prijonod8179652008-01-23 20:39:07 +0000969 (sess_info.stream_info[i].proto == PJMEDIA_TP_PROTO_RTP_AVP ||
970 sess_info.stream_info[i].proto == PJMEDIA_TP_PROTO_RTP_SAVP))
Benny Prijono91e567e2007-12-28 08:51:58 +0000971 {
972 si = &sess_info.stream_info[i];
973 break;
974 }
975 }
976
977 if (si == NULL) {
978 /* Not found */
979 return PJMEDIA_EINVALIMEDIATYPE;
980 }
981
982
983 /* Reset session info with only one media stream */
984 sess_info.stream_cnt = 1;
985 if (si != &sess_info.stream_info[0])
986 pj_memcpy(&sess_info.stream_info[0], si, sizeof(pjmedia_stream_info));
Benny Prijonoc97608e2007-03-23 16:34:20 +0000987
988 /* Check if media is put on-hold */
Benny Prijono91e567e2007-12-28 08:51:58 +0000989 if (sess_info.stream_cnt == 0 || si->dir == PJMEDIA_DIR_NONE)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000990 {
991
992 /* Determine who puts the call on-hold */
993 if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
994 if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
995 /* It was local who offer hold */
996 call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
997 } else {
998 call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
999 }
1000 }
1001
1002 call->media_dir = PJMEDIA_DIR_NONE;
1003
Benny Prijonod8179652008-01-23 20:39:07 +00001004 /* Shutdown transport's session */
1005 pjmedia_transport_media_stop(call->med_tp);
Benny Prijono667952e2007-04-02 19:27:54 +00001006
Benny Prijonoc97608e2007-03-23 16:34:20 +00001007 /* No need because we need keepalive? */
1008
1009 } else {
Benny Prijonod8179652008-01-23 20:39:07 +00001010 /* Start media transport */
1011 status = pjmedia_transport_media_start(call->med_tp,
1012 call->inv->pool,
1013 local_sdp, remote_sdp, 0);
1014 if (status != PJ_SUCCESS)
1015 return status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001016
1017 /* Override ptime, if this option is specified. */
1018 if (pjsua_var.media_cfg.ptime != 0) {
Benny Prijono91e567e2007-12-28 08:51:58 +00001019 si->param->setting.frm_per_pkt = (pj_uint8_t)
1020 (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime);
1021 if (si->param->setting.frm_per_pkt == 0)
1022 si->param->setting.frm_per_pkt = 1;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001023 }
1024
1025 /* Disable VAD, if this option is specified. */
1026 if (pjsua_var.media_cfg.no_vad) {
Benny Prijono91e567e2007-12-28 08:51:58 +00001027 si->param->setting.vad = 0;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001028 }
1029
1030
1031 /* Optionally, application may modify other stream settings here
1032 * (such as jitter buffer parameters, codec ptime, etc.)
1033 */
Benny Prijono91e567e2007-12-28 08:51:58 +00001034 si->jb_init = pjsua_var.media_cfg.jb_init;
1035 si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
1036 si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
1037 si->jb_max = pjsua_var.media_cfg.jb_max;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001038
Benny Prijono8147f402007-11-21 14:50:07 +00001039 /* Set SSRC */
Benny Prijono91e567e2007-12-28 08:51:58 +00001040 si->ssrc = call->ssrc;
Benny Prijono8147f402007-11-21 14:50:07 +00001041
Benny Prijonoc97608e2007-03-23 16:34:20 +00001042 /* Create session based on session info. */
1043 status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
1044 &call->med_tp,
1045 call, &call->session );
1046 if (status != PJ_SUCCESS) {
1047 return status;
1048 }
1049
1050 /* If DTMF callback is installed by application, install our
1051 * callback to the session.
1052 */
1053 if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
1054 pjmedia_session_set_dtmf_callback(call->session, 0,
1055 &dtmf_callback,
Benny Prijonod8179652008-01-23 20:39:07 +00001056 (void*)(long)(call->index));
Benny Prijonoc97608e2007-03-23 16:34:20 +00001057 }
1058
1059 /* Get the port interface of the first stream in the session.
1060 * We need the port interface to add to the conference bridge.
1061 */
1062 pjmedia_session_get_port(call->session, 0, &media_port);
1063
Benny Prijonofc13bf62008-02-20 08:56:15 +00001064 /* Notify application about stream creation.
1065 * Note: application may modify media_port to point to different
1066 * media port
1067 */
1068 if (pjsua_var.ua_cfg.cb.on_stream_created) {
1069 pjsua_var.ua_cfg.cb.on_stream_created(call_id, call->session,
1070 0, &media_port);
1071 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001072
1073 /*
1074 * Add the call to conference bridge.
1075 */
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001076 {
1077 char tmp[PJSIP_MAX_URL_SIZE];
1078 pj_str_t port_name;
1079
1080 port_name.ptr = tmp;
1081 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
1082 call->inv->dlg->remote.info->uri,
1083 tmp, sizeof(tmp));
1084 if (port_name.slen < 1) {
1085 port_name = pj_str("call");
1086 }
1087 status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
1088 media_port,
1089 &port_name,
1090 (unsigned*)&call->conf_slot);
1091 if (status != PJ_SUCCESS) {
1092 return status;
1093 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001094 }
1095
1096 /* Call's media state is active */
1097 call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
Benny Prijono91e567e2007-12-28 08:51:58 +00001098 call->media_dir = si->dir;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001099 }
1100
1101 /* Print info. */
1102 {
1103 char info[80];
1104 int info_len = 0;
1105 unsigned i;
1106
1107 for (i=0; i<sess_info.stream_cnt; ++i) {
1108 int len;
1109 const char *dir;
1110 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1111
1112 switch (strm_info->dir) {
1113 case PJMEDIA_DIR_NONE:
1114 dir = "inactive";
1115 break;
1116 case PJMEDIA_DIR_ENCODING:
1117 dir = "sendonly";
1118 break;
1119 case PJMEDIA_DIR_DECODING:
1120 dir = "recvonly";
1121 break;
1122 case PJMEDIA_DIR_ENCODING_DECODING:
1123 dir = "sendrecv";
1124 break;
1125 default:
1126 dir = "unknown";
1127 break;
1128 }
1129 len = pj_ansi_sprintf( info+info_len,
1130 ", stream #%d: %.*s (%s)", i,
1131 (int)strm_info->fmt.encoding_name.slen,
1132 strm_info->fmt.encoding_name.ptr,
1133 dir);
1134 if (len > 0)
1135 info_len += len;
1136 }
1137 PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
1138 }
1139
1140 return PJ_SUCCESS;
1141}
1142
1143
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001144/*
1145 * Get maxinum number of conference ports.
1146 */
1147PJ_DEF(unsigned) pjsua_conf_get_max_ports(void)
1148{
1149 return pjsua_var.media_cfg.max_media_ports;
1150}
1151
1152
1153/*
1154 * Get current number of active ports in the bridge.
1155 */
1156PJ_DEF(unsigned) pjsua_conf_get_active_ports(void)
1157{
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001158 unsigned ports[PJSUA_MAX_CONF_PORTS];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001159 unsigned count = PJ_ARRAY_SIZE(ports);
1160 pj_status_t status;
1161
1162 status = pjmedia_conf_enum_ports(pjsua_var.mconf, ports, &count);
1163 if (status != PJ_SUCCESS)
1164 count = 0;
1165
1166 return count;
1167}
1168
1169
1170/*
1171 * Enumerate all conference ports.
1172 */
1173PJ_DEF(pj_status_t) pjsua_enum_conf_ports(pjsua_conf_port_id id[],
1174 unsigned *count)
1175{
1176 return pjmedia_conf_enum_ports(pjsua_var.mconf, (unsigned*)id, count);
1177}
1178
1179
1180/*
1181 * Get information about the specified conference port
1182 */
1183PJ_DEF(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id,
1184 pjsua_conf_port_info *info)
1185{
1186 pjmedia_conf_port_info cinfo;
Benny Prijono0498d902006-06-19 14:49:14 +00001187 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001188 pj_status_t status;
1189
1190 status = pjmedia_conf_get_port_info( pjsua_var.mconf, id, &cinfo);
1191 if (status != PJ_SUCCESS)
1192 return status;
1193
Benny Prijonoac623b32006-07-03 15:19:31 +00001194 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001195 info->slot_id = id;
1196 info->name = cinfo.name;
1197 info->clock_rate = cinfo.clock_rate;
1198 info->channel_count = cinfo.channel_count;
1199 info->samples_per_frame = cinfo.samples_per_frame;
1200 info->bits_per_sample = cinfo.bits_per_sample;
1201
1202 /* Build array of listeners */
Benny Prijonoc78c3a32006-06-16 15:54:43 +00001203 info->listener_cnt = cinfo.listener_cnt;
1204 for (i=0; i<cinfo.listener_cnt; ++i) {
1205 info->listeners[i] = cinfo.listener_slots[i];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001206 }
1207
1208 return PJ_SUCCESS;
1209}
1210
1211
1212/*
Benny Prijonoe909eac2006-07-27 22:04:56 +00001213 * Add arbitrary media port to PJSUA's conference bridge.
1214 */
1215PJ_DEF(pj_status_t) pjsua_conf_add_port( pj_pool_t *pool,
1216 pjmedia_port *port,
1217 pjsua_conf_port_id *p_id)
1218{
1219 pj_status_t status;
1220
1221 status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
1222 port, NULL, (unsigned*)p_id);
1223 if (status != PJ_SUCCESS) {
1224 if (p_id)
1225 *p_id = PJSUA_INVALID_ID;
1226 }
1227
1228 return status;
1229}
1230
1231
1232/*
1233 * Remove arbitrary slot from the conference bridge.
1234 */
1235PJ_DEF(pj_status_t) pjsua_conf_remove_port(pjsua_conf_port_id id)
1236{
1237 return pjmedia_conf_remove_port(pjsua_var.mconf, (unsigned)id);
1238}
1239
1240
1241/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001242 * Establish unidirectional media flow from souce to sink.
1243 */
1244PJ_DEF(pj_status_t) pjsua_conf_connect( pjsua_conf_port_id source,
1245 pjsua_conf_port_id sink)
1246{
1247 return pjmedia_conf_connect_port(pjsua_var.mconf, source, sink, 0);
1248}
1249
1250
1251/*
1252 * Disconnect media flow from the source to destination port.
1253 */
1254PJ_DEF(pj_status_t) pjsua_conf_disconnect( pjsua_conf_port_id source,
1255 pjsua_conf_port_id sink)
1256{
1257 return pjmedia_conf_disconnect_port(pjsua_var.mconf, source, sink);
1258}
1259
1260
Benny Prijono6dd967c2006-12-26 02:27:14 +00001261/*
1262 * Adjust the signal level to be transmitted from the bridge to the
1263 * specified port by making it louder or quieter.
1264 */
1265PJ_DEF(pj_status_t) pjsua_conf_adjust_tx_level(pjsua_conf_port_id slot,
1266 float level)
1267{
1268 return pjmedia_conf_adjust_tx_level(pjsua_var.mconf, slot,
1269 (int)((level-1) * 128));
1270}
1271
1272/*
1273 * Adjust the signal level to be received from the specified port (to
1274 * the bridge) by making it louder or quieter.
1275 */
1276PJ_DEF(pj_status_t) pjsua_conf_adjust_rx_level(pjsua_conf_port_id slot,
1277 float level)
1278{
1279 return pjmedia_conf_adjust_rx_level(pjsua_var.mconf, slot,
1280 (int)((level-1) * 128));
1281}
1282
1283
1284/*
1285 * Get last signal level transmitted to or received from the specified port.
1286 */
1287PJ_DEF(pj_status_t) pjsua_conf_get_signal_level(pjsua_conf_port_id slot,
1288 unsigned *tx_level,
1289 unsigned *rx_level)
1290{
1291 return pjmedia_conf_get_signal_level(pjsua_var.mconf, slot,
1292 tx_level, rx_level);
1293}
1294
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001295/*****************************************************************************
1296 * File player.
1297 */
1298
Benny Prijonod5696da2007-07-17 16:25:45 +00001299static char* get_basename(const char *path, unsigned len)
1300{
1301 char *p = ((char*)path) + len;
1302
1303 if (len==0)
1304 return p;
1305
Benny Prijono1f61a8f2007-08-16 10:11:44 +00001306 for (--p; p!=path && *p!='/' && *p!='\\'; ) --p;
Benny Prijonod5696da2007-07-17 16:25:45 +00001307
1308 return (p==path) ? p : p+1;
1309}
1310
1311
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001312/*
1313 * Create a file player, and automatically connect this player to
1314 * the conference bridge.
1315 */
1316PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename,
1317 unsigned options,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001318 pjsua_player_id *p_id)
1319{
1320 unsigned slot, file_id;
Benny Prijonoa66c3312007-01-21 23:12:40 +00001321 char path[PJ_MAXPATH];
Benny Prijonod5696da2007-07-17 16:25:45 +00001322 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001323 pjmedia_port *port;
1324 pj_status_t status;
1325
1326 if (pjsua_var.player_cnt >= PJ_ARRAY_SIZE(pjsua_var.player))
1327 return PJ_ETOOMANY;
1328
1329 PJSUA_LOCK();
1330
1331 for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.player); ++file_id) {
1332 if (pjsua_var.player[file_id].port == NULL)
1333 break;
1334 }
1335
1336 if (file_id == PJ_ARRAY_SIZE(pjsua_var.player)) {
1337 /* This is unexpected */
1338 PJSUA_UNLOCK();
1339 pj_assert(0);
1340 return PJ_EBUG;
1341 }
1342
1343 pj_memcpy(path, filename->ptr, filename->slen);
1344 path[filename->slen] = '\0';
Benny Prijonod5696da2007-07-17 16:25:45 +00001345
1346 pool = pjsua_pool_create(get_basename(path, filename->slen), 1000, 1000);
1347 if (!pool) {
1348 PJSUA_UNLOCK();
1349 return PJ_ENOMEM;
1350 }
1351
1352 status = pjmedia_wav_player_port_create(pool, path,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001353 pjsua_var.mconf_cfg.samples_per_frame *
1354 1000 / pjsua_var.media_cfg.clock_rate,
Benny Prijono00cae612006-07-31 15:19:36 +00001355 options, 0, &port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001356 if (status != PJ_SUCCESS) {
1357 PJSUA_UNLOCK();
1358 pjsua_perror(THIS_FILE, "Unable to open file for playback", status);
Benny Prijonod5696da2007-07-17 16:25:45 +00001359 pj_pool_release(pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001360 return status;
1361 }
1362
1363 status = pjmedia_conf_add_port(pjsua_var.mconf, pjsua_var.pool,
1364 port, filename, &slot);
1365 if (status != PJ_SUCCESS) {
1366 pjmedia_port_destroy(port);
1367 PJSUA_UNLOCK();
Benny Prijono32e4f492007-01-24 00:44:26 +00001368 pjsua_perror(THIS_FILE, "Unable to add file to conference bridge",
1369 status);
Benny Prijonod5696da2007-07-17 16:25:45 +00001370 pj_pool_release(pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001371 return status;
1372 }
1373
Benny Prijonoa66c3312007-01-21 23:12:40 +00001374 pjsua_var.player[file_id].type = 0;
Benny Prijonod5696da2007-07-17 16:25:45 +00001375 pjsua_var.player[file_id].pool = pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001376 pjsua_var.player[file_id].port = port;
1377 pjsua_var.player[file_id].slot = slot;
1378
1379 if (p_id) *p_id = file_id;
1380
1381 ++pjsua_var.player_cnt;
1382
1383 PJSUA_UNLOCK();
1384 return PJ_SUCCESS;
1385}
1386
1387
1388/*
Benny Prijonoa66c3312007-01-21 23:12:40 +00001389 * Create a file playlist media port, and automatically add the port
1390 * to the conference bridge.
1391 */
1392PJ_DEF(pj_status_t) pjsua_playlist_create( const pj_str_t file_names[],
1393 unsigned file_count,
1394 const pj_str_t *label,
1395 unsigned options,
1396 pjsua_player_id *p_id)
1397{
1398 unsigned slot, file_id, ptime;
Benny Prijonod5696da2007-07-17 16:25:45 +00001399 pj_pool_t *pool;
Benny Prijonoa66c3312007-01-21 23:12:40 +00001400 pjmedia_port *port;
1401 pj_status_t status;
1402
1403 if (pjsua_var.player_cnt >= PJ_ARRAY_SIZE(pjsua_var.player))
1404 return PJ_ETOOMANY;
1405
1406 PJSUA_LOCK();
1407
1408 for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.player); ++file_id) {
1409 if (pjsua_var.player[file_id].port == NULL)
1410 break;
1411 }
1412
1413 if (file_id == PJ_ARRAY_SIZE(pjsua_var.player)) {
1414 /* This is unexpected */
1415 PJSUA_UNLOCK();
1416 pj_assert(0);
1417 return PJ_EBUG;
1418 }
1419
1420
1421 ptime = pjsua_var.mconf_cfg.samples_per_frame * 1000 /
1422 pjsua_var.media_cfg.clock_rate;
1423
Benny Prijonod5696da2007-07-17 16:25:45 +00001424 pool = pjsua_pool_create("playlist", 1000, 1000);
1425 if (!pool) {
1426 PJSUA_UNLOCK();
1427 return PJ_ENOMEM;
1428 }
1429
1430 status = pjmedia_wav_playlist_create(pool, label,
Benny Prijonoa66c3312007-01-21 23:12:40 +00001431 file_names, file_count,
1432 ptime, options, 0, &port);
1433 if (status != PJ_SUCCESS) {
1434 PJSUA_UNLOCK();
1435 pjsua_perror(THIS_FILE, "Unable to create playlist", status);
Benny Prijonod5696da2007-07-17 16:25:45 +00001436 pj_pool_release(pool);
Benny Prijonoa66c3312007-01-21 23:12:40 +00001437 return status;
1438 }
1439
Benny Prijonod5696da2007-07-17 16:25:45 +00001440 status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
Benny Prijonoa66c3312007-01-21 23:12:40 +00001441 port, &port->info.name, &slot);
1442 if (status != PJ_SUCCESS) {
1443 pjmedia_port_destroy(port);
1444 PJSUA_UNLOCK();
1445 pjsua_perror(THIS_FILE, "Unable to add port", status);
Benny Prijonod5696da2007-07-17 16:25:45 +00001446 pj_pool_release(pool);
Benny Prijonoa66c3312007-01-21 23:12:40 +00001447 return status;
1448 }
1449
1450 pjsua_var.player[file_id].type = 1;
Benny Prijonod5696da2007-07-17 16:25:45 +00001451 pjsua_var.player[file_id].pool = pool;
Benny Prijonoa66c3312007-01-21 23:12:40 +00001452 pjsua_var.player[file_id].port = port;
1453 pjsua_var.player[file_id].slot = slot;
1454
1455 if (p_id) *p_id = file_id;
1456
1457 ++pjsua_var.player_cnt;
1458
1459 PJSUA_UNLOCK();
1460 return PJ_SUCCESS;
1461
1462}
1463
1464
1465/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001466 * Get conference port ID associated with player.
1467 */
1468PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id)
1469{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001470 PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001471 PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
1472
1473 return pjsua_var.player[id].slot;
1474}
1475
Benny Prijono469b1522006-12-26 03:05:17 +00001476/*
1477 * Get the media port for the player.
1478 */
Benny Prijonobe41d862008-01-18 13:24:28 +00001479PJ_DEF(pj_status_t) pjsua_player_get_port( pjsua_player_id id,
Benny Prijono469b1522006-12-26 03:05:17 +00001480 pjmedia_port **p_port)
1481{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001482 PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL);
Benny Prijono469b1522006-12-26 03:05:17 +00001483 PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
1484 PJ_ASSERT_RETURN(p_port != NULL, PJ_EINVAL);
1485
1486 *p_port = pjsua_var.player[id].port;
1487
1488 return PJ_SUCCESS;
1489}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001490
1491/*
1492 * Set playback position.
1493 */
1494PJ_DEF(pj_status_t) pjsua_player_set_pos( pjsua_player_id id,
1495 pj_uint32_t samples)
1496{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001497 PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001498 PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
Benny Prijonoa66c3312007-01-21 23:12:40 +00001499 PJ_ASSERT_RETURN(pjsua_var.player[id].type == 0, PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001500
1501 return pjmedia_wav_player_port_set_pos(pjsua_var.player[id].port, samples);
1502}
1503
1504
1505/*
1506 * Close the file, remove the player from the bridge, and free
1507 * resources associated with the file player.
1508 */
1509PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id)
1510{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001511 PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512 PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
1513
1514 PJSUA_LOCK();
1515
1516 if (pjsua_var.player[id].port) {
1517 pjmedia_conf_remove_port(pjsua_var.mconf,
1518 pjsua_var.player[id].slot);
1519 pjmedia_port_destroy(pjsua_var.player[id].port);
1520 pjsua_var.player[id].port = NULL;
1521 pjsua_var.player[id].slot = 0xFFFF;
Benny Prijonod5696da2007-07-17 16:25:45 +00001522 pj_pool_release(pjsua_var.player[id].pool);
1523 pjsua_var.player[id].pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001524 pjsua_var.player_cnt--;
1525 }
1526
1527 PJSUA_UNLOCK();
1528
1529 return PJ_SUCCESS;
1530}
1531
1532
1533/*****************************************************************************
1534 * File recorder.
1535 */
1536
1537/*
1538 * Create a file recorder, and automatically connect this recorder to
1539 * the conference bridge.
1540 */
1541PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,
Benny Prijono8f310522006-10-20 11:08:49 +00001542 unsigned enc_type,
1543 void *enc_param,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001544 pj_ssize_t max_size,
1545 unsigned options,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001546 pjsua_recorder_id *p_id)
1547{
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001548 enum Format
1549 {
1550 FMT_UNKNOWN,
1551 FMT_WAV,
1552 FMT_MP3,
1553 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001554 unsigned slot, file_id;
Benny Prijonoa66c3312007-01-21 23:12:40 +00001555 char path[PJ_MAXPATH];
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001556 pj_str_t ext;
Benny Prijono8f310522006-10-20 11:08:49 +00001557 int file_format;
Benny Prijonod5696da2007-07-17 16:25:45 +00001558 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001559 pjmedia_port *port;
1560 pj_status_t status;
1561
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001562 /* Filename must present */
1563 PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);
1564
Benny Prijono00cae612006-07-31 15:19:36 +00001565 /* Don't support max_size at present */
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001566 PJ_ASSERT_RETURN(max_size == 0 || max_size == -1, PJ_EINVAL);
Benny Prijono00cae612006-07-31 15:19:36 +00001567
Benny Prijono8f310522006-10-20 11:08:49 +00001568 /* Don't support encoding type at present */
1569 PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL);
Benny Prijono00cae612006-07-31 15:19:36 +00001570
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001571 if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder))
1572 return PJ_ETOOMANY;
1573
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001574 /* Determine the file format */
1575 ext.ptr = filename->ptr + filename->slen - 4;
1576 ext.slen = 4;
1577
1578 if (pj_stricmp2(&ext, ".wav") == 0)
1579 file_format = FMT_WAV;
1580 else if (pj_stricmp2(&ext, ".mp3") == 0)
1581 file_format = FMT_MP3;
1582 else {
1583 PJ_LOG(1,(THIS_FILE, "pjsua_recorder_create() error: unable to "
1584 "determine file format for %.*s",
1585 (int)filename->slen, filename->ptr));
1586 return PJ_ENOTSUP;
1587 }
1588
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001589 PJSUA_LOCK();
1590
1591 for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) {
1592 if (pjsua_var.recorder[file_id].port == NULL)
1593 break;
1594 }
1595
1596 if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {
1597 /* This is unexpected */
1598 PJSUA_UNLOCK();
1599 pj_assert(0);
1600 return PJ_EBUG;
1601 }
1602
1603 pj_memcpy(path, filename->ptr, filename->slen);
1604 path[filename->slen] = '\0';
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001605
Benny Prijonod5696da2007-07-17 16:25:45 +00001606 pool = pjsua_pool_create(get_basename(path, filename->slen), 1000, 1000);
1607 if (!pool) {
1608 PJSUA_UNLOCK();
1609 return PJ_ENOMEM;
1610 }
1611
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001612 if (file_format == FMT_WAV) {
Benny Prijonod5696da2007-07-17 16:25:45 +00001613 status = pjmedia_wav_writer_port_create(pool, path,
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001614 pjsua_var.media_cfg.clock_rate,
1615 pjsua_var.mconf_cfg.channel_count,
1616 pjsua_var.mconf_cfg.samples_per_frame,
1617 pjsua_var.mconf_cfg.bits_per_sample,
1618 options, 0, &port);
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001619 } else {
Benny Prijonoc95a0f02007-04-09 07:06:08 +00001620 PJ_UNUSED_ARG(enc_param);
Benny Prijonob3cdb2b2006-10-19 15:49:47 +00001621 port = NULL;
1622 status = PJ_ENOTSUP;
1623 }
1624
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625 if (status != PJ_SUCCESS) {
1626 PJSUA_UNLOCK();
1627 pjsua_perror(THIS_FILE, "Unable to open file for recording", status);
Benny Prijonod5696da2007-07-17 16:25:45 +00001628 pj_pool_release(pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001629 return status;
1630 }
1631
Benny Prijonod5696da2007-07-17 16:25:45 +00001632 status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001633 port, filename, &slot);
1634 if (status != PJ_SUCCESS) {
1635 pjmedia_port_destroy(port);
1636 PJSUA_UNLOCK();
Benny Prijonod5696da2007-07-17 16:25:45 +00001637 pj_pool_release(pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001638 return status;
1639 }
1640
1641 pjsua_var.recorder[file_id].port = port;
1642 pjsua_var.recorder[file_id].slot = slot;
Benny Prijonod5696da2007-07-17 16:25:45 +00001643 pjsua_var.recorder[file_id].pool = pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644
1645 if (p_id) *p_id = file_id;
1646
1647 ++pjsua_var.rec_cnt;
1648
1649 PJSUA_UNLOCK();
1650 return PJ_SUCCESS;
1651}
1652
1653
1654/*
1655 * Get conference port associated with recorder.
1656 */
1657PJ_DEF(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id)
1658{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001659 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),
1660 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001661 PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL);
1662
1663 return pjsua_var.recorder[id].slot;
1664}
1665
Benny Prijono469b1522006-12-26 03:05:17 +00001666/*
1667 * Get the media port for the recorder.
1668 */
1669PJ_DEF(pj_status_t) pjsua_recorder_get_port( pjsua_recorder_id id,
1670 pjmedia_port **p_port)
1671{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001672 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),
1673 PJ_EINVAL);
Benny Prijono469b1522006-12-26 03:05:17 +00001674 PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL);
1675 PJ_ASSERT_RETURN(p_port != NULL, PJ_EINVAL);
1676
1677 *p_port = pjsua_var.recorder[id].port;
1678 return PJ_SUCCESS;
1679}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001680
1681/*
1682 * Destroy recorder (this will complete recording).
1683 */
1684PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id)
1685{
Benny Prijonoa1e69682007-05-11 15:14:34 +00001686 PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),
1687 PJ_EINVAL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001688 PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL);
1689
1690 PJSUA_LOCK();
1691
1692 if (pjsua_var.recorder[id].port) {
1693 pjmedia_conf_remove_port(pjsua_var.mconf,
1694 pjsua_var.recorder[id].slot);
1695 pjmedia_port_destroy(pjsua_var.recorder[id].port);
1696 pjsua_var.recorder[id].port = NULL;
1697 pjsua_var.recorder[id].slot = 0xFFFF;
Benny Prijonod5696da2007-07-17 16:25:45 +00001698 pj_pool_release(pjsua_var.recorder[id].pool);
1699 pjsua_var.recorder[id].pool = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700 pjsua_var.rec_cnt--;
1701 }
1702
1703 PJSUA_UNLOCK();
1704
1705 return PJ_SUCCESS;
1706}
1707
1708
1709/*****************************************************************************
1710 * Sound devices.
1711 */
1712
1713/*
1714 * Enum sound devices.
1715 */
1716PJ_DEF(pj_status_t) pjsua_enum_snd_devs( pjmedia_snd_dev_info info[],
1717 unsigned *count)
1718{
1719 unsigned i, dev_count;
1720
1721 dev_count = pjmedia_snd_get_dev_count();
1722
1723 if (dev_count > *count) dev_count = *count;
1724
1725 for (i=0; i<dev_count; ++i) {
1726 const pjmedia_snd_dev_info *ci;
1727
1728 ci = pjmedia_snd_get_dev_info(i);
1729 pj_memcpy(&info[i], ci, sizeof(*ci));
1730 }
1731
1732 *count = dev_count;
1733
1734 return PJ_SUCCESS;
1735}
1736
1737
1738/* Close existing sound device */
1739static void close_snd_dev(void)
1740{
1741 /* Close sound device */
1742 if (pjsua_var.snd_port) {
1743 const pjmedia_snd_dev_info *cap_info, *play_info;
1744
1745 cap_info = pjmedia_snd_get_dev_info(pjsua_var.cap_dev);
1746 play_info = pjmedia_snd_get_dev_info(pjsua_var.play_dev);
1747
1748 PJ_LOG(4,(THIS_FILE, "Closing %s sound playback device and "
1749 "%s sound capture device",
1750 play_info->name, cap_info->name));
1751
1752 pjmedia_snd_port_disconnect(pjsua_var.snd_port);
1753 pjmedia_snd_port_destroy(pjsua_var.snd_port);
1754 pjsua_var.snd_port = NULL;
1755 }
1756
1757 /* Close null sound device */
1758 if (pjsua_var.null_snd) {
1759 PJ_LOG(4,(THIS_FILE, "Closing null sound device.."));
1760 pjmedia_master_port_destroy(pjsua_var.null_snd, PJ_FALSE);
1761 pjsua_var.null_snd = NULL;
1762 }
1763}
1764
1765/*
1766 * Select or change sound device. Application may call this function at
1767 * any time to replace current sound device.
1768 */
1769PJ_DEF(pj_status_t) pjsua_set_snd_dev( int capture_dev,
1770 int playback_dev)
1771{
1772 pjmedia_port *conf_port;
Benny Prijono6dd967c2006-12-26 02:27:14 +00001773 const pjmedia_snd_dev_info *play_info;
Benny Prijonof3758ee2008-02-26 15:32:16 +00001774 unsigned clock_rates[] = {0, 22050, 44100, 48000, 32000, 16000,
1775 8000};
Benny Prijono658a1c52006-10-11 21:56:16 +00001776 unsigned selected_clock_rate = 0;
Benny Prijono26056d82006-10-11 16:03:41 +00001777 unsigned i;
Benny Prijonoc53c6d72006-11-27 09:54:03 +00001778 pjmedia_snd_stream *strm;
1779 pjmedia_snd_stream_info si;
1780 pj_str_t tmp;
Benny Prijono26056d82006-10-11 16:03:41 +00001781 pj_status_t status = -1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001782
1783 /* Close existing sound port */
1784 close_snd_dev();
1785
1786
Benny Prijono26056d82006-10-11 16:03:41 +00001787 /* Set default clock rate */
Benny Prijonof3758ee2008-02-26 15:32:16 +00001788 clock_rates[0] = pjsua_var.media_cfg.snd_clock_rate;
1789 if (clock_rates[0] == 0)
1790 clock_rates[0] = pjsua_var.media_cfg.clock_rate;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001791
Benny Prijono26056d82006-10-11 16:03:41 +00001792 /* Attempts to open the sound device with different clock rates */
1793 for (i=0; i<PJ_ARRAY_SIZE(clock_rates); ++i) {
1794 char errmsg[PJ_ERR_MSG_SIZE];
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001795 unsigned fps;
Benny Prijono26056d82006-10-11 16:03:41 +00001796
1797 PJ_LOG(4,(THIS_FILE,
1798 "pjsua_set_snd_dev(): attempting to open devices "
1799 "@%d Hz", clock_rates[i]));
1800
1801 /* Create the sound device. Sound port will start immediately. */
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001802 fps = 1000 / pjsua_var.media_cfg.audio_frame_ptime;
Benny Prijono26056d82006-10-11 16:03:41 +00001803 status = pjmedia_snd_port_create(pjsua_var.pool, capture_dev,
1804 playback_dev,
1805 clock_rates[i], 1,
Benny Prijonocf0b4b22007-10-06 17:31:09 +00001806 clock_rates[i]/fps,
Benny Prijono26056d82006-10-11 16:03:41 +00001807 16, 0, &pjsua_var.snd_port);
1808
Benny Prijono658a1c52006-10-11 21:56:16 +00001809 if (status == PJ_SUCCESS) {
1810 selected_clock_rate = clock_rates[i];
Benny Prijono26056d82006-10-11 16:03:41 +00001811 break;
Benny Prijono658a1c52006-10-11 21:56:16 +00001812 }
Benny Prijono26056d82006-10-11 16:03:41 +00001813
1814 pj_strerror(status, errmsg, sizeof(errmsg));
Benny Prijono658a1c52006-10-11 21:56:16 +00001815 PJ_LOG(4, (THIS_FILE, "..failed: %s", errmsg));
Benny Prijono26056d82006-10-11 16:03:41 +00001816 }
1817
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001818 if (status != PJ_SUCCESS) {
1819 pjsua_perror(THIS_FILE, "Unable to open sound device", status);
1820 return status;
1821 }
1822
1823 /* Get the port0 of the conference bridge. */
1824 conf_port = pjmedia_conf_get_master_port(pjsua_var.mconf);
1825 pj_assert(conf_port != NULL);
1826
Benny Prijonof20687a2006-08-04 18:27:19 +00001827 /* Set AEC */
Benny Prijono5da50432006-08-07 10:24:52 +00001828 pjmedia_snd_port_set_ec( pjsua_var.snd_port, pjsua_var.pool,
1829 pjsua_var.media_cfg.ec_tail_len,
1830 pjsua_var.media_cfg.ec_options);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001831
Benny Prijono658a1c52006-10-11 21:56:16 +00001832 /* If there's mismatch between sound port and conference's port,
1833 * create a resample port to bridge them.
1834 */
1835 if (selected_clock_rate != pjsua_var.media_cfg.clock_rate) {
1836 pjmedia_port *resample_port;
Benny Prijonof3758ee2008-02-26 15:32:16 +00001837 unsigned resample_opt = 0;
Benny Prijono658a1c52006-10-11 21:56:16 +00001838
Benny Prijonof3758ee2008-02-26 15:32:16 +00001839 if (pjsua_var.media_cfg.quality >= 3 &&
1840 pjsua_var.media_cfg.quality <= 4)
1841 {
1842 resample_opt |= PJMEDIA_CONF_SMALL_FILTER;
1843 }
1844 else if (pjsua_var.media_cfg.quality < 3) {
1845 resample_opt |= PJMEDIA_CONF_USE_LINEAR;
1846 }
1847
Benny Prijono658a1c52006-10-11 21:56:16 +00001848 status = pjmedia_resample_port_create(pjsua_var.pool, conf_port,
Benny Prijonof3758ee2008-02-26 15:32:16 +00001849 selected_clock_rate,
1850 resample_opt, &resample_port);
Benny Prijono658a1c52006-10-11 21:56:16 +00001851 if (status != PJ_SUCCESS) {
1852 pjsua_perror("Error creating resample port", THIS_FILE, status);
1853 return status;
1854 }
1855
1856 conf_port = resample_port;
1857 }
1858
Benny Prijono52a93912006-08-04 20:54:37 +00001859 /* Connect sound port to the bridge */
1860 status = pjmedia_snd_port_connect(pjsua_var.snd_port,
1861 conf_port );
1862 if (status != PJ_SUCCESS) {
1863 pjsua_perror(THIS_FILE, "Unable to connect conference port to "
1864 "sound device", status);
1865 pjmedia_snd_port_destroy(pjsua_var.snd_port);
1866 pjsua_var.snd_port = NULL;
1867 return status;
1868 }
1869
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001870 /* Save the device IDs */
1871 pjsua_var.cap_dev = capture_dev;
1872 pjsua_var.play_dev = playback_dev;
1873
Benny Prijonoc53c6d72006-11-27 09:54:03 +00001874 /* Update sound device name. */
1875 strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port);
1876 pjmedia_snd_stream_get_info(strm, &si);
1877 play_info = pjmedia_snd_get_dev_info(si.rec_id);
1878
Benny Prijonof3758ee2008-02-26 15:32:16 +00001879 if (si.clock_rate != pjsua_var.media_cfg.clock_rate) {
1880 char tmp_buf[128];
1881 int tmp_buf_len = sizeof(tmp_buf);
1882
1883 tmp_buf_len = pj_ansi_snprintf(tmp_buf, sizeof(tmp_buf)-1, "%s (%dKHz)",
1884 play_info->name, si.clock_rate/1000);
1885 pj_strset(&tmp, tmp_buf, tmp_buf_len);
1886 pjmedia_conf_set_port0_name(pjsua_var.mconf, &tmp);
1887 } else {
1888 pjmedia_conf_set_port0_name(pjsua_var.mconf,
1889 pj_cstr(&tmp, play_info->name));
1890 }
Benny Prijonoc53c6d72006-11-27 09:54:03 +00001891
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001892 return PJ_SUCCESS;
1893}
1894
1895
1896/*
Benny Prijonoebdf8772007-02-01 19:25:50 +00001897 * Get currently active sound devices. If sound devices has not been created
1898 * (for example when pjsua_start() is not called), it is possible that
1899 * the function returns PJ_SUCCESS with -1 as device IDs.
1900 */
1901PJ_DEF(pj_status_t) pjsua_get_snd_dev(int *capture_dev,
1902 int *playback_dev)
1903{
1904 if (capture_dev) {
1905 *capture_dev = pjsua_var.cap_dev;
1906 }
1907 if (playback_dev) {
1908 *playback_dev = pjsua_var.play_dev;
1909 }
1910
1911 return PJ_SUCCESS;
1912}
1913
1914
1915/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001916 * Use null sound device.
1917 */
1918PJ_DEF(pj_status_t) pjsua_set_null_snd_dev(void)
1919{
1920 pjmedia_port *conf_port;
1921 pj_status_t status;
1922
1923 /* Close existing sound device */
1924 close_snd_dev();
1925
1926 PJ_LOG(4,(THIS_FILE, "Opening null sound device.."));
1927
1928 /* Get the port0 of the conference bridge. */
1929 conf_port = pjmedia_conf_get_master_port(pjsua_var.mconf);
1930 pj_assert(conf_port != NULL);
1931
1932 /* Create master port, connecting port0 of the conference bridge to
1933 * a null port.
1934 */
1935 status = pjmedia_master_port_create(pjsua_var.pool, pjsua_var.null_port,
1936 conf_port, 0, &pjsua_var.null_snd);
1937 if (status != PJ_SUCCESS) {
1938 pjsua_perror(THIS_FILE, "Unable to create null sound device",
1939 status);
1940 return status;
1941 }
1942
1943 /* Start the master port */
1944 status = pjmedia_master_port_start(pjsua_var.null_snd);
1945 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
1946
1947 return PJ_SUCCESS;
1948}
1949
1950
Benny Prijonoe909eac2006-07-27 22:04:56 +00001951
1952/*
1953 * Use no device!
1954 */
1955PJ_DEF(pjmedia_port*) pjsua_set_no_snd_dev(void)
1956{
1957 /* Close existing sound device */
1958 close_snd_dev();
1959
1960 pjsua_var.no_snd = PJ_TRUE;
1961 return pjmedia_conf_get_master_port(pjsua_var.mconf);
1962}
1963
1964
Benny Prijonof20687a2006-08-04 18:27:19 +00001965/*
1966 * Configure the AEC settings of the sound port.
1967 */
Benny Prijono5da50432006-08-07 10:24:52 +00001968PJ_DEF(pj_status_t) pjsua_set_ec(unsigned tail_ms, unsigned options)
Benny Prijonof20687a2006-08-04 18:27:19 +00001969{
1970 pjsua_var.media_cfg.ec_tail_len = tail_ms;
1971
1972 if (pjsua_var.snd_port)
Benny Prijono5da50432006-08-07 10:24:52 +00001973 return pjmedia_snd_port_set_ec( pjsua_var.snd_port, pjsua_var.pool,
1974 tail_ms, options);
Benny Prijonof20687a2006-08-04 18:27:19 +00001975
1976 return PJ_SUCCESS;
1977}
1978
1979
1980/*
1981 * Get current AEC tail length.
1982 */
Benny Prijono22dfe592006-08-06 12:07:13 +00001983PJ_DEF(pj_status_t) pjsua_get_ec_tail(unsigned *p_tail_ms)
Benny Prijonof20687a2006-08-04 18:27:19 +00001984{
1985 *p_tail_ms = pjsua_var.media_cfg.ec_tail_len;
1986 return PJ_SUCCESS;
1987}
1988
Benny Prijonoe909eac2006-07-27 22:04:56 +00001989
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001990/*****************************************************************************
1991 * Codecs.
1992 */
1993
1994/*
1995 * Enum all supported codecs in the system.
1996 */
1997PJ_DEF(pj_status_t) pjsua_enum_codecs( pjsua_codec_info id[],
1998 unsigned *p_count )
1999{
2000 pjmedia_codec_mgr *codec_mgr;
2001 pjmedia_codec_info info[32];
2002 unsigned i, count, prio[32];
2003 pj_status_t status;
2004
2005 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2006 count = PJ_ARRAY_SIZE(info);
2007 status = pjmedia_codec_mgr_enum_codecs( codec_mgr, &count, info, prio);
2008 if (status != PJ_SUCCESS) {
2009 *p_count = 0;
2010 return status;
2011 }
2012
2013 if (count > *p_count) count = *p_count;
2014
2015 for (i=0; i<count; ++i) {
2016 pjmedia_codec_info_to_id(&info[i], id[i].buf_, sizeof(id[i].buf_));
2017 id[i].codec_id = pj_str(id[i].buf_);
2018 id[i].priority = (pj_uint8_t) prio[i];
2019 }
2020
2021 *p_count = count;
2022
2023 return PJ_SUCCESS;
2024}
2025
2026
2027/*
2028 * Change codec priority.
2029 */
2030PJ_DEF(pj_status_t) pjsua_codec_set_priority( const pj_str_t *codec_id,
2031 pj_uint8_t priority )
2032{
2033 pjmedia_codec_mgr *codec_mgr;
2034
2035 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2036
2037 return pjmedia_codec_mgr_set_codec_priority(codec_mgr, codec_id,
2038 priority);
2039}
2040
2041
2042/*
2043 * Get codec parameters.
2044 */
2045PJ_DEF(pj_status_t) pjsua_codec_get_param( const pj_str_t *codec_id,
2046 pjmedia_codec_param *param )
2047{
2048 const pjmedia_codec_info *info;
2049 pjmedia_codec_mgr *codec_mgr;
2050 unsigned count = 1;
2051 pj_status_t status;
2052
2053 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2054
2055 status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
2056 &count, &info, NULL);
2057 if (status != PJ_SUCCESS)
2058 return status;
2059
2060 if (count != 1)
2061 return PJ_ENOTFOUND;
2062
2063 status = pjmedia_codec_mgr_get_default_param( codec_mgr, info, param);
2064 return status;
2065}
2066
2067
2068/*
2069 * Set codec parameters.
2070 */
2071PJ_DEF(pj_status_t) pjsua_codec_set_param( const pj_str_t *id,
2072 const pjmedia_codec_param *param)
2073{
Benny Prijono00cae612006-07-31 15:19:36 +00002074 PJ_UNUSED_ARG(id);
2075 PJ_UNUSED_ARG(param);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002076 PJ_TODO(set_codec_param);
2077 return PJ_SUCCESS;
2078}