Beispiel #1
0
static void clock_cb(const pj_timestamp *ts, void *user_data)
{
    play_file_data *play_file = (play_file_data*)user_data;
    pjmedia_frame read_frame, write_frame;
    pj_status_t status;

    PJ_UNUSED_ARG(ts);

    /* Read frame from file */
    read_frame.buf = play_file->read_buf;
    read_frame.size = play_file->read_buf_size;
    pjmedia_port_get_frame(play_file->play_port, &read_frame);

    /* Decode frame, if needed */
    if (play_file->decoder) {
	pjmedia_vid_codec *decoder = play_file->decoder;

	write_frame.buf = play_file->dec_buf;
	write_frame.size = play_file->dec_buf_size;
	status = pjmedia_vid_codec_decode(decoder, 1, &read_frame,
	                                  write_frame.size, &write_frame);
	if (status != PJ_SUCCESS)
	    return;
    } else {
	write_frame = read_frame;
    }

    /* Display frame locally */
    if (play_file->renderer)
	pjmedia_port_put_frame(play_file->renderer, &write_frame);

    /* Send frame */
    pjmedia_port_put_frame(play_file->stream_port, &write_frame);
}
Beispiel #2
0
static pj_status_t plc_put_frame( pjmedia_port *this_port,
				 const pjmedia_frame *frame)
{
    pj_status_t status;
    int i;
    struct plc_port *plcp = (struct plc_port*)this_port;
    PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL);
    PJ_LOG(6, (THIS_FILE, "packet: sz=%d ts=%llu",
                frame->size/sizeof(pj_uint16_t), frame->timestamp.u64));

    if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) {
        em_plc_mode mode = plcp->frame.type == PJMEDIA_FRAME_TYPE_NONE ? \
            EM_PLC_EMPTY : plcp->plc_mode;
        switch (mode) {
            case EM_PLC_SMART:
            for (i=0; i<plcp->fpp; i++){
                status = plcp->codec->op->recover(plcp->codec, BUF_SIZE, &plcp->frame);
                plcp->frame.timestamp.u64 = 0;
                if (status != PJ_SUCCESS) return status;
                status = pjmedia_port_put_frame(plcp->dn_port, &plcp->frame);
                if (status != PJ_SUCCESS) return status;
            }
            break;
            case EM_PLC_REPEAT:
            for (i=0; i<plcp->fpp; i++){
                plcp->frame.timestamp.u64 = 0;
                status = pjmedia_port_put_frame(plcp->dn_port, &plcp->frame);
                if (status != PJ_SUCCESS) return status;
            }
            break;
            case EM_PLC_NOISE:
            for (i=0; i<plcp->fpp; i++){
                int j=0;
                plcp->frame.size = plcp->dn_port->info.bytes_per_frame;
                plcp->frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
                plcp->frame.timestamp.u64 = 0;
                for  (j=0; j<plcp->frame.size; j++)
                    ((char*)plcp->frame.buf)[j] = ((char)pj_rand()) >> 5;
                status = pjmedia_port_put_frame(plcp->dn_port, &plcp->frame);
                if (status != PJ_SUCCESS) return status;
            }
            default:
            for (i=0; i<plcp->fpp; i++){
                plcp->frame.size = plcp->dn_port->info.bytes_per_frame;
                plcp->frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
                plcp->frame.timestamp.u64 = 0;
                pj_bzero(plcp->frame.buf, plcp->frame.size);
                status = pjmedia_port_put_frame(plcp->dn_port, &plcp->frame);
            }
        }
        plcp->stats.lost++;
    } else {
Beispiel #3
0
/*
 * The callback called by sound recorder when it has finished capturing a
 * frame.
 */
static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
{
    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
    pjmedia_port *port;

    pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp);

    port = snd_port->port;
    if (port == NULL)
	return PJ_SUCCESS;

    /* Cancel echo */
    if (snd_port->ec_state && !snd_port->ec_suspended) {
	pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0);
    }

#ifdef MY_SAVE_FILE_BEFORE_SPEEX
	fwrite(frame->buf,1,frame->size,fd_bfspeex);
#endif
    if (pjmedia_audio_use_speex_ns == PJ_TRUE){
	speex_preprocess_run(snd_port->speex_st, frame->buf);
        //PJ_LOG(5,(THIS_FILE, "frame->size:%d",frame->size));
    }
#ifdef MY_SAVE_FILE_SEND
	fwrite(frame->buf,1,frame->size,fd_save);
#endif

    pjmedia_port_put_frame(port, frame);


    return PJ_SUCCESS;
}
Beispiel #4
0
static pj_status_t resample_put_frame(pjmedia_port *this_port,
				      const pjmedia_frame *frame)
{
    struct resample_port *rport = (struct resample_port*) this_port;
    pjmedia_frame downstream_frame;

    /* Return if we don't have downstream port. */
    if (rport->dn_port == NULL) {
	return PJ_SUCCESS;
    }

    if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
	pjmedia_resample_run( rport->resample_put, 
			      (const pj_int16_t*) frame->buf, 
			      rport->put_buf);

	downstream_frame.buf = rport->put_buf;
	downstream_frame.size = rport->dn_port->info.bytes_per_frame;
    } else {
	downstream_frame.buf = frame->buf;
	downstream_frame.size = frame->size;
    }

    downstream_frame.type = frame->type;
    downstream_frame.timestamp.u64 = frame->timestamp.u64;

    return pjmedia_port_put_frame( rport->dn_port, &downstream_frame );
}
Beispiel #5
0
void pjs_audio_combine::media_sync(pjs_media_sync_param_t param)
{
	if (m_ready)
	{
		switch (param)
		{
			case PJMS_SYNC_BEFORE:
			{
				if (!m_oneway && m_putbuffer)
				{
					pjmedia_frame frame;
					frame.type = PJMEDIA_FRAME_TYPE_NONE;
					frame.buf = m_putbuffer;
					frame.size = m_buffer_size;
					get_frame(&frame);
					if (frame.type != PJMEDIA_FRAME_TYPE_NONE)
						pjmedia_port_put_frame(m_combiner, &frame);
				}
				break;
			}
			case PJMS_SYNC_AFTER:
			{
				pjmedia_frame frame;
				frame.type = PJMEDIA_FRAME_TYPE_NONE;
				frame.buf = m_getbuffer;
				frame.size = m_buffer_size;
				if (pjmedia_port_get_frame(m_combiner, &frame) == PJ_SUCCESS)
					put_frame(&frame);
				break;
			}
		}

	}
}
/*
 * The callback called by sound recorder when it has finished capturing a
 * frame.
 */
