1
/* $Id: endpoint.c 3999 2012-03-30 07:10:13Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
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>
32
#include <pj/string.h>
35
#define THIS_FILE "endpoint.c"
37
static const pj_str_t STR_AUDIO = { "audio", 5};
38
static const pj_str_t STR_VIDEO = { "video", 5};
39
static const pj_str_t STR_IN = { "IN", 2 };
40
static const pj_str_t STR_IP4 = { "IP4", 3};
41
static const pj_str_t STR_IP6 = { "IP6", 3};
42
static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 };
43
static const pj_str_t STR_SDP_NAME = { "pjmedia", 7 };
44
static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
48
/* Config to control rtpmap inclusion for static payload types */
49
pj_bool_t pjmedia_add_rtpmap_for_static_pt =
50
PJMEDIA_ADD_RTPMAP_FOR_STATIC_PT;
54
/* Worker thread proc. */
55
static int PJ_THREAD_FUNC worker_proc(void*);
58
#define MAX_THREADS 16
61
/* List of media endpoint exit callback. */
62
typedef struct exit_cb
64
PJ_DECL_LIST_MEMBER (struct exit_cb);
65
pjmedia_endpt_exit_callback func;
69
/** Concrete declaration of media endpoint. */
79
pjmedia_codec_mgr codec_mgr;
81
/** IOqueue instance. */
82
pj_ioqueue_t *ioqueue;
84
/** Do we own the ioqueue? */
85
pj_bool_t own_ioqueue;
87
/** Number of threads. */
90
/** IOqueue polling thread, if any. */
91
pj_thread_t *thread[MAX_THREADS];
93
/** To signal polling thread to quit. */
96
/** Is telephone-event enable */
97
pj_bool_t has_telephone_event;
99
/** List of exit callback. */
100
exit_cb exit_cb_list;
104
* Initialize and get the instance of media endpoint.
106
PJ_DEF(pj_status_t) pjmedia_endpt_create(pj_pool_factory *pf,
107
pj_ioqueue_t *ioqueue,
109
pjmedia_endpt **p_endpt)
112
pjmedia_endpt *endpt;
116
status = pj_register_strerror(PJMEDIA_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
118
pj_assert(status == PJ_SUCCESS);
120
PJ_ASSERT_RETURN(pf && p_endpt, PJ_EINVAL);
121
PJ_ASSERT_RETURN(worker_cnt <= MAX_THREADS, PJ_EINVAL);
123
pool = pj_pool_create(pf, "med-ept", 512, 512, NULL);
127
endpt = PJ_POOL_ZALLOC_T(pool, struct pjmedia_endpt);
130
endpt->ioqueue = ioqueue;
131
endpt->thread_cnt = worker_cnt;
132
endpt->has_telephone_event = PJ_TRUE;
135
status = pjmedia_aud_subsys_init(pf);
136
if (status != PJ_SUCCESS)
139
/* Init codec manager. */
140
status = pjmedia_codec_mgr_init(&endpt->codec_mgr, endpt->pf);
141
if (status != PJ_SUCCESS)
144
/* Initialize exit callback list. */
145
pj_list_init(&endpt->exit_cb_list);
147
/* Create ioqueue if none is specified. */
148
if (endpt->ioqueue == NULL) {
150
endpt->own_ioqueue = PJ_TRUE;
152
status = pj_ioqueue_create( endpt->pool, PJ_IOQUEUE_MAX_HANDLES,
154
if (status != PJ_SUCCESS)
157
if (worker_cnt == 0) {
158
PJ_LOG(4,(THIS_FILE, "Warning: no worker thread is created in"
159
"media endpoint for internal ioqueue"));
163
/* Create worker threads if asked. */
164
for (i=0; i<worker_cnt; ++i) {
165
status = pj_thread_create( endpt->pool, "media", &worker_proc,
166
endpt, 0, 0, &endpt->thread[i]);
167
if (status != PJ_SUCCESS)
177
/* Destroy threads */
178
for (i=0; i<endpt->thread_cnt; ++i) {
179
if (endpt->thread[i]) {
180
pj_thread_destroy(endpt->thread[i]);
184
/* Destroy internal ioqueue */
185
if (endpt->ioqueue && endpt->own_ioqueue)
186
pj_ioqueue_destroy(endpt->ioqueue);
188
pjmedia_codec_mgr_destroy(&endpt->codec_mgr);
189
pjmedia_aud_subsys_shutdown();
190
pj_pool_release(pool);
195
* Get the codec manager instance.
197
PJ_DEF(pjmedia_codec_mgr*) pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt)
199
return &endpt->codec_mgr;
203
* Deinitialize media endpoint.
205
PJ_DEF(pj_status_t) pjmedia_endpt_destroy (pjmedia_endpt *endpt)
210
PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
212
endpt->quit_flag = 1;
214
/* Destroy threads */
215
for (i=0; i<endpt->thread_cnt; ++i) {
216
if (endpt->thread[i]) {
217
pj_thread_join(endpt->thread[i]);
218
pj_thread_destroy(endpt->thread[i]);
219
endpt->thread[i] = NULL;
223
/* Destroy internal ioqueue */
224
if (endpt->ioqueue && endpt->own_ioqueue) {
225
pj_ioqueue_destroy(endpt->ioqueue);
226
endpt->ioqueue = NULL;
231
pjmedia_codec_mgr_destroy(&endpt->codec_mgr);
232
pjmedia_aud_subsys_shutdown();
234
/* Call all registered exit callbacks */
235
ecb = endpt->exit_cb_list.next;
236
while (ecb != &endpt->exit_cb_list) {
241
pj_pool_release (endpt->pool);
246
PJ_DEF(pj_status_t) pjmedia_endpt_set_flag( pjmedia_endpt *endpt,
247
pjmedia_endpt_flag flag,
250
PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
253
case PJMEDIA_ENDPT_HAS_TELEPHONE_EVENT_FLAG:
254
endpt->has_telephone_event = *(pj_bool_t*)value;
263
PJ_DEF(pj_status_t) pjmedia_endpt_get_flag( pjmedia_endpt *endpt,
264
pjmedia_endpt_flag flag,
267
PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
270
case PJMEDIA_ENDPT_HAS_TELEPHONE_EVENT_FLAG:
271
*(pj_bool_t*)value = endpt->has_telephone_event;
281
* Get the ioqueue instance of the media endpoint.
283
PJ_DEF(pj_ioqueue_t*) pjmedia_endpt_get_ioqueue(pjmedia_endpt *endpt)
285
PJ_ASSERT_RETURN(endpt, NULL);
286
return endpt->ioqueue;
290
* Get the number of worker threads in media endpoint.
292
PJ_DEF(unsigned) pjmedia_endpt_get_thread_count(pjmedia_endpt *endpt)
294
PJ_ASSERT_RETURN(endpt, 0);
295
return endpt->thread_cnt;
299
* Get a reference to one of the worker threads of the media endpoint
301
PJ_DEF(pj_thread_t*) pjmedia_endpt_get_thread(pjmedia_endpt *endpt,
304
PJ_ASSERT_RETURN(endpt, NULL);
305
PJ_ASSERT_RETURN(index < endpt->thread_cnt, NULL);
307
/* here should be an assert on index >= 0 < endpt->thread_cnt */
309
return endpt->thread[index];
313
* Worker thread proc.
315
static int PJ_THREAD_FUNC worker_proc(void *arg)
317
pjmedia_endpt *endpt = (pjmedia_endpt*) arg;
319
while (!endpt->quit_flag) {
320
pj_time_val timeout = { 0, 500 };
321
pj_ioqueue_poll(endpt->ioqueue, &timeout);
330
PJ_DEF(pj_pool_t*) pjmedia_endpt_create_pool( pjmedia_endpt *endpt,
335
pj_assert(endpt != NULL);
337
return pj_pool_create(endpt->pf, name, initial, increment, NULL);
340
/* Common initialization for both audio and video SDP media line */
341
static pj_status_t init_sdp_media(pjmedia_sdp_media *m,
343
const pj_str_t *media_type,
344
const pjmedia_sock_info *sock_info)
346
char tmp_addr[PJ_INET6_ADDRSTRLEN];
347
pjmedia_sdp_attr *attr;
348
const pj_sockaddr *addr;
350
pj_strdup(pool, &m->desc.media, media_type);
352
addr = &sock_info->rtp_addr_name;
354
/* Validate address family */
355
PJ_ASSERT_RETURN(addr->addr.sa_family == pj_AF_INET() ||
356
addr->addr.sa_family == pj_AF_INET6(),
359
/* SDP connection line */
360
m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
361
m->conn->net_type = STR_IN;
362
m->conn->addr_type = (addr->addr.sa_family==pj_AF_INET())? STR_IP4:STR_IP6;
363
pj_sockaddr_print(addr, tmp_addr, sizeof(tmp_addr), 0);
364
pj_strdup2(pool, &m->conn->addr, tmp_addr);
366
/* Port and transport in media description */
367
m->desc.port = pj_sockaddr_get_port(addr);
368
m->desc.port_count = 1;
369
pj_strdup (pool, &m->desc.transport, &STR_RTP_AVP);
371
/* Add "rtcp" attribute */
372
#if defined(PJMEDIA_HAS_RTCP_IN_SDP) && PJMEDIA_HAS_RTCP_IN_SDP!=0
373
if (sock_info->rtcp_addr_name.addr.sa_family != 0) {
374
attr = pjmedia_sdp_attr_create_rtcp(pool, &sock_info->rtcp_addr_name);
376
pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
380
/* Add sendrecv attribute. */
381
attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
382
attr->name = STR_SENDRECV;
383
m->attr[m->attr_count++] = attr;
388
/* Create m=audio SDP media line */
389
PJ_DEF(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt,
391
const pjmedia_sock_info *si,
393
pjmedia_sdp_media **p_m)
395
const pj_str_t STR_AUDIO = { "audio", 5 };
396
pjmedia_sdp_media *m;
397
pjmedia_sdp_attr *attr;
399
unsigned max_bitrate = 0;
402
PJ_UNUSED_ARG(options);
404
/* Check that there are not too many codecs */
405
PJ_ASSERT_RETURN(endpt->codec_mgr.codec_cnt <= PJMEDIA_MAX_SDP_FMT,
408
/* Create and init basic SDP media */
409
m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
410
status = init_sdp_media(m, pool, &STR_AUDIO, si);
411
if (status != PJ_SUCCESS)
414
/* Add format, rtpmap, and fmtp (when applicable) for each codec */
415
for (i=0; i<endpt->codec_mgr.codec_cnt; ++i) {
417
pjmedia_codec_info *codec_info;
418
pjmedia_sdp_rtpmap rtpmap;
420
pjmedia_codec_param codec_param;
423
if (endpt->codec_mgr.codec_desc[i].prio == PJMEDIA_CODEC_PRIO_DISABLED)
426
codec_info = &endpt->codec_mgr.codec_desc[i].info;
427
pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr, codec_info,
429
fmt = &m->desc.fmt[m->desc.fmt_count++];
431
fmt->ptr = (char*) pj_pool_alloc(pool, 8);
432
fmt->slen = pj_utoa(codec_info->pt, fmt->ptr);
435
rtpmap.enc_name = codec_info->encoding_name;
437
#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0)
438
if (codec_info->pt == PJMEDIA_RTP_PT_G722)
439
rtpmap.clock_rate = 8000;
441
rtpmap.clock_rate = codec_info->clock_rate;
443
rtpmap.clock_rate = codec_info->clock_rate;
446
/* For audio codecs, rtpmap parameters denotes the number
447
* of channels, which can be omited if the value is 1.
449
if (codec_info->type == PJMEDIA_TYPE_AUDIO &&
450
codec_info->channel_cnt > 1)
452
/* Can only support one digit channel count */
453
pj_assert(codec_info->channel_cnt < 10);
455
tmp_param[0] = (char)('0' + codec_info->channel_cnt);
457
rtpmap.param.ptr = tmp_param;
458
rtpmap.param.slen = 1;
461
rtpmap.param.ptr = "";
462
rtpmap.param.slen = 0;
465
if (codec_info->pt >= 96 || pjmedia_add_rtpmap_for_static_pt) {
466
pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
467
m->attr[m->attr_count++] = attr;
470
/* Add fmtp params */
471
if (codec_param.setting.dec_fmtp.cnt > 0) {
472
enum { MAX_FMTP_STR_LEN = 160 };
473
char buf[MAX_FMTP_STR_LEN];
474
unsigned buf_len = 0, i;
475
pjmedia_codec_fmtp *dec_fmtp = &codec_param.setting.dec_fmtp;
478
buf_len += pj_ansi_snprintf(buf,
479
MAX_FMTP_STR_LEN - buf_len,
483
for (i = 0; i < dec_fmtp->cnt; ++i) {
484
unsigned test_len = 2;
486
/* Check if buf still available */
487
test_len = dec_fmtp->param[i].val.slen +
488
dec_fmtp->param[i].name.slen;
489
if (test_len + buf_len >= MAX_FMTP_STR_LEN)
492
/* Print delimiter */
493
buf_len += pj_ansi_snprintf(&buf[buf_len],
494
MAX_FMTP_STR_LEN - buf_len,
497
/* Print an fmtp param */
498
if (dec_fmtp->param[i].name.slen)
499
buf_len += pj_ansi_snprintf(
501
MAX_FMTP_STR_LEN - buf_len,
503
(int)dec_fmtp->param[i].name.slen,
504
dec_fmtp->param[i].name.ptr,
505
(int)dec_fmtp->param[i].val.slen,
506
dec_fmtp->param[i].val.ptr);
508
buf_len += pj_ansi_snprintf(&buf[buf_len],
509
MAX_FMTP_STR_LEN - buf_len,
511
(int)dec_fmtp->param[i].val.slen,
512
dec_fmtp->param[i].val.ptr);
515
attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
517
attr->name = pj_str("fmtp");
518
attr->value = pj_strdup3(pool, buf);
519
m->attr[m->attr_count++] = attr;
522
/* Find maximum bitrate in this media */
523
if (max_bitrate < codec_param.info.max_bps)
524
max_bitrate = codec_param.info.max_bps;
527
#if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \
528
PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0
530
* Add support telephony event
532
if (endpt->has_telephone_event) {
533
m->desc.fmt[m->desc.fmt_count++] =
534
pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR);
537
attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
538
attr->name = pj_str("rtpmap");
539
attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR
540
" telephone-event/8000");
541
m->attr[m->attr_count++] = attr;
544
attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
545
attr->name = pj_str("fmtp");
546
attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-15");
547
m->attr[m->attr_count++] = attr;
551
/* Put bandwidth info in media level using bandwidth modifier "TIAS"
555
const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 };
556
pjmedia_sdp_bandw *b;
558
b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
559
b->modifier = STR_BANDW_MODIFIER;
560
b->value = max_bitrate;
561
m->bandw[m->bandw_count++] = b;
569
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
571
/* Create m=video SDP media line */
572
PJ_DEF(pj_status_t) pjmedia_endpt_create_video_sdp(pjmedia_endpt *endpt,
574
const pjmedia_sock_info *si,
576
pjmedia_sdp_media **p_m)
580
const pj_str_t STR_VIDEO = { "video", 5 };
581
pjmedia_sdp_media *m;
582
pjmedia_vid_codec_info codec_info[PJMEDIA_VID_CODEC_MGR_MAX_CODECS];
583
unsigned codec_prio[PJMEDIA_VID_CODEC_MGR_MAX_CODECS];
584
pjmedia_sdp_attr *attr;
586
unsigned max_bitrate = 0;
589
PJ_UNUSED_ARG(options);
591
/* Make sure video codec manager is instantiated */
592
if (!pjmedia_vid_codec_mgr_instance())
593
pjmedia_vid_codec_mgr_create(endpt->pool, NULL);
595
/* Create and init basic SDP media */
596
m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
597
status = init_sdp_media(m, pool, &STR_VIDEO, si);
598
if (status != PJ_SUCCESS)
601
cnt = PJ_ARRAY_SIZE(codec_info);
602
status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &cnt,
603
codec_info, codec_prio);
605
/* Check that there are not too many codecs */
606
PJ_ASSERT_RETURN(0 <= PJMEDIA_MAX_SDP_FMT,
609
/* Add format, rtpmap, and fmtp (when applicable) for each codec */
610
for (i=0; i<cnt; ++i) {
611
pjmedia_sdp_rtpmap rtpmap;
612
pjmedia_vid_codec_param codec_param;
614
pjmedia_video_format_detail *vfd;
616
pj_bzero(&rtpmap, sizeof(rtpmap));
618
if (codec_prio[i] == PJMEDIA_CODEC_PRIO_DISABLED)
621
if (i > PJMEDIA_MAX_SDP_FMT) {
622
/* Too many codecs, perhaps it is better to tell application by
623
* returning appropriate status code.
625
PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY,
626
"Skipping some video codecs"));
630
/* Must support RTP packetization and bidirectional */
631
if ((codec_info[i].packings & PJMEDIA_VID_PACKING_PACKETS) == 0 ||
632
codec_info[i].dir != PJMEDIA_DIR_ENCODING_DECODING)
637
pjmedia_vid_codec_mgr_get_default_param(NULL, &codec_info[i],
640
fmt = &m->desc.fmt[m->desc.fmt_count++];
641
fmt->ptr = (char*) pj_pool_alloc(pool, 8);
642
fmt->slen = pj_utoa(codec_info[i].pt, fmt->ptr);
646
rtpmap.enc_name = codec_info[i].encoding_name;
649
rtpmap.clock_rate = codec_info[i].clock_rate;
651
if (codec_info[i].pt >= 96 || pjmedia_add_rtpmap_for_static_pt) {
652
pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
653
m->attr[m->attr_count++] = attr;
656
/* Add fmtp params */
657
if (codec_param.dec_fmtp.cnt > 0) {
658
enum { MAX_FMTP_STR_LEN = 160 };
659
char buf[MAX_FMTP_STR_LEN];
660
unsigned buf_len = 0, j;
661
pjmedia_codec_fmtp *dec_fmtp = &codec_param.dec_fmtp;
664
buf_len += pj_ansi_snprintf(buf,
665
MAX_FMTP_STR_LEN - buf_len,
669
for (j = 0; j < dec_fmtp->cnt; ++j) {
670
unsigned test_len = 2;
672
/* Check if buf still available */
673
test_len = dec_fmtp->param[j].val.slen +
674
dec_fmtp->param[j].name.slen;
675
if (test_len + buf_len >= MAX_FMTP_STR_LEN)
678
/* Print delimiter */
679
buf_len += pj_ansi_snprintf(&buf[buf_len],
680
MAX_FMTP_STR_LEN - buf_len,
683
/* Print an fmtp param */
684
if (dec_fmtp->param[j].name.slen)
685
buf_len += pj_ansi_snprintf(
687
MAX_FMTP_STR_LEN - buf_len,
689
(int)dec_fmtp->param[j].name.slen,
690
dec_fmtp->param[j].name.ptr,
691
(int)dec_fmtp->param[j].val.slen,
692
dec_fmtp->param[j].val.ptr);
694
buf_len += pj_ansi_snprintf(&buf[buf_len],
695
MAX_FMTP_STR_LEN - buf_len,
697
(int)dec_fmtp->param[j].val.slen,
698
dec_fmtp->param[j].val.ptr);
701
attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
703
attr->name = pj_str("fmtp");
704
attr->value = pj_strdup3(pool, buf);
705
m->attr[m->attr_count++] = attr;
708
/* Find maximum bitrate in this media */
709
vfd = pjmedia_format_get_video_format_detail(&codec_param.enc_fmt,
711
if (vfd && max_bitrate < vfd->max_bps)
712
max_bitrate = vfd->max_bps;
715
/* Put bandwidth info in media level using bandwidth modifier "TIAS"
719
const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 };
720
pjmedia_sdp_bandw *b;
722
b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
723
b->modifier = STR_BANDW_MODIFIER;
724
b->value = max_bitrate;
725
m->bandw[m->bandw_count++] = b;
732
#endif /* PJMEDIA_HAS_VIDEO */
736
* Create a "blank" SDP session description. The SDP will contain basic SDP
737
* fields such as origin, time, and name, but without any media lines.
739
PJ_DEF(pj_status_t) pjmedia_endpt_create_base_sdp( pjmedia_endpt *endpt,
741
const pj_str_t *sess_name,
742
const pj_sockaddr *origin,
743
pjmedia_sdp_session **p_sdp)
746
pjmedia_sdp_session *sdp;
748
/* Sanity check arguments */
749
PJ_ASSERT_RETURN(endpt && pool && p_sdp, PJ_EINVAL);
751
sdp = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);
753
pj_gettimeofday(&tv);
754
sdp->origin.user = pj_str("-");
755
sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
756
sdp->origin.net_type = STR_IN;
758
if (origin->addr.sa_family == pj_AF_INET()) {
759
sdp->origin.addr_type = STR_IP4;
760
pj_strdup2(pool, &sdp->origin.addr,
761
pj_inet_ntoa(origin->ipv4.sin_addr));
762
} else if (origin->addr.sa_family == pj_AF_INET6()) {
763
char tmp_addr[PJ_INET6_ADDRSTRLEN];
765
sdp->origin.addr_type = STR_IP6;
766
pj_strdup2(pool, &sdp->origin.addr,
767
pj_sockaddr_print(origin, tmp_addr, sizeof(tmp_addr), 0));
770
pj_assert(!"Invalid address family");
775
pj_strdup(pool, &sdp->name, sess_name);
777
sdp->name = STR_SDP_NAME;
779
/* SDP time and attributes. */
780
sdp->time.start = sdp->time.stop = 0;
790
* Create a SDP session description that describes the endpoint
793
PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
796
const pjmedia_sock_info sock_info[],
797
pjmedia_sdp_session **p_sdp )
799
const pj_sockaddr *addr0;
800
pjmedia_sdp_session *sdp;
801
pjmedia_sdp_media *m;
804
/* Sanity check arguments */
805
PJ_ASSERT_RETURN(endpt && pool && p_sdp && stream_cnt, PJ_EINVAL);
806
PJ_ASSERT_RETURN(stream_cnt < PJMEDIA_MAX_SDP_MEDIA, PJ_ETOOMANY);
808
addr0 = &sock_info[0].rtp_addr_name;
810
/* Create and initialize basic SDP session */
811
status = pjmedia_endpt_create_base_sdp(endpt, pool, NULL, addr0, &sdp);
812
if (status != PJ_SUCCESS)
815
/* Audio is first, by convention */
816
status = pjmedia_endpt_create_audio_sdp(endpt, pool,
817
&sock_info[0], 0, &m);
818
if (status != PJ_SUCCESS)
820
sdp->media[sdp->media_count++] = m;
822
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
826
/* The remaining stream, if any, are videos (by convention as well) */
827
for (i=1; i<stream_cnt; ++i) {
828
status = pjmedia_endpt_create_video_sdp(endpt, pool,
829
&sock_info[i], 0, &m);
830
if (status != PJ_SUCCESS)
832
sdp->media[sdp->media_count++] = m;
845
#if PJ_LOG_MAX_LEVEL >= 3
846
static const char *good_number(char *buf, pj_int32_t val)
849
pj_ansi_sprintf(buf, "%d", val);
850
} else if (val < 1000000) {
851
pj_ansi_sprintf(buf, "%d.%dK",
855
pj_ansi_sprintf(buf, "%d.%02dM",
857
(val % 1000000) / 10000);
864
PJ_DEF(pj_status_t) pjmedia_endpt_dump(pjmedia_endpt *endpt)
867
#if PJ_LOG_MAX_LEVEL >= 3
869
pjmedia_codec_info codec_info[32];
872
PJ_LOG(3,(THIS_FILE, "Dumping PJMEDIA capabilities:"));
874
count = PJ_ARRAY_SIZE(codec_info);
875
if (pjmedia_codec_mgr_enum_codecs(&endpt->codec_mgr,
876
&count, codec_info, prio) != PJ_SUCCESS)
878
PJ_LOG(3,(THIS_FILE, " -error: failed to enum codecs"));
882
PJ_LOG(3,(THIS_FILE, " Total number of installed codecs: %d", count));
883
for (i=0; i<count; ++i) {
885
pjmedia_codec_param param;
888
switch (codec_info[i].type) {
889
case PJMEDIA_TYPE_AUDIO:
890
type = "Audio"; break;
891
case PJMEDIA_TYPE_VIDEO:
892
type = "Video"; break;
894
type = "Unknown type"; break;
897
if (pjmedia_codec_mgr_get_default_param(&endpt->codec_mgr,
899
¶m) != PJ_SUCCESS)
901
pj_bzero(¶m, sizeof(pjmedia_codec_param));
905
" %s codec #%2d: pt=%d (%.*s @%dKHz/%d, %sbps, %dms%s%s%s%s%s)",
906
type, i, codec_info[i].pt,
907
(int)codec_info[i].encoding_name.slen,
908
codec_info[i].encoding_name.ptr,
909
codec_info[i].clock_rate/1000,
910
codec_info[i].channel_cnt,
911
good_number(bps, param.info.avg_bps),
912
param.info.frm_ptime * param.setting.frm_per_pkt,
913
(param.setting.vad ? " vad" : ""),
914
(param.setting.cng ? " cng" : ""),
915
(param.setting.plc ? " plc" : ""),
916
(param.setting.penh ? " penh" : ""),
917
(prio[i]==PJMEDIA_CODEC_PRIO_DISABLED?" disabled":"")));
924
PJ_DEF(pj_status_t) pjmedia_endpt_atexit( pjmedia_endpt *endpt,
925
pjmedia_endpt_exit_callback func)
929
PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL);
931
if (endpt->quit_flag)
932
return PJ_EINVALIDOP;
934
new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb);
937
pj_enter_critical_section();
938
pj_list_push_back(&endpt->exit_cb_list, new_cb);
939
pj_leave_critical_section();