Beispiel #1
0
static pj_status_t g729_dealloc_codec(pjmedia_codec_factory *factory,
				      pjmedia_codec *codec )
{
    struct g729_private *priv = (struct g729_private*) codec->codec_data;
    int i = 0;

    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
    PJ_ASSERT_RETURN(factory==&g729_factory.base, PJ_EINVAL);

    /* Close codec, if it's not closed. */
    g729_close(codec);

#if !PLC_DISABLED
    /* Clear left samples in the PLC, since codec+plc will be reused
     * next time.
     */
    for (i=0; i<2; ++i) {
	pj_int16_t frame[80];
	pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
	pjmedia_plc_save(priv->plc, frame);
    }
#else
    PJ_UNUSED_ARG(i);
    PJ_UNUSED_ARG(priv);
#endif

    /* Re-init silence_period */
    pj_set_timestamp32(&priv->last_tx, 0, 0);

    pj_pool_release(priv->pool);

    return PJ_SUCCESS;
}
Beispiel #2
0
/*
 * Let the Echo Canceller knows that a frame has been captured from 
 * the microphone.
 */
PJ_DEF(pj_status_t) pjmedia_echo_capture( pjmedia_echo_state *echo,
					  pj_int16_t *rec_frm,
					  unsigned options )
{
    struct frame *oldest_frm;
    pj_status_t status, rc;

    if (!echo->lat_ready) {
	/* Prefetching to fill in the desired latency */
	PJ_LOG(5,(echo->obj_name, "Prefetching.."));
	return PJ_SUCCESS;
    }

    /* Retrieve oldest frame from the latency buffer */
    oldest_frm = echo->lat_buf.next;
    pj_list_erase(oldest_frm);

    /* Cancel echo using this reference frame */
    status = pjmedia_echo_cancel(echo, rec_frm, oldest_frm->buf, 
				 options, NULL);

    /* Move one frame from delay buffer to the latency buffer. */
    rc = pjmedia_delay_buf_get(echo->delay_buf, oldest_frm->buf);
    if (rc != PJ_SUCCESS) {
	/* Ooops.. no frame! */
	PJ_LOG(5,(echo->obj_name, 
		  "No frame from delay buffer. This will upset EC later"));
	pjmedia_zero_samples(oldest_frm->buf, echo->samples_per_frame);
    }
    pj_list_push_back(&echo->lat_buf, oldest_frm);
    
    return status;
}
Beispiel #3
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);
}
Beispiel #4
0
/*
 * Let the AEC knows that a frame has been captured from the microphone.
 */