static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
{
    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
    pjmedia_port *port;

    pjmedia_clock_src_update(&snd_port->cap_clocksrc, &frame->timestamp);

    /* Invoke preview callback */
    if (snd_port->on_rec_frame)
	(*snd_port->on_rec_frame)(snd_port->user_data, frame);

    port = snd_port->port;
    if (port == NULL)
	return PJ_SUCCESS;

    /* Cancel echo */
    if (snd_port->ec_state && !snd_port->ec_suspended) {
	pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0);
    }

    pjmedia_port_put_frame(port, frame);


    return PJ_SUCCESS;
}
Beispiel #7
0
static void run_one_frame(pjmedia_port *src, pjmedia_port *dst,
			  pj_bool_t *has_frame)
{
    pjmedia_frame frame;
    pj_status_t status;

    pj_bzero(&frame, sizeof(frame));
    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
    frame.buf = g_app.framebuf;
    frame.size = PJMEDIA_PIA_SPF(&dst->info) * 2;
    
    status = pjmedia_port_get_frame(src, &frame);
    pj_assert(status == PJ_SUCCESS);

    if (status!= PJ_SUCCESS || frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
	frame.buf = g_app.framebuf;
	pjmedia_zero_samples(g_app.framebuf, PJMEDIA_PIA_SPF(&src->info));
	frame.size = PJMEDIA_PIA_SPF(&src->info) * 2;
	if (has_frame)
	    *has_frame = PJ_FALSE;
    } else {
	if (has_frame)
	    *has_frame = PJ_TRUE;
    }


    status = pjmedia_port_put_frame(dst, &frame);
    pj_assert(status == PJ_SUCCESS);
}
static pj_status_t ec_put_frame( pjmedia_port *this_port, 
				 pjmedia_frame *frame)
{
    struct ec *ec = (struct ec*)this_port;

    PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL);

    if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) {
	return pjmedia_port_put_frame(ec->dn_port, frame);
    }

    PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
		     PJ_EINVAL);

    pjmedia_echo_capture(ec->ec, (pj_int16_t*)frame->buf, 0);

    return pjmedia_port_put_frame(ec->dn_port, frame);
}
Beispiel #9
0
static pj_status_t ec_put_frame( pjmedia_port *this_port, 
				 const pjmedia_frame *frame)
{
    struct ec *ec = (struct ec*)this_port;

    PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL);

    if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) {
	return pjmedia_port_put_frame(ec->dn_port, frame);
    }

    PJ_ASSERT_RETURN(frame->size == this_port->info.samples_per_frame * 2,
		     PJ_EINVAL);

    pjmedia_echo_capture(ec->ec, frame->buf, 0);

    return pjmedia_port_put_frame(ec->dn_port, frame);
}
Beispiel #10
0
/*
 * The callback called by sound recorder when it has finished capturing a
 * frame. This version is for non-PCM data.
 */
static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
{
    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
    pjmedia_port *port;

    port = snd_port->port;
    if (port == NULL)
	return PJ_SUCCESS;

    pjmedia_port_put_frame(port, frame);

    return PJ_SUCCESS;
}
Beispiel #11
0
/*
 * Callback to be called for each clock ticks.
 */
static void clock_callback(const pj_timestamp *ts, void *user_data)
{
    pjmedia_master_port *m = (pjmedia_master_port*) user_data;
    pjmedia_frame frame;
    pj_status_t status;

    
    /* Lock access to ports. */
    pj_lock_acquire(m->lock);

    /* Get frame from upstream port and pass it to downstream port */
    pj_bzero(&frame, sizeof(frame));
    frame.buf = m->buff;
    frame.size = m->buff_size;
    frame.timestamp.u64 = ts->u64;

    status = pjmedia_port_get_frame(m->u_port, &frame);
    if (status != PJ_SUCCESS)
	frame.type = PJMEDIA_FRAME_TYPE_NONE;

    status = pjmedia_port_put_frame(m->d_port, &frame);

    /* Get frame from downstream port and pass it to upstream port */
    pj_bzero(&frame, sizeof(frame));
    frame.buf = m->buff;
    frame.size = m->buff_size;
    frame.timestamp.u64 = ts->u64;

    status = pjmedia_port_get_frame(m->d_port, &frame);
    if (status != PJ_SUCCESS)
	frame.type = PJMEDIA_FRAME_TYPE_NONE;

    status = pjmedia_port_put_frame(m->u_port, &frame);

    /* Release lock */
    pj_lock_release(m->lock);
}
/*
 * The callback called by sound recorder when it has finished capturing a
 * frame. This version is for non-PCM data.
 */
static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
{
    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
    pjmedia_port *port;

    /* Invoke preview callback */
    if (snd_port->on_rec_frame)
	(*snd_port->on_rec_frame)(snd_port->user_data, frame);

    port = snd_port->port;
    if (port == NULL)
	return PJ_SUCCESS;

    pjmedia_port_put_frame(port, frame);

    return PJ_SUCCESS;
}
Beispiel #13
0
/*
 * The callback called by sound recorder when it has finished capturing a
 * frame.
 */
