blob: 1a9d2eb6a0d2fb61650a2a294d9ff0ef5373e5ed [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia/endpoint.h>
21#include <pjmedia/errno.h>
22#include <pjmedia/sdp.h>
23#include <pjmedia/vid_codec.h>
24#include <pjmedia-audiodev/audiodev.h>
25#include <pj/assert.h>
26#include <pj/ioqueue.h>
27#include <pj/lock.h>
28#include <pj/log.h>
29#include <pj/os.h>
30#include <pj/pool.h>
31#include <pj/sock.h>
32#include <pj/string.h>
33
34
35#define THIS_FILE "endpoint.c"
36
37static const pj_str_t STR_AUDIO = { "audio", 5};
38static const pj_str_t STR_VIDEO = { "video", 5};
39static const pj_str_t STR_IN = { "IN", 2 };
40static const pj_str_t STR_IP4 = { "IP4", 3};
41static const pj_str_t STR_IP6 = { "IP6", 3};
42static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 };
43static const pj_str_t STR_SDP_NAME = { "pjmedia", 7 };
44static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
45
46
47
48/* Config to control rtpmap inclusion for static payload types */
49pj_bool_t pjmedia_add_rtpmap_for_static_pt =
50 PJMEDIA_ADD_RTPMAP_FOR_STATIC_PT;
51
52/* Config to control use of RFC3890 TIAS */
53pj_bool_t pjmedia_add_bandwidth_tias_in_sdp =
54 PJMEDIA_ADD_BANDWIDTH_TIAS_IN_SDP;
55
56
57
58/* Worker thread proc. */
59static int PJ_THREAD_FUNC worker_proc(void*);
60
61
62#define MAX_THREADS 16
63
64
65/* List of media endpoint exit callback. */
66typedef struct exit_cb
67{
68 PJ_DECL_LIST_MEMBER (struct exit_cb);
69 pjmedia_endpt_exit_callback func;
70} exit_cb;
71
72
73/** Concrete declaration of media endpoint. */
74struct pjmedia_endpt
75{
76 /** Pool. */
77 pj_pool_t *pool;
78
79 /** Pool factory. */
80 pj_pool_factory *pf;
81
82 /** Codec manager. */
83 pjmedia_codec_mgr codec_mgr;
84
85 /** IOqueue instance. */
86 pj_ioqueue_t *ioqueue;
87
88 /** Do we own the ioqueue? */
89 pj_bool_t own_ioqueue;
90
91 /** Number of threads. */
92 unsigned thread_cnt;
93
94 /** IOqueue polling thread, if any. */
95 pj_thread_t *thread[MAX_THREADS];
96
97 /** To signal polling thread to quit. */
98 pj_bool_t quit_flag;
99
100 /** Is telephone-event enable */
101 pj_bool_t has_telephone_event;
102
103 /** List of exit callback. */
104 exit_cb exit_cb_list;
105};
106
107/**
108 * Initialize and get the instance of media endpoint.
109 */
110PJ_DEF(pj_status_t) pjmedia_endpt_create(pj_pool_factory *pf,
111 pj_ioqueue_t *ioqueue,
112 unsigned worker_cnt,
113 pjmedia_endpt **p_endpt)
114{
115 pj_pool_t *pool;
116 pjmedia_endpt *endpt;
117 unsigned i;
118 pj_status_t status;
119
120 status = pj_register_strerror(PJMEDIA_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
121 &pjmedia_strerror);
122 pj_assert(status == PJ_SUCCESS);
123
124 PJ_ASSERT_RETURN(pf && p_endpt, PJ_EINVAL);
125 PJ_ASSERT_RETURN(worker_cnt <= MAX_THREADS, PJ_EINVAL);
126
127 pool = pj_pool_create(pf, "med-ept", 512, 512, NULL);
128 if (!pool)
129 return PJ_ENOMEM;
130
131 endpt = PJ_POOL_ZALLOC_T(pool, struct pjmedia_endpt);
132 endpt->pool = pool;
133 endpt->pf = pf;
134 endpt->ioqueue = ioqueue;
135 endpt->thread_cnt = worker_cnt;
136 endpt->has_telephone_event = PJ_TRUE;
137
138 /* Sound */
139 status = pjmedia_aud_subsys_init(pf);
140 if (status != PJ_SUCCESS)
141 goto on_error;
142
143 /* Init codec manager. */
144 status = pjmedia_codec_mgr_init(&endpt->codec_mgr, endpt->pf);
145 if (status != PJ_SUCCESS)
146 goto on_error;
147
148 /* Initialize exit callback list. */
149 pj_list_init(&endpt->exit_cb_list);
150
151 /* Create ioqueue if none is specified. */
152 if (endpt->ioqueue == NULL) {
153
154 endpt->own_ioqueue = PJ_TRUE;
155
156 status = pj_ioqueue_create( endpt->pool, PJ_IOQUEUE_MAX_HANDLES,
157 &endpt->ioqueue);
158 if (status != PJ_SUCCESS)
159 goto on_error;
160
161 if (worker_cnt == 0) {
162 PJ_LOG(4,(THIS_FILE, "Warning: no worker thread is created in"
163 "media endpoint for internal ioqueue"));
164 }
165 }
166
167 /* Create worker threads if asked. */
168 for (i=0; i<worker_cnt; ++i) {
169 status = pj_thread_create( endpt->pool, "media", &worker_proc,
170 endpt, 0, 0, &endpt->thread[i]);
171 if (status != PJ_SUCCESS)
172 goto on_error;
173 }
174
175
176 *p_endpt = endpt;
177 return PJ_SUCCESS;
178
179on_error:
180
181 /* Destroy threads */
182 for (i=0; i<endpt->thread_cnt; ++i) {
183 if (endpt->thread[i]) {
184 pj_thread_destroy(endpt->thread[i]);
185 }
186 }
187
188 /* Destroy internal ioqueue */
189 if (endpt->ioqueue && endpt->own_ioqueue)
190 pj_ioqueue_destroy(endpt->ioqueue);
191
192 pjmedia_codec_mgr_destroy(&endpt->codec_mgr);
193 pjmedia_aud_subsys_shutdown();
194 pj_pool_release(pool);
195 return status;
196}
197
198/**
199 * Get the codec manager instance.
200 */
201PJ_DEF(pjmedia_codec_mgr*) pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt)
202{
203 return &endpt->codec_mgr;
204}
205
206/**
207 * Deinitialize media endpoint.
208 */
209PJ_DEF(pj_status_t) pjmedia_endpt_destroy (pjmedia_endpt *endpt)
210{
211 exit_cb *ecb;
212
213 pjmedia_endpt_stop_threads(endpt);
214
215 /* Destroy internal ioqueue */
216 if (endpt->ioqueue && endpt->own_ioqueue) {
217 pj_ioqueue_destroy(endpt->ioqueue);
218 endpt->ioqueue = NULL;
219 }
220
221 endpt->pf = NULL;
222
223 pjmedia_codec_mgr_destroy(&endpt->codec_mgr);
224 pjmedia_aud_subsys_shutdown();
225
226 /* Call all registered exit callbacks */
227 ecb = endpt->exit_cb_list.next;
228 while (ecb != &endpt->exit_cb_list) {
229 (*ecb->func)(endpt);
230 ecb = ecb->next;
231 }
232
233 pj_pool_release (endpt->pool);
234
235 return PJ_SUCCESS;
236}
237
238PJ_DEF(pj_status_t) pjmedia_endpt_set_flag( pjmedia_endpt *endpt,
239 pjmedia_endpt_flag flag,
240 const void *value)
241{
242 PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
243
244 switch (flag) {
245 case PJMEDIA_ENDPT_HAS_TELEPHONE_EVENT_FLAG:
246 endpt->has_telephone_event = *(pj_bool_t*)value;
247 break;
248 default:
249 return PJ_EINVAL;
250 }
251
252 return PJ_SUCCESS;
253}
254
255PJ_DEF(pj_status_t) pjmedia_endpt_get_flag( pjmedia_endpt *endpt,
256 pjmedia_endpt_flag flag,
257 void *value)
258{
259 PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
260
261 switch (flag) {
262 case PJMEDIA_ENDPT_HAS_TELEPHONE_EVENT_FLAG:
263 *(pj_bool_t*)value = endpt->has_telephone_event;
264 break;
265 default:
266 return PJ_EINVAL;
267 }
268
269 return PJ_SUCCESS;
270}
271
272/**
273 * Get the ioqueue instance of the media endpoint.
274 */
275PJ_DEF(pj_ioqueue_t*) pjmedia_endpt_get_ioqueue(pjmedia_endpt *endpt)
276{
277 PJ_ASSERT_RETURN(endpt, NULL);
278 return endpt->ioqueue;
279}
280
281/**
282 * Get the number of worker threads in media endpoint.
283 */
284PJ_DEF(unsigned) pjmedia_endpt_get_thread_count(pjmedia_endpt *endpt)
285{
286 PJ_ASSERT_RETURN(endpt, 0);
287 return endpt->thread_cnt;
288}
289
290/**
291 * Get a reference to one of the worker threads of the media endpoint
292 */
293PJ_DEF(pj_thread_t*) pjmedia_endpt_get_thread(pjmedia_endpt *endpt,
294 unsigned index)
295{
296 PJ_ASSERT_RETURN(endpt, NULL);
297 PJ_ASSERT_RETURN(index < endpt->thread_cnt, NULL);
298
299 /* here should be an assert on index >= 0 < endpt->thread_cnt */
300
301 return endpt->thread[index];
302}
303
304/**
305 * Stop and destroy the worker threads of the media endpoint
306 */
307PJ_DEF(pj_status_t) pjmedia_endpt_stop_threads(pjmedia_endpt *endpt)
308{
309 unsigned i;
310
311 PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
312
313 endpt->quit_flag = 1;
314
315 /* Destroy threads */
316 for (i=0; i<endpt->thread_cnt; ++i) {
317 if (endpt->thread[i]) {
318 pj_thread_join(endpt->thread[i]);
319 pj_thread_destroy(endpt->thread[i]);
320 endpt->thread[i] = NULL;
321 }
322 }
323
324 return PJ_SUCCESS;
325}
326
327/**
328 * Worker thread proc.
329 */
330static int PJ_THREAD_FUNC worker_proc(void *arg)
331{
332 pjmedia_endpt *endpt = (pjmedia_endpt*) arg;
333
334 while (!endpt->quit_flag) {
335 pj_time_val timeout = { 0, 500 };
336 pj_ioqueue_poll(endpt->ioqueue, &timeout);
337 }
338
339 return 0;
340}
341
342/**
343 * Create pool.
344 */
345PJ_DEF(pj_pool_t*) pjmedia_endpt_create_pool( pjmedia_endpt *endpt,
346 const char *name,
347 pj_size_t initial,
348 pj_size_t increment)
349{
350 pj_assert(endpt != NULL);
351
352 return pj_pool_create(endpt->pf, name, initial, increment, NULL);
353}
354
355/* Common initialization for both audio and video SDP media line */
356static pj_status_t init_sdp_media(pjmedia_sdp_media *m,
357 pj_pool_t *pool,
358 const pj_str_t *media_type,
359 const pjmedia_sock_info *sock_info)
360{
361 char tmp_addr[PJ_INET6_ADDRSTRLEN];
362 pjmedia_sdp_attr *attr;
363 const pj_sockaddr *addr;
364
365 pj_strdup(pool, &m->desc.media, media_type);
366
367 addr = &sock_info->rtp_addr_name;
368
369 /* Validate address family */
370 PJ_ASSERT_RETURN(addr->addr.sa_family == pj_AF_INET() ||
371 addr->addr.sa_family == pj_AF_INET6(),
372 PJ_EAFNOTSUP);
373
374 /* SDP connection line */
375 m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
376 m->conn->net_type = STR_IN;
377 m->conn->addr_type = (addr->addr.sa_family==pj_AF_INET())? STR_IP4:STR_IP6;
378 pj_sockaddr_print(addr, tmp_addr, sizeof(tmp_addr), 0);
379 pj_strdup2(pool, &m->conn->addr, tmp_addr);
380
381 /* Port and transport in media description */
382 m->desc.port = pj_sockaddr_get_port(addr);
383 m->desc.port_count = 1;
384 pj_strdup (pool, &m->desc.transport, &STR_RTP_AVP);
385
386 /* Add "rtcp" attribute */
387#if defined(PJMEDIA_HAS_RTCP_IN_SDP) && PJMEDIA_HAS_RTCP_IN_SDP!=0
388 if (sock_info->rtcp_addr_name.addr.sa_family != 0) {
389 attr = pjmedia_sdp_attr_create_rtcp(pool, &sock_info->rtcp_addr_name);
390 if (attr)
391 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
392 }
393#endif
394
395 /* Add sendrecv attribute. */
396 attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
397 attr->name = STR_SENDRECV;
398 m->attr[m->attr_count++] = attr;
399
400 return PJ_SUCCESS;
401}
402
403/* Create m=audio SDP media line */
404PJ_DEF(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt,
405 pj_pool_t *pool,
406 const pjmedia_sock_info *si,
407 unsigned options,
408 pjmedia_sdp_media **p_m)
409{
410 const pj_str_t STR_AUDIO = { "audio", 5 };
411 pjmedia_sdp_media *m;
412 pjmedia_sdp_attr *attr;
413 unsigned i;
414 unsigned max_bitrate = 0;
415 pj_status_t status;
416
417 PJ_UNUSED_ARG(options);
418
419 /* Check that there are not too many codecs */
420 PJ_ASSERT_RETURN(endpt->codec_mgr.codec_cnt <= PJMEDIA_MAX_SDP_FMT,
421 PJ_ETOOMANY);
422
423 /* Create and init basic SDP media */
424 m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
425 status = init_sdp_media(m, pool, &STR_AUDIO, si);
426 if (status != PJ_SUCCESS)
427 return status;
428
429 /* Add format, rtpmap, and fmtp (when applicable) for each codec */
430 for (i=0; i<endpt->codec_mgr.codec_cnt; ++i) {
431
432 pjmedia_codec_info *codec_info;
433 pjmedia_sdp_rtpmap rtpmap;
434 char tmp_param[3];
435 pjmedia_codec_param codec_param;
436 pj_str_t *fmt;
437
438 if (endpt->codec_mgr.codec_desc[i].prio == PJMEDIA_CODEC_PRIO_DISABLED)
439 break;
440
441 codec_info = &endpt->codec_mgr.codec_desc[i].info;
442 pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, codec_info,
443 &codec_param);
444 fmt = &m->desc.fmt[m->desc.fmt_count++];
445
446 fmt->ptr = (char*) pj_pool_alloc(pool, 8);
447 fmt->slen = pj_utoa(codec_info->pt, fmt->ptr);
448
449 rtpmap.pt = *fmt;
450 rtpmap.enc_name = codec_info->encoding_name;
451
452#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0)
453 if (codec_info->pt == PJMEDIA_RTP_PT_G722)
454 rtpmap.clock_rate = 8000;
455 else
456 rtpmap.clock_rate = codec_info->clock_rate;
457#else
458 rtpmap.clock_rate = codec_info->clock_rate;
459#endif
460
461 /* For audio codecs, rtpmap parameters denotes the number
462 * of channels, which can be omited if the value is 1.
463 */
464 if (codec_info->type == PJMEDIA_TYPE_AUDIO &&
465 codec_info->channel_cnt > 1)
466 {
467 /* Can only support one digit channel count */
468 pj_assert(codec_info->channel_cnt < 10);
469
470 tmp_param[0] = (char)('0' + codec_info->channel_cnt);
471
472 rtpmap.param.ptr = tmp_param;
473 rtpmap.param.slen = 1;
474
475 } else {
476 rtpmap.param.ptr = "";
477 rtpmap.param.slen = 0;
478 }
479
480 if (codec_info->pt >= 96 || pjmedia_add_rtpmap_for_static_pt) {
481 pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
482 m->attr[m->attr_count++] = attr;
483 }
484
485 /* Add fmtp params */
486 if (codec_param.setting.dec_fmtp.cnt > 0) {
487 enum { MAX_FMTP_STR_LEN = 160 };
488 char buf[MAX_FMTP_STR_LEN];
489 unsigned buf_len = 0, i;
490 pjmedia_codec_fmtp *dec_fmtp = &codec_param.setting.dec_fmtp;
491
492 /* Print codec PT */
493 buf_len += pj_ansi_snprintf(buf,
494 MAX_FMTP_STR_LEN - buf_len,
495 "%d",
496 codec_info->pt);
497
498 for (i = 0; i < dec_fmtp->cnt; ++i) {
499 pj_size_t test_len = 2;
500
501 /* Check if buf still available */
502 test_len = dec_fmtp->param[i].val.slen +
503 dec_fmtp->param[i].name.slen;
504 if (test_len + buf_len >= MAX_FMTP_STR_LEN)
505 return PJ_ETOOBIG;
506
507 /* Print delimiter */
508 buf_len += pj_ansi_snprintf(&buf[buf_len],
509 MAX_FMTP_STR_LEN - buf_len,
510 (i == 0?" ":";"));
511
512 /* Print an fmtp param */
513 if (dec_fmtp->param[i].name.slen)
514 buf_len += pj_ansi_snprintf(
515 &buf[buf_len],
516 MAX_FMTP_STR_LEN - buf_len,
517 "%.*s=%.*s",
518 (int)dec_fmtp->param[i].name.slen,
519 dec_fmtp->param[i].name.ptr,
520 (int)dec_fmtp->param[i].val.slen,
521 dec_fmtp->param[i].val.ptr);
522 else
523 buf_len += pj_ansi_snprintf(&buf[buf_len],
524 MAX_FMTP_STR_LEN - buf_len,
525 "%.*s",
526 (int)dec_fmtp->param[i].val.slen,
527 dec_fmtp->param[i].val.ptr);
528 }
529
530 attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
531
532 attr->name = pj_str("fmtp");
533 attr->value = pj_strdup3(pool, buf);
534 m->attr[m->attr_count++] = attr;
535 }
536
537 /* Find maximum bitrate in this media */
538 if (max_bitrate < codec_param.info.max_bps)
539 max_bitrate = codec_param.info.max_bps;
540 }
541
542#if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \
543 PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0
544 /*
545 * Add support telephony event
546 */
547 if (endpt->has_telephone_event) {
548 m->desc.fmt[m->desc.fmt_count++] =
549 pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR);
550
551 /* Add rtpmap. */
552 attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
553 attr->name = pj_str("rtpmap");
554 attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR
555 " telephone-event/8000");
556 m->attr[m->attr_count++] = attr;
557
558 /* Add fmtp */
559 attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
560 attr->name = pj_str("fmtp");
561 attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-15");
562 m->attr[m->attr_count++] = attr;
563 }
564#endif
565
566 /* Put bandwidth info in media level using bandwidth modifier "TIAS"
567 * (RFC3890).
568 */
569 if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) {
570 const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 };
571 pjmedia_sdp_bandw *b;
572
573 b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
574 b->modifier = STR_BANDW_MODIFIER;
575 b->value = max_bitrate;
576 m->bandw[m->bandw_count++] = b;
577 }
578
579 *p_m = m;
580 return PJ_SUCCESS;
581}
582
583
584#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
585
586/* Create m=video SDP media line */
587PJ_DEF(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt,
588 pj_pool_t *pool,
589 const pjmedia_sock_info *si,
590 unsigned options,
591 pjmedia_sdp_media **p_m)
592{
593
594
595 const pj_str_t STR_VIDEO = { "video", 5 };
596 pjmedia_sdp_media *m;
597 pjmedia_vid_codec_info codec_info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS];
598 unsigned codec_prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS];
599 pjmedia_sdp_attr *attr;
600 unsigned cnt, i;
601 unsigned max_bitrate = 0;
602 pj_status_t status;
603
604 PJ_UNUSED_ARG(options);
605
606 /* Make sure video codec manager is instantiated */
607 if (!pjmedia_vid_codec_mgr_instance())
608 pjmedia_vid_codec_mgr_create(endpt->pool, NULL);
609
610 /* Create and init basic SDP media */
611 m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
612 status = init_sdp_media(m, pool, &STR_VIDEO, si);
613 if (status != PJ_SUCCESS)
614 return status;
615
616 cnt = PJ_ARRAY_SIZE(codec_info);
617 status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &cnt,
618 codec_info, codec_prio);
619
620 /* Check that there are not too many codecs */
621 PJ_ASSERT_RETURN(0 <= PJMEDIA_MAX_SDP_FMT,
622 PJ_ETOOMANY);
623
624 /* Add format, rtpmap, and fmtp (when applicable) for each codec */
625 for (i=0; i<cnt; ++i) {
626 pjmedia_sdp_rtpmap rtpmap;
627 pjmedia_vid_codec_param codec_param;
628 pj_str_t *fmt;
629 pjmedia_video_format_detail *vfd;
630
631 pj_bzero(&rtpmap, sizeof(rtpmap));
632
633 if (codec_prio[i] == PJMEDIA_CODEC_PRIO_DISABLED)
634 break;
635
636 if (i > PJMEDIA_MAX_SDP_FMT) {
637 /* Too many codecs, perhaps it is better to tell application by
638 * returning appropriate status code.
639 */
640 PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY,
641 "Skipping some video codecs"));
642 break;
643 }
644
645 /* Must support RTP packetization and bidirectional */
646 if ((codec_info[i].packings & PJMEDIA_VID_PACKING_PACKETS) == 0 ||
647 codec_info[i].dir != PJMEDIA_DIR_ENCODING_DECODING)
648 {
649 continue;
650 }
651
652 pjmedia_vid_codec_mgr_get_default_param(NULL, &codec_info[i],
653 &codec_param);
654
655 fmt = &m->desc.fmt[m->desc.fmt_count++];
656 fmt->ptr = (char*) pj_pool_alloc(pool, 8);
657 fmt->slen = pj_utoa(codec_info[i].pt, fmt->ptr);
658 rtpmap.pt = *fmt;
659
660 /* Encoding name */
661 rtpmap.enc_name = codec_info[i].encoding_name;
662
663 /* Clock rate */
664 rtpmap.clock_rate = codec_info[i].clock_rate;
665
666 if (codec_info[i].pt >= 96 || pjmedia_add_rtpmap_for_static_pt) {
667 pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
668 m->attr[m->attr_count++] = attr;
669 }
670
671 /* Add fmtp params */
672 if (codec_param.dec_fmtp.cnt > 0) {
673 enum { MAX_FMTP_STR_LEN = 160 };
674 char buf[MAX_FMTP_STR_LEN];
675 unsigned buf_len = 0, j;
676 pjmedia_codec_fmtp *dec_fmtp = &codec_param.dec_fmtp;
677
678 /* Print codec PT */
679 buf_len += pj_ansi_snprintf(buf,
680 MAX_FMTP_STR_LEN - buf_len,
681 "%d",
682 codec_info[i].pt);
683
684 for (j = 0; j < dec_fmtp->cnt; ++j) {
685 pj_size_t test_len = 2;
686
687 /* Check if buf still available */
688 test_len = dec_fmtp->param[j].val.slen +
689 dec_fmtp->param[j].name.slen;
690 if (test_len + buf_len >= MAX_FMTP_STR_LEN)
691 return PJ_ETOOBIG;
692
693 /* Print delimiter */
694 buf_len += pj_ansi_snprintf(&buf[buf_len],
695 MAX_FMTP_STR_LEN - buf_len,
696 (j == 0?" ":";"));
697
698 /* Print an fmtp param */
699 if (dec_fmtp->param[j].name.slen)
700 buf_len += pj_ansi_snprintf(
701 &buf[buf_len],
702 MAX_FMTP_STR_LEN - buf_len,
703 "%.*s=%.*s",
704 (int)dec_fmtp->param[j].name.slen,
705 dec_fmtp->param[j].name.ptr,
706 (int)dec_fmtp->param[j].val.slen,
707 dec_fmtp->param[j].val.ptr);
708 else
709 buf_len += pj_ansi_snprintf(&buf[buf_len],
710 MAX_FMTP_STR_LEN - buf_len,
711 "%.*s",
712 (int)dec_fmtp->param[j].val.slen,
713 dec_fmtp->param[j].val.ptr);
714 }
715
716 attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
717
718 attr->name = pj_str("fmtp");
719 attr->value = pj_strdup3(pool, buf);
720 m->attr[m->attr_count++] = attr;
721 }
722
723 /* Find maximum bitrate in this media */
724 vfd = pjmedia_format_get_video_format_detail(&codec_param.enc_fmt,
725 PJ_TRUE);
726 if (vfd && max_bitrate < vfd->max_bps)
727 max_bitrate = vfd->max_bps;
728 }
729
730 /* Put bandwidth info in media level using bandwidth modifier "TIAS"
731 * (RFC3890).
732 */
733 if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) {
734 const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 };
735 pjmedia_sdp_bandw *b;
736
737 b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
738 b->modifier = STR_BANDW_MODIFIER;
739 b->value = max_bitrate;
740 m->bandw[m->bandw_count++] = b;
741 }
742
743 *p_m = m;
744 return PJ_SUCCESS;
745}
746
747#endif /* PJMEDIA_HAS_VIDEO */
748
749
750/**
751 * Create a "blank" SDP session description. The SDP will contain basic SDP
752 * fields such as origin, time, and name, but without any media lines.
753 */
754PJ_DEF(pj_status_t) pjmedia_endpt_create_base_sdp( pjmedia_endpt *endpt,
755 pj_pool_t *pool,
756 const pj_str_t *sess_name,
757 const pj_sockaddr *origin,
758 pjmedia_sdp_session **p_sdp)
759{
760 pj_time_val tv;
761 pjmedia_sdp_session *sdp;
762
763 /* Sanity check arguments */
764 PJ_ASSERT_RETURN(endpt && pool && p_sdp, PJ_EINVAL);
765
766 sdp = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
767
768 pj_gettimeofday(&tv);
769 sdp->origin.user = pj_str("-");
770 sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
771 sdp->origin.net_type = STR_IN;
772
773 if (origin->addr.sa_family == pj_AF_INET()) {
774 sdp->origin.addr_type = STR_IP4;
775 pj_strdup2(pool, &sdp->origin.addr,
776 pj_inet_ntoa(origin->ipv4.sin_addr));
777 } else if (origin->addr.sa_family == pj_AF_INET6()) {
778 char tmp_addr[PJ_INET6_ADDRSTRLEN];
779
780 sdp->origin.addr_type = STR_IP6;
781 pj_strdup2(pool, &sdp->origin.addr,
782 pj_sockaddr_print(origin, tmp_addr, sizeof(tmp_addr), 0));
783
784 } else {
785 pj_assert(!"Invalid address family");
786 return PJ_EAFNOTSUP;
787 }
788
789 if (sess_name)
790 pj_strdup(pool, &sdp->name, sess_name);
791 else
792 sdp->name = STR_SDP_NAME;
793
794 /* SDP time and attributes. */
795 sdp->time.start = sdp->time.stop = 0;
796 sdp->attr_count = 0;
797
798 /* Done */
799 *p_sdp = sdp;
800
801 return PJ_SUCCESS;
802}
803
804/**
805 * Create a SDP session description that describes the endpoint
806 * capability.
807 */
808PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
809 pj_pool_t *pool,
810 unsigned stream_cnt,
811 const pjmedia_sock_info sock_info[],
812 pjmedia_sdp_session **p_sdp )
813{
814 const pj_sockaddr *addr0;
815 pjmedia_sdp_session *sdp;
816 pjmedia_sdp_media *m;
817 pj_status_t status;
818
819 /* Sanity check arguments */
820 PJ_ASSERT_RETURN(endpt && pool && p_sdp && stream_cnt, PJ_EINVAL);
821 PJ_ASSERT_RETURN(stream_cnt < PJMEDIA_MAX_SDP_MEDIA, PJ_ETOOMANY);
822
823 addr0 = &sock_info[0].rtp_addr_name;
824
825 /* Create and initialize basic SDP session */
826 status = pjmedia_endpt_create_base_sdp(endpt, pool, NULL, addr0, &sdp);
827 if (status != PJ_SUCCESS)
828 return status;
829
830 /* Audio is first, by convention */
831 status = pjmedia_endpt_create_audio_sdp(endpt, pool,
832 &sock_info[0], 0, &m);
833 if (status != PJ_SUCCESS)
834 return status;
835 sdp->media[sdp->media_count++] = m;
836
837#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
838 {
839 unsigned i;
840
841 /* The remaining stream, if any, are videos (by convention as well) */
842 for (i=1; i<stream_cnt; ++i) {
843 status = pjmedia_endpt_create_video_sdp(endpt, pool,
844 &sock_info[i], 0, &m);
845 if (status != PJ_SUCCESS)
846 return status;
847 sdp->media[sdp->media_count++] = m;
848 }
849 }
850#endif
851
852 /* Done */
853 *p_sdp = sdp;
854
855 return PJ_SUCCESS;
856}
857
858
859
860#if PJ_LOG_MAX_LEVEL >= 3
861static const char *good_number(char *buf, pj_int32_t val)
862{
863 if (val < 1000) {
864 pj_ansi_sprintf(buf, "%d", val);
865 } else if (val < 1000000) {
866 pj_ansi_sprintf(buf, "%d.%dK",
867 val / 1000,
868 (val % 1000) / 100);
869 } else {
870 pj_ansi_sprintf(buf, "%d.%02dM",
871 val / 1000000,
872 (val % 1000000) / 10000);
873 }
874
875 return buf;
876}
877#endif
878
879PJ_DEF(pj_status_t) pjmedia_endpt_dump(pjmedia_endpt *endpt)
880{
881
882#if PJ_LOG_MAX_LEVEL >= 3
883 unsigned i, count;
884 pjmedia_codec_info codec_info[32];
885 unsigned prio[32];
886
887 PJ_LOG(3,(THIS_FILE, "Dumping PJMEDIA capabilities:"));
888
889 count = PJ_ARRAY_SIZE(codec_info);
890 if (pjmedia_codec_mgr_enum_codecs(&endpt->codec_mgr,
891 &count, codec_info, prio) != PJ_SUCCESS)
892 {
893 PJ_LOG(3,(THIS_FILE, " -error: failed to enum codecs"));
894 return PJ_SUCCESS;
895 }
896
897 PJ_LOG(3,(THIS_FILE, " Total number of installed codecs: %d", count));
898 for (i=0; i<count; ++i) {
899 const char *type;
900 pjmedia_codec_param param;
901 char bps[32];
902
903 switch (codec_info[i].type) {
904 case PJMEDIA_TYPE_AUDIO:
905 type = "Audio"; break;
906 case PJMEDIA_TYPE_VIDEO:
907 type = "Video"; break;
908 default:
909 type = "Unknown type"; break;
910 }
911
912 if (pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr,
913 &codec_info[i],
914 &param) != PJ_SUCCESS)
915 {
916 pj_bzero(&param, sizeof(pjmedia_codec_param));
917 }
918
919 PJ_LOG(3,(THIS_FILE,
920 " %s codec #%2d: pt=%d (%.*s @%dKHz/%d, %sbps, %dms%s%s%s%s%s)",
921 type, i, codec_info[i].pt,
922 (int)codec_info[i].encoding_name.slen,
923 codec_info[i].encoding_name.ptr,
924 codec_info[i].clock_rate/1000,
925 codec_info[i].channel_cnt,
926 good_number(bps, param.info.avg_bps),
927 param.info.frm_ptime * param.setting.frm_per_pkt,
928 (param.setting.vad ? " vad" : ""),
929 (param.setting.cng ? " cng" : ""),
930 (param.setting.plc ? " plc" : ""),
931 (param.setting.penh ? " penh" : ""),
932 (prio[i]==PJMEDIA_CODEC_PRIO_DISABLED?" disabled":"")));
933 }
934#endif
935
936 return PJ_SUCCESS;
937}
938
939PJ_DEF(pj_status_t) pjmedia_endpt_atexit( pjmedia_endpt *endpt,
940 pjmedia_endpt_exit_callback func)
941{
942 exit_cb *new_cb;
943
944 PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL);
945
946 if (endpt->quit_flag)
947 return PJ_EINVALIDOP;
948
949 new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb);
950 new_cb->func = func;
951
952 pj_enter_critical_section();
953 pj_list_push_back(&endpt->exit_cb_list, new_cb);
954 pj_leave_critical_section();
955
956 return PJ_SUCCESS;
957}