PJ_DEF(pj_status_t) echo_supp_capture( void *state,
				       pj_int16_t *rec_frm,
				       unsigned options )
{
    echo_supp *ec = state;
    pj_time_val now;
    unsigned delay_ms;

    PJ_UNUSED_ARG(options);

    pj_gettimeofday(&now);

    PJ_TIME_VAL_SUB(now, ec->last_signal);
    delay_ms = PJ_TIME_VAL_MSEC(now);

    if (delay_ms < ec->tail_ms) {
#if defined(PJMEDIA_ECHO_SUPPRESS_FACTOR) && PJMEDIA_ECHO_SUPPRESS_FACTOR!=0
	unsigned i;
	for (i=0; i<ec->samples_per_frame; ++i) {
	    rec_frm[i] = (pj_int16_t)(rec_frm[i] >> 
				      PJMEDIA_ECHO_SUPPRESS_FACTOR);
	}
#else
	pjmedia_zero_samples(rec_frm, ec->samples_per_frame);
#endif
    }
Beispiel #5
0
/*
 * Recover lost frame.
 */
static pj_status_t  codec_recover( pjmedia_codec *codec,
				   unsigned output_buf_len,
				   struct pjmedia_frame *output )
{
    struct opus_data *opus_data = (struct opus_data *)codec->codec_data;
    int decoded_samples;
    pjmedia_frame *inframe;

    pj_mutex_lock (opus_data->mutex);

    if (opus_data->dec_frame_index == -1) {
        /* Recover the first packet? Don't think so, fill it with zeroes. */
	pj_uint16_t samples_per_frame;
	samples_per_frame = (opus_data->cfg.sample_rate * 
			     opus_data->ptime) / 1000;
	output->type = PJMEDIA_FRAME_TYPE_AUDIO;
	output->size = samples_per_frame << 1;
	pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
        pj_mutex_unlock (opus_data->mutex);

        return PJ_SUCCESS;
    }

    inframe = &opus_data->dec_frame[opus_data->dec_frame_index];
    decoded_samples = opus_decode(opus_data->dec,
				  inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ?
				  inframe->buf : NULL,
				  inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ?
				  inframe->size : 0,
				  (opus_int16*)output->buf,
				  output->size / (sizeof(opus_int16) *
				  opus_data->cfg.channel_cnt),
				  0);

    /* Mark current indexed frame as invalid */
    inframe->type = PJMEDIA_FRAME_TYPE_NONE;
    
    /* Update current frame index */
    opus_data->dec_frame_index++;
    if (opus_data->dec_frame_index > 1)
        opus_data->dec_frame_index = 0;
    /* Mark current indexed frame as invalid */
    inframe = &opus_data->dec_frame[opus_data->dec_frame_index];
    inframe->type = PJMEDIA_FRAME_TYPE_NONE;

    if (decoded_samples < 0) {
        PJ_LOG(4, (THIS_FILE, "Recover failed!"));
        pj_mutex_unlock (opus_data->mutex);
        return PJMEDIA_CODEC_EFAILED;
    }

    output->size = decoded_samples * sizeof(opus_int16) * 
    		   opus_data->cfg.channel_cnt;
    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
    output->timestamp = inframe->timestamp;

    pj_mutex_unlock (opus_data->mutex);
    return PJ_SUCCESS;
}
Beispiel #6
0
/*
 * Get frame from file.
 */
static pj_status_t null_get_frame(pjmedia_port *this_port, 
				  pjmedia_frame *frame)
{
    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
    frame->size = this_port->info.samples_per_frame * 2;
    frame->timestamp.u32.lo += this_port->info.samples_per_frame;
    pjmedia_zero_samples(frame->buf, this_port->info.samples_per_frame);

    return PJ_SUCCESS;
}
Beispiel #7
0
/*
 * Let the Echo Canceller knows that a frame has been captured from
 * the microphone.
 */
pj_status_t pjs_echo_canceller::capture(pj_int16_t *rec_frm, unsigned size) {
	struct frame *oldest_frm;
	pj_status_t status, rc;

	if(samples_per_frame!=size)
	{
		PJ_LOG(1, (THIS_FILE, "WRONG SIZE ON CAPTURE %d != %d",size,samples_per_frame));
		return -1;
	}
	for (unsigned i = 0; i < samples_per_frame; i++)
	{

		REAL f = hp00.highpass(rec_frm[i]);
		f = hp0.highpass(f);
		rec_frm[i] = round(f);
	}

	PPJ_WaitAndLock wl(*lock);
	if (!lat_ready) {
		/* Prefetching to fill in the desired latency */
		PJ_LOG(4, (THIS_FILE, "Prefetching.."));
		return PJ_SUCCESS;
	}

	/* Retrieve oldest frame from the latency buffer */
	oldest_frm = lat_buf.next;
	pj_list_erase(oldest_frm);

	lock->release();

    speex_echo_cancellation(state, (const spx_int16_t*)rec_frm,
			    (const spx_int16_t*)oldest_frm->buf,
			    (spx_int16_t*)tmp_frame);


    /* Preprocess output */
    speex_preprocess_run(preprocess, (spx_int16_t*)tmp_frame);
    pjmedia_copy_samples(rec_frm, tmp_frame, samples_per_frame);

	status = PJ_SUCCESS;
	/* Cancel echo using this reference frame */
	lock->acquire();

	/* Move one frame from delay buffer to the latency buffer. */
	rc = pjmedia_delay_buf_get(delay_buf, oldest_frm->buf);
	if (rc != PJ_SUCCESS) {
		/* Ooops.. no frame! */
		PJ_LOG(4,
				(THIS_FILE, "No frame from delay buffer. This will upset EC later"));
		pjmedia_zero_samples(oldest_frm->buf, samples_per_frame);
	}
	pj_list_push_back(&lat_buf, oldest_frm);

	return status;
}
Beispiel #8
0
/*
 * Get a mono frame from a reversed phase channel.
 */
static pj_status_t rport_put_frame(pjmedia_port *this_port, 
				   const pjmedia_frame *frame)
{
    struct reverse_port *rport = (struct reverse_port*) this_port;
    unsigned count;

    pj_assert(frame->size <= rport->base.info.bytes_per_frame);

    /* Check for overflows */
    if (rport->up_write_pos == rport->up_read_pos) {

	/* Only report overflow if the frame is constantly read
	 * at the other end of the buffer (the multichannel side).
	 * 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->up_read_pos != rport->up_overflow_pos) {
	    rport->up_overflow_pos = rport->up_read_pos;
	    LOG_UP_((THIS_FILE, "Overflow in upstream direction"));
	}

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

    /* Handle NULL frame */
    if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) {
	TRACE_UP_((THIS_FILE, "Upstream write %d null samples at buf pos %d",
		   this_port->info.samples_per_frame, rport->up_write_pos));
	pjmedia_zero_samples(rport->upstream_buf[rport->up_write_pos],
			     this_port->info.samples_per_frame);
	rport->up_write_pos = (rport->up_write_pos+1) % rport->buf_cnt;
	return PJ_SUCCESS;
    }

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

    /* Copy normal frame to curcular buffer */
    count = frame->size * 8 / this_port->info.bits_per_sample;

    TRACE_UP_((THIS_FILE, "Upstream write %d samples at buf pos %d",
	       count, rport->up_write_pos));


    pjmedia_copy_samples(rport->upstream_buf[rport->up_write_pos],
			 frame->buf, count);
    rport->up_write_pos = (rport->up_write_pos+1) % rport->buf_cnt;

    return PJ_SUCCESS;
}
Beispiel #9
0
static pj_status_t ec_get_frame( pjmedia_port *this_port, 
				 pjmedia_frame *frame)
{
    struct ec *ec = (struct ec*)this_port;
    pj_status_t status;

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

    status = pjmedia_port_get_frame(ec->dn_port, frame);
    if (status!=PJ_SUCCESS || frame->type!=PJMEDIA_FRAME_TYPE_AUDIO) {
	pjmedia_zero_samples(frame->buf, this_port->info.samples_per_frame);
    }

    pjmedia_echo_playback(ec->ec, frame->buf);

    return status;
}
Beispiel #10
0
/*
 * Free codec.
 */
static pj_status_t gsm_dealloc_codec( pjmedia_codec_factory *factory, 
				      pjmedia_codec *codec )
{
    struct gsm_data *gsm_data;
    int i;

    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
    PJ_ASSERT_RETURN(factory == &gsm_codec_factory.base, PJ_EINVAL);

    gsm_data = (struct gsm_data*) codec->codec_data;

    /* Close codec, if it's not closed. */
    gsm_codec_close(codec);

#if !PLC_DISABLED
    /* Clear left samples in the PLC, since codec+plc will be reused
     * next time.
     */
    for (i=0; i<2; ++i) {
	pj_int16_t frame[160];
	pjmedia_zero_samples(frame, PJ_ARRAY_SIZE(frame));
	pjmedia_plc_save(gsm_data->plc, frame);
    }
#else
    PJ_UNUSED_ARG(i);
#endif

    /* Re-init silence_period */
    pj_set_timestamp32(&gsm_data->last_tx, 0, 0);

    /* Put in the free list. */
    pj_mutex_lock(gsm_codec_factory.mutex);
    pj_list_push_front(&gsm_codec_factory.codec_list, codec);
    pj_mutex_unlock(gsm_codec_factory.mutex);

    return PJ_SUCCESS;
}
Beispiel #11
0
/*
 * Decode frame.
 */
static pj_status_t spx_codec_decode( pjmedia_codec *codec, 
				     const struct pjmedia_frame *input,
				     unsigned output_buf_len, 
				     struct pjmedia_frame *output)
{
    struct spx_private *spx;
    unsigned samples_per_frame;

    spx = (struct spx_private*) codec->codec_data;
    samples_per_frame=spx_factory.speex_param[spx->param_id].samples_per_frame;

    PJ_ASSERT_RETURN(output_buf_len >= samples_per_frame << 1,
		     PJMEDIA_CODEC_EPCMTOOSHORT);

    if (input->type != PJMEDIA_FRAME_TYPE_AUDIO) {
	pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame);
	output->size = samples_per_frame << 1;
	output->timestamp.u64 = input->timestamp.u64;
	output->type = PJMEDIA_FRAME_TYPE_AUDIO;
	return PJ_SUCCESS;
    }

    /* Copy the data into the bit-stream struct */
    speex_bits_read_from(&spx->dec_bits, (char*)input->buf, (int)input->size);
    
    /* Set Speex dec_bits pointer to the start bit of the frame */
    speex_bits_advance(&spx->dec_bits, input->bit_info);

    /* Decode the data */
    speex_decode_int(spx->dec, &spx->dec_bits, (spx_int16_t*)output->buf);

    output->type = PJMEDIA_FRAME_TYPE_AUDIO;
    output->size = samples_per_frame << 1;
    output->timestamp.u64 = input->timestamp.u64;

    return PJ_SUCCESS;
}
Beispiel #12
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 #13
0
/*
 * Get a multichannel frame.
 * This will get mono channel frame from each port and put the
 * mono frame into the multichannel frame.
 */