static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
{
    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
    pjmedia_port *port;

    port = snd_port->port;
    if (port == NULL)
	return PJ_SUCCESS;

    /* Cancel echo */
    if (snd_port->ec_state && !snd_port->ec_suspended) {
	pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0);
    }

    pjmedia_port_put_frame(port, frame);

    return PJ_SUCCESS;
}
Beispiel #14
0
static pj_status_t stereo_put_frame(pjmedia_port *this_port,
				    pjmedia_frame *frame)
{
    struct stereo_port *sport = (struct stereo_port*) this_port;
    const pjmedia_audio_format_detail *s_afd, *dn_afd;
    pjmedia_frame tmp_frame;

    /* Return if we don't have downstream port. */
    if (sport->dn_port == NULL) {
	return PJ_SUCCESS;
    }

    s_afd = pjmedia_format_get_audio_format_detail(&this_port->info.fmt, 1);
    dn_afd = pjmedia_format_get_audio_format_detail(&sport->dn_port->info.fmt,
						    1);

    if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
	tmp_frame.buf = sport->put_buf;
	if (dn_afd->channel_count == 1) {
	    pjmedia_convert_channel_nto1((pj_int16_t*)tmp_frame.buf, 
					 (const pj_int16_t*)frame->buf,
					 s_afd->channel_count,
					 PJMEDIA_AFD_SPF(s_afd),
					 (sport->options & PJMEDIA_STEREO_MIX),
					 0);
	} else {
	    pjmedia_convert_channel_1ton((pj_int16_t*)tmp_frame.buf, 
					 (const pj_int16_t*)frame->buf,
					 dn_afd->channel_count,
					 PJMEDIA_AFD_SPF(s_afd),
					 sport->options);
	}
	tmp_frame.size = PJMEDIA_AFD_AVG_FSZ(dn_afd);
    } else {
	tmp_frame.buf = frame->buf;
	tmp_frame.size = frame->size;
    }

    tmp_frame.type = frame->type;
    tmp_frame.timestamp.u64 = frame->timestamp.u64;

    return pjmedia_port_put_frame( sport->dn_port, &tmp_frame );
}
Beispiel #15
0
static pj_status_t stereo_put_frame(pjmedia_port *this_port,
				    const pjmedia_frame *frame)
{
    struct stereo_port *sport = (struct stereo_port*) this_port;
    pjmedia_frame tmp_frame;

    /* Return if we don't have downstream port. */
    if (sport->dn_port == NULL) {
	return PJ_SUCCESS;
    }

    if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
	tmp_frame.buf = sport->put_buf;
	if (sport->dn_port->info.channel_count == 1) {
	    pjmedia_convert_channel_nto1((pj_int16_t*)tmp_frame.buf, 
					 (const pj_int16_t*)frame->buf,
					 sport->base.info.channel_count, 
					 sport->base.info.samples_per_frame, 
					 (sport->options & PJMEDIA_STEREO_MIX),
					 0);
	} else {
	    pjmedia_convert_channel_1ton((pj_int16_t*)tmp_frame.buf, 
					 (const pj_int16_t*)frame->buf,
					 sport->dn_port->info.channel_count, 
					 sport->base.info.samples_per_frame,
					 sport->options);
	}
	tmp_frame.size = sport->dn_port->info.bytes_per_frame;
    } else {
	tmp_frame.buf = frame->buf;
	tmp_frame.size = frame->size;
    }

    tmp_frame.type = frame->type;
    tmp_frame.timestamp.u64 = frame->timestamp.u64;

    return pjmedia_port_put_frame( sport->dn_port, &tmp_frame );
}
static pj_status_t lb_push_frame(struct leaky_bucket_port *lb)
{
    struct leaky_bucket_item *fst = lb->first_item;
    pj_status_t status;
    if (fst == NULL){
        return PJ_SUCCESS;
    }
    PJ_LOG(6, (THIS_FILE, "push frame to dn port: sz=%u, ts=%llu",
                fst->frame.size/sizeof(pj_uint16_t), fst->frame.timestamp.u64));
    status = pjmedia_port_put_frame(lb->dn_port, &fst->frame);
    if (status != PJ_SUCCESS)
        return status;
    if (fst->frame.type == PJMEDIA_FRAME_TYPE_AUDIO){
        lb->items --;
    } else {
    }
    if (fst->next == fst) {
        lb->first_item = lb->last_item = NULL;
    } else {
        lb->first_item = fst->next;
        pj_list_erase(fst);
    }
    return PJ_SUCCESS;
}
Beispiel #17
0
static pj_status_t wav_rec_cb(void *user_data, pjmedia_frame *frame)
{
    return pjmedia_port_put_frame((pjmedia_port*)user_data, frame);
}
Beispiel #18
0
/*
 * main()
 */
