summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-02-09 14:01:40 +0000
committerBenny Prijono <bennylp@teluu.com>2006-02-09 14:01:40 +0000
commita1fd7f6ddafccef9cf47bb8c291749aaa835e7fa (patch)
tree5c3adf721788e7201b3e4bedad41241e689c33a0
parent6096b35446a1fa0cef3cfd51c29d9d2609d972e5 (diff)
Updated with new jitter buffer, and statistics in pjsua
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@169 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia/errno.h5
-rw-r--r--pjmedia/include/pjmedia/jbuf.h120
-rw-r--r--pjmedia/include/pjmedia/session.h45
-rw-r--r--pjmedia/include/pjmedia/types.h14
-rw-r--r--pjmedia/src/pjmedia/jbuf.c609
-rw-r--r--pjmedia/src/pjmedia/session.c45
-rw-r--r--pjmedia/src/pjmedia/stream.c101
-rw-r--r--pjsip/include/pjsip-ua/sip_inv.h5
-rw-r--r--pjsip/include/pjsip/sip_ua_layer.h5
-rw-r--r--pjsip/src/pjsip-ua/sip_inv.c1
-rw-r--r--pjsip/src/pjsip/sip_dialog.c8
-rw-r--r--pjsip/src/pjsip/sip_transport.c5
-rw-r--r--pjsip/src/pjsip/sip_ua_layer.c70
-rw-r--r--pjsip/src/pjsua/main.c150
-rw-r--r--pjsip/src/pjsua/pjsua.h29
-rw-r--r--pjsip/src/pjsua/pjsua_core.c4
-rw-r--r--pjsip/src/pjsua/pjsua_inv.c16
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);