static pj_status_t get_frame(pjmedia_port *this_port, 
			     pjmedia_frame *frame)
{
    struct splitcomb *sc = (struct splitcomb*) this_port;
    unsigned ch;
    pj_bool_t has_frame = PJ_FALSE;

    /* Clear output frame */
    pjmedia_zero_samples(frame->buf, this_port->info.samples_per_frame);

    /* Read frame from each port */
    for (ch=0; ch < this_port->info.channel_count; ++ch) {
	pjmedia_port *port = sc->port_desc[ch].port;
	pjmedia_frame mono_frame;
	pj_status_t status;

	if (!port)
	    continue;

	/* Read from the port */
	if (sc->port_desc[ch].reversed == PJ_FALSE) {
	    /* Read from normal port */
	    mono_frame.buf = sc->get_buf;
	    mono_frame.size = port->info.bytes_per_frame;
	    mono_frame.timestamp.u64 = frame->timestamp.u64;

	    status = pjmedia_port_get_frame(port, &mono_frame);
	    if (status != PJ_SUCCESS || 
		mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
	    {
		continue;
	    }

	    /* Combine the mono frame into multichannel frame */
	    store_mono_frame(mono_frame.buf, frame->buf, ch,
			     this_port->info.channel_count,
			     mono_frame.size * 8 /
				this_port->info.bits_per_sample);

	    frame->timestamp.u64 = mono_frame.timestamp.u64;

	} else {
	    /* Read from temporary buffer for reverse port */
	    struct reverse_port *rport = (struct reverse_port*)port;

	    /* Check for underflows */
	    if (rport->up_read_pos == rport->up_write_pos) {

		/* Only report underflow if the buffer is constantly filled
		 * up at the other side.
		 * It is possible that nobody writes the buffer, so causing
		 * underflow to happen rapidly, and writing log message this
		 * way does not seem to be wise.
		 */
		if (rport->up_write_pos != rport->up_underflow_pos) {
		    rport->up_underflow_pos = rport->up_write_pos;
		    LOG_UP_((THIS_FILE, "Underflow in upstream direction"));
		}

		/* Adjust read position */
		rport->up_read_pos = 
		    (rport->up_write_pos - rport->buf_cnt/2) %
		    rport->buf_cnt;
	    }

	    TRACE_UP_((THIS_FILE, "Upstream read at buffer pos %d", 
		       rport->up_read_pos));

	    /* Combine the mono frame into multichannel frame */
	    store_mono_frame(rport->upstream_buf[rport->up_read_pos], 
			     frame->buf, ch,
			     this_port->info.channel_count,
			     port->info.samples_per_frame);

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


	has_frame = PJ_TRUE;
    }

    /* Return NO_FRAME is we don't get any frames from downstream ports */
    if (has_frame) {
	frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
	frame->size = this_port->info.bytes_per_frame;
    } else
	frame->type = PJMEDIA_FRAME_TYPE_NONE;

    return PJ_SUCCESS;
}
Beispiel #14
0
PJ_DEF(pj_status_t) pjmedia_resample_create( pj_pool_t *pool,
					     pj_bool_t high_quality,
					     pj_bool_t large_filter,
					     unsigned channel_count,
					     unsigned rate_in,
					     unsigned rate_out,
					     unsigned samples_per_frame,
					     pjmedia_resample **p_resample)
{
    pjmedia_resample *resample;

    PJ_ASSERT_RETURN(pool && p_resample && rate_in &&
		     rate_out && samples_per_frame, PJ_EINVAL);

    resample = PJ_POOL_ZALLOC_T(pool, pjmedia_resample);
    PJ_ASSERT_RETURN(resample, PJ_ENOMEM);

    /*
     * If we're downsampling, always use the fast algorithm since it seems
     * to yield the same quality.
     */
    if (rate_out < rate_in) {
	//no this is not a good idea. It sounds pretty good with speech,
	//but very poor with background noise etc.
	//high_quality = 0;
    }

    resample->factor = rate_out * 1.0 / rate_in;
    resample->large_filter = large_filter;
    resample->high_quality = high_quality;
    resample->channel_cnt = channel_count;
    resample->frame_size = samples_per_frame;

    if (high_quality) {
	/* This is a bug in xoff calculation, thanks Stephane Lussier
	 * of Macadamian dot com.
	 *   resample->xoff = large_filter ? 32 : 6;
	 */
	resample->xoff = res_GetXOFF(resample->factor, (char)large_filter);
    } else {
	resample->xoff = 1;
    }

    if (channel_count == 1) {
	unsigned size;

	/* Allocate input buffer */
	size = (samples_per_frame + 2*resample->xoff) * sizeof(pj_int16_t);
	resample->buffer = (pj_int16_t*) pj_pool_alloc(pool, size);
	PJ_ASSERT_RETURN(resample->buffer, PJ_ENOMEM);

	pjmedia_zero_samples(resample->buffer, resample->xoff*2);

    } else if (channel_count > 1) {
	unsigned i, size;

	/* Allocate input buffer table */
	size = channel_count * sizeof(pj_int16_t*);
	resample->in_buffer = (pj_int16_t**)pj_pool_alloc(pool, size);

	/* Allocate input buffer */
	size = (samples_per_frame/channel_count + 2*resample->xoff) * 
	       sizeof(pj_int16_t);
	for (i = 0; i < channel_count; ++i) {
	    resample->in_buffer[i] = (pj_int16_t*)pj_pool_alloc(pool, size);
	    PJ_ASSERT_RETURN(resample->in_buffer, PJ_ENOMEM);
	    pjmedia_zero_samples(resample->in_buffer[i], resample->xoff*2);
	}

	/* Allocate temporary output buffer */
	size = (unsigned) (resample->frame_size * sizeof(pj_int16_t) * 
			   resample->factor / channel_count);
	resample->tmp_buffer = (pj_int16_t*) pj_pool_alloc(pool, size);
	PJ_ASSERT_RETURN(resample->tmp_buffer, PJ_ENOMEM);
    }

    *p_resample = resample;

    PJ_LOG(5,(THIS_FILE, "resample created: %s qualiy, %s filter, in/out "
			  "rate=%d/%d", 
			  (high_quality?"high":"low"),
			  (large_filter?"large":"small"),
			  rate_in, rate_out));
    return PJ_SUCCESS;
}
Beispiel #15
0
/****************************************************************************
 * sound latency test
 */
static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav,
			     unsigned *lat_sum, unsigned *lat_cnt,
			     unsigned *lat_min, unsigned *lat_max)
{
    pjmedia_frame frm;
    short *buf;
    unsigned i, clock_rate, samples_per_frame;
    pj_size_t read, len;
    unsigned start_pos;
    pj_bool_t first;
    pj_status_t status;

    *lat_sum = 0;
    *lat_cnt = 0;
    *lat_min = 10000;
    *lat_max = 0;

    samples_per_frame = PJMEDIA_PIA_SPF(&wav->info);
    clock_rate = PJMEDIA_PIA_SRATE(&wav->info);
    frm.buf = pj_pool_alloc(pool, samples_per_frame * 2);
    frm.size = samples_per_frame * 2;
    len = pjmedia_wav_player_get_len(wav);
    buf = pj_pool_alloc(pool, len + samples_per_frame);

    /* Read the whole file */
    read = 0;
    while (read < len/2) {
	status = pjmedia_port_get_frame(wav, &frm);
	if (status != PJ_SUCCESS)
	    break;

	pjmedia_copy_samples(buf+read, (short*)frm.buf, samples_per_frame);
	read += samples_per_frame;
    }

    if (read < 2 * clock_rate) {
	systest_perror("The WAV file is too short", PJ_SUCCESS);
	return -1;
    }

    /* Zero the first 500ms to remove loud click noises
     * (keypad press, etc.)
     */
    pjmedia_zero_samples(buf, clock_rate / 2);

    /* Loop to calculate latency */
    start_pos = 0;
    first = PJ_TRUE;
    while (start_pos < len/2 - clock_rate) {
	int max_signal = 0;
	unsigned max_signal_pos = start_pos;
	unsigned max_echo_pos = 0;
	unsigned pos;
	unsigned lat;

	/* Get the largest signal in the next 0.7s */
	for (i=start_pos; i<start_pos + clock_rate * 700 / 1000; ++i) {
	    if (abs(buf[i]) > max_signal) {
		max_signal = abs(buf[i]);
		max_signal_pos = i;
	    }
	}

	/* Advance 10ms from max_signal_pos */
	pos = max_signal_pos + 10 * clock_rate / 1000;

	/* Get the largest signal in the next 800ms */
	max_signal = 0;
	max_echo_pos = pos;
	for (i=pos; i<pos+clock_rate * 8 / 10; ++i) {
	    if (abs(buf[i]) > max_signal) {
		max_signal = abs(buf[i]);
		max_echo_pos = i;
	    }
	}

	lat = (max_echo_pos - max_signal_pos) * 1000 / clock_rate;

#if 0
	PJ_LOG(4,(THIS_FILE, "Signal at %dms, echo at %d ms, latency %d ms",
		  max_signal_pos * 1000 / clock_rate,
		  max_echo_pos * 1000 / clock_rate,
		  lat));
#endif

	*lat_sum += lat;
	(*lat_cnt)++;
	if (lat < *lat_min)
	    *lat_min = lat;
	if (lat > *lat_max)
	    *lat_max = lat;

	/* Advance next loop */
	if (first) {
	    start_pos = max_signal_pos + clock_rate * 9 / 10;
	    first = PJ_FALSE;
	} else {
	    start_pos += clock_rate;
	}
    }

    return 0;
}
Beispiel #16
0
/*
 * Put a frame into the buffer. When the buffer is full, flush the buffer
 * to the file.
 */
static pj_status_t file_put_frame(pjmedia_port *this_port, 
				  const pjmedia_frame *frame)
{
    struct mp3_file_port *fport = (struct mp3_file_port *)this_port;
    unsigned long MP3Err;
    pj_ssize_t	bytes;
    pj_status_t status;
    unsigned long WriteSize;

    /* Record silence if input is no-frame */
    if (frame->type == PJMEDIA_FRAME_TYPE_NONE || frame->size == 0) {
	unsigned samples_left = fport->base.info.samples_per_frame;
	unsigned samples_copied = 0;

	/* Only want to record at most 1 second of silence */
	if (fport->silence_duration >= fport->base.info.clock_rate)
	    return PJ_SUCCESS;

	while (samples_left) {
	    unsigned samples_needed = fport->mp3_samples_per_frame -
				      fport->mp3_sample_pos;
	    if (samples_needed > samples_left)
		samples_needed = samples_left;

	    pjmedia_zero_samples(fport->mp3_sample_buf + fport->mp3_sample_pos,
				 samples_needed);
	    fport->mp3_sample_pos += samples_needed;
	    samples_left -= samples_needed;
	    samples_copied += samples_needed;

	    /* Encode if we have full frame */
	    if (fport->mp3_sample_pos == fport->mp3_samples_per_frame) {
		
		/* Clear position */
		fport->mp3_sample_pos = 0;

		/* Encode ! */
		MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream,
						fport->mp3_samples_per_frame,
						fport->mp3_sample_buf, 
						fport->mp3_buf, 
						&WriteSize);
		if (MP3Err != BE_ERR_SUCCESSFUL)
		    return PJMEDIA_ERROR;

		/* Write the chunk */
		bytes = WriteSize;
		status = pj_file_write(fport->fd, fport->mp3_buf, &bytes);
		if (status != PJ_SUCCESS)
		    return status;

		/* Increment total written. */
		fport->total += bytes;
	    }
	}

	fport->silence_duration += fport->base.info.samples_per_frame;

    }
    /* If encoder is expecting different sample size, then we need to
     * buffer the samples.
     */
    else if (fport->mp3_samples_per_frame != 
	     fport->base.info.samples_per_frame) 
    {
	unsigned samples_left = frame->size / 2;
	unsigned samples_copied = 0;
	const pj_int16_t *src_samples = frame->buf;

	fport->silence_duration = 0;

	while (samples_left) {
	    unsigned samples_needed = fport->mp3_samples_per_frame -
				      fport->mp3_sample_pos;
	    if (samples_needed > samples_left)
		samples_needed = samples_left;

	    pjmedia_copy_samples(fport->mp3_sample_buf + fport->mp3_sample_pos,
				 src_samples + samples_copied,
				 samples_needed);
	    fport->mp3_sample_pos += samples_needed;
	    samples_left -= samples_needed;
	    samples_copied += samples_needed;

	    /* Encode if we have full frame */
	    if (fport->mp3_sample_pos == fport->mp3_samples_per_frame) {
		
		/* Clear position */
		fport->mp3_sample_pos = 0;

		/* Encode ! */
		MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream,
						fport->mp3_samples_per_frame,
						fport->mp3_sample_buf, 
						fport->mp3_buf, 
						&WriteSize);
		if (MP3Err != BE_ERR_SUCCESSFUL)
		    return PJMEDIA_ERROR;

		/* Write the chunk */
		bytes = WriteSize;
		status = pj_file_write(fport->fd, fport->mp3_buf, &bytes);
		if (status != PJ_SUCCESS)
		    return status;

		/* Increment total written. */
		fport->total += bytes;
	    }
	}

    } else {

	fport->silence_duration = 0;

	/* Encode ! */
	MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream,
					fport->mp3_samples_per_frame,
					frame->buf, 
					fport->mp3_buf, 
					&WriteSize);
	if (MP3Err != BE_ERR_SUCCESSFUL)
	    return PJMEDIA_ERROR;

	/* Write the chunk */
	bytes = WriteSize;
	status = pj_file_write(fport->fd, fport->mp3_buf, &bytes);
	if (status != PJ_SUCCESS)
	    return status;

	/* Increment total written. */
	fport->total += bytes;
    }

    /* Increment total written, and check if we need to call callback */
    
    if (fport->cb && fport->total >= fport->cb_size) {
	pj_status_t (*cb)(pjmedia_port*, void*);
	pj_status_t status;

	cb = fport->cb;
	fport->cb = NULL;

	status = (*cb)(this_port, this_port->port_data.pdata);
	return status;
    }

    return PJ_SUCCESS;
}
Beispiel #17
0
/*
 * Put a frame in the reverse port (upstream direction). This frame
 * will be picked up by get_frame() above.
 */