int main(int argc, char *argv[])
{
    pj_caching_pool cp;
    pjmedia_endpt *med_endpt;
    pj_pool_t	  *pool;
    pjmedia_port  *wav_play;
    pjmedia_port  *wav_rec;
    pjmedia_port  *wav_out;
    pj_status_t status;
    pjmedia_echo_state *ec;
    pjmedia_frame play_frame, rec_frame;
    unsigned opt = 0;
    unsigned latency_ms = 25;
    unsigned tail_ms = TAIL_LENGTH;
    pj_timestamp t0, t1;
    int i, repeat=1, interactive=0, c;

    pj_optind = 0;
    while ((c=pj_getopt(argc, argv, "d:l:a:r:i")) !=-1) {
	switch (c) {
	case 'd':
	    latency_ms = atoi(pj_optarg);
	    if (latency_ms < 25) {
		puts("Invalid delay");
		puts(desc);
	    }
	    break;
	case 'l':
	    tail_ms = atoi(pj_optarg);
	    break;
	case 'a':
	    {
		int alg = atoi(pj_optarg);
		switch (alg) {
		case 0:
		    opt = 0;
		case 1:
		    opt = PJMEDIA_ECHO_SPEEX;
		    break;
		case 3:
		    opt = PJMEDIA_ECHO_SIMPLE;
		    break;
		default:
		    puts("Invalid algorithm");
		    puts(desc);
		    return 1;
		}
	    }
	    break;
	case 'r':
	    repeat = atoi(pj_optarg);
	    if (repeat < 1) {
		puts("Invalid repeat count");
		puts(desc);
		return 1;
	    }
	    break;
	case 'i':
	    interactive = 1;
	    break;
	}
    }

    if (argc - pj_optind != 3) {
	puts("Error: missing argument(s)");
	puts(desc);
	return 1;
    }

    /* Must init PJLIB first: */
    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);

    /* 
     * 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);

    /* Create memory pool for our file player */
    pool = pj_pool_create( &cp.factory,	    /* pool factory	    */
			   "wav",	    /* pool name.	    */
			   4000,	    /* init size	    */
			   4000,	    /* increment size	    */
			   NULL		    /* callback on error    */
			   );

    /* Open wav_play */
    status = pjmedia_wav_player_port_create(pool, argv[pj_optind], PTIME, 
					    PJMEDIA_FILE_NO_LOOP, 0, 
					    &wav_play);
    if (status != PJ_SUCCESS) {
	app_perror(THIS_FILE, "Error opening playback WAV file", status);
	return 1;
    }
    
    /* Open recorded wav */
    status = pjmedia_wav_player_port_create(pool, argv[pj_optind+1], PTIME, 
					    PJMEDIA_FILE_NO_LOOP, 0, 
					    &wav_rec);
    if (status != PJ_SUCCESS) {
	app_perror(THIS_FILE, "Error opening recorded WAV file", status);
	return 1;
    }

    /* play and rec WAVs must have the same clock rate */
    if (wav_play->info.clock_rate != wav_rec->info.clock_rate) {
	puts("Error: clock rate mismatch in the WAV files");
	return 1;
    }

    /* .. and channel count */
    if (wav_play->info.channel_count != wav_rec->info.channel_count) {
	puts("Error: clock rate mismatch in the WAV files");
	return 1;
    }

    /* Create output wav */
    status = pjmedia_wav_writer_port_create(pool, argv[pj_optind+2],
					    wav_play->info.clock_rate,
					    wav_play->info.channel_count,
					    wav_play->info.samples_per_frame,
					    wav_play->info.bits_per_sample,
					    0, 0, &wav_out);
    if (status != PJ_SUCCESS) {
	app_perror(THIS_FILE, "Error opening output WAV file", status);
	return 1;
    }

    /* Create echo canceller */
    status = pjmedia_echo_create2(pool, wav_play->info.clock_rate,
				  wav_play->info.channel_count,
				  wav_play->info.samples_per_frame,
				  tail_ms, latency_ms,
				  opt, &ec);
    if (status != PJ_SUCCESS) {
	app_perror(THIS_FILE, "Error creating EC", status);
	return 1;
    }


    /* Processing loop */
    play_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1);
    rec_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1);
    pj_get_timestamp(&t0);
    for (i=0; i < repeat; ++i) {
	for (;;) {
	    play_frame.size = wav_play->info.samples_per_frame << 1;
	    status = pjmedia_port_get_frame(wav_play, &play_frame);
	    if (status != PJ_SUCCESS)
		break;

	    status = pjmedia_echo_playback(ec, (short*)play_frame.buf);

	    rec_frame.size = wav_play->info.samples_per_frame << 1;
	    status = pjmedia_port_get_frame(wav_rec, &rec_frame);
	    if (status != PJ_SUCCESS)
		break;

	    status = pjmedia_echo_capture(ec, (short*)rec_frame.buf, 0);

	    //status = pjmedia_echo_cancel(ec, (short*)rec_frame.buf, 
	    //			     (short*)play_frame.buf, 0, NULL);

	    pjmedia_port_put_frame(wav_out, &rec_frame);
	}

	pjmedia_wav_player_port_set_pos(wav_play, 0);
	pjmedia_wav_player_port_set_pos(wav_rec, 0);
    }
    pj_get_timestamp(&t1);

    i = pjmedia_wav_writer_port_get_pos(wav_out) / sizeof(pj_int16_t) * 1000 / 
	(wav_out->info.clock_rate * wav_out->info.channel_count);
    PJ_LOG(3,(THIS_FILE, "Processed %3d.%03ds audio",
	      i / 1000, i % 1000));
    PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1)));

    /* Destroy file port(s) */
    status = pjmedia_port_destroy( wav_play );
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
    status = pjmedia_port_destroy( wav_rec );
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
    status = pjmedia_port_destroy( wav_out );
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);

    /* Destroy ec */
    pjmedia_echo_destroy(ec);

    /* Release application pool */
    pj_pool_release( pool );

    /* Destroy media endpoint. */
    pjmedia_endpt_destroy( med_endpt );

    /* Destroy pool factory */
    pj_caching_pool_destroy( &cp );

    /* Shutdown PJLIB */
    pj_shutdown();

    if (interactive) {
	char s[10], *dummy;
	puts("ENTER to quit");
	dummy = fgets(s, sizeof(s), stdin);
    }

    /* Done. */
    return 0;
}
Beispiel #19
0
static pj_status_t codec_put_frame(pjmedia_port *port,
                                   pjmedia_frame *frame)
{
    enum { MAX_PACKETS = 50 };
    codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata;
    pj_status_t status;
    pjmedia_vid_codec *codec = port_data->codec;
    unsigned enc_cnt = 0;
    pj_uint8_t *enc_buf;
    unsigned enc_size_left;
    pjmedia_frame enc_frames[MAX_PACKETS];
    pj_bool_t has_more = PJ_FALSE;

    enc_buf = port_data->enc_buf;
    enc_size_left = port_data->enc_buf_size;

    /*
     * Encode
     */
    enc_frames[enc_cnt].buf = enc_buf;
    enc_frames[enc_cnt].size = enc_size_left;

    status = pjmedia_vid_codec_encode_begin(codec, NULL, frame, enc_size_left,
                                            &enc_frames[enc_cnt], &has_more);
    if (status != PJ_SUCCESS) goto on_error;

    enc_buf += enc_frames[enc_cnt].size;
    enc_size_left -= enc_frames[enc_cnt].size;

    ++enc_cnt;
    while (has_more) {
        enc_frames[enc_cnt].buf = enc_buf;
        enc_frames[enc_cnt].size = enc_size_left;

        status = pjmedia_vid_codec_encode_more(codec, enc_size_left,
                                               &enc_frames[enc_cnt],
                                               &has_more);
        if (status != PJ_SUCCESS)
            break;

        enc_buf += enc_frames[enc_cnt].size;
        enc_size_left -= enc_frames[enc_cnt].size;

        ++enc_cnt;

        if (enc_cnt >= MAX_PACKETS) {
            assert(!"Too many packets!");
            break;
        }
    }

    /*
     * Decode
     */
    status = pjmedia_vid_codec_decode(codec, enc_cnt, enc_frames,
                                      frame->size, frame);
    if (status != PJ_SUCCESS) goto on_error;

    /* Display */
    status = pjmedia_port_put_frame(
                 pjmedia_vid_port_get_passive_port(port_data->rdr_port),
                 frame);
    if (status != PJ_SUCCESS) goto on_error;

    return PJ_SUCCESS;

on_error:
    pj_perror(3, THIS_FILE, status, "codec_put_frame() error");
    return status;
}
static pj_status_t sp_put_frame( pjmedia_port *this_port,
				 const pjmedia_frame *frame)
{
    struct silence_port *sp = (struct silence_port*)this_port;
    unsigned frame_size = frame->size / sizeof(pj_uint16_t);
    unsigned samples_per_frame = sp->base.info.samples_per_frame;
    pj_int16_t tmp_buf[frame_size];
    pjmedia_frame tmp_frame;
    pj_status_t status;
    PJ_LOG(6, (THIS_FILE, "packet: sz=%d ts=%llu",
                frame->size/sizeof(pj_uint16_t), frame->timestamp.u64));
    PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL);
    if (frame->type == PJMEDIA_FRAME_TYPE_NONE ) {
        PJ_LOG(5, (THIS_FILE, "empty frame passed"));
	    return pjmedia_port_put_frame(sp->dn_port, frame);
    }

    if (sp->frames == 0) {
        PJ_LOG(5, (THIS_FILE, "%u: this is first received frame,  "
                    "so just update last timestamp with its "
                    "timestamp + size: %llu + %d",
                    sp->frames,
                    frame->timestamp.u64,
                    samples_per_frame));
        sp->last_ts.u64 = frame->timestamp.u64 + samples_per_frame;
    } else if (frame->timestamp.u64 == 0){
        PJ_LOG(5, (THIS_FILE, "%u: frame timestamp is unknown, so increment "
                    "by %d (samples per frame)", sp->frames, samples_per_frame));
        sp->last_ts.u64 += samples_per_frame;
    } else if (frame->timestamp.u64 < sp->last_ts.u64 ){
        PJ_LOG(5, (THIS_FILE, "%u: received frame timestamp is lesser than "
                    "latest timestamp (%llu < %llu)", sp->frames,
                    frame->timestamp.u64, sp->last_ts.u64));
        sp->last_ts.u64 = frame->timestamp.u64 + samples_per_frame;
    } else if (frame->timestamp.u64 > sp->last_ts.u64){
        unsigned zero_padding_count = frame->timestamp.u64 - sp->last_ts.u64;
        pj_int16_t zero_buf[zero_padding_count];
        PJ_LOG(5, (THIS_FILE, "%u: received frame timestamp is greater than "
                "latest timestamp (%llu > %llu), fill output buffer with %d "
                "empty frames",
                sp->frames,
                frame->timestamp.u64,
                sp->last_ts.u64,
                zero_padding_count));
        pj_bzero(zero_buf, sizeof(zero_buf));
        pjmedia_circ_buf_write(sp->buf, zero_buf, zero_padding_count);
        PJ_LOG(6, (THIS_FILE, "write in circ buf %u zeros",
                    zero_padding_count));
        sp->last_ts.u64 = frame->timestamp.u64 + samples_per_frame;
    } else {
        sp->last_ts.u64 += samples_per_frame;
    }
    pjmedia_circ_buf_write(sp->buf, (pj_uint16_t*)frame->buf, frame_size);
    PJ_LOG(6, (THIS_FILE, "write in circ buf %u bytes", frame_size));
    pj_memcpy(&tmp_frame, frame, sizeof(pjmedia_frame));
    tmp_frame.buf = (void*)tmp_buf;
    sp->frames ++;

    /* put everything saved into buffer
    FIXME: wrong timestamps. Fortunately, wav writer don't care about
    timestamps
    */
    while (pjmedia_circ_buf_get_len(sp->buf) >= frame_size){
        pjmedia_circ_buf_read(sp->buf, tmp_buf, frame_size);
        PJ_LOG(6, (THIS_FILE, "read from circ buf %u bytes", frame_size));
        status = pjmedia_port_put_frame(sp->dn_port, &tmp_frame);
        if (status != PJ_SUCCESS)
            return status;
    }
    return PJ_SUCCESS;
}
Beispiel #21
0
static void pcap2wav(const pj_str_t *codec,
		     const pj_str_t *wav_filename,
		     pjmedia_aud_dev_index dev_id,
		     const pj_str_t *srtp_crypto,
		     const pj_str_t *srtp_key)
{
    const pj_str_t WAV = {".wav", 4};
    struct pkt
    {
	pj_uint8_t	 buffer[320];
	pjmedia_rtp_hdr	*rtp;
	pj_uint8_t	*payload;
	unsigned	 payload_len;
    } pkt0;
    pjmedia_codec_mgr *cmgr;
    const pjmedia_codec_info *ci;
    pjmedia_codec_param param;
    unsigned samples_per_frame;
    pj_status_t status;

    /* Initialize all codecs */
    T( pjmedia_codec_register_audio_codecs(app.mept, NULL) );

    /* Create SRTP transport is needed */
#if PJMEDIA_HAS_SRTP
    if (srtp_crypto->slen) {
	pjmedia_srtp_crypto crypto;

	pj_bzero(&crypto, sizeof(crypto));
	crypto.key = *srtp_key;
	crypto.name = *srtp_crypto;
	T( pjmedia_transport_srtp_create(app.mept, NULL, NULL, &app.srtp) );
	T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) );
    }
