Index: /pjproject/trunk/pjmedia/include/pjmedia-codec/opus.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia-codec/opus.h	(revision 5733)
+++ /pjproject/trunk/pjmedia/include/pjmedia-codec/opus.h	(revision 5734)
@@ -100,4 +100,5 @@
     unsigned   sample_rate; /**< Sample rate in Hz.                     */
     unsigned   channel_cnt; /**< Number of channels.                    */
+    unsigned   frm_ptime;   /**< Frame time in msec.   			*/
     unsigned   bit_rate;    /**< Encoder bit rate in bps.		*/
     unsigned   packet_loss; /**< Encoder's expected packet loss pct.	*/
Index: /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h	(revision 5733)
+++ /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h	(revision 5734)
@@ -167,4 +167,16 @@
 					 unsigned max_count,
 					 pjmedia_jbuf **p_jb);
+
+/**
+ * Set the jitter buffer's frame duration.
+ *
+ * @param jb		The jitter buffer
+ * @param ptime		Frame duration.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_jbuf_set_ptime( pjmedia_jbuf *jb,
+					     unsigned ptime);
+
 
 /**
Index: /pjproject/trunk/pjmedia/src/pjmedia-codec/opus.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-codec/opus.c	(revision 5733)
+++ /pjproject/trunk/pjmedia/src/pjmedia-codec/opus.c	(revision 5734)
@@ -43,4 +43,7 @@
  */
 #define MAX_ENCODED_PACKET_SIZE 	1280
+
+/* Default frame time (msec) */
+#define PTIME			20
 
 /* Tracing */
@@ -137,5 +140,6 @@
     OpusRepacketizer  		*dec_packer;
     pjmedia_codec_opus_config 	 cfg;
-    unsigned   			 ptime;
+    unsigned   			 enc_ptime;
+    unsigned			 dec_ptime;
     pjmedia_frame      		 dec_frame[2];
     int                		 dec_frame_index;
@@ -150,4 +154,5 @@
     PJMEDIA_CODEC_OPUS_DEFAULT_SAMPLE_RATE,     /* Sample rate		*/
     1,						/* Channel count	*/
+    PTIME,					/* Frame time 		*/			
     PJMEDIA_CODEC_OPUS_DEFAULT_BIT_RATE,	/* Bit rate             */
     5,						/* Expected packet loss */
@@ -387,4 +392,5 @@
     param->info.clock_rate = opus_cfg.sample_rate = cfg->sample_rate;
     param->info.max_bps = opus_cfg.sample_rate * 2;
+    param->info.frm_ptime = opus_cfg.frm_ptime = cfg->frm_ptime;
 
     /* Set channel count */
@@ -468,5 +474,5 @@
     attr->info.avg_bps     	   = opus_cfg.bit_rate;
     attr->info.max_bps     	   = opus_cfg.sample_rate * 2;
-    attr->info.frm_ptime   	   = 20;
+    attr->info.frm_ptime   	   = opus_cfg.frm_ptime;
     attr->setting.frm_per_pkt 	   = 1;
     attr->info.pcm_bits_per_sample = 16;
@@ -601,5 +607,5 @@
     opus_data->cfg.sample_rate = attr->info.clock_rate;
     opus_data->cfg.channel_cnt = attr->info.channel_cnt;
-    opus_data->ptime       = attr->info.frm_ptime;
+    opus_data->enc_ptime = opus_data->dec_ptime = attr->info.frm_ptime;
 
     /* Allocate memory used by the codec */
@@ -815,7 +821,4 @@
     }
 
-    samples_per_frame = (opus_data->cfg.sample_rate *
-			 opus_data->ptime) / 1000;
-
     pj_memcpy(tmp_buf, pkt, pkt_size);
 
@@ -837,4 +840,23 @@
 	frames[i].buf = ((char*)pkt) + out_pos;
 	frames[i].size = size;
+	frames[i].bit_info = opus_packet_get_nb_samples(frames[i].buf,
+			     frames[i].size, opus_data->cfg.sample_rate);
+
+	if (i == 0) {
+    	    unsigned ptime = frames[i].bit_info * 1000 /
+    	    		     opus_data->cfg.sample_rate;
+    	    if (ptime != opus_data->dec_ptime) {
+             	PJ_LOG(4, (THIS_FILE, "Opus ptime change detected: %d ms "
+             			      "--> %d ms",
+             			      opus_data->dec_ptime, ptime));
+        	opus_data->dec_ptime = ptime;
+        	opus_data->dec_frame_index = -1;
+
+        	/* Signal to the stream about ptime change. */
+     	    	frames[i].bit_info |= 0x10000;
+    	    }
+     	    samples_per_frame = frames[i].bit_info;
+   	}
+
 	frames[i].timestamp.u64 = ts->u64 + i * samples_per_frame;
 	out_pos += size;
