Compare commits

...

10 Commits

Author SHA1 Message Date
Nanang Izzuddin f923e2d7f5 Ticket #461:
Initial integration of the new jitter buffer into stream




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1779 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-04 23:56:40 +00:00
Nanang Izzuddin 5785471d14 Ticket #461:
- added param userdata to JB callbacks
- added a bit optimization on pjmedia_jb2_get_frame(): shifting pointer instead of shifting frame buffer(memmove) when only parts of PCM frame needed
- modified DTX handling mechanism: detects jumping timestamp but good sequence to generate noise/silence frames, instead of relying on abnormal drift.
- modified behaviour on failure of allocating frame
- moved abnormal drift detection from compensate_drift() to update_state() 
- added/removed some log for debugging purpose




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1778 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-04 23:52:43 +00:00
Nanang Izzuddin 3f502e8b42 Ticket #466:
Fixed PCM frame size returned by speex decoder.



git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1777 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-04 23:37:01 +00:00
Nanang Izzuddin 2e29528c22 Ticket #461:
- added feature resample, to test the effect of drift compensation on resampling
- remove 'frame' options (specifies number of samples per frame)




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1776 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-03 16:59:30 +00:00
Nanang Izzuddin 10a4714ff5 Ticket #461:
- added JB phases(idle, learning, running)
- changed jb_vbuf_shrink_to() to jb_vbuf_shrink(), the size param specifies the shrink size
- added jb_vbuf_expand
- modified insert_samples(), automatically insert new frames needed to jb





git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1775 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-03 16:55:31 +00:00
Nanang Izzuddin 5112c49cea Ticket #461:
Fixed bug in find_matched_window(), the ref pointer is not correctly set.




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1773 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-02 16:37:43 +00:00
Nanang Izzuddin 77e3f0693c Ticket #461:
Added another test for JB2, which the objective is to measure the quality of drift compensation.
The test will read wav file (raw PCM) and supply frames to JB2 at rate controlled by pjmedia_clock so drift can be simulated via clock setting.




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1772 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-02 12:12:21 +00:00
Nanang Izzuddin 3101589616 Ticket #461:
Added test for new jitter buffer (JB2).
The test will read text file that contains sequence of characters that simulate JB2 operations (PUT & GET).
This test is useful to analyze JB2 behaviour on jitter, DTX, drift, and packet lost.




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1771 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-02 11:49:35 +00:00
Nanang Izzuddin 2082fc1626 Ticket #461:
Added the new jitter buffer, initial source.




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1770 74dad513-b988-da41-8d7b-12977e46ad98
2008-02-02 11:23:16 +00:00
Benny Prijono eac4062c0d Branched trunk to branches/projects/jbuf2 for new jitter buffer development
git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/jbuf2@1751 74dad513-b988-da41-8d7b-12977e46ad98
2008-01-26 10:09:39 +00:00
10 changed files with 2479 additions and 206 deletions

View File

@ -509,6 +509,10 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\src\pjmedia\jbuf2.c"
>
</File>
<File
RelativePath="..\src\pjmedia\master_port.c"
>
@ -1140,6 +1144,10 @@
RelativePath="..\include\pjmedia\jbuf.h"
>
</File>
<File
RelativePath="..\include\pjmedia\jbuf2.h"
>
</File>
<File
RelativePath="..\include\pjmedia\master_port.h"
>

View File

@ -36,6 +36,7 @@
#include <pjmedia/endpoint.h>
#include <pjmedia/g711.h>
#include <pjmedia/jbuf.h>
#include <pjmedia/jbuf2.h>
#include <pjmedia/master_port.h>
#include <pjmedia/mem_port.h>
#include <pjmedia/null_port.h>

View File