#else
    PJ_UNUSED_ARG(srtp_crypto);
    PJ_UNUSED_ARG(srtp_key);
#endif

    /* Read first packet */
    read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp, 
	     &pkt0.payload, &pkt0.payload_len, PJ_FALSE);

    cmgr = pjmedia_endpt_get_codec_mgr(app.mept);

    /* Get codec info and param for the specified payload type */
    app.pt = pkt0.rtp->pt;
    if (app.pt >=0 && app.pt < 96) {
	T( pjmedia_codec_mgr_get_codec_info(cmgr, pkt0.rtp->pt, &ci) );
    } else {
	unsigned cnt = 2;
	const pjmedia_codec_info *info[2];
	T( pjmedia_codec_mgr_find_codecs_by_id(cmgr, codec, &cnt, 
					       info, NULL) );
	if (cnt != 1)
	    err_exit("Codec ID must be specified and unique!", 0);

	ci = info[0];
    }
    T( pjmedia_codec_mgr_get_default_param(cmgr, ci, &param) );

    /* Alloc and init codec */
    T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) );
    T( pjmedia_codec_init(app.codec, app.pool) );
    T( pjmedia_codec_open(app.codec, &param) );

    /* Init audio device or WAV file */
    samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000;
    if (pj_strcmp2(wav_filename, "-") == 0) {
	pjmedia_aud_param aud_param;

	/* Open audio device */
	T( pjmedia_aud_dev_default_param(dev_id, &aud_param) );
	aud_param.dir = PJMEDIA_DIR_PLAYBACK;
	aud_param.channel_count = ci->channel_cnt;
	aud_param.clock_rate = ci->clock_rate;
	aud_param.samples_per_frame = samples_per_frame;
	T( pjmedia_aud_stream_create(&aud_param, NULL, &play_cb, 
				     NULL, &app.aud_strm) );
	T( pjmedia_aud_stream_start(app.aud_strm) );
    } else if (pj_stristr(wav_filename, &WAV)) {
	/* Open WAV file */
	T( pjmedia_wav_writer_port_create(app.pool, wav_filename->ptr,
					  ci->clock_rate, ci->channel_cnt,
					  samples_per_frame,
					  param.info.pcm_bits_per_sample, 0, 0,
					  &app.wav) );
    } else {
	err_exit("invalid output file", PJ_EINVAL);
    }

    /* Loop reading PCAP and writing WAV file */
    for (;;) {
	struct pkt pkt1;
	pj_timestamp ts;
	pjmedia_frame frames[16], pcm_frame;
	short pcm[320];
	unsigned i, frame_cnt;
	long samples_cnt, ts_gap;

	pj_assert(sizeof(pcm) >= samples_per_frame);

	/* Parse first packet */
	ts.u64 = 0;
	frame_cnt = PJ_ARRAY_SIZE(frames);
	T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len, 
				&ts, &frame_cnt, frames) );

	/* Decode and write to WAV file */
	samples_cnt = 0;
	for (i=0; i<frame_cnt; ++i) {
	    pjmedia_frame pcm_frame;

	    pcm_frame.buf = pcm;
	    pcm_frame.size = samples_per_frame * 2;

	    T( pjmedia_codec_decode(app.codec, &frames[i], 
				    (unsigned)pcm_frame.size, &pcm_frame) );
	    if (app.wav) {
		T( pjmedia_port_put_frame(app.wav, &pcm_frame) );
	    }
	    if (app.aud_strm) {
		T( wait_play(&pcm_frame) );
	    }
	    samples_cnt += samples_per_frame;
	}

	/* Read next packet */
	read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp,
		 &pkt1.payload, &pkt1.payload_len, PJ_TRUE);

	/* Fill in the gap (if any) between pkt0 and pkt1 */
	ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) -
		 samples_cnt;
	while (ts_gap >= (long)samples_per_frame) {

	    pcm_frame.buf = pcm;
	    pcm_frame.size = samples_per_frame * 2;

	    if (app.codec->op->recover) {
		T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, 
					 &pcm_frame) );
	    } else {
		pj_bzero(pcm_frame.buf, pcm_frame.size);
	    }

	    if (app.wav) {
		T( pjmedia_port_put_frame(app.wav, &pcm_frame) );
	    }
	    if (app.aud_strm) {
		T( wait_play(&pcm_frame) );
	    }
	    ts_gap -= samples_per_frame;
	}
	
	/* Next */
	pkt0 = pkt1;
	pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer;
	pkt0.payload = pkt0.buffer + (pkt1.payload - pkt1.buffer);
    }
}
Beispiel #22
0
static pj_status_t put_frame(pjmedia_port *this_port, 
			     pjmedia_frame *frame)
{
    struct bidir_port *p = (struct bidir_port*)this_port;
    return pjmedia_port_put_frame(p->put_port, frame);
}
Beispiel #23
0
/*
 * "Write" a multichannel frame. This would split the multichannel frame
 * into individual mono channel, and write it to the appropriate port.
 */