static pj_status_t rport_put_frame(pjmedia_port *this_port, 
				   pjmedia_frame *frame)
{
    struct reverse_port *rport = (struct reverse_port*) this_port;

    pj_assert(frame->size <= PJMEDIA_PIA_AVG_FSZ(&rport->base.info));

    /* Handle NULL frame */
    if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) {
	/* 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_UPSTREAM].null_cnt > rport->max_null_frames) {
	    /* Prevent the counter from overflowing and resetting back 
	     * to zero
	     */
	    rport->buf[DIR_UPSTREAM].null_cnt = rport->max_null_frames + 1;
	    return PJ_SUCCESS;
	}

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

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

	/* Generate zero frame. */
	pjmedia_zero_samples(rport->tmp_up_buf, 
			     PJMEDIA_PIA_SPF(&this_port->info));

	/* Put frame to delay buffer */
	return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, 
				     rport->tmp_up_buf);
    }

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

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

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

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

    /* Unfortunately must copy to temporary buffer since delay buf
     * modifies the frame content.
     */
    pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf,
		         PJMEDIA_PIA_SPF(&this_port->info));

    /* Put frame to delay buffer */
    return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, 
				 rport->tmp_up_buf);
}
Beispiel #18
0
/*
 * Get a multichannel frame upstream.
 * This will get mono channel frame from each port and put the
 * mono frame into the multichannel frame.
 */
