Index: pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 5671)
+++ 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
