From 612d2349e4664dd90bdf00850f22d2c4f5ee8c16 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Sun, 14 May 2006 18:23:34 +0000 Subject: Fixed more bugs with multiple frame handling git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@442 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia/jbuf.h | 3 + pjmedia/src/pjmedia/conference.c | 8 ++- pjmedia/src/pjmedia/jbuf.c | 37 +++++++++-- pjmedia/src/pjmedia/stream.c | 118 ++++++++++++++++++++++++++++----- pjsip-apps/src/samples/siprtp_report.c | 2 +- 5 files changed, 144 insertions(+), 24 deletions(-) diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h index 9bb1a995..013f61aa 100644 --- a/pjmedia/include/pjmedia/jbuf.h +++ b/pjmedia/include/pjmedia/jbuf.h @@ -111,6 +111,8 @@ typedef struct pjmedia_jbuf pjmedia_jbuf; * jitter buffer. This effectively means the maximum * delay that may be introduced by this jitter * buffer. + * @param ptime Indication of frame duration, used to calculate + * the interval between jitter recalculation. * @param p_jb Pointer to receive jitter buffer instance. * * @return PJ_SUCCESS on success. @@ -118,6 +120,7 @@ typedef struct pjmedia_jbuf pjmedia_jbuf; PJ_DECL(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, const pj_str_t *name, unsigned frame_size, + unsigned ptime, unsigned max_count, pjmedia_jbuf **p_jb); diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index bfdc24a9..60013c55 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -1095,6 +1095,10 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport, /* If port is muted or nobody is transmitting to this port, * transmit NULL frame. */ + /* note: + * the "cport->sources==0" checking will cause discontinuous + * transmission for RTP stream. + */ if (cport->tx_setting == PJMEDIA_PORT_MUTE || cport->sources==0) { pjmedia_frame frame; @@ -1124,7 +1128,7 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport, */ buf = (pj_int16_t*)cport->mix_buf; - if (cport->tx_adj_level != NORMAL_LEVEL) { + if (cport->tx_adj_level != NORMAL_LEVEL && cport->sources) { unsigned adj_level = cport->tx_adj_level; @@ -1168,7 +1172,7 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport, * for VU meter display. By doing it here, it should give the acceptable * indication of the signal level of the port. */ - if (cport->need_tx_level) { + if (cport->need_tx_level && cport->sources) { pj_uint32_t level; /* Get the signal level. */ diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c index d124abcb..7e7fd0d5 100644 --- a/pjmedia/src/pjmedia/jbuf.c +++ b/pjmedia/src/pjmedia/jbuf.c @@ -50,6 +50,7 @@ struct pjmedia_jbuf pj_str_t name; // jitter buffer name jb_framelist jb_framelist; pj_size_t jb_frame_size; // frame size + unsigned jb_frame_ptime; // frame duration. pj_size_t jb_max_count; // max frames in the jitter framelist->flist_buffer int jb_level; // delay between source & destination @@ -80,6 +81,15 @@ struct pjmedia_jbuf #define PJ_MIN(x, y) ((x < y) ? (x) : (y)) +/* Enabling this would log the jitter buffer state about once per + * second. + */ +#if 0 +# define TRACE__(args) PJ_LOG(4,args) +#else +# define TRACE__(args) +#endif + static pj_status_t jb_framelist_init( pj_pool_t *pool, jb_framelist *framelist, @@ -267,6 +277,7 @@ enum pjmedia_jb_op PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, const pj_str_t *name, unsigned frame_size, + unsigned ptime, unsigned max_count, pjmedia_jbuf **p_jb) { @@ -281,6 +292,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, pj_strdup_with_null(pool, &jb->name, name); jb->jb_frame_size = frame_size; + jb->jb_frame_ptime = ptime; jb->jb_last_seq_no = -1; jb->jb_level = 0; jb->jb_last_level = 0; @@ -367,7 +379,9 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb) static void jbuf_calculate_jitter(pjmedia_jbuf *jb) { - enum { STABLE_HISTORY_LIMIT = (100*2) }; + unsigned stable_history_limit; + + stable_history_limit = 1000 / jb->jb_frame_ptime; jb->jb_last_jitter = PJ_ABS(jb->jb_level-jb->jb_last_level); jb->jb_last_level = jb->jb_level; @@ -376,7 +390,7 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) if (jb->jb_last_jitter < jb->jb_prefetch) { jb->jb_stable_hist += jb->jb_last_jitter; - if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) { + if (jb->jb_stable_hist > (int)stable_history_limit) { int seq_diff = (jb->jb_prefetch - jb->jb_max_hist_jitter)/3; if (seq_diff<1) seq_diff = 1; @@ -387,7 +401,12 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) jb->jb_stable_hist = 0; jb->jb_max_hist_jitter = 0; - if (jb->jb_op_count >= STABLE_HISTORY_LIMIT*2 && + TRACE__((THIS_FILE,"jb updated(1), prefetch=%d, size=%d", + jb->jb_prefetch, jb_framelist_size(&jb->jb_framelist))); + + /* These code is used to shorten the delay in the jitter buffer. + + if (jb->jb_op_count >= stable_history_limit*2 && (int)jb_framelist_size(&jb->jb_framelist) > jb->jb_prefetch+2) { jb_framelist_remove_head(&jb->jb_framelist,1); @@ -398,6 +417,7 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) jb_framelist_size(&jb->jb_framelist))); jb->jb_op_count = 0; } + */ } } else { @@ -408,7 +428,15 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) jb->jb_stable_hist = 0; jb->jb_max_hist_jitter = 0; - if (jb->jb_op_count >= STABLE_HISTORY_LIMIT * 2) { + TRACE__((THIS_FILE,"jb updated(2), prefetch=%d, size=%d", + jb->jb_prefetch, jb_framelist_size(&jb->jb_framelist))); + + /* These code is used to shorten the delay in the jitter buffer + when the current size is larger than the prefetch. But it does + not really work when the stream supports multiple frames, since + the size may grow only temporarily. + + if (jb->jb_op_count >= stable_history_limit * 2) { if ((int)jb_framelist_size(&jb->jb_framelist) > jb->jb_prefetch+2) { jb_framelist_remove_head(&jb->jb_framelist,1); @@ -421,6 +449,7 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) jb->jb_op_count = 0; } + */ } } diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 62dd9c7d..37795243 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -40,6 +40,8 @@ #define LOGERR_(expr) stream_perror expr #define TRC_(expr) PJ_LOG(5,expr) +#define BYTES_PER_SAMPLE 2 + /** * Media channel. */ @@ -54,6 +56,7 @@ struct pjmedia_channel unsigned out_pkt_size; /**< Size of output buffer. */ void *out_pkt; /**< Output buffer. */ pjmedia_rtp_session rtp; /**< RTP session. */ + char last_frm_type; /**< Last frame type from jb */ }; @@ -152,10 +155,10 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) { pjmedia_stream *stream = port->user_data; pjmedia_channel *channel = stream->dec; - - pj_status_t status; unsigned samples_count, samples_per_frame, samples_required; pj_int16_t *p_out_samp; + pj_status_t status; + /* Return no frame is channel is paused */ if (channel->paused) { @@ -180,7 +183,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) for (samples_count=0; samples_count < samples_required; samples_count += samples_per_frame) { - char frame_type; + char frame_type; /* Get frame from jitter buffer. */ pjmedia_jbuf_get_frame(stream->jb, channel->out_pkt, &frame_type); @@ -198,6 +201,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) status = (*stream->codec->op->recover)(stream->codec, frame_out.size, &frame_out); + } else { status = -1; } @@ -206,25 +210,102 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) /* Either PLC failed or PLC not supported/enabled */ pjmedia_zero_samples(p_out_samp + samples_count, samples_required - samples_count); + PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost!")); + + } else { + PJ_LOG(5,(stream->port.info.name.ptr, + "Lost frame recovered")); } } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) { - /* Jitter buffer is empty, zero the remaining samples and break - * the loop. + /* Jitter buffer is empty. If this is the first "empty" state, + * activate PLC to smoothen the fade-out, otherwise zero + * the frame. */ - pjmedia_zero_samples(p_out_samp + samples_count, - samples_required - samples_count); + if (frame_type != channel->last_frm_type) { + pjmedia_jb_state jb_state; + + /* Activate PLC to smoothen the missing frame */ + if (stream->codec->op->recover && + stream->codec_param.setting.plc) + { + pjmedia_frame frame_out; + + do { + frame_out.buf = p_out_samp + samples_count; + frame_out.size = frame->size - samples_count*2; + status = (*stream->codec->op->recover)(stream->codec, + frame_out.size, + &frame_out); + if (status != PJ_SUCCESS) + break; + samples_count += samples_per_frame; + + } while (samples_count < samples_required); + + } + + /* Report the state of jitter buffer */ + pjmedia_jbuf_get_state(stream->jb, &jb_state); + PJ_LOG(5,(stream->port.info.name.ptr, + "Jitter buffer empty (prefetch=%d)", + jb_state.prefetch)); + + } + + if (samples_count < samples_required) { + pjmedia_zero_samples(p_out_samp + samples_count, + samples_required - samples_count); + } + + channel->last_frm_type = frame_type; break; } else if (frame_type != PJMEDIA_JB_NORMAL_FRAME) { + pjmedia_jb_state jb_state; + /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */ pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME); - /* Zero frame returned. We should zero the PCM buffer then.. */ - pjmedia_zero_samples(p_out_samp + samples_count, - samples_per_frame); + /* Get the state of jitter buffer */ + pjmedia_jbuf_get_state(stream->jb, &jb_state); + + /* Always activate PLC when it's available.. */ + if (stream->codec->op->recover && + stream->codec_param.setting.plc) + { + pjmedia_frame frame_out; + + do { + frame_out.buf = p_out_samp + samples_count; + frame_out.size = frame->size - samples_count*2; + status = (*stream->codec->op->recover)(stream->codec, + frame_out.size, + &frame_out); + if (status != PJ_SUCCESS) + break; + samples_count += samples_per_frame; + + } while (samples_count < samples_required); + + PJ_LOG(5,(stream->port.info.name.ptr, + "Jitter buffer is bufferring with plc (prefetch=%d)", + jb_state.prefetch)); + + } + + if (samples_count < samples_required) { + pjmedia_zero_samples(p_out_samp + samples_count, + samples_required - samples_count); + PJ_LOG(5,(stream->port.info.name.ptr, + "Jitter buffer is bufferring (prefetch=%d)..", + jb_state.prefetch)); + } + + channel->last_frm_type = frame_type; + break; } else { /* Got "NORMAL" frame from jitter buffer */ @@ -236,7 +317,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; /* ignored */ frame_out.buf = p_out_samp + samples_count; - frame_out.size = frame->size - samples_count*2; + frame_out.size = frame->size - samples_count*BYTES_PER_SAMPLE; status = stream->codec->op->decode( stream->codec, &frame_in, frame_out.size, &frame_out); if (status != 0) { @@ -247,6 +328,8 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_per_frame); } } + + channel->last_frm_type = frame_type; } @@ -261,7 +344,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) frame->size = 0; } else { frame->type = PJMEDIA_FRAME_TYPE_AUDIO; - frame->size = samples_count * 2; + frame->size = samples_count * BYTES_PER_SAMPLE; frame->timestamp.u64 = 0; } @@ -498,6 +581,7 @@ static pj_status_t put_frame( pjmedia_port *port, pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr)); + /* Send. */ sent = frame_out.size+sizeof(pjmedia_rtp_hdr); status = pj_sock_sendto(stream->skinfo.rtp_sock, channel->out_pkt, @@ -1029,28 +1113,28 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (info->jb_max > 0) jb_max = info->jb_max; else - jb_max = 240 / (stream->port.info.samples_per_frame * 1000 / - info->fmt.clock_rate); + jb_max = 360 / stream->codec_param.info.frm_ptime; if (info->jb_min_pre >= 0) jb_min_pre = info->jb_min_pre; else - jb_min_pre = 0; + jb_min_pre = 60 / stream->codec_param.info.frm_ptime; if (info->jb_max_pre > 0) jb_max_pre = info->jb_max_pre; else - jb_max_pre = jb_max * 4 / 5; + jb_max_pre = 240 / stream->codec_param.info.frm_ptime; if (info->jb_init >= 0) jb_init = info->jb_init; else - jb_init = jb_min_pre; + jb_init = (jb_min_pre + jb_max_pre) / 2; /* Create jitter buffer */ status = pjmedia_jbuf_create(pool, &stream->port.info.name, stream->frame_size, + stream->codec_param.info.frm_ptime, jb_max, &stream->jb); if (status != PJ_SUCCESS) goto err_cleanup; diff --git a/pjsip-apps/src/samples/siprtp_report.c b/pjsip-apps/src/samples/siprtp_report.c index 3b8b1ebe..dfb28301 100644 --- a/pjsip-apps/src/samples/siprtp_report.c +++ b/pjsip-apps/src/samples/siprtp_report.c @@ -59,7 +59,7 @@ static void print_call(int call_index) pj_gettimeofday(&now); /* Print duration */ - if (inv->state >= PJSIP_INV_STATE_CONFIRMED) { + if (inv->state >= PJSIP_INV_STATE_CONFIRMED && call->connect_time) { PJ_TIME_VAL_SUB(now, call->connect_time); -- cgit v1.2.3