summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-05-14 18:23:34 +0000
committerBenny Prijono <bennylp@teluu.com>2006-05-14 18:23:34 +0000
commit612d2349e4664dd90bdf00850f22d2c4f5ee8c16 (patch)
tree0ec2c27cfa237bf089a496dd7dc043b2223b6709 /pjmedia
parent4737bd521fcd7722fa27a570be5c610b0f08a1d3 (diff)
Fixed more bugs with multiple frame handling
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@442 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/include/pjmedia/jbuf.h3
-rw-r--r--pjmedia/src/pjmedia/conference.c8
-rw-r--r--pjmedia/src/pjmedia/jbuf.c37
-rw-r--r--pjmedia/src/pjmedia/stream.c118
4 files changed, 143 insertions, 23 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;