diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-05-13 22:46:23 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-05-13 22:46:23 +0000 |
commit | b4ff87018aef719a1427c47da854d0108b9ed6c4 (patch) | |
tree | f117b651f2daeea1824eca14945668df7d4898a3 | |
parent | 19d4fb0b0f2d39538018f16323c4b6a52c07903d (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
27 files changed, 804 insertions, 415 deletions
diff --git a/pjlib/src/pj/ioqueue_common_abs.c b/pjlib/src/pj/ioqueue_common_abs.c index 60a2b21d..d8c5e22b 100644 --- a/pjlib/src/pj/ioqueue_common_abs.c +++ b/pjlib/src/pj/ioqueue_common_abs.c @@ -71,6 +71,8 @@ static pj_status_t ioqueue_init_key( pj_pool_t *pool, pj_status_t rc; int optlen; + PJ_UNUSED_ARG(pool); + key->ioqueue = ioqueue; key->fd = sock; key->user_data = user_data; 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; diff --git a/pjsip-apps/build/Samples.mak b/pjsip-apps/build/Samples.mak index 95320b8f..db945109 100644 --- a/pjsip-apps/build/Samples.mak +++ b/pjsip-apps/build/Samples.mak @@ -1,14 +1,14 @@ include ../../build/common.mak -PJLIB_LIB:=../../pjlib/lib/libpj-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) -PJLIB_UTIL_LIB:=../../pjlib-util/lib/libpjlib-util-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) -PJMEDIA_LIB:=../../pjmedia/lib/libpjmedia-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) -PJMEDIA_CODEC_LIB:=../../pjmedia/lib/libpjmedia-codec-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) -PJSIP_LIB:=../../pjsip/lib/libpjsip-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) -PJSIP_UA_LIB:=../../pjsip/lib/libpjsip-ua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) -PJSIP_SIMPLE_LIB:=../../pjsip/lib/libpjsip-simple-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) -PJSUA_LIB_LIB=../../pjsip/lib/libpjsua-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) +PJLIB_LIB:=../../pjlib/lib/libpj-$(TARGET)$(LIBEXT) +PJLIB_UTIL_LIB:=../../pjlib-util/lib/libpjlib-util-$(TARGET)$(LIBEXT) +PJMEDIA_LIB:=../../pjmedia/lib/libpjmedia-$(TARGET)$(LIBEXT) +PJMEDIA_CODEC_LIB:=../../pjmedia/lib/libpjmedia-codec-$(TARGET)$(LIBEXT) +PJSIP_LIB:=../../pjsip/lib/libpjsip-$(TARGET)$(LIBEXT) +PJSIP_UA_LIB:=../../pjsip/lib/libpjsip-ua-$(TARGET)$(LIBEXT) +PJSIP_SIMPLE_LIB:=../../pjsip/lib/libpjsip-simple-$(TARGET)$(LIBEXT) +PJSUA_LIB_LIB=../../pjsip/lib/libpjsua-$(TARGET)$(LIBEXT) ############################################################################### @@ -35,17 +35,17 @@ export _LDFLAGS := $(LIBS) \ $(LDFLAGS) -lm SRCDIR := ../src/samples -OBJDIR := ./output/samples-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME) +OBJDIR := ./output/samples-$(TARGET) BINDIR := ../bin/samples SAMPLES := simpleua playfile playsine confsample sndinfo level recfile resampleplay \ siprtp streamutil -EXES := $(foreach file, $(SAMPLES), $(BINDIR)/$(file)-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE)) +EXES := $(foreach file, $(SAMPLES), $(BINDIR)/$(file)-$(TARGET)$(HOST_EXE)) all: $(OBJDIR) $(EXES) -$(BINDIR)/%-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE): $(OBJDIR)/%$(OBJEXT) $(LIBS) +$(BINDIR)/%-$(TARGET)$(HOST_EXE): $(OBJDIR)/%$(OBJEXT) $(LIBS) $(LD) $(LDOUT)$(subst /,$(HOST_PSEP),$@) \ $(subst /,$(HOST_PSEP),$<) \ $(_LDFLAGS) diff --git a/pjsip-apps/src/samples/debug.c b/pjsip-apps/src/samples/debug.c index ed6f2e9f..a3974046 100644 --- a/pjsip-apps/src/samples/debug.c +++ b/pjsip-apps/src/samples/debug.c @@ -27,5 +27,5 @@ * E.g.: * #include "playfile.c" */ -#include "streamutil.c" +#include "sndinfo.c" diff --git a/pjsip-apps/src/samples/playfile.c b/pjsip-apps/src/samples/playfile.c index 06f4c1f2..5b26cd57 100644 --- a/pjsip-apps/src/samples/playfile.c +++ b/pjsip-apps/src/samples/playfile.c @@ -76,7 +76,11 @@ int main(int argc, char *argv[]) pj_status_t status; - PJ_UNUSED_ARG(argc); + if (argc != 2) { + puts("Error: filename required"); + puts(desc); + return 1; + } /* Must init PJLIB first: */ diff --git a/pjsip-apps/src/samples/simpleua.c b/pjsip-apps/src/samples/simpleua.c index 59991af7..c55c3e99 100644 --- a/pjsip-apps/src/samples/simpleua.c +++ b/pjsip-apps/src/samples/simpleua.c @@ -585,6 +585,7 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) static void call_on_media_update( pjsip_inv_session *inv, pj_status_t status) { + pjmedia_session_info sess_info; const pjmedia_sdp_session *local_sdp; const pjmedia_sdp_session *remote_sdp; pjmedia_port *media_port; @@ -607,13 +608,27 @@ static void call_on_media_update( pjsip_inv_session *inv, status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); + /* Create session info based on the two SDPs. + * We only support one stream per session for now. + */ + status = pjmedia_session_info_from_sdp(inv->dlg->pool, g_med_endpt, 1, + &sess_info, &g_med_skinfo, + local_sdp, remote_sdp); + if (status != PJ_SUCCESS) { + app_perror( THIS_FILE, "Unable to create media session", status); + return; + } + + /* If required, we can also change some settings in the session info, + * (such as jitter buffer settings, codec settings, etc) before we + * create the session. + */ + /* Create new media session, passing the two SDPs, and also the * media socket that we created earlier. * The media session is active immediately. */ - status = pjmedia_session_create( g_med_endpt, 1, - &g_med_skinfo, - local_sdp, remote_sdp, + status = pjmedia_session_create( g_med_endpt, &sess_info, NULL, &g_med_session ); if (status != PJ_SUCCESS) { app_perror( THIS_FILE, "Unable to create media session", status); diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c index 24bc6dc4..4bc1f76b 100644 --- a/pjsip-apps/src/samples/siprtp.c +++ b/pjsip-apps/src/samples/siprtp.c @@ -1168,7 +1168,7 @@ static int media_thread(void *arg) 0, /* marker bit */ strm->bytes_per_frame, strm->samples_per_frame, - &hdr, &hdrlen); + (const void**)&hdr, &hdrlen); if (status == PJ_SUCCESS) { /* Copy RTP header to packet */ @@ -1266,7 +1266,7 @@ static void call_on_media_update( pjsip_inv_session *inv, pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); status = pjmedia_stream_info_from_sdp(&audio->si, inv->pool, app.med_endpt, - local_sdp, remote_sdp, 0); + NULL, local_sdp, remote_sdp, 0); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error creating stream info from SDP", status); return; diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 726f8f0c..18467129 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -195,6 +195,7 @@ struct pjsua pj_bool_t auto_conf; /**< Auto put to conference? */ int complexity; /**< Codec complexity. */ int quality; /**< Codec quality. */ + int ptime; /**< Codec ptime in msec. */ /* Codec arguments: */ diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index b8fff57f..73f5aac1 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -26,7 +26,7 @@ * Call (INVITE) related stuffs. */ -#define THIS_FILE "pjsua_inv.c" +#define THIS_FILE "pjsua_call.c" #define REFRESH_CALL_TIMER 0x63 @@ -153,10 +153,10 @@ static pj_status_t call_destroy_media(int call_index) call->session = NULL; - } + PJ_LOG(3,(THIS_FILE, "Media session for call %d is destroyed", + call_index)); - PJ_LOG(3,(THIS_FILE, "Media session for call %d is destroyed", - call_index)); + } return PJ_SUCCESS; } @@ -523,6 +523,11 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, case PJSIP_INV_STATE_DISCONNECTED: pj_gettimeofday(&call->dis_time); break; + default: + /* Nothing to do. Just to keep gcc from complaining about + * unused enums. + */ + break; } /* If this is an outgoing INVITE that was created because of @@ -557,6 +562,12 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, st_code = e->body.tsx_state.tsx->status_code; ev_state = PJSIP_EVSUB_STATE_TERMINATED; break; + + default: + /* Nothing to do. Just to keep gcc from complaining about + * unused enums. + */ + break; } if (st_code != -1) { @@ -915,6 +926,7 @@ static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, } +#if 0 /* Disconnect call */ static void call_disconnect(pjsip_inv_session *inv, int st_code) @@ -930,6 +942,7 @@ static void call_disconnect(pjsip_inv_session *inv, pjsua_perror(THIS_FILE, "Unable to disconnect call", status); } } +#endif /* * Callback to be called when SDP offer/answer negotiation has just completed @@ -940,6 +953,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, pj_status_t status) { pjsua_call *call; + pjmedia_session_info sess_info; const pjmedia_sdp_session *local_sdp; const pjmedia_sdp_session *remote_sdp; pjmedia_port *media_port; @@ -990,17 +1004,38 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, return; } - /* Create new media session. - * The media session is active immediately. - */ if (pjsua.null_audio) return; - - status = pjmedia_session_create( pjsua.med_endpt, 1, - &call->skinfo, - local_sdp, remote_sdp, - call, - &call->session ); + + /* Create media session info based on SDP parameters. + * We only support one stream per session at the moment + */ + status = pjmedia_session_info_from_sdp( call->inv->dlg->pool, + pjsua.med_endpt, 1, + &sess_info, &call->skinfo, + local_sdp, remote_sdp); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create media session", + status); + //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); + return; + } + + /* Override ptime, if this option is specified. */ + if (pjsua.ptime) { + sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t) + (pjsua.ptime / sess_info.stream_info[0].param->info.frm_ptime); + if (sess_info.stream_info[0].param->setting.frm_per_pkt==0) + sess_info.stream_info[0].param->setting.frm_per_pkt = 1; + } + + /* Optionally, application may modify other stream settings here + * (such as jitter buffer parameters, codec ptime, etc.) + */ + + /* Create session based on session info. */ + status = pjmedia_session_create( pjsua.med_endpt, &sess_info, + call, &call->session ); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create media session", status); diff --git a/pjsip/src/pjsua-lib/pjsua_settings.c b/pjsip/src/pjsua-lib/pjsua_settings.c index 46d2d945..cb98effe 100644 --- a/pjsip/src/pjsua-lib/pjsua_settings.c +++ b/pjsip/src/pjsua-lib/pjsua_settings.c @@ -94,6 +94,7 @@ static void usage(void) puts (" --rtp-port=N Base port to try for RTP (default=4000)"); puts (" --complexity=N Specify encoding complexity (0-10, default=none(-1))"); puts (" --quality=N Specify encoding quality (0-10, default=4)"); + puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)"); puts (""); puts ("Buddy List (can be more than one):"); puts (" --add-buddy url Add the specified URL to the buddy list."); @@ -227,7 +228,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[]) OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP, OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_PLAY_FILE, OPT_RTP_PORT, OPT_ADD_CODEC, - OPT_COMPLEXITY, OPT_QUALITY, + OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS, OPT_UAS_REFRESH, OPT_UAS_DURATION, }; @@ -266,6 +267,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[]) { "add-codec", 1, 0, OPT_ADD_CODEC}, { "complexity", 1, 0, OPT_COMPLEXITY}, { "quality", 1, 0, OPT_QUALITY}, + { "ptime", 1, 0, OPT_PTIME}, { "next-account",0,0, OPT_NEXT_ACCOUNT}, { "next-cred", 0, 0, OPT_NEXT_CRED}, { "max-calls", 1, 0, OPT_MAX_CALLS}, @@ -559,6 +561,15 @@ pj_status_t pjsua_parse_args(int argc, char *argv[]) } break; + case OPT_PTIME: + pjsua.ptime = my_atoi(pj_optarg); + if (pjsua.ptime < 10 || pjsua.ptime > 1000) { + PJ_LOG(1,(THIS_FILE, + "Error: invalid --ptime option")); + return -1; + } + break; + case OPT_AUTO_ANSWER: pjsua.auto_answer = my_atoi(pj_optarg); if (pjsua.auto_answer < 100 || pjsua.auto_answer > 699) { @@ -771,13 +782,15 @@ static void dump_media_session(pjmedia_session *session) } PJ_LOG(3,(THIS_FILE, - " TX pt=%d, stat last update: %s\n" + " TX pt=%d, ptime=%dms, stat last update: %s\n" " total %spkt %sB (%sB +IP hdr)%s\n" " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n" " (msec) min avg max last\n" " loss period: %7.3f %7.3f %7.3f %7.3f%s\n" " jitter : %7.3f %7.3f %7.3f %7.3f%s", info.stream_info[i].tx_pt, + info.stream_info[i].param->info.frm_ptime * + info.stream_info[i].param->setting.frm_per_pkt, last_update, good_number(packets, stat.tx.pkt), good_number(bytes, stat.tx.bytes), @@ -969,6 +982,8 @@ int pjsua_dump_settings(char *buf, pj_size_t max) pj_str_t cfg; char line[128]; + PJ_UNUSED_ARG(max); + cfg.ptr = buf; cfg.slen = 0; @@ -1096,6 +1111,13 @@ int pjsua_dump_settings(char *buf, pj_size_t max) pjsua.complexity); pj_strcat2(&cfg, line); + /* ptime */ + if (pjsua.ptime) { + pj_ansi_sprintf(line, "--ptime %d\n", + pjsua.ptime); + pj_strcat2(&cfg, line); + } + /* Start RTP port. */ pj_ansi_sprintf(line, "--rtp-port %d\n", pjsua.start_rtp_port); |