static pj_status_t put_frame(pjmedia_port *this_port, 
			     const pjmedia_frame *frame)
{
    struct splitcomb *sc = (struct splitcomb*) this_port;
    unsigned ch;

    /* Handle null frame */
    if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
	for (ch=0; ch < this_port->info.channel_count; ++ch) {
	    pjmedia_port *port = sc->port_desc[ch].port;

	    if (!port) continue;

	    pjmedia_port_put_frame(port, frame);
	}
	return PJ_SUCCESS;
    }

    /* Not sure how we would handle partial frame, so better reject
     * it for now.
     */
    PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame,
		     PJ_EINVAL);

    /* 
     * Write mono frame into each channels 
     */
    for (ch=0; ch < this_port->info.channel_count; ++ch) {
	pjmedia_port *port = sc->port_desc[ch].port;

	if (!port)
	    continue;

	if (!sc->port_desc[ch].reversed) {
	    /* Write to normal port */
	    pjmedia_frame mono_frame;

	    /* Extract the mono frame */
	    extract_mono_frame(frame->buf, sc->put_buf, ch, 
			       this_port->info.channel_count, 
			       frame->size * 8 / 
				 this_port->info.bits_per_sample /
				 this_port->info.channel_count);

	    mono_frame.buf = sc->put_buf;
	    mono_frame.size = frame->size / this_port->info.channel_count;
	    mono_frame.type = frame->type;
	    mono_frame.timestamp.u64 = frame->timestamp.u64;

	    /* Write */
	    pjmedia_port_put_frame(port, &mono_frame);

	} else {
	    /* Write to reversed phase port */
	    struct reverse_port *rport = (struct reverse_port*)port;
	    
	    if (rport->dn_write_pos == rport->dn_read_pos) {

		/* Only report overflow if the frame is constantly read
		 * by the 'consumer' of the reverse port.
		 * It is possible that nobody reads the buffer, so causing
		 * overflow to happen rapidly, and writing log message this
		 * way does not seem to be wise.
		 */
		if (rport->dn_read_pos != rport->dn_overflow_pos) {
		    rport->dn_overflow_pos = rport->dn_read_pos;
		    LOG_DN_((THIS_FILE, "Overflow in downstream direction"));
		}

		/* Adjust write position */
		rport->dn_write_pos = 
		    (rport->dn_write_pos + rport->buf_cnt/2) % 
		    rport->buf_cnt;
	    }

	    /* Extract mono-frame and put it in downstream buffer */
	    extract_mono_frame(frame->buf, 
			       rport->dnstream_buf[rport->dn_write_pos],
			       ch, this_port->info.channel_count, 
			       frame->size * 8 / 
				 this_port->info.bits_per_sample /
				 this_port->info.channel_count);

	    rport->dn_write_pos = (rport->dn_write_pos + 1) %
			           rport->buf_cnt;
	}
    }

    return PJ_SUCCESS;
}
Beispiel #24
0
/*
 * "Write" a multichannel frame downstream. This would split 
 * the multichannel frame into individual mono channel, and write 
 * it to the appropriate port.
 */
