summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-05-13 22:46:23 +0000
committerBenny Prijono <bennylp@teluu.com>2006-05-13 22:46:23 +0000
commitb4ff87018aef719a1427c47da854d0108b9ed6c4 (patch)
treef117b651f2daeea1824eca14945668df7d4898a3 /pjmedia
parent19d4fb0b0f2d39538018f16323c4b6a52c07903d (diff)
Another major modifications in PJMEDIA:
- handle multiple frames in one packet - split stream creation into two steps to allow customization - PLC framework and implementation with G.711 and speex - stream returns NO_FRAME correctly. - added ptime argument in pjsua git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@438 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/include/pjmedia/codec.h85
-rw-r--r--pjmedia/include/pjmedia/config.h2
-rw-r--r--pjmedia/include/pjmedia/errno.h5
-rw-r--r--pjmedia/include/pjmedia/jbuf.h27
-rw-r--r--pjmedia/include/pjmedia/session.h78
-rw-r--r--pjmedia/include/pjmedia/stream.h20
-rw-r--r--pjmedia/include/pjmedia/types.h48
-rw-r--r--pjmedia/src/pjmedia-codec/gsm.c45
-rw-r--r--pjmedia/src/pjmedia-codec/l16.c45
-rw-r--r--pjmedia/src/pjmedia-codec/speex/ltp.c2
-rw-r--r--pjmedia/src/pjmedia-codec/speex_codec.c122
-rw-r--r--pjmedia/src/pjmedia/endpoint.c12
-rw-r--r--pjmedia/src/pjmedia/errno.c1
-rw-r--r--pjmedia/src/pjmedia/g711.c120
-rw-r--r--pjmedia/src/pjmedia/jbuf.c23
-rw-r--r--pjmedia/src/pjmedia/plc_common.c36
-rw-r--r--pjmedia/src/pjmedia/session.c98
-rw-r--r--pjmedia/src/pjmedia/stream.c305
18 files changed, 692 insertions, 382 deletions
diff --git a/pjmedia/include/pjmedia/codec.h b/pjmedia/include/pjmedia/codec.h
index 04bf449e..00e60e52 100644
--- a/pjmedia/include/pjmedia/codec.h
+++ b/pjmedia/include/pjmedia/codec.h
@@ -110,22 +110,38 @@ typedef struct pjmedia_codec_info pjmedia_codec_info;
*/
struct pjmedia_codec_param
{
- unsigned clock_rate; /**< Sampling rate in Hz */
- unsigned channel_cnt; /**< Channel count. */
- pj_uint32_t avg_bps; /**< Average bandwidth in bits/sec */
-
- pj_uint16_t ptime; /**< Packet time in miliseconds */
- pj_uint8_t pcm_bits_per_sample;/**< Bits/sample in the PCM side */
-
- unsigned pt:8; /**< Payload type. */
- unsigned vad:1; /**< Voice Activity Detector. */
- unsigned cng:1; /**< Comfort Noise Generator. */
- unsigned lpf:1; /**< Low pass filter */
- unsigned hpf:1; /**< High pass filter */
- unsigned penh:1; /**< Perceptual Enhancement */
- unsigned concl:1; /**< Packet loss concealment */
- unsigned reserved:1; /**< Reserved, must be NULL. */
+ /**
+ * The "info" part of codec param describes the capability of the codec,
+ * and the value should NOT be changed by application.
+ */
+ struct {
+ unsigned clock_rate; /**< Sampling rate in Hz */
+ unsigned channel_cnt; /**< Channel count. */
+ pj_uint32_t avg_bps; /**< Average bandwidth in bits/sec */
+ pj_uint16_t frm_ptime; /**< Base frame ptime in msec. */
+ pj_uint8_t pcm_bits_per_sample; /**< Bits/sample in the PCM side */
+ pj_uint8_t pt; /**< Payload type. */
+ } info;
+ /**
+ * The "setting" part of codec param describes various settings to be
+ * applied to the codec. When the codec param is retrieved from the codec
+ * or codec factory, the values of these will be filled by the capability
+ * of the codec. Any features that are supported by the codec (e.g. vad
+ * or plc) will be turned on, so that application can query which
+ * capabilities are supported by the codec. Application may change the
+ * settings here before instantiating the codec/stream.
+ */
+ struct {
+ pj_uint8_t frm_per_pkt; /**< Number of frames per packet. */
+ unsigned vad:1; /**< Voice Activity Detector. */
+ unsigned cng:1; /**< Comfort Noise Generator. */
+ unsigned lpf:1; /**< Low pass filter */
+ unsigned hpf:1; /**< High pass filter */
+ unsigned penh:1; /**< Perceptual Enhancement */
+ unsigned plc:1; /**< Packet loss concealment */
+ unsigned reserved:1; /**< Reserved, must be zero. */
+ } setting;
};
/**
@@ -182,11 +198,14 @@ struct pjmedia_codec_op
/**
* Instruct the codec to inspect the specified payload/packet and
- * split the packet info individual frames.
+ * split the packet into individual base frames. Each output frames will
+ * have ptime that is equal to basic frame ptime (i.e. the value of
+ * info.frm_ptime in #pjmedia_codec_param).
*
* @param codec The codec instance
* @param pkt The input packet.
* @param pkt_size Size of the packet.
+ * @param timestamp The timestamp of the first sample in the packet.
* @param frame_cnt On input, specifies the maximum number of frames
* in the array. On output, the codec must fill
* with number of frames detected in the packet.
@@ -195,14 +214,17 @@ struct pjmedia_codec_op
*
* @return PJ_SUCCESS on success.
*/
- pj_status_t (*get_frames)(pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[]);
+ pj_status_t (*parse)( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *timestamp,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[]);
/**
- * Instruct the codec to encode the specified input frame.
+ * Instruct the codec to encode the specified input frame. The input
+ * PCM samples MUST have ptime that is exactly equal to base frame
+ * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param).
*
* @param codec The codec instance.
* @param input The input frame.
@@ -217,7 +239,11 @@ struct pjmedia_codec_op
struct pjmedia_frame *output);
/**
- * Instruct the codec to decode the specified input frame.
+ * Instruct the codec to decode the specified input frame. The input
+ * frame MUST have ptime that is exactly equal to base frame
+ * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param).
+ * Application can achieve this by parsing the packet into base
+ * frames before decoding each frame.
*
* @param codec The codec instance.
* @param input The input frame.
@@ -231,6 +257,19 @@ struct pjmedia_codec_op
unsigned out_size,
struct pjmedia_frame *output);
+ /**
+ * Instruct the codec to recover a missing frame. Not all codec has
+ * this capability, so this function may be NULL.
+ *
+ * @param codec The codec instance.
+ * @param out_size The length of buffer in the output frame.
+ * @param output The output frame.
+ *
+ * @return PJ_SUCCESS on success;
+ */
+ pj_status_t (*recover)(pjmedia_codec *codec,
+ unsigned out_size,
+ struct pjmedia_frame *output);
};
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 980d377f..7a71ae5f 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -77,6 +77,8 @@
/**
* Maximum frame duration (in msec) to be supported.
+ * This (among other thing) will affect the size of buffers to be allocated
+ * for outgoing packets.
*/
#ifndef PJMEDIA_MAX_FRAME_DURATION_MS
# define PJMEDIA_MAX_FRAME_DURATION_MS 200
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index 1dab1863..7909b557 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -275,6 +275,11 @@ PJ_BEGIN_DECL
* PCM buffer is too short.
*/
#define PJMEDIA_CODEC_EPCMTOOSHORT (PJMEDIA_ERRNO_START+83) /* 220083 */
+/**
+ * @hideinitializer
+ * Invalid codec frame length.
+ */
+#define PJMEDIA_CODEC_EFRMINLEN (PJMEDIA_ERRNO_START+84) /* 220084 */
/************************************************************
diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h
index c9bf495f..9bb1a995 100644
--- a/pjmedia/include/pjmedia/jbuf.h
+++ b/pjmedia/include/pjmedia/jbuf.h
@@ -55,6 +55,12 @@ enum pjmedia_jb_frame_type
/**
+ * @see pjmedia_jb_frame_type.
+ */
+typedef enum pjmedia_jb_frame_type pjmedia_jb_frame_type;
+
+
+/**
* This structure describes jitter buffer current status.
*/
struct pjmedia_jb_state
@@ -79,6 +85,11 @@ typedef struct pjmedia_jb_state pjmedia_jb_state;
*/
#define PJMEDIA_JB_DEFAULT_INIT_DELAY 15
+/**
+ * Opaque declaration for jitter buffer.
+ */
+typedef struct pjmedia_jbuf pjmedia_jbuf;
+
/**
* Create an adaptive jitter buffer according to the specification. If
@@ -181,10 +192,10 @@ PJ_DECL(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb);
*
* @return PJ_SUCCESS on success.
*/
-PJ_DECL(pj_status_t) pjmedia_jbuf_put_frame(pjmedia_jbuf *jb,
- const void *frame,
- pj_size_t size,
- int frame_seq);
+PJ_DECL(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb,
+ const void *frame,
+ pj_size_t size,
+ int frame_seq);
/**
* Get a frame from the jitter buffer. The jitter buffer will return the
@@ -210,12 +221,10 @@ PJ_DECL(pj_status_t) pjmedia_jbuf_put_frame(pjmedia_jbuf *jb,
* frame will be copied. If there is a frame, the jitter
* buffer will copy the frame to the buffer, and frame
* type will be set to PJMEDIA_JB_NORMAL_FRAME.
- *
- * @return Always returns PJ_SUCCESS.
*/
-PJ_DECL(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
- void *frame,
- char *p_frm_type);
+PJ_DECL(void) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
+ void *frame,
+ char *p_frm_type);
/**
diff --git a/pjmedia/include/pjmedia/session.h b/pjmedia/include/pjmedia/session.h
index cfc1bf8e..c93d4343 100644
--- a/pjmedia/include/pjmedia/session.h
+++ b/pjmedia/include/pjmedia/session.h
@@ -66,26 +66,73 @@ struct pjmedia_session_info
};
+/**
+ * Opaque declaration of media session.
+ */
+typedef struct pjmedia_session pjmedia_session;
+
+
/**
- * Initialize stream info from SDP media lines.
+ * @see pjmedia_session_info.
+ */
+typedef struct pjmedia_session_info pjmedia_session_info;
+
+
+/**
+ * This function will initialize the session info based on information
+ * in both SDP session descriptors. The remaining information will be
+ * taken from default codec parameters. If socket info array is specified,
+ * the socket will be copied to the session info as well.
*
- * @param si Stream info structure to be initialized.
- * @param pool Pool.
+ * @param pool Pool to allocate memory.
* @param endpt Pjmedia endpoint.
+ * @param max_streams Maximum number of stream infos to be created.
+ * @param si Session info structure to be initialized.
+ * @param skinfo Optional array of media socket info to be copied
+ * to the stream info. If this argument is specified,
+ * the array must contain sufficient elements for
+ * each stream to be initialized.
* @param local Local SDP session descriptor.
* @param remote Remote SDP session descriptor.
* @param stream_idx Media stream index in the session descriptor.
*
* @return PJ_SUCCESS if stream info is successfully initialized.
*/
-PJ_DECL(pj_status_t) pjmedia_stream_info_from_sdp(
- pjmedia_stream_info *si,
- pj_pool_t *pool,
- pjmedia_endpt *endpt,
- const pjmedia_sdp_session *local,
- const pjmedia_sdp_session *remote,
- unsigned stream_idx);
-
+PJ_DECL(pj_status_t)
+pjmedia_session_info_from_sdp( pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ unsigned max_streams,
+ pjmedia_session_info *si,
+ const pjmedia_sock_info skinfo[],
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote);
+
+
+/*
+ * This function will initialize the stream info based on information
+ * in both SDP session descriptors for the specified stream index.
+ * The remaining information will be taken from default codec parameters.
+ * If socket info array is specified, the socket will be copied to the
+ * session info as well.
+ *
+ * @param si Stream info structure to be initialized.
+ * @param pool Pool to allocate memory.
+ * @param endpt PJMEDIA endpoint instance.
+ * @param skinfo Optional socket info to be copied to the stream info.
+ * @param local Local SDP session descriptor.
+ * @param remote Remote SDP session descriptor.
+ * @param stream_idx Media stream index in the session descriptor.
+ *
+ * @return PJ_SUCCESS if stream info is successfully initialized.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_stream_info_from_sdp( pjmedia_stream_info *si,
+ pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ const pjmedia_sock_info *skinfo,
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote,
+ unsigned stream_idx);
/**
* Create media session based on the local and remote SDP. After the session
@@ -101,10 +148,6 @@ PJ_DECL(pj_status_t) pjmedia_stream_info_from_sdp(
* @param stream_cnt Maximum number of streams to be created. This
* also denotes the number of elements in the
* socket information.
- * @param skinfo Array of socket informations. The argument stream_cnt
- * specifies the number of elements in this array. One
- * element is needed for each media stream to be
- * created in the session.
* @param local_sdp The SDP describing local capability.
* @param rem_sdp The SDP describing remote capability.
* @param user_data Arbitrary user data to be kept in the session.
@@ -115,10 +158,7 @@ PJ_DECL(pj_status_t) pjmedia_stream_info_from_sdp(
*/
PJ_DECL(pj_status_t)
pjmedia_session_create( pjmedia_endpt *endpt,
- unsigned stream_cnt,
- const pjmedia_sock_info skinfo[],
- const pjmedia_sdp_session *local_sdp,
- const pjmedia_sdp_session *rem_sdp,
+ const pjmedia_session_info *si,
void *user_data,
pjmedia_session **p_session );
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index 41f690bd..335a40e2 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -72,16 +72,34 @@ struct pjmedia_stream_info
pjmedia_sock_info sock_info; /**< Media transport (RTP/RTCP sockets) */
pj_sockaddr_in rem_addr; /**< Remote RTP address */
pjmedia_codec_info fmt; /**< Incoming codec format info. */
+ pjmedia_codec_param *param; /**< Optional codec param. */
unsigned tx_pt; /**< Outgoing codec paylaod type. */
int tx_event_pt;/**< Outgoing pt for telephone-events. */
int rx_event_pt;/**< Incoming pt for telephone-events. */
pj_uint32_t ssrc; /**< RTP SSRC. */
- int jb_init; /**< Jitter buffer init delay in msec. */
+ int jb_init; /**< Jitter buffer init delay in msec.
+ (-1 for default). */
+ int jb_min_pre; /**< Jitter buffer minimum prefetch
+ delay in msec (-1 for default). */
+ int jb_max_pre; /**< Jitter buffer maximum prefetch
+ delay in msec (-1 for default). */
int jb_max; /**< Jitter buffer max delay in msec. */
};
/**
+ * @see pjmedia_stream_info.
+ */
+typedef struct pjmedia_stream_info pjmedia_stream_info;
+
+
+/**
+ * Opaque declaration for media stream.
+ */
+typedef struct pjmedia_stream pjmedia_stream;
+
+
+/**
* Create a media stream based on the specified parameter. After the stream
* has been created, application normally would want to get the media port
* interface of the streams, by calling pjmedia_stream_get_port(). The
diff --git a/pjmedia/include/pjmedia/types.h b/pjmedia/include/pjmedia/types.h
index 297e2f8f..f41edeb9 100644
--- a/pjmedia/include/pjmedia/types.h
+++ b/pjmedia/include/pjmedia/types.h
@@ -114,44 +114,20 @@ typedef struct pjmedia_sock_info
/**
- * Typedef for media stream information.
- */
-typedef struct pjmedia_stream_info pjmedia_stream_info;
-
-/**
- * Typedef for media stream statistic.
- */
-typedef struct pjmedia_stream_stat pjmedia_stream_stat;
-
-/**
- * Typedef for media stream.
- */
-typedef struct pjmedia_stream pjmedia_stream;
-
-/**
- * Individual channel statistic.
- */
-typedef struct pjmedia_channel_stat pjmedia_channel_stat;
-
-/**
- * Opaque declaration of media session.
- */
-typedef struct pjmedia_session pjmedia_session;
-
-/**
- * Media session info.
- */
-typedef struct pjmedia_session_info pjmedia_session_info;
-
-/**
- * Types of frame returned from jitter buffer (jbuf.h).
+ * This is a general purpose function set PCM samples to zero.
+ * Since this function is needed by many parts of the library, it is important
+ * that the library should select the best performance for this.
+ *
+ * @param samples The 16bit PCM samples.
+ * @param count Number of samples.
*/
-typedef enum pjmedia_jb_frame_type pjmedia_jb_frame_type;
+PJ_INLINE(void) pjmedia_zero_samples(pj_int16_t *samples, unsigned count)
+{
+ unsigned i;
+ for (i=0; i<count; ++i)
+ samples[i] = 0;
+}
-/**
- * Opaque declaration for jitter buffer.
- */
-typedef struct pjmedia_jbuf pjmedia_jbuf;
#endif /* __PJMEDIA_TYPES_H__ */
diff --git a/pjmedia/src/pjmedia-codec/gsm.c b/pjmedia/src/pjmedia-codec/gsm.c
index ebb6d1be..8689960f 100644
--- a/pjmedia/src/pjmedia-codec/gsm.c
+++ b/pjmedia/src/pjmedia-codec/gsm.c
@@ -54,11 +54,12 @@ static pj_status_t gsm_codec_init( pjmedia_codec *codec,
static pj_status_t gsm_codec_open( pjmedia_codec *codec,
pjmedia_codec_param *attr );
static pj_status_t gsm_codec_close( pjmedia_codec *codec );
-static pj_status_t gsm_codec_get_frames( pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[]);
+static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[]);
static pj_status_t gsm_codec_encode( pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
@@ -74,7 +75,7 @@ static pjmedia_codec_op gsm_op =
&gsm_codec_init,
&gsm_codec_open,
&gsm_codec_close,
- &gsm_codec_get_frames,
+ &gsm_codec_parse,
&gsm_codec_encode,
&gsm_codec_decode
};
@@ -230,12 +231,14 @@ static pj_status_t gsm_default_attr (pjmedia_codec_factory *factory,
PJ_UNUSED_ARG(id);
pj_memset(attr, 0, sizeof(pjmedia_codec_param));
- attr->clock_rate = 8000;
- attr->channel_cnt = 1;
- attr->avg_bps = 13200;
- attr->pcm_bits_per_sample = 16;
- attr->ptime = 20;
- attr->pt = PJMEDIA_RTP_PT_GSM;
+ attr->info.clock_rate = 8000;
+ attr->info.channel_cnt = 1;
+ attr->info.avg_bps = 13200;
+ attr->info.pcm_bits_per_sample = 16;
+ attr->info.frm_ptime = 20;
+ attr->info.pt = PJMEDIA_RTP_PT_GSM;
+
+ attr->setting.frm_per_pkt = 1;
/* Default all flag bits disabled. */
@@ -386,11 +389,12 @@ static pj_status_t gsm_codec_close( pjmedia_codec *codec )
/*
* Get frames in the packet.
*/
-static pj_status_t gsm_codec_get_frames( pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[])
+static pj_status_t gsm_codec_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[])
{
unsigned count = 0;
@@ -399,9 +403,10 @@ static pj_status_t gsm_codec_get_frames( pjmedia_codec *codec,
PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
while (pkt_size >= 33 && count < *frame_cnt) {
- frames[0].type = PJMEDIA_FRAME_TYPE_AUDIO;
- frames[0].buf = pkt;
- frames[0].size = 33;
+ frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frames[count].buf = pkt;
+ frames[count].size = 33;
+ frames[count].timestamp.u64 = ts->u64 + count * 160;
pkt = ((char*)pkt) + 33;
pkt_size -= 33;
diff --git a/pjmedia/src/pjmedia-codec/l16.c b/pjmedia/src/pjmedia-codec/l16.c
index 594aefc3..47e4c070 100644
--- a/pjmedia/src/pjmedia-codec/l16.c
+++ b/pjmedia/src/pjmedia-codec/l16.c
@@ -61,11 +61,12 @@ static pj_status_t l16_init( pjmedia_codec *codec,
static pj_status_t l16_open( pjmedia_codec *codec,
pjmedia_codec_param *attr );
static pj_status_t l16_close( pjmedia_codec *codec );
-static pj_status_t l16_get_frames(pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[]);
+static pj_status_t l16_parse(pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[]);
static pj_status_t l16_encode( pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
@@ -81,7 +82,7 @@ static pjmedia_codec_op l16_op =
&l16_init,
&l16_open,
&l16_close,
- &l16_get_frames,
+ &l16_parse,
&l16_encode,
&l16_decode
};
@@ -234,16 +235,18 @@ static pj_status_t l16_default_attr( pjmedia_codec_factory *factory,
PJ_UNUSED_ARG(factory);
pj_memset(attr, 0, sizeof(pjmedia_codec_param));
- attr->pt = id->pt;
- attr->clock_rate = id->clock_rate;
- attr->channel_cnt = id->channel_cnt;
- attr->avg_bps = id->clock_rate * id->channel_cnt * 16;
- attr->pcm_bits_per_sample = 16;
+ attr->info.pt = (pj_uint8_t)id->pt;
+ attr->info.clock_rate = id->clock_rate;
+ attr->info.channel_cnt = id->channel_cnt;
+ attr->info.avg_bps = id->clock_rate * id->channel_cnt * 16;
+ attr->info.pcm_bits_per_sample = 16;
/* To keep frame size below 1400 MTU, set ptime to 10ms for
* sampling rate > 35 KHz
*/
- attr->ptime = GET_PTIME(id->clock_rate);
+ attr->info.frm_ptime = GET_PTIME(id->clock_rate);
+
+ attr->setting.frm_per_pkt = 1;
/* Default all flag bits disabled. */
@@ -498,11 +501,12 @@ static pj_status_t l16_close( pjmedia_codec *codec )
return PJ_SUCCESS;
}
-static pj_status_t l16_get_frames( pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[])
+static pj_status_t l16_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[])
{
unsigned count = 0;
struct l16_data *data = (struct l16_data*) codec->codec_data;
@@ -511,9 +515,10 @@ static pj_status_t l16_get_frames( pjmedia_codec *codec,
PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
while (pkt_size >= data->frame_size && count < *frame_cnt) {
- frames[0].type = PJMEDIA_FRAME_TYPE_AUDIO;
- frames[0].buf = pkt;
- frames[0].size = data->frame_size;
+ frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frames[count].buf = pkt;
+ frames[count].size = data->frame_size;
+ frames[count].timestamp.u64 = ts->u64 + (count * data->frame_size);
pkt = ((char*)pkt) + data->frame_size;
pkt_size -= data->frame_size;
diff --git a/pjmedia/src/pjmedia-codec/speex/ltp.c b/pjmedia/src/pjmedia-codec/speex/ltp.c
index 94189c34..8620dd47 100644
--- a/pjmedia/src/pjmedia-codec/speex/ltp.c
+++ b/pjmedia/src/pjmedia-codec/speex/ltp.c
@@ -176,7 +176,7 @@ void open_loop_nbest_pitch(spx_sig_t *sw, int start, int end, int len, int *pitc
VARDECL(spx_word32_t *corr);
VARDECL(spx_word32_t *energy);
VARDECL(spx_word32_t *score);
- VARDECL(spx_word16_t *swn2);
+ /*VARDECL(spx_word16_t *swn2);*/
spx_word16_t *swn;
ALLOC(best_score, N, spx_word32_t);
diff --git a/pjmedia/src/pjmedia-codec/speex_codec.c b/pjmedia/src/pjmedia-codec/speex_codec.c
index 280cfbdd..1bf161b6 100644
--- a/pjmedia/src/pjmedia-codec/speex_codec.c
+++ b/pjmedia/src/pjmedia-codec/speex_codec.c
@@ -61,11 +61,12 @@ static pj_status_t spx_codec_init( pjmedia_codec *codec,
static pj_status_t spx_codec_open( pjmedia_codec *codec,
pjmedia_codec_param *attr );
static pj_status_t spx_codec_close( pjmedia_codec *codec );
-static pj_status_t spx_codec_get_frames( pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[]);
+static pj_status_t spx_codec_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[]);
static pj_status_t spx_codec_encode( pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
@@ -74,6 +75,9 @@ static pj_status_t spx_codec_decode( pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
struct pjmedia_frame *output);
+static pj_status_t spx_codec_recover(pjmedia_codec *codec,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output);
/* Definition for Speex codec operations. */
static pjmedia_codec_op spx_op =
@@ -81,9 +85,10 @@ static pjmedia_codec_op spx_op =
&spx_codec_init,
&spx_codec_open,
&spx_codec_close,
- &spx_codec_get_frames,
+ &spx_codec_parse,
&spx_codec_encode,
- &spx_codec_decode
+ &spx_codec_decode,
+ &spx_codec_recover
};
/* Definition for Speex codec factory operations. */
@@ -377,36 +382,38 @@ static pj_status_t spx_default_attr (pjmedia_codec_factory *factory,
PJ_ASSERT_RETURN(factory==&spx_factory.base, PJ_EINVAL);
pj_memset(attr, 0, sizeof(pjmedia_codec_param));
- attr->pt = id->pt;
- attr->channel_cnt = 1;
+ attr->info.pt = (pj_uint8_t)id->pt;
+ attr->info.channel_cnt = 1;
if (id->clock_rate <= 8000) {
- attr->clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
- attr->avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
+ attr->info.clock_rate = spx_factory.speex_param[PARAM_NB].clock_rate;
+ attr->info.avg_bps = spx_factory.speex_param[PARAM_NB].bitrate;
} else if (id->clock_rate <= 16000) {
- attr->clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
- attr->avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
+ attr->info.clock_rate = spx_factory.speex_param[PARAM_WB].clock_rate;
+ attr->info.avg_bps = spx_factory.speex_param[PARAM_WB].bitrate;
} else {
/* Wow.. somebody is doing ultra-wideband. Cool...! */
- attr->clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
- attr->avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
+ attr->info.clock_rate = spx_factory.speex_param[PARAM_UWB].clock_rate;
+ attr->info.avg_bps = spx_factory.speex_param[PARAM_UWB].bitrate;
}
- attr->pcm_bits_per_sample = 16;
- attr->ptime = 20;
- attr->pt = id->pt;
+ attr->info.pcm_bits_per_sample = 16;
+ attr->info.frm_ptime = 20;
+ attr->info.pt = (pj_uint8_t)id->pt;
+
+ attr->setting.frm_per_pkt = 1;
/* Default flags. */
- attr->cng = 1;
- attr->concl = 1;
- attr->hpf = 1;
- attr->lpf =1 ;
- attr->penh =1 ;
+ attr->setting.cng = 1;
+ attr->setting.plc = 1;
+ attr->setting.hpf = 1;
+ attr->setting.lpf =1 ;
+ attr->setting.penh =1 ;
/* Default, set VAD off as it caused voice chip off */
- attr->vad = 0;
+ attr->setting.vad = 0;
return PJ_SUCCESS;
}
@@ -559,12 +566,12 @@ static pj_status_t spx_codec_open( pjmedia_codec *codec,
}
/* Sampling rate. */
- tmp = attr->clock_rate;
+ tmp = attr->info.clock_rate;
speex_encoder_ctl(spx->enc, SPEEX_SET_SAMPLING_RATE,
&spx_factory.speex_param[id].clock_rate);
/* VAD */
- tmp = attr->vad;
+ tmp = attr->setting.vad;
speex_encoder_ctl(spx->enc, SPEEX_SET_VAD, &tmp);
/* Complexity */
@@ -588,7 +595,7 @@ static pj_status_t spx_codec_open( pjmedia_codec *codec,
&spx_factory.speex_param[id].clock_rate);
/* PENH */
- tmp = attr->penh;
+ tmp = attr->setting.penh;
speex_decoder_ctl(spx->dec, SPEEX_SET_ENH, &tmp);
return PJ_SUCCESS;
@@ -624,38 +631,43 @@ static pj_status_t spx_codec_close( pjmedia_codec *codec )
/*
* Get frames in the packet.
*/
-static pj_status_t spx_codec_get_frames( pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[])
+static pj_status_t spx_codec_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[])
{
struct spx_private *spx;
- unsigned speex_frame_size;
+ unsigned frame_size, samples_per_frame;
unsigned count;
spx = (struct spx_private*) codec->codec_data;
- speex_frame_size = spx_factory.speex_param[spx->param_id].framesize;
+ frame_size = spx_factory.speex_param[spx->param_id].framesize;
+ samples_per_frame = spx_factory.speex_param[spx->param_id].samples_per_frame;
/* Don't really know how to do this... */
count = 0;
- while (pkt_size >= speex_frame_size && count < *frame_cnt) {
+ while (pkt_size >= frame_size && count < *frame_cnt) {
frames[count].buf = pkt;
- frames[count].size = speex_frame_size;
+ frames[count].size = frame_size;
frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
- frames[count].timestamp.u64 = 0;
+ frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
- pkt_size -= speex_frame_size;
+ pkt_size -= frame_size;
++count;
- pkt = ((char*)pkt) + speex_frame_size;
+ pkt = ((char*)pkt) + frame_size;
}
+ /* Just in case speex has silence frame which size is less than normal
+ * frame size...
+ */
if (pkt_size && count < *frame_cnt) {
frames[count].buf = pkt;
frames[count].size = pkt_size;
frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
- frames[count].timestamp.u64 = 0;
+ frames[count].timestamp.u64 = ts->u64 + count * samples_per_frame;
++count;
}
@@ -764,5 +776,35 @@ static pj_status_t spx_codec_decode( pjmedia_codec *codec,
return PJ_SUCCESS;
}
+/*
+ * Recover lost frame.
+ */
+static pj_status_t spx_codec_recover(pjmedia_codec *codec,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output)
+{
+ struct spx_private *spx;
+ float tmp[642]; /* 20ms at 32KHz + 2 */
+ pj_int16_t *dst_buf;
+ unsigned i, count;
+
+ spx = (struct spx_private*) codec->codec_data;
+
+ count = spx_factory.speex_param[spx->param_id].clock_rate * 20 / 1000;
+ pj_assert((count <= output_buf_len/2) && count <= PJ_ARRAY_SIZE(tmp));
+
+ /* Recover packet loss */
+ speex_decode(spx->dec, NULL, tmp);
+
+ /* Copy from float to short samples. */
+ dst_buf = output->buf;
+ for (i=0; i<count; ++i) {
+ dst_buf[i] = (pj_int16_t)tmp[i];
+ }
+ output->size = count * 2;
+
+ return PJ_SUCCESS;
+}
+
#endif /* PJMEDIA_HAS_SPEEX_CODEC */
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index e403178d..becdb164 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -473,12 +473,12 @@ PJ_DEF(pj_status_t) pjmedia_endpt_dump(pjmedia_endpt *endpt)
codec_info[i].encoding_name.ptr,
codec_info[i].clock_rate/1000,
codec_info[i].channel_cnt,
- good_number(bps, param.avg_bps),
- param.ptime,
- (param.vad ? " vad" : ""),
- (param.cng ? " cng" : ""),
- (param.concl ? " plc" : ""),
- (param.penh ? " penh" : ""),
+ good_number(bps, param.info.avg_bps),
+ param.info.frm_ptime * param.setting.frm_per_pkt,
+ (param.setting.vad ? " vad" : ""),
+ (param.setting.cng ? " cng" : ""),
+ (param.setting.plc ? " plc" : ""),
+ (param.setting.penh ? " penh" : ""),
(prio[i]==PJMEDIA_CODEC_PRIO_DISABLED?" disabled":"")));
}
#endif
diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c
index c4c8531b..f7c5082e 100644
--- a/pjmedia/src/pjmedia/errno.c
+++ b/pjmedia/src/pjmedia/errno.c
@@ -87,6 +87,7 @@ static const struct
{ PJMEDIA_CODEC_EFAILED, "Codec internal creation error" },
{ PJMEDIA_CODEC_EFRMTOOSHORT, "Codec frame is too short" },
{ PJMEDIA_CODEC_EPCMTOOSHORT, "PCM frame is too short" },
+ { PJMEDIA_CODEC_EFRMINLEN, "Invalid codec frame length" },
/* Media errors. */
{ PJMEDIA_EINVALIDIP, "Invalid remote media (IP) address" },
diff --git a/pjmedia/src/pjmedia/g711.c b/pjmedia/src/pjmedia/g711.c
index 2a4bc2f8..c9500a5f 100644
--- a/pjmedia/src/pjmedia/g711.c
+++ b/pjmedia/src/pjmedia/g711.c
@@ -23,6 +23,7 @@
#include <pjmedia/endpoint.h>
#include <pjmedia/errno.h>
#include <pjmedia/port.h>
+#include <pjmedia/plc.h>
#include <pj/pool.h>
#include <pj/string.h>
#include <pj/assert.h>
@@ -30,16 +31,18 @@
#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
-#define G711_BPS 64000
-#define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */
-#define PTIME 20
-#define FRAME_SIZE (8000 * PTIME / 1000)
+#define G711_BPS 64000
+#define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */
+#define PTIME 10 /* basic frame size is 10 msec */
+#define FRAME_SIZE (8000 * PTIME / 1000) /* 80 bytes */
+#define SAMPLES_PER_FRAME (8000 * PTIME / 1000) /* 80 samples */
/* These are the only public functions exported to applications */
-PJ_DECL(pj_status_t) g711_init_factory (pjmedia_codec_factory *factory, pj_pool_t *pool);
+PJ_DECL(pj_status_t) g711_init_factory (pjmedia_codec_factory *factory,
+ pj_pool_t *pool);
/* Algorithm prototypes. */
-unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */
+unsigned char linear2alaw(int pcm_val);
int alaw2linear(unsigned char a_val);
unsigned char linear2ulaw(int pcm_val);
int ulaw2linear(unsigned char u_val);
@@ -65,11 +68,12 @@ static pj_status_t g711_init( pjmedia_codec *codec,
static pj_status_t g711_open( pjmedia_codec *codec,
pjmedia_codec_param *attr );
static pj_status_t g711_close( pjmedia_codec *codec );
-static pj_status_t g711_get_frames(pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[]);
+static pj_status_t g711_parse(pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *timestamp,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[]);
static pj_status_t g711_encode( pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
@@ -78,6 +82,9 @@ static pj_status_t g711_decode( pjmedia_codec *codec,
const struct pjmedia_frame *input,
unsigned output_buf_len,
struct pjmedia_frame *output);
+static pj_status_t g711_recover( pjmedia_codec *codec,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output);
/* Definition for G711 codec operations. */
static pjmedia_codec_op g711_op =
@@ -85,9 +92,10 @@ static pjmedia_codec_op g711_op =
&g711_init,
&g711_open,
&g711_close,
- &g711_get_frames,
+ &g711_parse,
&g711_encode,
- &g711_decode
+ &g711_decode,
+ &g711_recover
};
/* Definition for G711 codec factory operations. */
@@ -113,7 +121,9 @@ static struct g711_factory
/* G711 codec private data. */
struct g711_private
{
- unsigned pt;
+ unsigned pt;
+ pj_bool_t plc_enabled;
+ pjmedia_plc *plc;
};
@@ -217,7 +227,7 @@ static pj_status_t g711_test_alloc(pjmedia_codec_factory *factory,
PJ_UNUSED_ARG(factory);
/* It's sufficient to check payload type only. */
- return (id->pt==PJMEDIA_RTP_PT_PCMU || id->pt==PJMEDIA_RTP_PT_PCMA) ? 0 : -1;
+ return (id->pt==PJMEDIA_RTP_PT_PCMU || id->pt==PJMEDIA_RTP_PT_PCMA)? 0:-1;
}
static pj_status_t g711_default_attr (pjmedia_codec_factory *factory,
@@ -227,12 +237,18 @@ static pj_status_t g711_default_attr (pjmedia_codec_factory *factory,
PJ_UNUSED_ARG(factory);
pj_memset(attr, 0, sizeof(pjmedia_codec_param));
- attr->clock_rate = 8000;
- attr->channel_cnt = 1;
- attr->avg_bps = G711_BPS;
- attr->pcm_bits_per_sample = 16;
- attr->ptime = PTIME;
- attr->pt = id->pt;
+ attr->info.clock_rate = 8000;
+ attr->info.channel_cnt = 1;
+ attr->info.avg_bps = G711_BPS;
+ attr->info.pcm_bits_per_sample = 16;
+ attr->info.frm_ptime = PTIME;
+ attr->info.pt = (pj_uint8_t)id->pt;
+
+ /* Set default frames per packet to 2 (or 20ms) */
+ attr->setting.frm_per_pkt = 2;
+
+ /* Enable plc by default. */
+ attr->setting.plc = 1;
/* Default all flag bits disabled. */
@@ -274,6 +290,7 @@ static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory,
pjmedia_codec **p_codec)
{
pjmedia_codec *codec = NULL;
+ pj_status_t status;
PJ_ASSERT_RETURN(factory==&g711_factory.base, PJ_EINVAL);
@@ -292,8 +309,17 @@ static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory,
return PJ_ENOMEM;
}
+ /* Set the payload type */
codec_priv->pt = id->pt;
+ /* Create PLC, always with 10ms ptime */
+ status = pjmedia_plc_create(g711_factory.pool, 8000, 80,
+ 0, &codec_priv->plc);
+ if (status != PJ_SUCCESS) {
+ pj_mutex_unlock(g711_factory.mutex);
+ return status;
+ }
+
codec->factory = factory;
codec->op = &g711_op;
codec->codec_data = codec_priv;
@@ -350,7 +376,8 @@ static pj_status_t g711_open(pjmedia_codec *codec,
pjmedia_codec_param *attr )
{
struct g711_private *priv = codec->codec_data;
- priv->pt = attr->pt;
+ priv->pt = attr->info.pt;
+ priv->plc_enabled = (attr->setting.plc != 0);
return PJ_SUCCESS;
}
@@ -361,21 +388,24 @@ static pj_status_t g711_close( pjmedia_codec *codec )
return PJ_SUCCESS;
}
-static pj_status_t g711_get_frames(pjmedia_codec *codec,
- void *pkt,
- pj_size_t pkt_size,
- unsigned *frame_cnt,
- pjmedia_frame frames[])
+static pj_status_t g711_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[])
{
unsigned count = 0;
PJ_UNUSED_ARG(codec);
- PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL);
+
+ PJ_ASSERT_RETURN(ts && frame_cnt && frames, PJ_EINVAL);
while (pkt_size >= FRAME_SIZE && count < *frame_cnt) {
- frames[0].type = PJMEDIA_FRAME_TYPE_AUDIO;
- frames[0].buf = pkt;
- frames[0].size = FRAME_SIZE;
+ frames[count].type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frames[count].buf = pkt;
+ frames[count].size = FRAME_SIZE;
+ frames[count].timestamp.u64 = ts->u64 + SAMPLES_PER_FRAME * count;
pkt = ((char*)pkt) + FRAME_SIZE;
pkt_size -= FRAME_SIZE;
@@ -433,8 +463,12 @@ static pj_status_t g711_decode(pjmedia_codec *codec,
struct g711_private *priv = codec->codec_data;
/* Check output buffer length */
- if (output_buf_len < input->size * 2)
- return PJMEDIA_CODEC_EPCMTOOSHORT;
+ PJ_ASSERT_RETURN(output_buf_len >= input->size * 2,
+ PJMEDIA_CODEC_EPCMTOOSHORT);
+
+ /* Input buffer MUST have exactly 80 bytes long */
+ PJ_ASSERT_RETURN(input->size == FRAME_SIZE,
+ PJMEDIA_CODEC_EFRMINLEN);
/* Decode */
if (priv->pt == PJMEDIA_RTP_PT_PCMA) {
@@ -461,9 +495,29 @@ static pj_status_t g711_decode(pjmedia_codec *codec,
output->type = PJMEDIA_FRAME_TYPE_AUDIO;
output->size = input->size * 2;
+ if (priv->plc_enabled)
+ pjmedia_plc_save( priv->plc, output->buf);
+
return PJ_SUCCESS;
}
+static pj_status_t g711_recover( pjmedia_codec *codec,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output)
+{
+ struct g711_private *priv = codec->codec_data;
+
+ if (!priv->plc_enabled)
+ return PJ_EINVALIDOP;
+
+ PJ_ASSERT_RETURN(output_buf_len >= SAMPLES_PER_FRAME * 2,
+ PJMEDIA_CODEC_EPCMTOOSHORT);
+
+ pjmedia_plc_generate(priv->plc, output->buf);
+ output->size = SAMPLES_PER_FRAME * 2;
+
+ return PJ_SUCCESS;
+}
#endif /* PJMEDIA_HAS_G711_CODEC */
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index 228245a1..d124abcb 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -432,10 +432,10 @@ static void jbuf_update(pjmedia_jbuf *jb, int oper)
}
}
-PJ_DEF(pj_status_t) pjmedia_jbuf_put_frame(pjmedia_jbuf *jb,
- const void *frame,
- pj_size_t frame_size,
- int frame_seq)
+PJ_DEF(void) 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;
@@ -475,16 +475,14 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_put_frame(pjmedia_jbuf *jb,
{
jb_framelist_put_at(&jb->jb_framelist,frame_seq,frame,min_frame_size);
}
-
- return PJ_SUCCESS;
}
/*
* Get frame from jitter buffer.
*/
-PJ_DEF(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
- void *frame,
- char *p_frame_type)
+PJ_DEF(void) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
+ void *frame,
+ char *p_frame_type)
{
pjmedia_jb_frame_type ftype;
@@ -506,7 +504,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
else
*p_frame_type = PJMEDIA_JB_ZERO_PREFETCH_FRAME;
- return PJ_SUCCESS;
+ return;
}
/* Retrieve a frame from frame list */
@@ -515,7 +513,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
pj_memset(frame, 0, jb->jb_frame_size);
*p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
- return PJ_SUCCESS;
+ return;
}
/* We've successfully retrieved a frame from the frame list, but
@@ -525,9 +523,6 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
*p_frame_type = PJMEDIA_JB_NORMAL_FRAME;
else
*p_frame_type = PJMEDIA_JB_MISSING_FRAME;
-
-
- return PJ_SUCCESS;
}
/*
diff --git a/pjmedia/src/pjmedia/plc_common.c b/pjmedia/src/pjmedia/plc_common.c
index d5289251..241a0c2e 100644
--- a/pjmedia/src/pjmedia/plc_common.c
+++ b/pjmedia/src/pjmedia/plc_common.c
@@ -27,10 +27,6 @@ static void* plc_replay_create(pj_pool_t*, unsigned c, unsigned f);
static void plc_replay_save(void*, pj_int16_t*);
static void plc_replay_generate(void*, pj_int16_t*);
-static void* noplc_create(pj_pool_t*, unsigned c, unsigned f);
-static void noplc_save(void*, pj_int16_t*);
-static void noplc_generate(void*, pj_int16_t*);
-
extern void* pjmedia_plc_g711_create(pj_pool_t*, unsigned c, unsigned f);
extern void pjmedia_plc_g711_save(void*, pj_int16_t*);
extern void pjmedia_plc_g711_generate(void*, pj_int16_t*);
@@ -65,14 +61,6 @@ static struct plc_alg plc_replay =
};
-static struct plc_alg no_plc =
-{
- &noplc_create,
- &noplc_save,
- &noplc_generate
-};
-
-
struct pjmedia_plc
{
void *obj;
@@ -201,27 +189,3 @@ static void plc_replay_generate(void *plc, pj_int16_t *frame)
-//////////////////////////////////////////////////////////////////////////////
-/*
- * No PLC
- */
-static void* noplc_create(pj_pool_t *pool, unsigned clock_rate,
- unsigned samples_per_sec)
-{
- PJ_UNUSED_ARG(pool);
- PJ_UNUSED_ARG(clock_rate);
- return (void*) samples_per_sec;
-}
-
-static void noplc_save(void *plc, pj_int16_t *frame)
-{
- PJ_UNUSED_ARG(plc);
- PJ_UNUSED_ARG(frame);
-}
-
-static void noplc_generate(void *plc, pj_int16_t *frame)
-{
- unsigned samples_per_sec = (unsigned)plc;
- pj_memset(frame, 0, samples_per_sec);
-}
-
diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c
index 42207cad..6bd5d020 100644
--- a/pjmedia/src/pjmedia/session.c
+++ b/pjmedia/src/pjmedia/session.c
@@ -64,10 +64,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
pjmedia_stream_info *si,
pj_pool_t *pool,
pjmedia_endpt *endpt,
+ const pjmedia_sock_info *skinfo,
const pjmedia_sdp_session *local,
const pjmedia_sdp_session *remote,
unsigned stream_idx)
{
+ pjmedia_codec_mgr *mgr;
const pjmedia_sdp_attr *attr;
const pjmedia_sdp_media *local_m;
const pjmedia_sdp_media *rem_m;
@@ -85,6 +87,10 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
+ /* Get codec manager. */
+ mgr = pjmedia_endpt_get_codec_mgr(endpt);
+
+ /* Keep SDP shortcuts */
local_m = local->media[stream_idx];
rem_m = remote->media[stream_idx];
@@ -215,11 +221,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
si->fmt.channel_cnt = 1;
}
- } else {
- pjmedia_codec_mgr *mgr;
- pjmedia_codec_info *p_info;
-
- mgr = pjmedia_endpt_get_codec_mgr(endpt);
+ } else {
+ const pjmedia_codec_info *p_info;
status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
if (status != PJ_SUCCESS)
@@ -301,6 +304,11 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
}
+ /* Now that we have codec info, get the codec param. */
+ si->param = pj_pool_alloc(pool, sizeof(*si->param));
+ status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param);
+ if (status != PJ_SUCCESS)
+ return status;
/* Get incomming payload type for telephone-events */
si->rx_event_pt = -1;
@@ -334,12 +342,52 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
}
}
+ /* Copy skinfo */
+ if (skinfo)
+ si->sock_info = *skinfo;
/* Leave SSRC to random. */
si->ssrc = pj_rand();
- /* Leave jitter buffer parameter. */
-
+ /* Set default jitter buffer parameter. */
+ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Initialize session info from SDP session descriptors.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_session_info_from_sdp( pj_pool_t *pool,
+ pjmedia_endpt *endpt,
+ unsigned max_streams,
+ pjmedia_session_info *si,
+ const pjmedia_sock_info skinfo[],
+ const pjmedia_sdp_session *local,
+ const pjmedia_sdp_session *remote)
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(pool && endpt && si && local && remote, PJ_EINVAL);
+
+ si->stream_cnt = max_streams;
+ if (si->stream_cnt > local->media_count)
+ si->stream_cnt = local->media_count;
+
+ for (i=0; i<si->stream_cnt; ++i) {
+ pj_status_t status;
+
+ status = pjmedia_stream_info_from_sdp( &si->stream_info[i], pool,
+ endpt,
+ (skinfo ? &skinfo[i] : NULL),
+ local, remote, i);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ }
+
return PJ_SUCCESS;
}
@@ -348,10 +396,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
* Create new session.
*/
PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt,
- unsigned stream_cnt,
- const pjmedia_sock_info skinfo[],
- const pjmedia_sdp_session *local_sdp,
- const pjmedia_sdp_session *rem_sdp,
+ const pjmedia_session_info *si,
void *user_data,
pjmedia_session **p_session )
{
@@ -361,8 +406,7 @@ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt,
pj_status_t status;
/* Verify arguments. */
- PJ_ASSERT_RETURN(endpt && stream_cnt && skinfo &&
- local_sdp && rem_sdp && p_session, PJ_EINVAL);
+ PJ_ASSERT_RETURN(endpt && si && p_session, PJ_EINVAL);
/* Create pool for the session. */
pool = pjmedia_endpt_create_pool( endpt, "session",
@@ -373,35 +417,19 @@ PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt,
session = pj_pool_zalloc(pool, sizeof(pjmedia_session));
session->pool = pool;
session->endpt = endpt;
- session->stream_cnt = stream_cnt;
+ session->stream_cnt = si->stream_cnt;
session->user_data = user_data;
-
- /* Stream count is the lower number of stream_cnt or SDP m= lines count */
- if (stream_cnt < local_sdp->media_count)
- stream_cnt = local_sdp->media_count;
- /*
- * Create streams:
- */
- for (i=0; i<(int)stream_cnt; ++i) {
-
- pjmedia_stream_info *si = &session->stream_info[i];
-
- /* Build stream info based on media line in local SDP */
- status = pjmedia_stream_info_from_sdp(si, session->pool, endpt,
- local_sdp, rem_sdp, i);
- if (status != PJ_SUCCESS)
- return status;
-
- /* Assign sockinfo */
- si->sock_info = skinfo[i];
- }
+ /* Copy stream info (this simple memcpy may break sometime) */
+ pj_memcpy(session->stream_info, si->stream_info,
+ si->stream_cnt * sizeof(pjmedia_session_info));
/*
* Now create and start the stream!
*/
- for (i=0; i<(int)stream_cnt; ++i) {
+ for (i=0; i<(int)si->stream_cnt; ++i) {
+ /* Create the stream */
status = pjmedia_stream_create(endpt, session->pool,
&session->stream_info[i],
session,
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 41f506a0..d45589b8 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -53,8 +53,6 @@ struct pjmedia_channel
void *in_pkt; /**< Input buffer. */
unsigned out_pkt_size; /**< Size of output buffer. */
void *out_pkt; /**< Output buffer. */
- unsigned pcm_buf_size; /**< Size of PCM buffer. */
- void *pcm_buf; /**< PCM buffer. */
pjmedia_rtp_session rtp; /**< RTP session. */
};
@@ -85,7 +83,8 @@ struct pjmedia_stream
void *user_data; /**< User data. */
pjmedia_codec *codec; /**< Codec instance being used. */
- pj_size_t frame_size; /**< Size of encoded frame. */
+ pjmedia_codec_param codec_param; /**< Codec param. */
+ unsigned frame_size; /**< Size of encoded base frame.*/
pj_mutex_t *jb_mutex;
pjmedia_jbuf *jb; /**< Jitter buffer. */
@@ -154,9 +153,9 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
pjmedia_stream *stream = port->user_data;
pjmedia_channel *channel = stream->dec;
- char frame_type;
pj_status_t status;
- struct pjmedia_frame frame_in, frame_out;
+ unsigned samples_count, samples_per_frame, samples_required;
+ pj_int16_t *p_out_samp;
/* Return no frame is channel is paused */
if (channel->paused) {
@@ -164,47 +163,107 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
return PJ_SUCCESS;
}
- /* Lock jitter buffer mutex */
+ /* Repeat get frame from the jitter buffer and decode the frame
+ * until we have enough frames according to codec's ptime.
+ */
+
+ /* Lock jitter buffer mutex first */
pj_mutex_lock( stream->jb_mutex );
- /* Get frame from jitter buffer. */
- status = pjmedia_jbuf_get_frame(stream->jb, channel->out_pkt,
- &frame_type);
+ samples_required = stream->port.info.samples_per_frame;
+ samples_per_frame = stream->codec_param.info.frm_ptime *
+ stream->codec_param.info.clock_rate *
+ stream->codec_param.info.channel_cnt /
+ 1000;
+ p_out_samp = frame->buf;
- /* Unlock jitter buffer mutex. */
- pj_mutex_unlock( stream->jb_mutex );
+ for (samples_count=0; samples_count < samples_required;
+ samples_count += samples_per_frame)
+ {
+ char frame_type;
- if (status != PJ_SUCCESS || frame_type != PJMEDIA_JB_NORMAL_FRAME) {
- frame->type = PJMEDIA_FRAME_TYPE_NONE;
- return PJ_SUCCESS;
- }
+ /* Get frame from jitter buffer. */
+ pjmedia_jbuf_get_frame(stream->jb, channel->out_pkt, &frame_type);
+
+ if (frame_type == PJMEDIA_JB_MISSING_FRAME) {
+
+ /* Activate PLC */
+ if (stream->codec->op->recover &&
+ stream->codec_param.setting.plc)
+ {
+ pjmedia_frame frame_out;
+
+ 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);
+ } else {
+ status = -1;
+ }
- /* Decode */
- 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,
- channel->pcm_buf_size, &frame_out);
- if (status != 0) {
- LOGERR_((port->info.name.ptr, "codec decode() error", status));
+ if (status != PJ_SUCCESS) {
+ /* Either PLC failed or PLC not supported/enabled */
+ pjmedia_zero_samples(p_out_samp + samples_count,
+ samples_required - samples_count);
+ }
+
+ } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
- frame->type = PJMEDIA_FRAME_TYPE_NONE;
- return PJ_SUCCESS;
- }
+ /* Jitter buffer is empty, zero the remaining samples and break
+ * the loop.
+ */
+ pjmedia_zero_samples(p_out_samp + samples_count,
+ samples_required - samples_count);
+ break;
+
+ } else if (frame_type != PJMEDIA_JB_NORMAL_FRAME) {
+
+ /* 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);
- /* Put in sound buffer. */
- if (frame_out.size > frame->size) {
- PJ_LOG(4,(port->info.name.ptr,
- "Sound playout buffer truncated %d bytes",
- frame_out.size - frame->size));
- frame_out.size = frame->size;
+ } else {
+ /* Got "NORMAL" frame from jitter buffer */
+ pjmedia_frame frame_in, frame_out;
+
+ /* Decode */
+ frame_in.buf = channel->out_pkt;
+ frame_in.size = stream->frame_size;
+ frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; /* ignored */
+
+ frame_out.buf = p_out_samp + samples_count;
+ frame_out.size = frame->size - samples_count*2;
+ status = stream->codec->op->decode( stream->codec, &frame_in,
+ frame_out.size, &frame_out);
+ if (status != 0) {
+ LOGERR_((port->info.name.ptr, "codec decode() error",
+ status));
+
+ pjmedia_zero_samples(p_out_samp + samples_count,
+ samples_per_frame);
+ }
+ }
}
- frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
- frame->size = frame_out.size;
- frame->timestamp.u64 = 0;
- pj_memcpy(frame->buf, frame_out.buf, frame_out.size);
+
+ /* Unlock jitter buffer mutex. */
+ pj_mutex_unlock( stream->jb_mutex );
+
+ /* Return PJMEDIA_FRAME_TYPE_NONE if we have no frames at all
+ * (it can happen when jitter buffer returns PJMEDIA_JB_ZERO_EMPTY_FRAME).
+ */
+ if (samples_count == 0) {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ frame->size = 0;
+ } else {
+ frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame->size = samples_count * 2;
+ frame->timestamp.u64 = 0;
+ }
return PJ_SUCCESS;
}
@@ -318,7 +377,7 @@ static pj_status_t put_frame( pjmedia_port *port,
pjmedia_channel *channel = stream->enc;
pj_status_t status = 0;
struct pjmedia_frame frame_out;
- int ts_len;
+ unsigned ts_len;
pj_bool_t has_tx;
void *rtphdr;
int rtphdrlen;
@@ -355,17 +414,37 @@ static pj_status_t put_frame( pjmedia_port *port,
&rtphdrlen);
} else if (frame->type != PJMEDIA_FRAME_TYPE_NONE) {
- unsigned max_size;
+ unsigned ts, samples_per_frame;
has_tx = PJ_TRUE;
- max_size = channel->out_pkt_size - sizeof(pjmedia_rtp_hdr);
- status = stream->codec->op->encode( stream->codec, frame,
- max_size,
- &frame_out);
- if (status != 0) {
- LOGERR_((stream->port.info.name.ptr,
- "Codec encode() error", status));
- return status;
+
+ /* Repeatedly call encode if there are multiple frames to be
+ * sent.
+ */
+ samples_per_frame = stream->codec_param.info.frm_ptime *
+ stream->codec_param.info.clock_rate *
+ stream->codec_param.info.channel_cnt /
+ 1000;
+
+ for (ts=0; ts<ts_len; ts += samples_per_frame) {
+ pjmedia_frame tmp_frame;
+ unsigned max_size;
+
+ tmp_frame.buf = ((char*)frame_out.buf) + frame_out.size;
+
+ max_size = channel->out_pkt_size - sizeof(pjmedia_rtp_hdr) -
+ frame_out.size;
+
+ status = stream->codec->op->encode( stream->codec, frame,
+ max_size,
+ &tmp_frame);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((stream->port.info.name.ptr,
+ "Codec encode() error", status));
+ return status;
+ }
+
+ frame_out.size += tmp_frame.size;
}
//printf("p"); fflush(stdout);
@@ -625,10 +704,43 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset"));
} else {
- unsigned ext_seq;
- ext_seq = channel->rtp.seq_ctrl.cycles | pj_ntohs(hdr->seq);
- status = pjmedia_jbuf_put_frame(stream->jb, payload, payloadlen,
- ext_seq);
+ /*
+ * Packets may contain more than one frames, while the jitter
+ * buffer can only take one frame per "put" operation. So we need
+ * to ask the codec to "parse" the payload into multiple frames.
+ */
+ enum { MAX = 16 };
+ pj_timestamp ts;
+ unsigned i, count;
+ pjmedia_frame frames[MAX];
+
+ /* Get the timestamp of the first sample */
+ ts.u64 = pj_ntohl(hdr->ts);
+
+ /* Parse the payload. */
+ status = (*stream->codec->op->parse)(stream->codec,
+ (void*)payload,
+ payloadlen,
+ &ts,
+ &count,
+ frames);
+ if (status != PJ_SUCCESS) {
+ LOGERR_((stream->port.info.name.ptr,
+ "Codec parse() error",
+ status));
+ count = 0;
+ }
+
+ /* Put each frame to jitter buffer. */
+ for (i=0; i<count; ++i) {
+ unsigned ext_seq;
+
+ ext_seq = (unsigned)(frames[i].timestamp.u64 /
+ stream->port.info.samples_per_frame);
+ pjmedia_jbuf_put_frame(stream->jb, frames[i].buf,
+ frames[i].size, ext_seq);
+
+ }
}
pj_mutex_unlock( stream->jb_mutex );
@@ -727,7 +839,6 @@ static pj_status_t create_channel( pj_pool_t *pool,
pjmedia_dir dir,
unsigned pt,
const pjmedia_stream_info *param,
- const pjmedia_codec_param *codec_param,
pjmedia_channel **p_channel)
{
pjmedia_channel *channel;
@@ -755,7 +866,7 @@ static pj_status_t create_channel( pj_pool_t *pool,
/* Allocate buffer for outgoing packet. */
channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) +
- codec_param->avg_bps/8 *
+ stream->codec_param.info.avg_bps/8 *
PJMEDIA_MAX_FRAME_DURATION_MS /
1000;
@@ -766,15 +877,6 @@ static pj_status_t create_channel( pj_pool_t *pool,
PJ_ASSERT_RETURN(channel->out_pkt != NULL, PJ_ENOMEM);
- /* Allocate buffer for decoding to PCM: */
-
- channel->pcm_buf_size = codec_param->clock_rate *
- codec_param->channel_cnt *
- codec_param->pcm_bits_per_sample / 8 *
- PJMEDIA_MAX_FRAME_DURATION_MS / 1000;
- channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size);
- PJ_ASSERT_RETURN(channel->pcm_buf != NULL, PJ_ENOMEM);
-
/* Create RTP and RTCP sessions: */
@@ -799,10 +901,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
{
pjmedia_stream *stream;
- pjmedia_codec_param codec_param;
pj_ioqueue_callback ioqueue_cb;
pj_uint16_t rtcp_port;
- unsigned jbuf_init, jbuf_max;
+ unsigned jb_init, jb_max, jb_min_pre, jb_max_pre;
pj_status_t status;
PJ_ASSERT_RETURN(pool && info && p_stream, PJ_EINVAL);
@@ -866,31 +967,44 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
goto err_cleanup;
- /* Get default codec param: */
+ /* Get codec param: */
+ if (info->param)
+ stream->codec_param = *info->param;
+ else {
+ status = pjmedia_codec_mgr_get_default_param(stream->codec_mgr,
+ &info->fmt,
+ &stream->codec_param);
+ if (status != PJ_SUCCESS)
+ goto err_cleanup;
+ }
- //status = stream->codec->op->default_attr(stream->codec, &codec_param);
- status = pjmedia_codec_mgr_get_default_param( stream->codec_mgr,
- &info->fmt, &codec_param);
- if (status != PJ_SUCCESS)
- goto err_cleanup;
+ /* Check for invalid frame per packet. */
+ if (stream->codec_param.setting.frm_per_pkt < 1)
+ stream->codec_param.setting.frm_per_pkt = 1;
/* Set additional info. */
stream->port.info.bits_per_sample = 16;
- stream->port.info.samples_per_frame = info->fmt.clock_rate*codec_param.ptime/1000;
- stream->port.info.bytes_per_frame = codec_param.avg_bps/8 * codec_param.ptime/1000;
+ stream->port.info.samples_per_frame = info->fmt.clock_rate *
+ stream->codec_param.info.frm_ptime *
+ stream->codec_param.setting.frm_per_pkt /
+ 1000;
+ stream->port.info.bytes_per_frame = stream->codec_param.info.avg_bps/8 *
+ stream->codec_param.info.frm_ptime *
+ stream->codec_param.setting.frm_per_pkt /
+ 1000;
/* Open the codec: */
- status = stream->codec->op->open(stream->codec, &codec_param);
+ status = stream->codec->op->open(stream->codec, &stream->codec_param);
if (status != PJ_SUCCESS)
goto err_cleanup;
/* Get the frame size: */
- stream->frame_size = (codec_param.avg_bps / 8) * codec_param.ptime / 1000;
-
+ stream->frame_size = (stream->codec_param.info.avg_bps / 8) *
+ stream->codec_param.info.frm_ptime / 1000;
/* Init RTCP session: */
@@ -900,31 +1014,44 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
info->ssrc);
- /* Create jitter buffer: */
- if (info->jb_init)
- jbuf_init = info->jb_init;
+ /* Init jitter buffer parameters: */
+ if (info->jb_max > 0)
+ jb_max = info->jb_max;
else
- jbuf_init = 60 / (stream->port.info.samples_per_frame * 1000 /
- info->fmt.clock_rate);
+ jb_max = 240 / (stream->port.info.samples_per_frame * 1000 /
+ info->fmt.clock_rate);
- if (info->jb_max)
- jbuf_max = info->jb_max;
+ if (info->jb_min_pre >= 0)
+ jb_min_pre = info->jb_min_pre;
else
- jbuf_max = 240 / (stream->port.info.samples_per_frame * 1000 /
- info->fmt.clock_rate);
+ jb_min_pre = 0;
+
+ if (info->jb_max_pre > 0)
+ jb_max_pre = info->jb_max_pre;
+ else
+ jb_max_pre = jb_max * 4 / 5;
+
+ if (info->jb_init >= 0)
+ jb_init = info->jb_init;
+ else
+ jb_init = jb_min_pre;
+
+
+ /* Create jitter buffer */
status = pjmedia_jbuf_create(pool, &stream->port.info.name,
stream->frame_size,
- jbuf_max, &stream->jb);
+ jb_max, &stream->jb);
if (status != PJ_SUCCESS)
goto err_cleanup;
- /* Set jitter buffer to adaptive */
- pjmedia_jbuf_set_adaptive( stream->jb, jbuf_init, 1, jbuf_max * 4 / 5);
+
+ /* Set up jitter buffer */
+ pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre);
/* Create decoder channel: */
status = create_channel( pool, stream, PJMEDIA_DIR_DECODING,
- info->fmt.pt, info, &codec_param, &stream->dec);
+ info->fmt.pt, info, &stream->dec);
if (status != PJ_SUCCESS)
goto err_cleanup;
@@ -932,7 +1059,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
/* Create encoder channel: */
status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING,
- info->tx_pt, info, &codec_param, &stream->enc);
+ info->tx_pt, info, &stream->enc);
if (status != PJ_SUCCESS)
goto err_cleanup;