@@ -867,5 +889,5 @@
 
     samples_per_frame = (opus_data->cfg.sample_rate *
-			 opus_data->ptime) / 1000;
+			 opus_data->enc_ptime) / 1000;
     frame_size = samples_per_frame * opus_data->cfg.channel_cnt *
     		 sizeof(opus_int16);
@@ -982,5 +1004,5 @@
 	frm_size = PJ_MIN(frm_size,
 			  opus_data->cfg.sample_rate *
-			  opus_data->ptime / 1000);
+			  opus_data->dec_ptime / 1000);
     }
     decoded_samples = opus_decode( opus_data->dec,
@@ -1042,6 +1064,6 @@
         /* Recover the first packet? Don't think so, fill it with zeroes. */
 	pj_uint16_t samples_per_frame;
-	samples_per_frame = (pj_uint16_t)(opus_data->cfg.sample_rate * 
-					  opus_data->ptime) / 1000;
+	samples_per_frame = opus_data->cfg.sample_rate * opus_data->dec_ptime/
+			    1000;
 	output->type = PJMEDIA_FRAME_TYPE_AUDIO;
 	output->size = samples_per_frame << 1;
@@ -1057,5 +1079,5 @@
     if (inframe->type != PJMEDIA_FRAME_TYPE_AUDIO) {
 	frm_size = PJ_MIN(frm_size, opus_data->cfg.sample_rate *
-			  opus_data->ptime/1000);
+			  opus_data->dec_ptime/1000);
     }
     decoded_samples = opus_decode(opus_data->dec,
Index: /pjproject/trunk/pjmedia/src/pjmedia/jbuf.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 5733)
+++ /pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 5734)
@@ -586,4 +586,18 @@
 
 
+PJ_DEF(pj_status_t) pjmedia_jbuf_set_ptime( pjmedia_jbuf *jb,
+					    unsigned ptime)
+{
+    PJ_ASSERT_RETURN(jb, PJ_EINVAL);
+
+    jb->jb_frame_ptime    = ptime;
+    jb->jb_min_shrink_gap = PJMEDIA_JBUF_DISC_MIN_GAP / ptime;
+    jb->jb_max_burst	  = PJ_MAX(MAX_BURST_MSEC / ptime,
+    				   jb->jb_max_count*3/4);
+
+    return PJ_SUCCESS;
+}
+
+
 /*
  * Set the jitter buffer to fixed delay mode. The default behavior
Index: /pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 5733)
+++ /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 5734)
@@ -135,4 +135,15 @@
     unsigned		     enc_buf_count; /**< Number of samples in the
 						 encoding buffer.	    */
+
+    pj_int16_t		    *dec_buf;	    /**< Decoding buffer.	    */
+    unsigned		     dec_buf_size;  /**< Decoding buffer size, in
+						 samples.		    */
+    unsigned		     dec_buf_pos;   /**< First position in buf.	    */
+    unsigned		     dec_buf_count; /**< Number of samples in the
+						 decoding buffer.	    */
+
+    pj_uint16_t		     dec_ptime;	    /**< Decoder frame ptime in ms. */
+    pj_bool_t		     detect_ptime_change;
+    					    /**< Detect decode ptime change */
 
     unsigned		     plc_cnt;	    /**< # of consecutive PLC frames*/
@@ -500,5 +511,5 @@
 
     samples_required = PJMEDIA_PIA_SPF(&stream->port.info);
-    samples_per_frame = stream->codec_param.info.frm_ptime *
+    samples_per_frame = stream->dec_ptime *
 			stream->codec_param.info.clock_rate *
 			stream->codec_param.info.channel_cnt /
@@ -506,10 +517,22 @@
     p_out_samp = (pj_int16_t*) frame->buf;
 
-    for (samples_count=0; samples_count < samples_required;
-	 samples_count += samples_per_frame)
-    {
+    for (samples_count=0; samples_count < samples_required;) {
 	char frame_type;
 	pj_size_t frame_size;
 	pj_uint32_t bit_info;
+
+	if (stream->dec_buf && stream->dec_buf_pos < stream->dec_buf_count) {
+	    unsigned nsamples_req = samples_required - samples_count;
+	    unsigned nsamples_avail = stream->dec_buf_count -
+	    			      stream->dec_buf_pos;
+	    unsigned nsamples_copy = PJ_MIN(nsamples_req, nsamples_avail);
+	    
+	    pjmedia_copy_samples(p_out_samp + samples_count,
+		      		 stream->dec_buf + stream->dec_buf_pos,
+	    	      		 nsamples_copy);
+	    samples_count += nsamples_copy;
+	    stream->dec_buf_pos += nsamples_copy;
+	    continue;
+	}
 
 	/* Get frame from jitter buffer. */
@@ -559,4 +582,5 @@
 	    }
 
+	    samples_count += samples_per_frame;
 	} else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
 