static pj_status_t put_frame(pjmedia_port *this_port, 
			     pjmedia_frame *frame)
{
    struct splitcomb *sc = (struct splitcomb*) this_port;
    unsigned ch;

    /* Handle null frame */
    if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
	for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
	    pjmedia_port *port = sc->port_desc[ch].port;

	    if (!port) continue;

	    if (!sc->port_desc[ch].reversed) {
		pjmedia_port_put_frame(port, frame);
	    } else {
		struct reverse_port *rport = (struct reverse_port*)port;

		/* Update the number of NULL frames received. Once we have too
		 * many of this, we'll stop calling op_update() to let the
		 * media be suspended.
		 */

		if (++rport->buf[DIR_DOWNSTREAM].null_cnt > 
			rport->max_null_frames) 
		{
		    /* Prevent the counter from overflowing and resetting
		     * back to zero
		     */
		    rport->buf[DIR_DOWNSTREAM].null_cnt = 
			rport->max_null_frames + 1;
		    continue;
		}

		/* Write zero port to delaybuf so that it doesn't underflow. 
		 * If we don't do this, get_frame() on this direction will
		 * cause delaybuf to generate missing frame and the last
		 * frame transmitted to delaybuf will be replayed multiple
		 * times, which doesn't sound good.
		 */

		/* Update rport state. */
		op_update(rport, DIR_DOWNSTREAM, OP_PUT);

		/* Discard frame if rport is paused on this direction */
		if (rport->buf[DIR_DOWNSTREAM].paused)
		    continue;

		/* Generate zero frame. */
		pjmedia_zero_samples(sc->put_buf, 
				     PJMEDIA_PIA_SPF(&port->info));

		/* Put frame to delay buffer */
		pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
				      sc->put_buf);

	    }
	}
	return PJ_SUCCESS;
    }

    /* Not sure how we would handle partial frame, so better reject
     * it for now.
     */
    PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
		     PJ_EINVAL);

    /* 
     * Write mono frame into each channels 
     */
    for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
	pjmedia_port *port = sc->port_desc[ch].port;

	if (!port)
	    continue;

	/* Extract the mono frame to temporary buffer */
	extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch, 
			   PJMEDIA_PIA_CCNT(&this_port->info),
			   (unsigned)frame->size * 8 / 
			     PJMEDIA_PIA_BITS(&this_port->info) /
			     PJMEDIA_PIA_CCNT(&this_port->info));

	if (!sc->port_desc[ch].reversed) {
	    /* Write to normal port */
	    pjmedia_frame mono_frame;

	    mono_frame.buf = sc->put_buf;
	    mono_frame.size = frame->size / PJMEDIA_PIA_CCNT(&this_port->info);
	    mono_frame.type = frame->type;
	    mono_frame.timestamp.u64 = frame->timestamp.u64;

	    /* Write */
	    pjmedia_port_put_frame(port, &mono_frame);

	} else {
	    /* Write to reversed phase port */
	    struct reverse_port *rport = (struct reverse_port*)port;

	    /* Reset NULL frame counter */
	    rport->buf[DIR_DOWNSTREAM].null_cnt = 0;

	    /* Update rport state. */
	    op_update(rport, DIR_DOWNSTREAM, OP_PUT);

	    if (!rport->buf[DIR_DOWNSTREAM].paused) {
		pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf, 
				      sc->put_buf);
	    }
	}
    }

    return PJ_SUCCESS;
}
Beispiel #25
0
static pj_status_t tee_put_frame(pjmedia_port *port, pjmedia_frame *frame)
{
    vid_tee_port *tee = (vid_tee_port*)port;
    unsigned i, j;
    const pj_uint8_t PUT_FRM_DONE = 1;

    pj_bzero(tee->put_frm_flag, tee->dst_port_cnt *
				sizeof(tee->put_frm_flag[0]));

    for (i = 0; i < tee->dst_port_cnt; ++i) {
	pjmedia_frame frame_ = *frame;

        if (tee->put_frm_flag[i])
            continue;
        
        if (tee->tee_conv[i].conv) {
            pj_status_t status;
            
            frame_.buf  = tee->buf[0];
            frame_.size = tee->tee_conv[i].conv_buf_size;
            status = pjmedia_converter_convert(tee->tee_conv[i].conv,
                                               frame, &frame_);
            if (status != PJ_SUCCESS) {
                PJ_LOG(3, (THIS_FILE,
			       "Failed to convert frame for destination"
                               " port %d (%.*s)", i,
                               tee->dst_ports[i].dst->info.name.slen,
                               tee->dst_ports[i].dst->info.name.ptr));
                continue;
            }
        }
        
        /* Find other destination ports which has the same format so
         * we don't need to do the same conversion twice.
         */
        for (j = i; j < tee->dst_port_cnt; ++j) {
            pjmedia_frame framep;
            
            if (tee->put_frm_flag[j] ||
                (tee->dst_ports[j].dst->info.fmt.id != 
                 tee->dst_ports[i].dst->info.fmt.id) ||
                (tee->dst_ports[j].dst->info.fmt.det.vid.size.w != 
                 tee->dst_ports[i].dst->info.fmt.det.vid.size.w) ||
                (tee->dst_ports[j].dst->info.fmt.det.vid.size.h != 
                 tee->dst_ports[i].dst->info.fmt.det.vid.size.h))
            {
                continue;
            }
            
            framep = frame_;
            /* For dst_ports that do in-place processing, we need to duplicate
             * the data source first.
             */
            if (tee->dst_ports[j].option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)
            {
                PJ_ASSERT_RETURN(tee->buf_size <= frame_.size, PJ_ETOOBIG);
                framep.buf = tee->buf[tee->buf_cnt-1];
                framep.size = frame_.size;
                pj_memcpy(framep.buf, frame_.buf, frame_.size);
            }

            /* Deliver the data */
            pjmedia_port_put_frame(tee->dst_ports[j].dst, &framep);
            tee->put_frm_flag[j] = PUT_FRM_DONE;
            
            if (!tee->tee_conv[i].conv)
                break;
        }
    }

    return PJ_SUCCESS;
}
Beispiel #26
0
static pj_status_t enc_dec_test(const char *codec_id,
				const char *filein,
			        const char *fileout)
{
    pj_pool_t *pool;
    pjmedia_codec_mgr *cm;
    pjmedia_codec *codec;
    const pjmedia_codec_info *pci;
    pjmedia_codec_param param;
    unsigned cnt, samples_per_frame;
    pj_str_t tmp;
    pjmedia_port *wavin, *wavout;
    unsigned lost_pct;
    pj_status_t status;

#define T   file_msec_duration/1000, file_msec_duration%1000
    
    pool = pjmedia_endpt_create_pool(mept, "encdec", 1000, 1000);

    cm = pjmedia_endpt_get_codec_mgr(mept);

#ifdef LOST_PCT
    lost_pct = LOST_PCT;
#else
    lost_pct = 0;
#endif
    
    cnt = 1;
    CHECK( pjmedia_codec_mgr_find_codecs_by_id(cm, pj_cstr(&tmp, codec_id), 
					       &cnt, &pci, NULL) );
    CHECK( pjmedia_codec_mgr_get_default_param(cm, pci, &param) );

    samples_per_frame = param.info.clock_rate * param.info.frm_ptime / 1000;

    /* Control VAD */
    param.setting.vad = 1;

    /* Open wav for reading */
    CHECK( pjmedia_wav_player_port_create(pool, filein, 
					  param.info.frm_ptime, 
					  PJMEDIA_FILE_NO_LOOP, 0, &wavin) );

    /* Open wav for writing */
    CHECK( pjmedia_wav_writer_port_create(pool, fileout,
					  param.info.clock_rate, 
					  param.info.channel_cnt,
					  samples_per_frame,
					  16, 0, 0, &wavout) );

    /* Alloc codec */
    CHECK( pjmedia_codec_mgr_alloc_codec(cm, pci, &codec) );
    CHECK( codec->op->init(codec, pool) );
    CHECK( codec->op->open(codec, &param) );
    
    for (;;) {
	pjmedia_frame frm_pcm, frm_bit, out_frm, frames[4];
	pj_int16_t pcmbuf[320];
	pj_timestamp ts;
	pj_uint8_t bitstream[160];

	frm_pcm.buf = (char*)pcmbuf;
	frm_pcm.size = samples_per_frame * 2;

	/* Read from WAV */
	if (pjmedia_port_get_frame(wavin, &frm_pcm) != PJ_SUCCESS)
	    break;
	if (frm_pcm.type != PJMEDIA_FRAME_TYPE_AUDIO)
	    break;;

	/* Update duration */
	file_msec_duration += samples_per_frame * 1000 / 
			      param.info.clock_rate;

	/* Encode */
	frm_bit.buf = bitstream;
	frm_bit.size = sizeof(bitstream);
	CHECK(codec->op->encode(codec, &frm_pcm, sizeof(bitstream), &frm_bit));

	/* On DTX, write zero frame to wavout to maintain duration */
	if (frm_bit.size == 0 || frm_bit.type != PJMEDIA_FRAME_TYPE_AUDIO) {
	    out_frm.buf = (char*)pcmbuf;
	    out_frm.size = 160;
	    CHECK( pjmedia_port_put_frame(wavout, &out_frm) );
	    TRACE_((THIS_FILE, "%d.%03d read: %u, enc: %u",
		    T, frm_pcm.size, frm_bit.size));
	    continue;
	}
	
	/* Parse the bitstream (not really necessary for this case
	 * since we always decode 1 frame, but it's still good
	 * for testing)
	 */
	ts.u64 = 0;
	cnt = PJ_ARRAY_SIZE(frames);
	CHECK( codec->op->parse(codec, bitstream, frm_bit.size, &ts, &cnt, 
			        frames) );
	CHECK( (cnt==1 ? PJ_SUCCESS : -1) );

	/* Decode or simulate packet loss */
	out_frm.buf = (char*)pcmbuf;
	out_frm.size = sizeof(pcmbuf);
	
	if ((pj_rand() % 100) < (int)lost_pct) {
	    /* Simulate loss */
	    CHECK( codec->op->recover(codec, sizeof(pcmbuf), &out_frm) );
	    TRACE_((THIS_FILE, "%d.%03d Packet lost", T));
	} else {
	    /* Decode */
	    CHECK( codec->op->decode(codec, &frames[0], sizeof(pcmbuf), 
				     &out_frm) );
	}

	/* Write to WAV */
	CHECK( pjmedia_port_put_frame(wavout, &out_frm) );

	TRACE_((THIS_FILE, "%d.%03d read: %u, enc: %u, dec/write: %u",
		T, frm_pcm.size, frm_bit.size, out_frm.size));
    }

    /* Close wavs */
    pjmedia_port_destroy(wavout);
    pjmedia_port_destroy(wavin);

    /* Close codec */
    codec->op->close(codec);
    pjmedia_codec_mgr_dealloc_codec(cm, codec);

    /* Release pool */
    pj_pool_release(pool);

    return PJ_SUCCESS;
}