@ -0,0 +1,302 @@
/* $Id$ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __PJMEDIA_JBUF2_H__
#define __PJMEDIA_JBUF2_H__
/**
* @file jbuf2.h
* @brief Adaptive jitter buffer implementation.
*/
#include <pjmedia/types.h>
/**
* @defgroup PJMED_JBUF2 Adaptive jitter buffer
* @ingroup PJMEDIA_FRAME_OP
* @{
* This section describes PJMEDIA's implementation of de-jitter buffer.
* The de-jitter buffer may be set to operate in adaptive mode or fixed
* delay mode.
*
* The jitter buffer is also able to report the status of the current
* frame (#pjmedia_jb_frame_type). This status is used for examply by
* @ref PJMED_STRM to invoke the codec's @ref PJMED_PLC algorithm.
*/
PJ_BEGIN_DECL
/**
* Opaque declaration for jitter buffer.
*/
typedef struct pjmedia_jb2_t pjmedia_jb2_t;
/**
* Types of jitter buffer phase.
*/
typedef enum pjmedia_jb2_phase
{
PJMEDIA_JB_PH_IDLE = 0, /**< No activity in PUT/GET or both */
PJMEDIA_JB_PH_LEARNING = 1, /**< Learning */
PJMEDIA_JB_PH_RUNNING = 2, /**< Running */
} pjmedia_jb2_phase;
/**
* Types of frame returned by the jitter buffer.
*/
typedef enum pjmedia_jb2_frame_type
{
PJMEDIA_JB_FT_NULL_FRAME = 0, /**< Frame not available */
PJMEDIA_JB_FT_NORMAL_FRAME = 1, /**< Normal encoded frame */
PJMEDIA_JB_FT_NORMAL_RAW_FRAME = 2, /**< Normal PCM frame */
PJMEDIA_JB_FT_INTERP_RAW_FRAME = 3, /**< Interpolation frame */
} pjmedia_jb2_frame_type ;
/**
* Jitter buffer frame definition.
*/
typedef struct pjmedia_jb2_frame
{
pjmedia_jb2_frame_type type;
void *buffer;
pj_size_t size; /* in bytes */
pj_uint8_t pt;
pj_uint16_t seq;
pj_uint32_t ts;
} pjmedia_jb2_frame;
/*
* Jitter buffer state.
*/
typedef struct pjmedia_jb2_state
{
pjmedia_jb2_phase phase;
/* in frames */
pj_uint16_t level;
pj_uint32_t frame_cnt;
/* in samples */
pj_int32_t drift;
pj_uint32_t drift_span;
pj_uint32_t cur_size;
pj_uint32_t opt_size;
} pjmedia_jb2_state;
/*
* Jitter buffer statistic.
*/
typedef struct pjmedia_jb2_stat
{
/* in frames */
pj_uint32_t lost;
pj_uint32_t late;
pj_uint32_t ooo;
pj_uint32_t out;
pj_uint32_t in;
/* in ticks */
pj_uint32_t full;
pj_uint32_t empty;
/* in samples */
pj_uint32_t max_size;
pj_int32_t max_drift;
pj_int32_t max_drift_span;
pj_int32_t max_comp;
/* in frames */
pj_uint16_t max_level;
} pjmedia_jb2_stat;
/**
* @see pjmedia_jb_frame_type.
*/
typedef enum pjmedia_jb2_frame_type pjmedia_jb2_frame_type;
/**
* This structure describes jitter buffer callback
*/
typedef struct pjmedia_jb2_cb
{
pj_status_t (*decode) (pjmedia_jb2_frame *frame, void *userdata);
pj_status_t (*plc) (pjmedia_jb2_frame *frame, void *userdata);
pj_status_t (*cng) (pjmedia_jb2_frame *frame, void *userdata);
void *user_data;
} pjmedia_jb2_cb;
/**
* This structure describes jitter buffer current status.
*/
typedef struct pjmedia_jb2_setting
{
/**
* The maximum bytes number of each raw/PCM frame that will be kept in
* the jitter buffer.
*/
pj_size_t frame_size;
/**
* The number of samples for each frame in GET or PUT operation.
* == timestamp interval between two consecutive frames.
*/
unsigned samples_per_frame;
/**
* Maximum number of frames that can be kept in the jitter buffer. This
* effectively means the maximum delay that may be introduced by this jitter
* buffer. Set this to zero for adaptive jitter buffer and non-zero for
* fixed jitter buffer.
*/
unsigned max_frames;
} pjmedia_jb2_setting;
/**
* Create an adaptive jitter buffer according to the specification. If
* application wants to have a fixed jitter buffer, it may call
* #pjmedia_jb2_set_fixed() after the jitter buffer is created.
*
* This function may allocate large chunk of memory to keep the frames in
* the buffer.
*
* @param pool The pool to allocate memory.
* @param name Name to identify the jitter buffer for logging
* purpose.
* @param p_jb Pointer to receive jitter buffer instance.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_jb2_create(pj_pool_t *pool,
const pj_str_t *name,
const pjmedia_jb2_setting *setting,
const pjmedia_jb2_cb *cb,
pjmedia_jb2_t **p_jb);
/**
* Destroy jitter buffer instance.
*
* @param jb The jitter buffer.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_jb2_destroy(pjmedia_jb2_t *jb);
/**
* Restart jitter. This function flushes all packets in the buffer and
* reset the internal sequence number.
*
* @param jb The jitter buffer.
*
* @param setting New setting to be applied, it can be NULL
* if user want to use the current setting.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_jb2_reset(pjmedia_jb2_t *jb);
/**
* Put a frame to the jitter buffer. If the frame can be accepted (based
* on the sequence number), the jitter buffer will copy the frame and put
* it in the appropriate position in the buffer.
*
* Application MUST manage it's own synchronization when multiple threads
* are accessing the jitter buffer at the same time.
*
* @param jb The jitter buffer.
* @param frame Pointer to frame buffer to be stored in the jitter
* buffer.
* @param size The frame size.
* @param frame_seq The frame sequence number.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_jb2_put_frame(pjmedia_jb2_t *jb,
const pjmedia_jb2_frame *frm);
/**
* Get a frame from the jitter buffer. The jitter buffer will return the
* oldest frame from it's buffer, when it is available.
*
* Application MUST manage it's own synchronization when multiple threads
* are accessing the jitter buffer at the same time.
*
* @param jb The jitter buffer.
* @param frame Buffer to receive the payload from the jitter buffer.
* Application MUST make sure that the buffer has
* appropriate size (i.e. not less than the frame size,
* as specified when the jitter buffer was created).
* The jitter buffer only copied a frame to this
* buffer when the frame type returned by this function
* is PJMEDIA_JB_NORMAL_FRAME.
* @param p_frm_type Pointer to receive frame type. If jitter buffer is
* currently empty or bufferring, the frame type will
* be set to PJMEDIA_JB_ZERO_FRAME, and no frame will
* be copied. If the jitter buffer detects that frame is
* missing with current sequence number, the frame type
* will be set to PJMEDIA_JB_MISSING_FRAME, and no
* 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.
*/
PJ_DECL(pj_status_t) pjmedia_jb2_get_frame(pjmedia_jb2_t *jb,
pjmedia_jb2_frame *frm);
/**
* Get jitter buffer current state.
*
* @param jb The jitter buffer.
* @param state Buffer to receive jitter buffer state.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_jb2_get_state(pjmedia_jb2_t *jb,
pjmedia_jb2_state *state);
/**
* Get jitter buffer current statistic.
*
* @param jb The jitter buffer.
* @param statistic Buffer to receive jitter buffer statistic.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_jb2_get_stat(pjmedia_jb2_t *jb,
pjmedia_jb2_stat *stat);
PJ_END_DECL
/**
* @}
*/
#endif /* __PJMEDIA_JBUF2_H__ */