@@ -676,4 +700,5 @@
 	    /* Got "NORMAL" frame from jitter buffer */
 	    pjmedia_frame frame_in, frame_out;
+	    pj_bool_t use_dec_buf = PJ_FALSE;
 
 	    stream->plc_cnt = 0;
@@ -687,4 +712,15 @@
 	    frame_out.buf = p_out_samp + samples_count;
 	    frame_out.size = frame->size - samples_count*BYTES_PER_SAMPLE;
+	    if (stream->dec_buf &&
+	    	bit_info * sizeof(pj_int16_t) > frame_out.size)
+	    {
+	    	stream->dec_buf_pos = 0;
+	    	stream->dec_buf_count = bit_info;
+
+	    	use_dec_buf = PJ_TRUE;
+	    	frame_out.buf = stream->dec_buf;
+	    	frame_out.size = stream->dec_buf_size;
+	    }
+
 	    status = pjmedia_codec_decode( stream->codec, &frame_in,
 					   (unsigned)frame_out.size,
@@ -694,6 +730,13 @@
 			 status));
 
-		pjmedia_zero_samples(p_out_samp + samples_count,
-				     samples_per_frame);
+		if (use_dec_buf) {
+		    pjmedia_zero_samples(p_out_samp + samples_count,
+				     	 samples_per_frame);
+		} else {
+		    pjmedia_zero_samples(stream->dec_buf,
+				     	 stream->dec_buf_count);
+		}
+	    } else if (use_dec_buf) {
+	    	stream->dec_buf_count = frame_out.size / sizeof(pj_int16_t);
 	    }
 
@@ -710,4 +753,6 @@
 		stream->jb_last_frm_cnt++;
 	    }
+	    if (!use_dec_buf)
+	    	samples_count += samples_per_frame;
 	}
     }
@@ -1755,4 +1800,18 @@
 		     status));
 	    count = 0;
+	} else if (stream->detect_ptime_change &&
+		   frames[0].bit_info > 0xFFFF)
+	{
+	    unsigned dec_ptime;
+	    
+	    PJ_LOG(4, (stream->port.info.name.ptr, "codec decode "
+	               "ptime change detected"));
+	    frames[0].bit_info &= 0xFFFF;
+	    dec_ptime = frames[0].bit_info * 1000 /
+	    		stream->codec_param.info.clock_rate;
+	    stream->rtp_rx_ts_len_per_frame= stream->rtp_rx_ts_len_per_frame *
+	    				     dec_ptime / stream->dec_ptime;
+	    stream->dec_ptime = dec_ptime;
+	    pjmedia_jbuf_set_ptime(stream->jb, stream->dec_ptime);
 	}
 
@@ -1825,10 +1884,10 @@
 
 	} else {
-	    ts_span = stream->codec_param.info.frm_ptime *
+	    ts_span = stream->dec_ptime *
 		      stream->codec_param.info.clock_rate /
 		      1000;
 	}
 #else
-	ts_span = stream->codec_param.info.frm_ptime *
+	ts_span = stream->dec_ptime *
 		  stream->codec_param.info.clock_rate /
 		  1000;
@@ -2124,4 +2183,13 @@
 	stream->codec_param.info.clock_rate = info->fmt.clock_rate;
 	stream->codec_param.info.channel_cnt = info->fmt.channel_cnt;
+
+	/* Allocate decoding buffer as Opus can send a packet duration of
+	 * up to 120 ms.
+	 */
+	stream->dec_buf_size = stream->codec_param.info.clock_rate * 120 /
+			       1000;
+	stream->dec_buf = (pj_int16_t*)pj_pool_alloc(pool,
+			    	      		     stream->dec_buf_size *
+			    	      		     sizeof(pj_int16_t));
     }
 
@@ -2131,4 +2199,5 @@
 
     /* Set additional info and callbacks. */
+    stream->dec_ptime = stream->codec_param.info.frm_ptime;
     afd->bits_per_sample = 16;
     afd->frame_time_usec = stream->codec_param.info.frm_ptime *
@@ -2248,4 +2317,5 @@
 	stream->rtp_tx_ts_len_per_pkt *= opus_ts_modifier;
 	stream->rtp_rx_ts_len_per_frame *= opus_ts_modifier;
+	stream->detect_ptime_change = PJ_TRUE;
     }
 #endif