static pj_status_t get_frame(pjmedia_port *this_port, 
			     pjmedia_frame *frame)
{
    struct splitcomb *sc = (struct splitcomb*) this_port;
    unsigned ch;
    pj_bool_t has_frame = PJ_FALSE;

    /* Read frame from each port */
    for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
	pjmedia_port *port = sc->port_desc[ch].port;
	pjmedia_frame mono_frame;
	pj_status_t status;

	if (!port) {
	    pjmedia_zero_samples(sc->get_buf, 
				 PJMEDIA_PIA_SPF(&this_port->info) /
				  PJMEDIA_PIA_CCNT(&this_port->info));

	} else if (sc->port_desc[ch].reversed == PJ_FALSE) {
	    /* Read from normal port */
	    mono_frame.buf = sc->get_buf;
	    mono_frame.size = PJMEDIA_PIA_AVG_FSZ(&port->info);
	    mono_frame.timestamp.u64 = frame->timestamp.u64;

	    status = pjmedia_port_get_frame(port, &mono_frame);
	    if (status != PJ_SUCCESS || 
		mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
	    {
		pjmedia_zero_samples(sc->get_buf, 
				     PJMEDIA_PIA_SPF(&port->info));
	    }

	    frame->timestamp.u64 = mono_frame.timestamp.u64;

	} else {
	    /* Read from temporary buffer for reverse port */
	    struct reverse_port *rport = (struct reverse_port*)port;

	    /* Update rport state. */
	    op_update(rport, DIR_UPSTREAM, OP_GET);

	    if (!rport->buf[DIR_UPSTREAM].paused) {
		pjmedia_delay_buf_get(rport->buf[DIR_UPSTREAM].dbuf, 
				      sc->get_buf);

	    } else {
		pjmedia_zero_samples(sc->get_buf, 
				     PJMEDIA_PIA_SPF(&port->info));
	    }

	    frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64;
	}

	/* Combine the mono frame into multichannel frame */
	store_mono_frame(sc->get_buf, 
			 (pj_int16_t*)frame->buf, ch,
			 PJMEDIA_PIA_CCNT(&this_port->info),
			 PJMEDIA_PIA_SPF(&this_port->info) /
			  PJMEDIA_PIA_CCNT(&this_port->info));

	has_frame = PJ_TRUE;
    }

    /* Return NO_FRAME is we don't get any frames from downstream ports */
    if (has_frame) {
	frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
	frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
    } else
	frame->type = PJMEDIA_FRAME_TYPE_NONE;

    return PJ_SUCCESS;
}
Beispiel #19
0
/*
 * play_callback()
 *
 * 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;
    pjmedia_channel *channel = stream->dec;
    unsigned samples_count, samples_per_frame, samples_required;
    pj_int16_t *p_out_samp;
    pj_status_t status;


    /* Return no frame is channel is paused */
    if (channel->paused) {
	frame->type = PJMEDIA_FRAME_TYPE_NONE;
	return PJ_SUCCESS;
    }

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

    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 = (pj_int16_t*) frame->buf;

    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;
    }


    /* 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 * BYTES_PER_SAMPLE;
	frame->timestamp.u64 = 0;
    }

    return PJ_SUCCESS;
}