View File

@ -768,17 +768,18 @@ static pj_status_t spx_codec_decode( pjmedia_codec *codec,
unsigned output_buf_len,
struct pjmedia_frame *output)
{
struct spx_private *spx;
struct spx_private *spx = (struct spx_private*) codec->codec_data;
unsigned pcm_size;
spx = (struct spx_private*) codec->codec_data;
pcm_size = spx_factory.speex_param[spx->param_id].samples_per_frame << 1;
PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
PJ_ASSERT_RETURN(output_buf_len >= pcm_size, PJMEDIA_CODEC_EPCMTOOSHORT);
if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
pjmedia_zero_samples((pj_int16_t*)output->buf, 160);
output->size = 320;
output->size = pcm_size;
output->timestamp.u64 = input->timestamp.u64;
output->type = PJMEDIA_FRAME_TYPE_AUDIO;
pj_bzero(output->buf, output->size);
return PJ_SUCCESS;
}
@ -789,7 +790,7 @@ static pj_status_t spx_codec_decode( pjmedia_codec *codec,
speex_decode_int(spx->dec, &spx->dec_bits, (spx_int16_t*)output->buf);
output->type = PJMEDIA_FRAME_TYPE_AUDIO;
output->size = 320;
output->size = pcm_size;
output->timestamp.u64 = input->timestamp.u64;

1378
pjmedia/src/pjmedia/jbuf2.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@
#include <pjmedia/errno.h>
#include <pjmedia/rtp.h>
#include <pjmedia/rtcp.h>
#include <pjmedia/jbuf.h>
#include <pjmedia/jbuf2.h>
#include <pj/array.h>
#include <pj/assert.h>
#include <pj/ctype.h>
@ -108,8 +108,7 @@ struct pjmedia_stream
pj_uint32_t tx_duration; /**< TX duration in timestamp. */
pj_mutex_t *jb_mutex;
pjmedia_jbuf *jb; /**< Jitter buffer. */
char jb_last_frm; /**< Last frame type from jb */
pjmedia_jb2_t *jb; /**< Jitter buffer. */
pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */
@ -152,6 +151,89 @@ static void stream_perror(const char *sender, const char *title,
PJ_LOG(4,(sender, "%s: %s [err:%d]", title, errmsg, status));
}
static pj_status_t jb_decode(pjmedia_jb2_frame *frame, void *userdata)
{
pjmedia_stream *stream = (pjmedia_stream*) userdata;
pj_status_t status = PJ_SUCCESS;
pjmedia_frame frame_in, frame_out;
pj_assert(frame && userdata);
/* Decode */
frame_in.buf = stream->dec->out_pkt;
frame_in.size = frame->size;
frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO;
pj_memcpy(frame_in.buf, frame->buffer, frame->size);
frame_out.buf = frame->buffer;
frame_out.size = (stream->codec_param.info.clock_rate *
stream->codec_param.info.frm_ptime / 1000)*
(stream->codec_param.info.pcm_bits_per_sample/8);
status = stream->codec->op->decode( stream->codec, &frame_in,
frame_out.size, &frame_out);
/* NOTE: frame_out.size is not valid for speex/16000 */
frame->size = frame_out.size;
if (status != PJ_SUCCESS) {
LOGERR_((stream->port.info.name.ptr, "codec decode() error",
status));
pj_bzero(frame->buffer, frame->size);
}
return status;
}
static pj_status_t jb_plc(pjmedia_jb2_frame *frame, void *userdata)
{
pjmedia_stream *stream = (pjmedia_stream*) userdata;
pj_status_t status = PJ_SUCCESS;
pj_assert(frame && stream);
/* Activate PLC */
if (stream->codec->op->recover &&
stream->codec_param.setting.plc)
{
pjmedia_frame frame_out;
frame_out.buf = frame->buffer;
frame_out.size = frame->size;
status = (*stream->codec->op->recover)(stream->codec,
frame_out.size,
&frame_out);
} else {
status = PJ_EINVAL;
}
if (status != PJ_SUCCESS) {
/* Either PLC failed or PLC not supported/enabled */
pj_bzero(frame->buffer, frame->size);
PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost!"));
} else {
PJ_LOG(5,(stream->port.info.name.ptr,
"Lost frame recovered"));
}
return status;
}
static pj_status_t jb_cng(pjmedia_jb2_frame *frame, void *userdata)
{
pjmedia_stream *stream = (pjmedia_stream*) userdata;
pj_assert(frame && stream);
frame->size = (stream->codec_param.info.clock_rate *
stream->codec_param.info.frm_ptime / 1000)*
(stream->codec_param.info.pcm_bits_per_sample/8);
pj_bzero(frame->buffer, frame->size);
return PJ_SUCCESS;
}
/*
* play_callback()
@ -159,6 +241,7 @@ static void stream_perror(const char *sender, const char *title,
* This callback is called by sound device's player thread when it
* needs to feed the player with some frames.
*/
static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
{
pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
@ -166,7 +249,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
unsigned samples_count, samples_per_frame, samples_required;
pj_int16_t *p_out_samp;
pj_status_t status;
pjmedia_jb2_frame jb_frame;
/* Return no frame is channel is paused */
if (channel->paused) {
@ -191,157 +274,10 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame)
for (samples_count=0; samples_count < samples_required;
samples_count += samples_per_frame)
{
char frame_type;
/* 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;
}
if (status != PJ_SUCCESS) {
/* Either PLC failed or PLC not supported/enabled */
pjmedia_zero_samples(p_out_samp + samples_count,
samples_required - samples_count);
PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost!"));
} else {
PJ_LOG(5,(stream->port.info.name.ptr,
"Lost frame recovered"));
}
} else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
/* Jitter buffer is empty. If this is the first "empty" state,
* activate PLC to smoothen the fade-out, otherwise zero
* the frame.
*/
if (frame_type != stream->jb_last_frm) {
pjmedia_jb_state jb_state;
/* Activate PLC to smoothen the missing frame */
if (stream->codec->op->recover &&
stream->codec_param.setting.plc)
{
pjmedia_frame frame_out;
do {
frame_out.buf = p_out_samp + samples_count;
frame_out.size = frame->size - samples_count*2;
status = (*stream->codec->op->recover)(stream->codec,
frame_out.size,
&frame_out);
if (status != PJ_SUCCESS)
break;
samples_count += samples_per_frame;
} while (samples_count < samples_required);
}
/* Report the state of jitter buffer */
pjmedia_jbuf_get_state(stream->jb, &jb_state);
PJ_LOG(5,(stream->port.info.name.ptr,
"Jitter buffer empty (prefetch=%d)",
jb_state.prefetch));
}
if (samples_count < samples_required) {
pjmedia_zero_samples(p_out_samp + samples_count,
samples_required - samples_count);
samples_count = samples_required;
}
stream->jb_last_frm = frame_type;
break;
} else if (frame_type != PJMEDIA_JB_NORMAL_FRAME) {
pjmedia_jb_state jb_state;
/* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */
pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME);
/* Get the state of jitter buffer */
pjmedia_jbuf_get_state(stream->jb, &jb_state);
/* Always activate PLC when it's available.. */
if (stream->codec->op->recover &&
stream->codec_param.setting.plc)
{
pjmedia_frame frame_out;
do {
frame_out.buf = p_out_samp + samples_count;
frame_out.size = frame->size - samples_count*2;
status = (*stream->codec->op->recover)(stream->codec,
frame_out.size,
&frame_out);
if (status != PJ_SUCCESS)
break;
samples_count += samples_per_frame;
} while (samples_count < samples_required);
if (stream->jb_last_frm != frame_type) {
PJ_LOG(5,(stream->port.info.name.ptr,
"Jitter buffer is bufferring with plc (prefetch=%d)",
jb_state.prefetch));
}
}
if (samples_count < samples_required) {
pjmedia_zero_samples(p_out_samp + samples_count,
samples_required - samples_count);
samples_count = samples_required;
PJ_LOG(5,(stream->port.info.name.ptr,
"Jitter buffer is bufferring (prefetch=%d)..",
jb_state.prefetch));
}
stream->jb_last_frm = frame_type;
break;
} 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*BYTES_PER_SAMPLE;
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);
}
}
stream->jb_last_frm = frame_type;
jb_frame.buffer = p_out_samp + samples_count;
jb_frame.size = frame->size - samples_count*2;
status = pjmedia_jb2_get_frame(stream->jb, &jb_frame);
}
@ -999,7 +935,7 @@ static void on_rx_rtp( void *data,
*/
pj_mutex_lock( stream->jb_mutex );
if (seq_st.status.flag.restart) {
status = pjmedia_jbuf_reset(stream->jb);
status = pjmedia_jb2_reset(stream->jb);
PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset"));
} else {
@ -1013,6 +949,7 @@ static void on_rx_rtp( void *data,
unsigned i, count = MAX;
unsigned samples_per_frame;
pjmedia_frame frames[MAX];
pjmedia_jb2_frame jb_frame;
/* Get the timestamp of the first sample */
ts.u64 = pj_ntohl(hdr->ts);
@ -1038,17 +975,22 @@ static void on_rx_rtp( void *data,
1000;
for (i=0; i<count; ++i) {
unsigned ext_seq;
ext_seq = (unsigned)(frames[i].timestamp.u64 /
samples_per_frame);
pjmedia_jbuf_put_frame(stream->jb, frames[i].buf,
frames[i].size, ext_seq);
jb_frame.buffer = frames[i].buf;
jb_frame.pt = (pj_uint8_t)hdr->pt;
jb_frame.seq = ntohs(hdr->seq);
jb_frame.size = frames[i].size;
jb_frame.ts = ntohl(hdr->ts) + samples_per_frame*i;
jb_frame.type = PJMEDIA_JB_FT_NORMAL_FRAME;
status = pjmedia_jb2_put_frame(stream->jb, &jb_frame);
if (status != PJ_SUCCESS) {
LOGERR_((stream->port.info.name.ptr, "Jitter buffer put() error",
status));
}
}
}
pj_mutex_unlock( stream->jb_mutex );
pj_mutex_unlock( stream->jb_mutex );
/* Check if now is the time to transmit RTCP SR/RR report.
* We only do this when stream direction is "decoding only",
@ -1057,12 +999,6 @@ static void on_rx_rtp( void *data,
if (stream->dir == PJMEDIA_DIR_DECODING) {
check_tx_rtcp(stream, pj_ntohl(hdr->ts));
}
if (status != 0) {
LOGERR_((stream->port.info.name.ptr, "Jitter buffer put() error",
status));
return;
}
}
@ -1160,8 +1096,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
enum { M = 32 };
pjmedia_stream *stream;
pj_str_t name;
unsigned jb_init, jb_max, jb_min_pre, jb_max_pre;
pj_status_t status;
pjmedia_jb2_setting jb_setting;
pjmedia_jb2_cb jb_cb;
PJ_ASSERT_RETURN(pool && info && p_stream, PJ_EINVAL);
@ -1315,39 +1252,28 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
/* Init jitter buffer parameters: */
if (info->jb_max > 0)
jb_max = info->jb_max;
else
jb_max = 360 / stream->codec_param.info.frm_ptime;
if (info->jb_min_pre >= 0)
jb_min_pre = info->jb_min_pre;
else
jb_min_pre = 60 / stream->codec_param.info.frm_ptime;
if (info->jb_max_pre > 0)
jb_max_pre = info->jb_max_pre;
else
jb_max_pre = 240 / stream->codec_param.info.frm_ptime;
if (info->jb_init >= 0)
jb_init = info->jb_init;
else
jb_init = (jb_min_pre + jb_max_pre) / 2;
pj_bzero(&jb_setting, sizeof(jb_setting));
jb_setting.samples_per_frame = info->fmt.clock_rate *
stream->codec_param.info.frm_ptime /
1000;
jb_setting.frame_size = jb_setting.samples_per_frame *
stream->codec_param.info.pcm_bits_per_sample / 8;
jb_setting.max_frames = 0;
pj_bzero(&jb_cb, sizeof(jb_cb));
jb_cb.decode = &jb_decode;
jb_cb.plc = &jb_plc;
jb_cb.cng = NULL; //&jb_cng;
jb_cb.user_data = stream;
/* Create jitter buffer */
status = pjmedia_jbuf_create(pool, &stream->port.info.name,
stream->frame_size,
stream->codec_param.info.frm_ptime,
jb_max, &stream->jb);
status = pjmedia_jb2_create(pool, &stream->port.info.name, &jb_setting,
&jb_cb, &stream->jb);
if (status != PJ_SUCCESS)
goto err_cleanup;
/* 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,
@ -1416,6 +1342,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
stream->codec = NULL;
}
/* Destroy JB */
if (stream->jb) {
pjmedia_jb2_destroy(stream->jb);
stream->jb = NULL;
}
/* Free mutex */
if (stream->jb_mutex) {
@ -1507,7 +1439,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream,
/* Also reset jitter buffer */
pj_mutex_lock( stream->jb_mutex );
pjmedia_jbuf_reset(stream->jb);
pjmedia_jb2_reset(stream->jb);
pj_mutex_unlock( stream->jb_mutex );
PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused"));

View File

@ -0,0 +1,197 @@
/* $Id$ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <ctype.h>
#include <pjmedia/jbuf2.h>
#include <pj/pool.h>
#pragma warning(disable: 4996)
#define REPORT
#define PRINT_COMMENT
#define SAMPLES_PER_FRAME 160
#define FRAME_SIZE (SAMPLES_PER_FRAME*2)
int jbuf2_main(pj_pool_factory *pf)
{
pjmedia_jb2_t *jb;
pjmedia_jb2_setting jb_setting;
pjmedia_jb2_cb jb_cb;
pjmedia_jb2_frame jb_get_frame;
pjmedia_jb2_frame jb_put_frame;
pjmedia_jb2_state jb_last_state;
pjmedia_jb2_state jb_state;
pjmedia_jb2_stat jb_stat;
FILE *input = fopen("..\\bin\\JB2TEST.DAT", "rt");
char line[1024*128], *p;
pj_pool_t *pool;
pj_status_t status;
char dummy[640];
pj_init();
pool = pj_pool_create(pf, "JBPOOL", 256*16, 256*16, NULL);
jb_setting.max_frames = 0;
jb_setting.samples_per_frame = SAMPLES_PER_FRAME;
jb_setting.frame_size = FRAME_SIZE;
pj_bzero(&jb_cb, sizeof(jb_cb));
pj_bzero(&jb_get_frame, sizeof(jb_get_frame));
pj_bzero(&jb_put_frame, sizeof(jb_put_frame));
pjmedia_jb2_create(pool, NULL, &jb_setting, &jb_cb, &jb);
jb_put_frame.type = PJMEDIA_JB_FT_NORMAL_RAW_FRAME;
jb_put_frame.size = jb_setting.frame_size;
jb_put_frame.buffer = dummy;
jb_get_frame.buffer = dummy;
while ((p=fgets(line, sizeof(line), input)) != NULL) {
int i;
while (*p && isspace(*p))
++p;
if (!*p)
continue;
/* RTrim */
i = strlen(p);
while (--i >= 0 && p[i] == '\r' || p[i] == '\n' || p[i] == ' ')
p[i] = '\0';
if (*p == '#') {
#ifdef PRINT_COMMENT
printf("%s\n", p+1);
#endif
continue;
}
/* ignored line */
if (!*p || *p == ';') {
if (*p && *(p+1)==';')
break;
continue;
}
pjmedia_jb2_reset(jb);
pj_bzero(&jb_last_state, sizeof(jb_last_state));
pj_bzero(&jb_put_frame, sizeof(jb_put_frame));
jb_put_frame.type = PJMEDIA_JB_FT_NORMAL_RAW_FRAME;
jb_put_frame.size = jb_setting.frame_size;
jb_put_frame.buffer = dummy;
while (*p) {
int c;
unsigned seq = 0;
c = *p++;
if (isspace(c))
continue;
if (c == '/') {
char *end;
printf("/*");
do {
putchar(*++p);
} while (*p != '/');
putchar('\n');
c = *++p;
end = p;
}
if (isspace(c))
continue;
switch (toupper(c)) {
case 'G': /* get */
printf("G");
jb_get_frame.size = jb_setting.frame_size;
status = pjmedia_jb2_get_frame(jb, &jb_get_frame);
break;
case 'P': /* put */
printf("P");
status = pjmedia_jb2_put_frame(jb, &jb_put_frame);
jb_put_frame.ts += jb_setting.samples_per_frame;
jb_put_frame.seq += 1;
break;
case 'L': /* loss */
printf("L");
jb_put_frame.ts += jb_setting.samples_per_frame;
jb_put_frame.seq += 1;
break;
default:
printf("Unknown character '%c'\n", c);
break;
}
pjmedia_jb2_get_state(jb, &jb_state);
if (jb_state.drift != jb_last_state.drift
|| jb_state.level != jb_last_state.level
|| jb_state.opt_size != jb_last_state.opt_size
)
{
printf("\n");
printf("drift:%3d/%d ", jb_state.drift, jb_state.drift_span);
printf("level:%3d ", jb_state.level);
printf("cur_size:%5d ", jb_state.cur_size);
printf("opt_size:%5d ", jb_state.opt_size);
printf("\n");
jb_last_state = jb_state;
}
}
#ifdef REPORT
pjmedia_jb2_get_state(jb, &jb_state);
printf("\nStop condition:\n");
printf("drift:%3d/%d ", jb_state.drift, jb_state.drift_span);
printf("level:%3d ", jb_state.level);
printf("cur_size:%5d ", jb_state.cur_size);
printf("opt_size:%5d ", jb_state.opt_size);
printf("frame_cnt:%d ", jb_state.frame_cnt);
printf("\n");
pjmedia_jb2_get_stat(jb, &jb_stat);
printf("lost\t\t = %d\n", jb_stat.lost);
printf("ooo\t\t = %d\n", jb_stat.ooo);
printf("full\t\t = %d\n", jb_stat.full);
printf("empty\t\t = %d\n", jb_stat.empty);
printf("out\t\t = %d\n", jb_stat.out);
printf("in\t\t = %d\n", jb_stat.in);
printf("max_size\t = %d\n", jb_stat.max_size);
printf("max_comp\t = %d\n", jb_stat.max_comp);
printf("max_drift\t = %d/%d\n", jb_stat.max_drift,
jb_stat.max_drift_span);
#endif
}
if (input != stdin)
fclose(input);
pj_pool_release(pool);
return 0;
}

View File

@ -35,7 +35,7 @@ pj_pool_factory *mem;
void app_perror(pj_status_t status, const char *msg)
{
char errbuf[PJMEDIA_ERR_MSG_SIZE];
char errbuf[PJ_ERR_MSG_SIZE];
pjmedia_strerror(status, errbuf, sizeof(errbuf));
@ -50,15 +50,17 @@ int test_main(void)
pj_init();
pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
pj_log_set_decor(PJ_LOG_HAS_NEWLINE);
pj_log_set_decor(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC);
pj_log_set_level(4);
mem = &caching_pool.factory;
DO_TEST(sdp_neg_test());
//DO_TEST(sdp_neg_test());
//sdp_test (&caching_pool.factory);
//rtp_test(&caching_pool.factory);
//session_test (&caching_pool.factory);
//jbuf_main(&caching_pool.factory);
DO_TEST(jbuf2_main(&caching_pool.factory));
PJ_LOG(3,(THIS_FILE," "));

View File

@ -27,6 +27,7 @@ int session_test(void);
int rtp_test(void);
int sdp_test(void);
int jbuf_main(void);
int jbuf2_main(pj_pool_factory *pf);
int sdp_neg_test(void);
extern pj_pool_factory *mem;

View File

@ -0,0 +1,451 @@
/* $Id: sndtest.c 1663 2008-01-04 18:00:11Z bennylp $ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia.h>
#include <pjlib.h>
#include <pjlib-util.h>
#include <pjmedia/jbuf2.h>
#include <stdlib.h> /* atoi() */
#include <stdio.h>
#define THIS_FILE "jbtest.c"
struct test_data {
unsigned clock_rate;
unsigned samples_per_frame;
pjmedia_jb2_t *jb;
pjmedia_port *read_port;
pjmedia_port *write_port;
pjmedia_resample *resampler;
pj_lock_t *mutex;
pj_int16_t seq;
unsigned ts;
};
static const char *desc =
THIS_FILE "\n"
" \n"
" PURPOSE: \n"
" Test drift compensation of jbuf2 with real sound device.\n"
" \n"
" USAGE: \n"
" sndtest --help \n"
" sndtest [options] wavfile \n"
" \n"
" options: \n"
" --id=ID -i Use device ID (default is -1) \n"
" --rate=HZ -r Set test clock rate (default=8000)\n"
" --drift=N -d Set clock drift (-2000 < N < 2000)\n"
" --verbose -v Show verbose result \n"
" --help -h Show this screen \n"
;
static void app_perror(const char *title, pj_status_t status)
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
printf( "%s: %s (err=%d)\n",
title, errmsg, status);
}
static void enum_devices(void)
{
int i, count;
count = pjmedia_snd_get_dev_count();
if (count == 0) {
PJ_LOG(3,(THIS_FILE, "No devices found"));
return;
}
PJ_LOG(3,(THIS_FILE, "Found %d devices:", count));
for (i=0; i<count; ++i) {
const pjmedia_snd_dev_info *info;
info = pjmedia_snd_get_dev_info(i);
pj_assert(info != NULL);
PJ_LOG(3,(THIS_FILE," %d: %s (capture=%d, playback=%d)",
i, info->name, info->input_count, info->output_count));
}
}
/*
* Callback to be called for each clock ticks.
*/
static void clock_callback(const pj_timestamp *ts, void *user_data)
{
struct test_data *test_data = user_data;
pjmedia_jb2_frame f;
pjmedia_frame f_;
pj_status_t status;
char buf[5*1024];
PJ_UNUSED_ARG(ts);
PJ_ASSERT_ON_FAIL(test_data, return);
f_.buf = buf;
f_.size = test_data->read_port->info.bytes_per_frame;
status = pjmedia_port_get_frame(test_data->read_port, &f_);
if (status != PJ_SUCCESS) {
app_perror("Failed to get frame from file", status);
return;
}
test_data->seq++;
test_data->ts += test_data->read_port->info.samples_per_frame;
f.buffer = f_.buf;
f.pt = 0;
f.seq = test_data->seq;
f.ts = test_data->ts;
f.size = f_.size;
f.type = PJMEDIA_JB_FT_NORMAL_RAW_FRAME;
pj_lock_acquire(test_data->mutex);
pjmedia_jb2_put_frame(test_data->jb, &f);
pj_lock_release(test_data->mutex);
}
static pj_status_t play_cb(void *user_data, pj_uint32_t timestamp,
void *output, unsigned size)
{
struct test_data *test_data = user_data;
pjmedia_jb2_frame f;
pjmedia_frame f_;
pj_status_t status;
char buf[5*1024];
PJ_UNUSED_ARG(timestamp);
PJ_ASSERT_RETURN(test_data, PJ_EINVAL);
f.buffer = test_data->resampler? buf : output;
f.size = sizeof(buf);
pj_lock_acquire(test_data->mutex);
pjmedia_jb2_get_frame(test_data->jb, &f);
pj_lock_release(test_data->mutex);
if (test_data->resampler)
pjmedia_resample_run(test_data->resampler, f.buffer, output);
f_.buf = output;
f_.size = size;
f_.type = PJMEDIA_FRAME_TYPE_AUDIO;
f_.timestamp.u64 = f.ts;
status = pjmedia_port_put_frame(test_data->write_port, &f_);
if (status != PJ_SUCCESS) {
app_perror("Failed to put frame to file", status);
return status;
}
return PJ_SUCCESS;
}
static int perform_test(pj_pool_t *pool, const char *inputfile, int dev_id,
unsigned clock_rate, int drift)
{
const int PTIME = 20;
pj_status_t status = PJ_SUCCESS;
pjmedia_snd_stream *strm;
pjmedia_clock *clock;
struct test_data test_data;
pjmedia_snd_stream_info si;
unsigned samples_per_frame;
pjmedia_jb2_setting jb_setting;
pjmedia_jb2_cb jb_cb;
pjmedia_jb2_state jb_state;
pjmedia_jb2_stat jb_stat;
char s[8];
PJ_ASSERT_RETURN(pool && inputfile, PJ_EINVAL);
samples_per_frame = PTIME * clock_rate / 1000;
/*
* Init test parameters
*/
pj_bzero(&test_data, sizeof(test_data));
test_data.clock_rate = clock_rate;
test_data.samples_per_frame = samples_per_frame;
pj_lock_create_recursive_mutex(pool, "sndtest", &test_data.mutex);
/* Create WAV player port */
status = pjmedia_wav_player_port_create( pool, /* memory pool */
inputfile,/* file to play */
PTIME, /* ptime. */
0, /* flags */
0, /* default buffer */
&test_data.read_port
/* returned port */
);
if (status != PJ_SUCCESS) {
app_perror("Unable to create WAV player", status);
return 1;
}
if (test_data.read_port->info.channel_count != 1) {
app_perror("WAV file not mono.", status);
return PJ_EINVAL;
}
/* Init JB setting */
pj_bzero(&jb_setting, sizeof(jb_setting));
jb_setting.max_frames = 0;
jb_setting.samples_per_frame = test_data.read_port->info.samples_per_frame;
jb_setting.frame_size = test_data.read_port->info.bytes_per_frame;
/* Init JB callback */
pj_bzero(&jb_cb, sizeof(jb_cb));
/* Create jitter buffer */
status = pjmedia_jb2_create(pool, NULL, &jb_setting, &jb_cb, &test_data.jb);
if (status != PJ_SUCCESS) {
app_perror("Unable to create jitter buffer", status);
return status;
}
/* Create the resample port when needed. */
if (test_data.read_port->info.clock_rate != clock_rate) {
status = pjmedia_resample_create( pool, PJ_TRUE, PJ_FALSE, 1,
test_data.read_port->info.clock_rate,
clock_rate,
jb_setting.samples_per_frame,
&test_data.resampler);
if (status != PJ_SUCCESS) {
app_perror("Unable to create resample port", status);
return 1;
}
} else {
test_data.resampler = NULL;
}
/* Create WAV writer port */
status = pjmedia_wav_writer_port_create( pool, "jbtestout.wav",
clock_rate, 1,
samples_per_frame,
16, 0, 0,
&test_data.write_port);
if (status != PJ_SUCCESS) {
app_perror("Unable to create WAV writer", status);
return 1;
}
/* Create media clock */
status = pjmedia_clock_create(pool, clock_rate + drift, samples_per_frame,
0, &clock_callback, &test_data, &clock);
if (status != PJ_SUCCESS) {
app_perror("Unable to create clock", status);
return status;
}
/*
* Open device.
*/
status = pjmedia_snd_open_player( dev_id, clock_rate, 1,
samples_per_frame, 16, &play_cb,
&test_data, &strm);
if (status != PJ_SUCCESS) {
app_perror("Unable to open device for playing", status);
return status;
}
pjmedia_snd_stream_get_info(strm, &si);
if (si.play_id >= 0) {
PJ_LOG(3,(THIS_FILE, "Testing playback device %s:",
pjmedia_snd_get_dev_info(si.play_id)->name));
PJ_LOG(4,(THIS_FILE, "Channel: %d clock: %dhz samples/frame: %d",
si.channel_count, si.clock_rate, si.samples_per_frame));
}
/*
* Start the stream.
*/
status = pjmedia_snd_stream_start(strm);
if (status != PJ_SUCCESS) {
app_perror("Unable to start capture stream", status);
return status;
}
/*
* Start the clock.
*/
status = pjmedia_clock_start(clock);
if (status != PJ_SUCCESS) {
app_perror("Unable to start clock", status);
return status;
}
/* Begin gather data */
puts("\nTest is running...");
puts("\nPress <ENTER> to quit");
fgets(s, sizeof(s), stdin);
/*
* Print results.
*/
pjmedia_jb2_get_state(test_data.jb, &jb_state);
printf("\nStop condition:\n");
printf("drift:%3d/%d ", jb_state.drift, jb_state.drift_span);
printf("level:%3d ", jb_state.level);
printf("cur_size:%5d ", jb_state.cur_size);
printf("opt_size:%5d ", jb_state.opt_size);
printf("frame_cnt:%d ", jb_state.frame_cnt);
printf("\n");
pjmedia_jb2_get_stat(test_data.jb, &jb_stat);
printf("lost\t\t = %d\n", jb_stat.lost);
printf("ooo\t\t = %d\n", jb_stat.ooo);
printf("full\t\t = %d\n", jb_stat.full);
printf("empty\t\t = %d\n", jb_stat.empty);
printf("out\t\t = %d\n", jb_stat.out);
printf("in\t\t = %d\n", jb_stat.in);
printf("max_size\t = %d\n", jb_stat.max_size);
printf("max_comp\t = %d\n", jb_stat.max_comp);
printf("max_drift\t = %d/%d\n", jb_stat.max_drift,
jb_stat.max_drift_span);
/* Close stream */
pjmedia_snd_stream_close(strm);
/* Destroy clock */
pjmedia_clock_destroy(clock);
/* Destroy resample */
if (test_data.resampler)
pjmedia_resample_destroy(test_data.resampler);
/* Destroy file port */
pjmedia_port_destroy(test_data.read_port);
pjmedia_port_destroy(test_data.write_port);
/* Destroy jitter buffer */
pjmedia_jb2_destroy(test_data.jb);
return PJ_SUCCESS;
}
int main(int argc, char *argv[])
{
pj_caching_pool cp;
pj_pool_t *pool;
pjmedia_endpt *med_endpt;
int id = -1, verbose = 0;
int clock_rate = 8000;
char *inputfile = NULL;
int drift = 0;
struct pj_getopt_option long_options[] = {
{ "id", 1, 0, 'i' },
{ "rate", 1, 0, 'r' },
{ "drift", 1, 0, 'd' },
{ "verbose", 0, 0, 'v' },
{ "help", 0, 0, 'h' },
{ NULL, 0, 0, 0 }
};
int c, option_index;
pj_status_t status;
/* Init pjlib */
status = pj_init();
PJ_ASSERT_RETURN(status==PJ_SUCCESS, 1);
/* Must create a pool factory before we can allocate any memory. */
pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
/* Also create pool for misc purposes */
pool = pj_pool_create(&cp.factory, "jbuf2test", 1000, 1000, NULL);
/*
* Initialize media endpoint.
* This will implicitly initialize PJMEDIA too.
*/
status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
/* Print devices */
enum_devices();
/* Parse options */
pj_optind = 0;
while((c=pj_getopt_long(argc,argv, "i:r:f:n:vh",
long_options, &option_index))!=-1)
{
switch (c) {
case 'i':
id = atoi(pj_optarg);
break;
case 'r':
clock_rate = atoi(pj_optarg);
break;
case 'd':
drift = atoi(pj_optarg);
break;
case 'v':
verbose = 1;
break;
case 'h':
puts(desc);
return 0;
break;
default:
printf("Error: invalid options %s\n", argv[pj_optind-1]);
puts(desc);
return 1;
}
}
if ((argc - pj_optind) != 1) {
printf("Error: invalid options\n");
puts(desc);
return 1;
}
inputfile = argv[argc - 1];
if (!verbose)
pj_log_set_level(3);
else
pj_log_set_level(4);
status = perform_test(pool, inputfile, id, clock_rate, drift);
pjmedia_endpt_destroy(med_endpt);
pj_pool_release(pool);
pj_caching_pool_destroy(&cp);
pj_shutdown();
return status == PJ_SUCCESS ? 0 : 1;
}