diff options
-rw-r--r-- | pjmedia/include/pjmedia/errno.h | 5 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/jbuf.h | 120 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/session.h | 45 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/types.h | 14 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/jbuf.c | 609 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/session.c | 45 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/stream.c | 101 | ||||
-rw-r--r-- | pjsip/include/pjsip-ua/sip_inv.h | 5 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_ua_layer.h | 5 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_inv.c | 1 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_dialog.c | 8 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transport.c | 5 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_ua_layer.c | 70 | ||||
-rw-r--r-- | pjsip/src/pjsua/main.c | 150 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua.h | 29 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua_core.c | 4 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua_inv.c | 16 |
17 files changed, 707 insertions, 525 deletions
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h index d57a23b6..8e5479aa 100644 --- a/pjmedia/include/pjmedia/errno.h +++ b/pjmedia/include/pjmedia/errno.h @@ -276,6 +276,11 @@ PJ_BEGIN_DECL +/************************************************************ + * JITTER BUFFER + ***********************************************************/ + + PJ_END_DECL #endif /* __PJMEDIA_ERRNO_H__ */ diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h index 8ad89f38..d725c51c 100644 --- a/pjmedia/include/pjmedia/jbuf.h +++ b/pjmedia/include/pjmedia/jbuf.h @@ -30,106 +30,38 @@ * @{ */ -#include <pj/types.h> +#include <pjmedia/types.h> PJ_BEGIN_DECL -/** - * Opaque declaration of internal frame type used by jitter buffer. - */ -struct pj_jbframe; - - -/** - * Miscelaneous operation result/status. - */ -typedef enum jb_op_status -{ - PJ_JB_STATUS_TOO_OLD = -2, /** The packet is too old. */ - PJ_JB_STATUS_TOO_SOON = -3, /** The packet is too soon. */ - PJ_JB_STATUS_FRAME_NULL = -4, /** No packet can be retrieved */ - PJ_JB_STATUS_FRAME_MISSING = -5, /** The specified packet is missing/lost */ -} jb_op_status; - - -/* - * Frame list, container abstraction for ordered list with fixed maximum - * size. It is used internally by the jitter buffer. - */ -typedef struct pj_jbframelist +enum pjmedia_jb_frame_type { - unsigned head, count, maxcount; - struct pj_jbframe *frames; -} pj_jbframelist; - - -/** - * Jitter buffer implementation. - */ -typedef struct pj_jitter_buffer -{ - pj_jbframelist lst; /** The frame list. */ - int level; /** Current, real-time jitter level. */ - int max_level; /** Maximum level for the period. */ - unsigned prefetch; /** Prefetch count currently used. */ - unsigned get_cnt; /** Number of get operation during prefetch state. */ - unsigned min; /** Minimum jitter size, in packets. */ - unsigned max; /** Maximum jitter size, in packets. */ - pj_uint32_t lastseq; /** Last sequence number put to jitter buffer. */ - unsigned upd_count; /** Internal counter to manage update interval. */ - int state; /** Jitter buffer state (1==operational) */ - int last_op; /** Last jitter buffer operation. */ -} pj_jitter_buffer; - - -/** - * Initialize jitter buffer with the specified parameters. - * This function will allocate internal frame buffer from the specified pool. - * @param jb The jitter buffer to be initialized. - * @param pool Pool where memory will be allocated for the frame buffer. - * @param min The minimum value of jitter buffer, in packets. - * @param max The maximum value of jitter buffer, in packets. - * @param maxcount The maximum number of delay, in packets, which must be - * greater than max. - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool, - unsigned min, unsigned max, unsigned maxcount); - -/** - * Reset jitter buffer according to the parameters specified when the jitter - * buffer was initialized. Any packets currently in the buffer will be - * discarded. - */ -PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb); - -/** - * Put a pointer to the buffer with the specified sequence number. The pointer - * normally points to a buffer held by application, and this pointer will be - * returned later on when pj_jb_get() is called. The jitter buffer will not try - * to interpret the content of this pointer. - * @return: - * - PJ_SUCCESS on success. - * - PJ_JB_STATUS_TOO_OLD when the packet is too old. - * - PJ_JB_STATUS_TOO_SOON when the packet is too soon. - */ -PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf ); - -/** - * Get the earliest data from the jitter buffer. ONLY when the operation succeeds, - * the function returns both sequence number and a pointer in the parameters. - * This returned data corresponds to sequence number and pointer that were - * given to jitter buffer in the previous pj_jb_put operation. - * @return - * - PJ_SUCCESS on success - * - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned. - * - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet - * is lost. Application may run packet concealment algorithm when it - * receives this status. - */ -PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf ); + PJMEDIA_JB_MISSING_FRAME = 0, + PJMEDIA_JB_NORMAL_FRAME = 1, + PJMEDIA_JB_ZERO_FRAME = 2, +}; + + +#define PJMEDIA_JB_DEFAULT_INIT_PREFETCH 15 + + +PJ_DECL(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, + int frame_size, + int initial_prefetch, + int max_count, + pjmedia_jbuf **p_jb); +PJ_DECL(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb); +PJ_DECL(pj_status_t) pjmedia_jbuf_put_frame(pjmedia_jbuf *jb, + const void *frame, + pj_size_t frame_size, + int frame_seq); +PJ_DECL(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb, + void *frame, + char *p_frame_type); +PJ_DECL(unsigned) pjmedia_jbuf_get_prefetch_size(pjmedia_jbuf *jb); +PJ_DECL(unsigned) pjmedia_jbuf_get_current_size(pjmedia_jbuf *jb); diff --git a/pjmedia/include/pjmedia/session.h b/pjmedia/include/pjmedia/session.h index cbef4424..17fae89c 100644 --- a/pjmedia/include/pjmedia/session.h +++ b/pjmedia/include/pjmedia/session.h @@ -39,6 +39,17 @@ PJ_BEGIN_DECL /** + * Session info, retrieved from a session by calling + * #pjmedia_session_get_info(). + */ +struct pjmedia_session_info +{ + unsigned stream_cnt; + pjmedia_stream_info stream_info[PJSDP_MAX_MEDIA]; +}; + + +/** * Create new session offering based on the local and remote SDP. * The session will start immediately. * @@ -66,6 +77,18 @@ PJ_DECL(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, /** + * Get session info. + * + * @param session The session which info is being queried. + * @param info Pointer to receive session info. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_session_get_info( pjmedia_session *session, + pjmedia_session_info *info ); + + +/** * Activate all streams in media session for the specified direction. * * @param session The media session. @@ -137,28 +160,12 @@ PJ_DECL(pj_status_t) pjmedia_session_enum_streams(const pjmedia_session *session * indicators such as packet count, packet lost, jitter, delay, etc. * * @param session The media session. - * @param count On input, specifies the number of elements in - * the array. On output, the number will be filled - * with number of streams in the session. - * @param stat Array of stream statistics. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pjmedia_session_get_stat(const pjmedia_session *session, - unsigned *count, - pjmedia_stream_stat stat[]); - -/** - * Get individual stream statistics. The stream statistic shows various - * indicators such as packet count, packet lost, jitter, delay, etc. - * - * @param s The media session. - * @param index The stream index. - * @param stat Stream statistics. + * @param index Stream index. + * @param stat Stream statistic. * * @return PJ_SUCCESS on success. */ -PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(const pjmedia_session *s, +PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(pjmedia_session *session, unsigned index, pjmedia_stream_stat *stat); diff --git a/pjmedia/include/pjmedia/types.h b/pjmedia/include/pjmedia/types.h index 726a33ec..cdbb129d 100644 --- a/pjmedia/include/pjmedia/types.h +++ b/pjmedia/include/pjmedia/types.h @@ -166,6 +166,11 @@ typedef struct pjmedia_channel_stat pjmedia_channel_stat; typedef struct pjmedia_session pjmedia_session; /** + * Media session info. + */ +typedef struct pjmedia_session_info pjmedia_session_info; + +/** * Forward declaration for SDP attribute (sdp.h) */ typedef struct pjmedia_sdp_attr pjmedia_sdp_attr; @@ -205,6 +210,15 @@ typedef enum pjmedia_sdp_neg_state pjmedia_sdp_neg_state; */ typedef struct pjmedia_sdp_neg pjmedia_sdp_neg; +/** + * Types of frame returned from jitter buffer (jbuf.h). + */ +typedef enum pjmedia_jb_frame_type pjmedia_jb_frame_type; + +/** + * Opaque declaration for jitter buffer. + */ +typedef struct pjmedia_jbuf pjmedia_jbuf; #endif /* __PJMEDIA_TYPES_H__ */ diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c index 7661bcce..0463e545 100644 --- a/pjmedia/src/pjmedia/jbuf.c +++ b/pjmedia/src/pjmedia/jbuf.c @@ -17,406 +17,387 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <pjmedia/jbuf.h> -#include <pj/log.h> +#include <pjmedia/errno.h> #include <pj/pool.h> #include <pj/assert.h> #include <pj/string.h> -/* - * At the current state, this is basicly an ugly jitter buffer. - * It worked before by observing level, bit it doesn't work. - * Then I used the size, which makes the level obsolete. - * That's why it's ugly! - */ -#define MAX_SEQ_RANGE 1000 /* Range in which sequence is considered still within session */ -#define UPDATE_DURATION 20 /* Number of frames retrieved before jitter is updated */ +struct jb_framelist +{ + char *flist_buffer; + int *flist_frame_type; + unsigned flist_frame_size; + unsigned flist_max_count; + unsigned flist_empty; + unsigned flist_head; + unsigned flist_tail; + unsigned flist_origin; +}; + -#define THIS_FILE "jbuf" +typedef struct jb_framelist jb_framelist; -/* Individual frame in the frame list. */ -struct pj_jbframe +struct pjmedia_jbuf { - pj_uint32_t extseq; - void *buf; + jb_framelist jb_framelist; + pj_size_t jb_frame_size; // frame size + pj_size_t jb_max_count; // max frames in the jitter framelist->flist_buffer + + int jb_level; // delay between source & destination + // (calculated according of the number of get/put operations) + int jb_last_level; // last delay + int jb_last_jitter; // last jitter calculated + int jb_max_hist_jitter; // max jitter during the last jitter calculations + int jb_stable_hist; // num of times the delay has been lower then the prefetch num + int jb_last_op; // last operation executed on the framelist->flist_buffer (put/get) + int jb_last_seq_no; // seq no. of the last frame inserted to the framelist->flist_buffer + int jb_prefetch; // no. of frame to insert before removing some + // (at the beginning of the framelist->flist_buffer operation) + int jb_prefetch_cnt; // prefetch counter + int jb_status; // status is 'init' until the first 'put' operation + + }; -/* Jitter buffer state. */ -typedef enum jb_state_t -{ - JB_PREFETCH, - JB_NORMAL, -} jb_state_t; +#define JB_STATUS_INITIALIZING 0 +#define JB_STATUS_PROCESSING 1 + +#define PJ_ABS(x) ((x > 0) ? (x) : -(x)) +#define PJ_MAX(x, y) ((x > y) ? (x) : (y)) +#define PJ_MIN(x, y) ((x < y) ? (x) : (y)) + -/* Jitter buffer last operation. */ -typedef enum jb_op_t +static pj_status_t jb_framelist_init( pj_pool_t *pool, + jb_framelist *framelist, + unsigned frame_size, + unsigned max_count) { - JB_PUT, - JB_GET, -} jb_op_t; + PJ_ASSERT_RETURN(pool && framelist, PJ_EINVAL); + + pj_memset(framelist, 0, sizeof(jb_framelist)); + + framelist->flist_frame_size = frame_size; + framelist->flist_max_count = max_count; + framelist->flist_buffer = pj_pool_zalloc(pool, + framelist->flist_frame_size * + framelist->flist_max_count); + + framelist->flist_frame_type = pj_pool_zalloc(pool, + sizeof(framelist->flist_frame_type[0]) * + framelist->flist_max_count); + framelist->flist_empty = 1; + framelist->flist_head = framelist->flist_tail = framelist->flist_origin = 0; -/* Short name for convenience. */ -typedef struct pj_jitter_buffer JB; + return PJ_SUCCESS; + +} + +static pj_status_t jb_framelist_destroy(jb_framelist *framelist) +{ + PJ_UNUSED_ARG(framelist); + return PJ_SUCCESS; +} -/* Initialize framelist. */ -static pj_status_t -pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount ) +static unsigned jb_framelist_size(jb_framelist *framelist) { - PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount)); - - pj_memset(lst, 0, sizeof(*lst)); - lst->maxcount = maxcount; - lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) ); - if (lst->frames == NULL) { - PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!")); - return -1; + if (framelist->flist_tail == framelist->flist_head) { + return framelist->flist_empty ? 0 : framelist->flist_max_count; + } else { + return (framelist->flist_tail - framelist->flist_head + + framelist->flist_max_count) % framelist->flist_max_count; } - return 0; } -/* Reset framelist. */ -static void -pj_framelist_reset( pj_jbframelist *lst ) + +static pj_bool_t jb_framelist_get(jb_framelist *framelist, + void *frame, + pjmedia_jb_frame_type *p_type) { - PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst)); + if (!framelist->flist_empty) { + pj_memcpy(frame, + framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size, + framelist->flist_frame_size); + *p_type = (pjmedia_jb_frame_type) framelist->flist_frame_type[framelist->flist_head]; + + pj_memset(framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size, + 0, framelist->flist_frame_size); + framelist->flist_frame_type[framelist->flist_head] = 0; + + framelist->flist_origin++; + framelist->flist_head = ++framelist->flist_head % framelist->flist_max_count; + if (framelist->flist_head == framelist->flist_tail) + framelist->flist_empty = PJ_TRUE; + + return PJ_TRUE; - lst->count = 0; - lst->head = 0; - lst->frames[0].extseq = 0; + } else { + pj_memset(frame, 0, framelist->flist_frame_size); + return PJ_FALSE; + } } -/* Put a buffer with the specified sequence into the ordered list. */ -static int -pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf ) + +static void jb_framelist_remove_head( jb_framelist *framelist, + unsigned count) { - unsigned pos = (unsigned)-1; - pj_uint32_t startseq = lst->frames[lst->head].extseq; - - if (lst->count == 0) { - /* Empty list. Initialize frame list. */ - PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d", - lst, extseq, lst->head)); - - lst->head = 0; - lst->count = 1; - lst->frames[0].buf = buf; - lst->frames[0].extseq = extseq; - return 0; - - } else if (extseq < startseq) { - /* The sequence number is lower than our oldest packet. This can mean - two things: - - old packet has been receieved, or - - the sequence number has wrapped around. - */ - if (startseq + lst->maxcount <= extseq) { - /* The sequence number has wrapped around, but it is beyond - the capacity of the list (i.e. too soon). - */ - PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", - lst, extseq, startseq)); - return PJ_JB_STATUS_TOO_SOON; - - } else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) { - /* The sequence number has wrapped around, and it is still inside - the 'window' of the framelist. - */ - pos = extseq - startseq; + unsigned cur_size; + + cur_size = jb_framelist_size(framelist); + if (count > cur_size) + count = cur_size; + + if (count) { + // may be done in two steps if overlapping + unsigned step1,step2; + unsigned tmp = framelist->flist_head+count; + + if (tmp > framelist->flist_max_count) { + step1 = framelist->flist_max_count - framelist->flist_head; + step2 = count-step1; } else { - /* The new frame is too old. */ - PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", - lst, extseq, startseq)); - return PJ_JB_STATUS_TOO_OLD; + step1 = count; + step2 = 0; } - - } else if (extseq > startseq + lst->maxcount) { - /* Two possibilities here. Either: - - packet is really too soon, or - - sequence number of startseq has just wrapped around, and old packet - which hasn't wrapped is received. - */ - if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) { - PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", - lst, extseq, startseq)); - return PJ_JB_STATUS_TOO_SOON; - } else { - PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", - lst, extseq, startseq)); - return PJ_JB_STATUS_TOO_OLD; + + pj_memset(framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size, + 0, + step1*framelist->flist_frame_size); + pj_memset(framelist->flist_frame_type+framelist->flist_head, + 0, + step1*sizeof(framelist->flist_frame_type[0])); + + if (step2) { + pj_memset(framelist->flist_buffer, + 0, + step2*framelist->flist_frame_size); + pj_memset(framelist->flist_frame_type, + 0, + step2*sizeof(framelist->flist_frame_type[0])); } - } - /* The new frame is within the framelist capacity. - Calculate position where to put it in the list. - */ - if (pos == (unsigned)-1) - pos = ((extseq - startseq) + lst->head) % lst->maxcount; + // update pointers + framelist->flist_origin += count; + framelist->flist_head = (framelist->flist_head+count) % framelist->flist_max_count; + if (framelist->flist_head == framelist->flist_tail) + framelist->flist_empty = PJ_TRUE; + } +} - pj_assert(pos < lst->maxcount); - - /* Update count only if we're not overwriting existing frame. */ - if (lst->frames[pos].buf == NULL) - ++lst->count; - lst->frames[pos].buf = buf; - lst->frames[pos].extseq = extseq; +static pj_bool_t jb_framelist_put_at(jb_framelist *framelist, + unsigned index, + const void *frame, + unsigned frame_size) +{ + unsigned where; - PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d", - lst, extseq, startseq, lst->head, pos)); - return 0; -} + // too late + if (index < framelist->flist_origin) + return PJ_FALSE; -/* Get the first element of the list. */ -static int -pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf ) -{ - if (lst->count == 0) { - /* Empty. */ - *buf = NULL; - *extseq = 0; - PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p], empty!", lst)); - return -1; - - } else { - *buf = lst->frames[lst->head].buf; - *extseq = lst->frames[lst->head].extseq; - lst->frames[lst->head].buf = NULL; + // too soon + if ((index > (framelist->flist_origin + framelist->flist_max_count - 1)) && !framelist->flist_empty) + return PJ_FALSE; + + assert(frame_size <= framelist->flist_frame_size); + + if (!framelist->flist_empty) { + unsigned cur_size; - PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p] seq=%u, head=%d", - lst, *extseq, lst->head)); + where = (index - framelist->flist_origin + framelist->flist_head) % framelist->flist_max_count; - lst->head = (lst->head + 1) % lst->maxcount; - --lst->count; - return 0; + // update framelist->flist_tail pointer + cur_size = jb_framelist_size(framelist); + if (index >= framelist->flist_origin + cur_size) { + unsigned diff = (index - (framelist->flist_origin + cur_size)); + framelist->flist_tail = (framelist->flist_tail + diff + 1) % framelist->flist_max_count; + } + } else { + where = framelist->flist_tail; + framelist->flist_origin = index; + framelist->flist_tail = (++framelist->flist_tail % framelist->flist_max_count); + framelist->flist_empty = PJ_FALSE; } + + pj_memcpy(framelist->flist_buffer + where * framelist->flist_frame_size, + frame, frame_size); + + framelist->flist_frame_type[where] = PJMEDIA_JB_NORMAL_FRAME; + + return PJ_TRUE; } -/***************************************************************************** - * Reset jitter buffer. - **************************************************************************** -*/ -PJ_DEF(void) pj_jb_reset(JB *jb) + +enum pjmedia_jb_op { - PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb)); - - jb->level = jb->max_level = 1; - jb->prefetch = jb->min; - jb->get_cnt = 0; - jb->lastseq = 0; - jb->state = JB_PREFETCH; - jb->upd_count = 0; - jb->last_op = -1; - pj_framelist_reset( &jb->lst ); -} + JB_OP_INIT = -1, + JB_OP_PUT = 1, + JB_OP_GET = 2 +}; -/***************************************************************************** - * Create jitter buffer. - ***************************************************************************** - */ -PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool, - unsigned min, unsigned max, unsigned maxcount) +PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, + int frame_size, + int initial_prefetch, + int max_count, + pjmedia_jbuf **p_jb) { + pjmedia_jbuf *jb; pj_status_t status; - if (maxcount <= max) { - maxcount = max * 5 / 4; - PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted.")); - } - - jb->min = min; - jb->max = max; + jb = pj_pool_zalloc(pool, sizeof(pjmedia_jbuf)); - status = pj_framelist_init( &jb->lst, pool, maxcount ); + status = jb_framelist_init(pool, &jb->jb_framelist, frame_size, max_count); if (status != PJ_SUCCESS) return status; - pj_jb_reset(jb); - - PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d", - jb, min, max, maxcount)); + jb->jb_frame_size = frame_size; + jb->jb_last_seq_no = -1; + jb->jb_level = 0; + jb->jb_last_level = 0; + jb->jb_last_jitter = 0; + jb->jb_last_op = JB_OP_INIT; + jb->jb_prefetch = PJ_MIN(initial_prefetch, max_count*4/5); + jb->jb_prefetch_cnt = 0; + jb->jb_stable_hist = 0; + jb->jb_status = JB_STATUS_INITIALIZING; + jb->jb_max_hist_jitter = 0; + jb->jb_max_count = max_count; + + *p_jb = jb; return PJ_SUCCESS; } -/***************************************************************************** - * Put a packet to the jitter buffer. - ***************************************************************************** - */ -PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf ) +PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb) { - unsigned distance; - int status; - - PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf)); - - if (jb->lastseq == 0) - jb->lastseq = extseq - 1; - - /* Calculate distance between this packet and last received packet - to detect long jump (indicating probably remote has just been - restarted. - */ - distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq; - if (distance > MAX_SEQ_RANGE) { - /* Distance is out of range, reset jitter while maintaining current jitter - level. - */ - int old_level = jb->level; - int old_prefetch = jb->prefetch; - - PJ_LOG(4, (THIS_FILE, " ..[jb=%p] distance out of range, resetting", jb)); - - pj_jb_reset(jb); - jb->level = old_level; - jb->prefetch = old_prefetch; - distance = 1; - jb->lastseq = extseq - 1; - } - - jb->lastseq = extseq; + return jb_framelist_destroy(&jb->jb_framelist); +} - status = pj_framelist_put( &jb->lst, extseq, buf ); - if (status == PJ_JB_STATUS_TOO_OLD) - return -1; - if (status == PJ_JB_STATUS_TOO_SOON) { - /* TODO: discard old packets.. */ - /* No, don't do it without putting a way to inform application so that - it can free the memory */ - } +static void jbuf_calculate_jitter(pjmedia_jbuf *jb) +{ + enum { STABLE_HISTORY_LIMIT = (500/20) }; + jb->jb_last_jitter = PJ_ABS(jb->jb_level-jb->jb_last_level); + jb->jb_last_level = jb->jb_level; + jb->jb_max_hist_jitter = PJ_MAX(jb->jb_max_hist_jitter,jb->jb_last_jitter); + + if (jb->jb_last_jitter< jb->jb_prefetch) { + jb->jb_stable_hist += jb->jb_last_jitter; + if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) { + int seq_diff = (jb->jb_prefetch - jb->jb_max_hist_jitter)/3; + if (seq_diff<1) seq_diff = 1; + + jb->jb_prefetch -= seq_diff; + if (jb->jb_prefetch < 1) jb->jb_prefetch = 1; - if (jb->last_op != JB_PUT) { - if (jb->state != JB_PREFETCH) - jb->level--; + jb->jb_stable_hist = 0; + jb->jb_max_hist_jitter = 0; + } } else { - jb->level++; + jb->jb_prefetch = PJ_MIN(jb->jb_last_jitter,(int)(jb->jb_max_count*4/5)); + jb->jb_stable_hist = 0; + jb->jb_max_hist_jitter = 0; } - - if ((int)jb->lst.count > jb->max_level) - jb->max_level++; - - jb->last_op = JB_PUT; - return 0; } - -/* - * Update jitter buffer algorithm. - */ -static void jb_update(JB *jb, int apply, int log_info) +static void jbuf_update(pjmedia_jbuf *jb, int oper) { - unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level; - unsigned new_prefetch; - - /* Update prefetch count */ - if (abs_level > jb->prefetch) - new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10; - else { - new_prefetch = (jb->prefetch*4 + abs_level) / 5; - pj_assert(new_prefetch <= jb->prefetch); + if(jb->jb_last_op != oper) { + jbuf_calculate_jitter(jb); + jb->jb_last_op = oper; } +} - if (log_info) { - PJ_LOG(5, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", - jb, jb->level, jb->max_level, jb->prefetch, new_prefetch)); - } else { - PJ_LOG(6, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", - jb, jb->level, jb->max_level, jb->prefetch, new_prefetch)); +PJ_DEF(pj_status_t) pjmedia_jbuf_put_frame(pjmedia_jbuf *jb, + const void *frame, + pj_size_t frame_size, + int frame_seq) +{ + pj_size_t min_frame_size; + int seq_diff; + + if (jb->jb_last_seq_no == -1) { + jb->jb_last_seq_no = frame_seq - 1; } - if (new_prefetch < jb->min) new_prefetch = jb->min; - if (new_prefetch > jb->max) new_prefetch = jb->max; + seq_diff = frame_seq - jb->jb_last_seq_no; + jb->jb_last_seq_no = PJ_MAX(jb->jb_last_seq_no, frame_seq); + if (seq_diff > 0) jb->jb_level += seq_diff; - /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the - new prefetch setting. - */ - if (jb->lst.count == 0) { - jb->state = JB_PREFETCH; - jb->get_cnt = 0; + if(jb->jb_status == JB_STATUS_INITIALIZING) { + jb->jb_status = JB_STATUS_PROCESSING; + jb->jb_level = 0; + jb->jb_last_level = 0; + jb->jb_last_jitter = 0; } else { - /* Check if delay is too long, which in this case probably better to - discard some frames.. - */ - /* No, don't do it without putting a way to inform application so that - it can free the memory */ + jbuf_update(jb, JB_OP_PUT); } + min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size); + if (seq_diff > 0) { - if (apply) { - jb->prefetch = new_prefetch; - if (jb->max_level > 0) - jb->max_level--; - } else { - jb->level = new_prefetch; - } -} + while (!jb_framelist_put_at(&jb->jb_framelist,frame_seq,frame,min_frame_size)) { + jb_framelist_remove_head(&jb->jb_framelist,PJ_MAX(jb->jb_max_count/4,1)); + } + if (jb->jb_prefetch_cnt < jb->jb_prefetch) + jb->jb_prefetch_cnt += seq_diff; -/***************************************************************************** - * Get the oldest frame from jitter buffer. - ***************************************************************************** - */ -PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf ) -{ - pj_status_t status; - - PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb)); - - /* - * Check whether we're ready to give frame. When we're in JB_PREFETCH state, - * only give frames only when: - * - the buffer has enough frames in it (jb->list.count > jb->prefetch), OR - * - after 'prefetch' attempts, there's still no frame, which in this - * case PJ_JB_STATUS_FRAME_NULL will be returned by the next check. - */ - if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) { - jb->get_cnt++; - jb->last_op = JB_GET; - PJ_LOG(5, (THIS_FILE, " ..[jb=%p] bufferring...", jb)); - return PJ_JB_STATUS_FRAME_NULL; } - - /* Attempt to get one frame from the list. */ - status = pj_framelist_get( &jb->lst, extseq, buf ); - if (status != 0) { - PJ_LOG(6, (THIS_FILE, " ..[jb=%p] no packet!", jb)); - status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL; - jb_update(jb, 1, 0); - return status; + else + { + jb_framelist_put_at(&jb->jb_framelist,frame_seq,frame,min_frame_size); } - /* Force state to NORMAL */ - jb->state = JB_NORMAL; + return PJ_SUCCESS; +} - /* Increase level only when last operation is GET. - * This is to prevent level from increasing during silence period, which - * no packets is receieved. - */ - if (jb->last_op != JB_GET) { - int apply; +PJ_DEF(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb, + void *frame, + char *p_frame_type) +{ + pjmedia_jb_frame_type ftype; - //jb->level++; - jb->last_op = JB_GET; + jb->jb_level--; - apply = (++jb->upd_count > UPDATE_DURATION); - if (apply) - jb->upd_count = 0; + jbuf_update(jb, JB_OP_GET); - jb_update(jb, apply, apply); + if (jb_framelist_size(&jb->jb_framelist) == 0) { + jb->jb_prefetch_cnt = 0; } - PJ_LOG(6, (THIS_FILE, " ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d", - jb, *extseq, jb->level, jb->prefetch, jb->lst.count, - jb->lastseq - *extseq)); - return 0; + if ((jb->jb_prefetch_cnt < jb->jb_prefetch) || !jb_framelist_get(&jb->jb_framelist,frame,&ftype)) { + pj_memset(frame, 0, jb->jb_frame_size); + *p_frame_type = PJMEDIA_JB_ZERO_FRAME; + return PJ_SUCCESS; + } + + if (ftype == PJMEDIA_JB_NORMAL_FRAME) { + *p_frame_type = PJMEDIA_JB_NORMAL_FRAME; + } else { + *p_frame_type = PJMEDIA_JB_MISSING_FRAME; + } + + return PJ_SUCCESS; +} + +PJ_DEF(unsigned) pjmedia_jbuf_get_prefetch_size(pjmedia_jbuf *jb) +{ + return jb->jb_prefetch; +} + +PJ_DEF(unsigned) pjmedia_jbuf_get_current_size(pjmedia_jbuf *jb) +{ + return jb_framelist_size(&jb->jb_framelist); } diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c index 1ddb13b4..18938db1 100644 --- a/pjmedia/src/pjmedia/session.c +++ b/pjmedia/src/pjmedia/session.c @@ -233,13 +233,16 @@ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, } /* - * Now create the stream! + * Now create and start the stream! */ for (i=0; i<(int)stream_cnt; ++i) { status = pjmedia_stream_create(endpt, session->pool, &session->stream_info[i], &session->stream[i]); + if (status == PJ_SUCCESS) + status = pjmedia_stream_start(session->stream[i]); + if (status != PJ_SUCCESS) { for ( --i; i>=0; ++i) { @@ -259,6 +262,22 @@ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, } +/* + * Get session info. + */ +PJ_DEF(pj_status_t) pjmedia_session_get_info( pjmedia_session *session, + pjmedia_session_info *info ) +{ + PJ_ASSERT_RETURN(session && info, PJ_EINVAL); + + info->stream_cnt = session->stream_cnt; + pj_memcpy(info->stream_info, session->stream_info, + session->stream_cnt * sizeof(pjmedia_stream_info)); + + return PJ_SUCCESS; +} + + /** * Destroy media session. */ @@ -368,28 +387,14 @@ PJ_DEF(pj_status_t) pjmedia_session_enum_streams(const pjmedia_session *session, /** * Get statistics */ -PJ_DEF(pj_status_t) pjmedia_session_get_stat(const pjmedia_session *session, - unsigned *count, - pjmedia_stream_stat stat[]) -{ - PJ_ASSERT_RETURN(session && count && *count && stat, PJ_EINVAL); - - *count = 0; - pj_memset(stat, 0, *count * sizeof(pjmedia_stream_stat)); - return PJ_EINVALIDOP; -} - - -/** - * Get individual stream statistic. - */ -PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( const pjmedia_session *s, +PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( pjmedia_session *session, unsigned index, pjmedia_stream_stat *stat) { - PJ_ASSERT_RETURN(s && index < s->stream_cnt && stat, PJ_EINVAL); - pj_memset(stat, 0, sizeof(pjmedia_stream_stat)); - return PJ_EINVALIDOP; + PJ_ASSERT_RETURN(session && stat && index < session->stream_cnt, + PJ_EINVAL); + + return pjmedia_stream_get_stat(session->stream[index], stat); } diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 5846e27e..cc11f9c6 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -39,15 +39,6 @@ #define PJMEDIA_MAX_BUFFER_SIZE_MS 2000 #define PJMEDIA_MAX_MTU 1500 -struct jb_frame -{ - unsigned size; - void *buf; -}; - -#define pj_fifobuf_alloc(fifo,size) malloc(size) -#define pj_fifobuf_unalloc(fifo,buf) free(buf) -#define pj_fifobuf_free(fifo, buf) free(buf) /** @@ -88,13 +79,13 @@ struct pjmedia_stream pjmedia_codec_mgr *codec_mgr; /**< Codec manager instance. */ pjmedia_codec *codec; /**< Codec instance being used. */ - + pj_size_t frame_size; /**< Size of encoded frame. */ pj_mutex_t *jb_mutex; - pj_jitter_buffer jb; /**< Jitter buffer. */ + pjmedia_jbuf *jb; /**< Jitter buffer. */ - pj_sock_t rtp_sock; /**< RTP socket. */ - pj_sock_t rtcp_sock; /**< RTCP socket. */ - pj_sockaddr_in dst_addr; /**< Destination RTP address. */ + pjmedia_sock_info skinfo; /**< Transport info. */ + pj_sockaddr_in rem_rtp_addr; /**< Remote RTP address. */ + pj_sockaddr_in rem_rtcp_addr; /**< Remote RTCP address. */ pj_rtcp_session rtcp; /**< RTCP for incoming RTP. */ @@ -117,9 +108,7 @@ static pj_status_t play_callback(/* in */ void *user_data, { pjmedia_channel *channel = user_data; pjmedia_stream *stream = channel->stream; - struct jb_frame *jb_frame; - void *p; - pj_uint32_t extseq; + char frame_type; pj_status_t status; struct pjmedia_frame frame_in, frame_out; @@ -133,20 +122,23 @@ static pj_status_t play_callback(/* in */ void *user_data, pj_mutex_lock( stream->jb_mutex ); /* Get frame from jitter buffer. */ - status = pj_jb_get(&stream->jb, &extseq, &p); + status = pjmedia_jbuf_get_frame(stream->jb, channel->out_pkt, + &frame_type); /* Unlock jitter buffer mutex. */ pj_mutex_unlock( stream->jb_mutex ); - jb_frame = p; - if (status != PJ_SUCCESS || jb_frame == NULL) { + if (status != PJ_SUCCESS || frame_type == PJMEDIA_JB_ZERO_FRAME || + frame_type == PJMEDIA_JB_MISSING_FRAME) + { pj_memset(frame, 0, size); return 0; } + /* Decode */ - frame_in.buf = jb_frame->buf; - frame_in.size = jb_frame->size; + frame_in.buf = channel->out_pkt; + frame_in.size = stream->frame_size; frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; /* ignored */ frame_out.buf = channel->pcm_buf; status = stream->codec->op->decode( stream->codec, &frame_in, @@ -155,7 +147,6 @@ static pj_status_t play_callback(/* in */ void *user_data, TRACE_((THIS_FILE, "decode() has return error status %d", status)); pj_memset(frame, 0, size); - pj_fifobuf_free (&channel->fifobuf, jb_frame); return 0; } @@ -167,7 +158,6 @@ static pj_status_t play_callback(/* in */ void *user_data, } pj_memcpy(frame, frame_out.buf, size); - pj_fifobuf_free (&channel->fifobuf, jb_frame); return 0; } @@ -238,8 +228,8 @@ static pj_status_t rec_callback( /* in */ void *user_data, /* Send. */ sent = frame_out.size+sizeof(pj_rtp_hdr); - status = pj_sock_sendto(stream->rtp_sock, channel->out_pkt, &sent, 0, - &stream->dst_addr, sizeof(stream->dst_addr)); + status = pj_sock_sendto(stream->skinfo.rtp_sock, channel->out_pkt, &sent, 0, + &stream->rem_rtp_addr, sizeof(stream->rem_rtp_addr)); if (status != PJ_SUCCESS) return status; @@ -260,31 +250,39 @@ static int PJ_THREAD_FUNC jitter_buffer_thread (void*arg) pjmedia_stream *stream = arg; pjmedia_channel *channel = stream->dec; + while (!stream->quit_flag) { - pj_ssize_t len, size; + pj_ssize_t len; const pj_rtp_hdr *hdr; const void *payload; unsigned payloadlen; int status; - struct jb_frame *jb_frame; /* Wait for packet. */ pj_fd_set_t fds; pj_time_val timeout; PJ_FD_ZERO (&fds); - PJ_FD_SET (stream->rtp_sock, &fds); + PJ_FD_SET (stream->skinfo.rtp_sock, &fds); timeout.sec = 0; timeout.msec = 1; /* Wait with timeout. */ - status = pj_sock_select(stream->rtp_sock, &fds, NULL, NULL, &timeout); - if (status != 1) + status = pj_sock_select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + if (status < 0) { + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(pj_get_netos_error(), errmsg, sizeof(errmsg)); + TRACE_((THIS_FILE, "Jitter buffer select() error: %s", + errmsg)); + pj_thread_sleep(500); + continue; + } else if (status == 0) continue; /* Get packet from socket. */ len = channel->in_pkt_size; - status = pj_sock_recv(stream->rtp_sock, channel->in_pkt, &len, 0); + status = pj_sock_recv(stream->skinfo.rtp_sock, + channel->in_pkt, &len, 0); if (len < 1 || status != PJ_SUCCESS) { if (pj_get_netos_error() == PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { /* On Win2K SP2 (or above) and WinXP, recv() will get @@ -325,28 +323,12 @@ static int PJ_THREAD_FUNC jitter_buffer_thread (void*arg) stream->stat.dec.pkt++; stream->stat.dec.bytes += len; - /* Copy to FIFO buffer. */ - size = payloadlen+sizeof(struct jb_frame); - jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size); - if (jb_frame == NULL) { - TRACE_((THIS_FILE, "Unable to allocate %d bytes FIFO buffer", - size)); - continue; - } - - /* Copy the payload */ - jb_frame->size = payloadlen; - jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame); - pj_memcpy (jb_frame->buf, payload, payloadlen); - /* Put to jitter buffer. */ pj_mutex_lock( stream->jb_mutex ); - status = pj_jb_put(&stream->jb, pj_ntohs(hdr->seq), jb_frame); + status = pjmedia_jbuf_put_frame(stream->jb, payload, payloadlen, pj_ntohs(hdr->seq)); pj_mutex_unlock( stream->jb_mutex ); if (status != 0) { - pj_fifobuf_unalloc (&channel->fifobuf, jb_frame); - TRACE_((THIS_FILE, "Jitter buffer put() has returned error status %d", status)); @@ -485,6 +467,10 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->dir = info->dir; stream->codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); + stream->skinfo = info->sock_info; + stream->rem_rtp_addr = info->rem_addr; + + PJ_TODO(INITIALIZE_RTCP_REMOTE_ADDRESS); /* Create mutex to protect jitter buffer: */ @@ -515,15 +501,20 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, goto err_cleanup; + /* Get the frame size: */ + + stream->frame_size = (codec_param.avg_bps / 8) * codec_param.ptime / 1000; + + /* Init RTCP session: */ pj_rtcp_init(&stream->rtcp, info->ssrc); - /* Init jitter buffer: */ + /* Create jitter buffer: */ - status = pj_jb_init(&stream->jb, pool, - info->jb_min, info->jb_max, info->jb_maxcnt); + status = pjmedia_jbuf_create(pool, stream->frame_size, 15, 100, + &stream->jb); if (status != PJ_SUCCESS) goto err_cleanup; @@ -532,7 +523,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, status = pj_thread_create(pool, "decode", &jitter_buffer_thread, stream, - 0, 0, &stream->thread); + 0, PJ_THREAD_SUSPENDED, &stream->thread); if (status != PJ_SUCCESS) goto err_cleanup; @@ -552,6 +543,10 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (status != PJ_SUCCESS) goto err_cleanup; + /* Resume jitter buffer thread. */ + status = pj_thread_resume( stream->thread ); + if (status != PJ_SUCCESS) + goto err_cleanup; /* Success! */ *p_stream = stream; diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h index b0dab63c..a8276bb5 100644 --- a/pjsip/include/pjsip-ua/sip_inv.h +++ b/pjsip/include/pjsip-ua/sip_inv.h @@ -200,6 +200,11 @@ PJ_DECL(pj_status_t) pjsip_inv_usage_init(pjsip_endpoint *endpt, PJ_DECL(pjsip_module*) pjsip_inv_usage_instance(void); +/** + * Dump user agent contents (e.g. all dialogs). + */ +PJ_DECL(void) pjsip_inv_usage_dump(void); + /** * Create UAC invite session for the specified dialog in dlg. diff --git a/pjsip/include/pjsip/sip_ua_layer.h b/pjsip/include/pjsip/sip_ua_layer.h index 02f94579..012628a5 100644 --- a/pjsip/include/pjsip/sip_ua_layer.h +++ b/pjsip/include/pjsip/sip_ua_layer.h @@ -73,6 +73,11 @@ PJ_DECL(pjsip_user_agent*) pjsip_ua_instance(void); PJ_DECL(pj_status_t) pjsip_ua_destroy(void); /** + * Dump user agent contents (e.g. all dialogs). + */ +PJ_DEF(void) pjsip_ua_dump(void); + +/** * Get the endpoint instance of a user agent module. * * @param ua The user agent instance. diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index 65e46431..a655c86f 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -320,6 +320,7 @@ PJ_DEF(pjsip_module*) pjsip_inv_usage_instance(void) } + /* * Return the invite session for the specified dialog. */ diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index f41b5d53..9f0434a4 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -929,6 +929,9 @@ static void dlg_beautify_response(pjsip_dialog *dlg, pj_assert(to != NULL); to->tag = dlg->local.info->tag; + + if (dlg->state == PJSIP_DIALOG_STATE_NULL) + dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED; } } @@ -1097,7 +1100,9 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) dlg->remote.cseq = rdata->msg_info.cseq->cseq; /* Create UAS transaction for this request. */ - if (pjsip_rdata_get_tsx(rdata) == NULL) { + if (pjsip_rdata_get_tsx(rdata) == NULL && + rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) + { status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx); PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;}); @@ -1280,4 +1285,3 @@ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg, } } - diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 2531ae22..fb247b18 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1044,6 +1044,11 @@ PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr) pj_lock_acquire(mgr->lock); +#if defined(PJ_DEBUG) && PJ_DEBUG!=0 + PJ_LOG(3,(THIS_FILE, " Outstanding transmit buffers: %d", + pj_atomic_get(mgr->tdata_counter))); +#endif + itr = pj_hash_first(mgr->table, &itr_val); if (itr) { PJ_LOG(3, (THIS_FILE, " Dumping transports:")); diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c index ada14ad2..c1fd6332 100644 --- a/pjsip/src/pjsip/sip_ua_layer.c +++ b/pjsip/src/pjsip/sip_ua_layer.c @@ -720,3 +720,73 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata) } +#if PJ_LOG_MAX_LEVEL >= 3 +static void print_dialog( const char *title, + pjsip_dialog *dlg, char *buf, pj_size_t size) +{ + int len; + char userinfo[128]; + + len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); + if (len < 1) + pj_native_strcpy(userinfo, "<--uri too long-->"); + else + userinfo[len] = '\0'; + + len = pj_snprintf(buf, size, "%s[%s] %s", + title, + (dlg->state==PJSIP_DIALOG_STATE_NULL ? " - " : + "est"), + userinfo); + if (len < 1 || len >= (int)size) { + pj_native_strcpy(buf, "<--uri too long-->"); + } else + buf[len] = '\0'; +} +#endif + +/* + * Dump user agent contents (e.g. all dialogs). + */ +PJ_DEF(void) pjsip_ua_dump(void) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + pj_hash_iterator_t itbuf, *it; + char dlginfo[128]; + + pj_mutex_lock(mod_ua.mutex); + + PJ_LOG(3, (THIS_FILE, "Number of dialog sets: %u", pj_hash_count(mod_ua.dlg_table))); + PJ_LOG(3, (THIS_FILE, "Dumping dialog sets:")); + + it = pj_hash_first(mod_ua.dlg_table, &itbuf); + for (; it != NULL; it = pj_hash_next(mod_ua.dlg_table, it)) { + struct dlg_set *dlg_set; + pjsip_dialog *dlg; + const char *title; + + dlg_set = pj_hash_this(mod_ua.dlg_table, it); + if (!dlg_set || pj_list_empty(&dlg_set->dlg_list)) continue; + + /* First dialog in dialog set. */ + dlg = dlg_set->dlg_list.next; + if (dlg->role == PJSIP_ROLE_UAC) + title = " [out] "; + else + title = " [in] "; + + print_dialog(title, dlg, dlginfo, sizeof(dlginfo)); + PJ_LOG(3,(THIS_FILE, "%s", dlginfo)); + + /* Next dialog in dialog set (forked) */ + dlg = dlg->next; + while (dlg != (pjsip_dialog*) &dlg_set->dlg_list) { + print_dialog(" [forked] ", dlg, dlginfo, sizeof(dlginfo)); + dlg = dlg->next; + } + } + + pj_mutex_unlock(mod_ua.mutex); +#endif +} + diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index 287f5624..6b85bfaf 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -25,26 +25,27 @@ static pjsip_inv_session *inv_session; +static const char *inv_state_names[] = +{ + "NULL ", + "CALLING ", + "INCOMING ", + "EARLY ", + "CONNECTING", + "CONFIRMED ", + "DISCONNCTD", + "TERMINATED", +}; + /* * Notify UI when invite state has changed. */ void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) { - const char *state_names[] = - { - "NULL", - "CALLING", - "INCOMING", - "EARLY", - "CONNECTING", - "CONFIRMED", - "DISCONNECTED", - "TERMINATED", - }; - PJ_UNUSED_ARG(e); - PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", state_names[inv->state])); + PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", + inv_state_names[inv->state])); if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { if (inv == inv_session) @@ -57,11 +58,130 @@ void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) } } + +static void print_invite_session(const char *title, + struct pjsua_inv_data *inv_data, + char *buf, pj_size_t size) +{ + int len; + pjsip_inv_session *inv = inv_data->inv; + pjsip_dialog *dlg = inv->dlg; + char userinfo[128]; + + /* Dump invite sesion info. */ + + len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); + if (len < 1) + pj_native_strcpy(userinfo, "<--uri too long-->"); + else + userinfo[len] = '\0'; + + len = pj_snprintf(buf, size, "%s[%s] %s", + title, + inv_state_names[inv->state], + userinfo); + if (len < 1 || len >= (int)size) { + pj_native_strcpy(buf, "<--uri too long-->"); + len = 18; + } else + buf[len] = '\0'; +} + +static void dump_media_session(pjmedia_session *session) +{ + unsigned i; + pjmedia_session_info info; + + pjmedia_session_get_info(session, &info); + + for (i=0; i<info.stream_cnt; ++i) { + pjmedia_stream_stat strm_stat; + const char *rem_addr; + int rem_port; + const char *dir; + + pjmedia_session_get_stream_stat(session, i, &strm_stat); + rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr); + rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port); + + if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING) + dir = "sendonly"; + else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING) + dir = "recvonly"; + else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING) + dir = "sendrecv"; + else + dir = "inactive"; + + + PJ_LOG(3,(THIS_FILE, + "%s[Media strm#%d] %.*s, %s, peer=%s:%d", + " ", + i, + info.stream_info[i].fmt.encoding_name.slen, + info.stream_info[i].fmt.encoding_name.ptr, + dir, + rem_addr, rem_port)); + PJ_LOG(3,(THIS_FILE, + "%s tx {pkt=%u, bytes=%u} rx {pkt=%u, bytes=%u}", + " ", + strm_stat.enc.pkt, strm_stat.enc.bytes, + strm_stat.dec.pkt, strm_stat.dec.bytes)); + + } +} + +/* + * Dump application states. + */ +static void pjsua_dump(void) +{ + struct pjsua_inv_data *inv_data; + char buf[128]; + unsigned log_decor; + + log_decor = pj_log_get_decor(); + pj_log_set_decor(PJ_LOG_HAS_NEWLINE); + + pjsip_endpt_dump(pjsua.endpt, 1); + pjsip_ua_dump(); + + /* Dump all invite sessions: */ + PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:")); + + if (pj_list_empty(&pjsua.inv_list)) { + + PJ_LOG(3,(THIS_FILE, " - no sessions -")); + + } else { + + inv_data = pjsua.inv_list.next; + + while (inv_data != &pjsua.inv_list) { + + print_invite_session(" ", inv_data, buf, sizeof(buf)); + PJ_LOG(3,(THIS_FILE, "%s", buf)); + + if (inv_data->session) + dump_media_session(inv_data->session); + + inv_data = inv_data->next; + } + } + + pj_log_set_decor(log_decor); +} + + +/* + * Show a bit of help. + */ static void ui_help(void) { puts(""); puts("Console keys:"); puts(" m Make a call/another call"); + puts(" d Dump application states"); puts(" a Answer incoming call"); puts(" h Hangup current call"); puts(" q Quit"); @@ -122,6 +242,10 @@ static void ui_console_main(void) break; + case 'd': + pjsua_dump(); + break; + case 'a': if (inv_session == NULL || inv_session->role != PJSIP_ROLE_UAS || diff --git a/pjsip/src/pjsua/pjsua.h b/pjsip/src/pjsua/pjsua.h index 810c57b5..f882e873 100644 --- a/pjsip/src/pjsua/pjsua.h +++ b/pjsip/src/pjsua/pjsua.h @@ -37,6 +37,22 @@ PJ_BEGIN_DECL + +/** + * Structure to be attached to all dialog. + * Given a dialog "dlg", application can retrieve this structure + * by accessing dlg->mod_data[pjsua.mod.id]. + */ +struct pjsua_inv_data +{ + PJ_DECL_LIST_MEMBER(struct pjsua_inv_data); + + pjsip_inv_session *inv; + pjmedia_session *session; +}; + + + /* PJSUA application variables. */ struct pjsua { @@ -109,6 +125,9 @@ struct pjsua unsigned log_decor; /**< Log decoration. */ char *log_filename; /**< Log filename. */ + /* List of invite sessions: */ + + struct pjsua_inv_data inv_list; }; @@ -116,16 +135,6 @@ struct pjsua extern struct pjsua pjsua; -/** - * Structure to be attached to all dialog. - * Given a dialog "dlg", application can retrieve this structure - * by accessing dlg->mod_data[pjsua.mod.id]. - */ -struct pjsua_inv_data -{ - pjmedia_session *session; -}; - /***************************************************************************** * PJSUA API (defined in pjsua_core.c). diff --git a/pjsip/src/pjsua/pjsua_core.c b/pjsip/src/pjsua/pjsua_core.c index cc9ae86f..7eb414bc 100644 --- a/pjsip/src/pjsua/pjsua_core.c +++ b/pjsip/src/pjsua/pjsua_core.c @@ -75,6 +75,10 @@ void pjsua_default(void) /* Init route set list: */ pj_list_init(&pjsua.route_set); + + /* Init invite session list: */ + + pj_list_init(&pjsua.inv_list); } diff --git a/pjsip/src/pjsua/pjsua_inv.c b/pjsip/src/pjsua/pjsua_inv.c index 72272c32..7c6dbf26 100644 --- a/pjsip/src/pjsua/pjsua_inv.c +++ b/pjsip/src/pjsua/pjsua_inv.c @@ -78,6 +78,7 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri, /* Create and associate our data in the session. */ inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data)); + inv_data->inv = inv; dlg->mod_data[pjsua.mod.id] = inv_data; @@ -110,6 +111,10 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri, goto on_error; } + /* Add invite session to the list. */ + + pj_list_push_back(&pjsua.inv_list, inv_data); + /* Done. */ @@ -214,8 +219,11 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata) /* Create and attach pjsua data to the dialog: */ inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data)); + inv_data->inv = inv; dlg->mod_data[pjsua.mod.id] = inv_data; + pj_list_push_back(&pjsua.inv_list, inv_data); + /* Answer with 100 (using the dialog, not invite): */ @@ -244,6 +252,9 @@ void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) struct pjsua_inv_data *inv_data; inv_data = inv->dlg->mod_data[pjsua.mod.id]; + + pj_assert(inv_data != NULL); + if (inv_data && inv_data->session) { pjmedia_session_destroy(inv_data->session); inv_data->session = NULL; @@ -251,6 +262,11 @@ void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) PJ_LOG(3,(THIS_FILE,"Media session is destroyed")); } + if (inv_data) { + + pj_list_erase(inv_data); + + } } pjsua_ui_inv_on_state_changed(inv, e); |