示例#1
0
/*
 * Let the Echo Canceller know that a frame has been played to the speaker.
 */
pj_status_t pjs_echo_canceller::playback(pj_int16_t *play_frm, unsigned size) {
	/* Playing frame should be stored, as it will be used by echo_capture()
	 * as reference frame, delay buffer is used for storing the playing frames
	 * as in case there was clock drift between mic & speaker.
	 *
	 * Ticket #830:
	 * Note that pjmedia_delay_buf_put() may modify the input frame and those
	 * modified frames may not be smooth, i.e: if there were two or more
	 * consecutive pjmedia_delay_buf_get() before next pjmedia_delay_buf_put(),
	 * so we'll just feed the delay buffer with the copy of playing frame,
	 * instead of the original playing frame. However this will cause the EC
	 * uses slight 'different' frames (for reference) than actually played
	 * by the speaker.
	 */
	if(samples_per_frame!=size)
	{
		PJ_LOG(1, (THIS_FILE, "WRONG SIZE ON PLAYBACK %d != %d",size,samples_per_frame));
		return -1;
	}

	PPJ_WaitAndLock wl(*lock);

	pjmedia_copy_samples(frm_buf, play_frm, samples_per_frame);
	pjmedia_delay_buf_put(delay_buf, frm_buf);

	if (!lat_ready) {
		/* We've not built enough latency in the buffer, so put this frame
		 * in the latency buffer list.
		 */
		struct frame *frm;

		if (pj_list_empty(&lat_free)) {
			lat_ready = PJ_TRUE;
			PJ_LOG(4, (THIS_FILE, "Latency bufferring complete"));
			return PJ_SUCCESS;
		}

		frm = lat_free.prev;
		pj_list_erase(frm);

		/* Move one frame from delay buffer to the latency buffer. */
		pjmedia_delay_buf_get(delay_buf, frm_buf);
		pjmedia_copy_samples(frm->buf, frm_buf, samples_per_frame);
		pj_list_push_back(&lat_buf, frm);
	}

	return PJ_SUCCESS;
}
示例#2
0
/*
 * Let the Echo Canceller know that a frame has been played to the speaker.
 */
PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo,
					   pj_int16_t *play_frm )
{
    /* If EC algo has playback handler, just pass the frame. */
    if (echo->op->ec_playback) {
	return (*echo->op->ec_playback)(echo->state, play_frm);
    }

    /* Playing frame should be stored, as it will be used by echo_capture() 
     * as reference frame, delay buffer is used for storing the playing frames
     * as in case there was clock drift between mic & speaker.
     *
     * Ticket #830:
     * Note that pjmedia_delay_buf_put() may modify the input frame and those
     * modified frames may not be smooth, i.e: if there were two or more
     * consecutive pjmedia_delay_buf_get() before next pjmedia_delay_buf_put(),
     * so we'll just feed the delay buffer with the copy of playing frame,
     * instead of the original playing frame. However this will cause the EC 
     * uses slight 'different' frames (for reference) than actually played 
     * by the speaker.
     */
    pjmedia_copy_samples(echo->frm_buf, play_frm, 
			 echo->samples_per_frame);
    pjmedia_delay_buf_put(echo->delay_buf, echo->frm_buf);

    if (!echo->lat_ready) {
	/* We've not built enough latency in the buffer, so put this frame
	 * in the latency buffer list.
	 */
	struct frame *frm;

	if (pj_list_empty(&echo->lat_free)) {
	    echo->lat_ready = PJ_TRUE;
	    PJ_LOG(5,(echo->obj_name, "Latency bufferring complete"));
	    return PJ_SUCCESS;
	}
	    
	frm = echo->lat_free.prev;
	pj_list_erase(frm);

	/* Move one frame from delay buffer to the latency buffer. */
	pjmedia_delay_buf_get(echo->delay_buf, echo->frm_buf);
	pjmedia_copy_samples(frm->buf, echo->frm_buf, echo->samples_per_frame);
	pj_list_push_back(&echo->lat_buf, frm);
    }

    return PJ_SUCCESS;
}
示例#3
0
/*
 * Perform echo cancellation.
 */
PJ_DEF(pj_status_t) speex_aec_cancel_echo( void *state,
					   pj_int16_t *rec_frm,
					   const pj_int16_t *play_frm,
					   unsigned options,
					   void *reserved )
{
    speex_ec *echo = (speex_ec*) state;

    /* Sanity checks */
    PJ_ASSERT_RETURN(echo && rec_frm && play_frm && options==0 &&
		     reserved==NULL, PJ_EINVAL);

    /* Cancel echo, put output in temporary buffer */
    speex_echo_cancellation(echo->state, (const spx_int16_t*)rec_frm,
			    (const spx_int16_t*)play_frm,
			    (spx_int16_t*)echo->tmp_frame);


    /* Preprocess output */
    speex_preprocess_run(echo->preprocess, (spx_int16_t*)echo->tmp_frame);

    /* Copy temporary buffer back to original rec_frm */
    pjmedia_copy_samples(rec_frm, echo->tmp_frame, echo->samples_per_frame);

    return PJ_SUCCESS;

}
示例#4
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;
}
示例#5
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;
}
示例#6
0
/*
 * Get a mono frame from a reversed phase channel.
 */
static pj_status_t rport_get_frame(pjmedia_port *this_port, 
				   pjmedia_frame *frame)
{
    struct reverse_port *rport = (struct reverse_port*) this_port;
    unsigned count;

    count = rport->base.info.samples_per_frame;

    frame->size = this_port->info.bytes_per_frame;
    frame->type = PJMEDIA_FRAME_TYPE_AUDIO;

    /* Check for underflows */
    if (rport->dn_read_pos == rport->dn_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->dn_write_pos != rport->dn_underflow_pos) {
	    rport->dn_underflow_pos = rport->dn_write_pos;
	    LOG_DN_((THIS_FILE, "Underflow in downstream direction"));
	}

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

    /* Get the samples from the circular buffer */
    pjmedia_copy_samples(frame->buf, 
			 rport->dnstream_buf[rport->dn_read_pos],
			 count);
    rport->dn_read_pos = (rport->dn_read_pos+1) % rport->buf_cnt;

    return PJ_SUCCESS;
}
示例#7
0
/*
 * Perform echo cancellation to captured frame.
 */
PJ_DEF(pj_status_t) speex_aec_capture( void *state,
				       pj_int16_t *rec_frm,
				       unsigned options )
{
    speex_ec *echo = (speex_ec*) state;

    /* Sanity checks */
    PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL);

    PJ_UNUSED_ARG(options);

    /* Cancel echo */
    pjmedia_copy_samples(echo->tmp_frame, rec_frm, echo->samples_per_frame);
    speex_echo_capture(echo->state,
		       (spx_int16_t*)echo->tmp_frame,
		       (spx_int16_t*)rec_frm);

    /* Apply preprocessing */
    speex_preprocess_run(echo->preprocess, (spx_int16_t*)rec_frm);

    return PJ_SUCCESS;
}
示例#8
0
/*
 * Find out latency
 */
static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav)
{
    pjmedia_frame frm;
    short *buf;
    unsigned i, samples_per_frame, read, len;
    unsigned start_pos;
    pj_status_t status;

    unsigned lat_sum = 0,
	     lat_cnt = 0,
	     lat_min = 10000,
	     lat_max = 0;

    samples_per_frame = PJMEDIA_PIA_SPF(&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 = 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 * PJMEDIA_PIA_SRATE(&wav->info)) {
	puts("Error: too short");
	return -1;
    }

    start_pos = 0;
    while (start_pos < len/2 - PJMEDIA_PIA_SRATE(&wav->info)) {
	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 + PJMEDIA_PIA_SRATE(&wav->info) * 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 * PJMEDIA_PIA_SRATE(&wav->info) / 1000;

	/* Get the largest signal in the next 500ms */
	max_signal = 0;
	max_echo_pos = pos;
	for (i=pos; i<pos+PJMEDIA_PIA_SRATE(&wav->info)/2; ++i) {
	    if (abs(buf[i]) > max_signal) {
		max_signal = abs(buf[i]);
		max_echo_pos = i;
	    }
	}

	lat = (max_echo_pos - max_signal_pos) * 1000 / PJMEDIA_PIA_SRATE(&wav->info);
	
#if 0
	printf("Latency = %u\n", lat);
#endif

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

	/* Advance next loop */
	start_pos += PJMEDIA_PIA_SRATE(&wav->info);
    }

    printf("Latency average = %u\n", lat_sum / lat_cnt);
    printf("Latency minimum = %u\n", lat_min);
    printf("Latency maximum = %u\n", lat_max);
    printf("Number of data  = %u\n", lat_cnt);
    return 0;
}
示例#9
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;
}
示例#10
0
static int PaPlayerCallback( const void *input, 
			     void *output,
			     unsigned long frameCount,
			     const PaStreamCallbackTimeInfo* timeInfo,
			     PaStreamCallbackFlags statusFlags,
			     void *userData )
{
    struct pa_aud_stream *stream = (struct pa_aud_stream*) userData;
    pj_status_t status = 0;
    unsigned nsamples_req = frameCount * stream->channel_count;

    PJ_UNUSED_ARG(input);
    PJ_UNUSED_ARG(timeInfo);

    if (stream->quit_flag)
	goto on_break;

    if (output == NULL)
	return paContinue;

    /* Known cases of callback's thread:
     * - The thread may be changed in the middle of a session, e.g: in MacOS 
     *   it happens when plugging/unplugging headphone.
     * - The same thread may be reused in consecutive sessions. The first
     *   session will leave TLS set, but release the TLS data address,
     *   so the second session must re-register the callback's thread.
     */
    if (stream->play_thread_initialized == 0 || !pj_thread_is_registered()) 
    {
	pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
	status = pj_thread_register("portaudio", stream->play_thread_desc,
				    &stream->play_thread);
	stream->play_thread_initialized = 1;
	PJ_LOG(5,(THIS_FILE, "Player thread started"));
    }

    if (statusFlags & paOutputUnderflow)
	++stream->underflow;
    if (statusFlags & paOutputOverflow)
	++stream->overflow;


    /* Check if any buffered samples */
    if (stream->play_buf_count) {
	/* samples buffered >= requested by sound device */
	if (stream->play_buf_count >= nsamples_req) {
	    pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, 
				 nsamples_req);
	    stream->play_buf_count -= nsamples_req;
	    pjmedia_move_samples(stream->play_buf, 
				 stream->play_buf + nsamples_req,
				 stream->play_buf_count);
	    nsamples_req = 0;
	    
	    return paContinue;
	}

	/* samples buffered < requested by sound device */
	pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, 
			     stream->play_buf_count);
	nsamples_req -= stream->play_buf_count;
	output = (pj_int16_t*)output + stream->play_buf_count;
	stream->play_buf_count = 0;
    }

    /* Fill output buffer as requested */
    while (nsamples_req && status == 0) {
	if (nsamples_req >= stream->samples_per_frame) {
	    pjmedia_frame frame;

	    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
	    frame.buf = output;
	    frame.size = stream->samples_per_frame *  stream->bytes_per_sample;
	    frame.timestamp.u64 = stream->play_timestamp.u64;
	    frame.bit_info = 0;

	    status = (*stream->play_cb)(stream->user_data, &frame);
	    if (status != PJ_SUCCESS)
		goto on_break;

	    if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
		pj_bzero(frame.buf, frame.size);

	    nsamples_req -= stream->samples_per_frame;
	    output = (pj_int16_t*)output + stream->samples_per_frame;
	} else {
	    pjmedia_frame frame;

	    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
	    frame.buf = stream->play_buf;
	    frame.size = stream->samples_per_frame *  stream->bytes_per_sample;
	    frame.timestamp.u64 = stream->play_timestamp.u64;
	    frame.bit_info = 0;

	    status = (*stream->play_cb)(stream->user_data, &frame);
	    if (status != PJ_SUCCESS)
		goto on_break;

	    if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
		pj_bzero(frame.buf, frame.size);

	    pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, 
				 nsamples_req);
	    stream->play_buf_count = stream->samples_per_frame - nsamples_req;
	    pjmedia_move_samples(stream->play_buf, 
				 stream->play_buf+nsamples_req,
				 stream->play_buf_count);
	    nsamples_req = 0;
	}

	stream->play_timestamp.u64 += stream->samples_per_frame /
				      stream->channel_count;
    }
    
    if (status==0) 
	return paContinue;

on_break:
    stream->play_thread_exited = 1;
    return paAbort;
}
示例#11
0
static int PaRecorderCallback(const void *input, 
			      void *output,
			      unsigned long frameCount,
			      const PaStreamCallbackTimeInfo* timeInfo,
			      PaStreamCallbackFlags statusFlags,
			      void *userData )
{
    struct pa_aud_stream *stream = (struct pa_aud_stream*) userData;
    pj_status_t status = 0;
    unsigned nsamples;

    PJ_UNUSED_ARG(output);
    PJ_UNUSED_ARG(timeInfo);

    if (stream->quit_flag)
	goto on_break;

    if (input == NULL)
	return paContinue;

    /* Known cases of callback's thread:
     * - The thread may be changed in the middle of a session, e.g: in MacOS 
     *   it happens when plugging/unplugging headphone.
     * - The same thread may be reused in consecutive sessions. The first
     *   session will leave TLS set, but release the TLS data address,
     *   so the second session must re-register the callback's thread.
     */
    if (stream->rec_thread_initialized == 0 || !pj_thread_is_registered()) 
    {
	pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
	status = pj_thread_register("pa_rec", stream->rec_thread_desc, 
				    &stream->rec_thread);
	stream->rec_thread_initialized = 1;
	PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
    }

    if (statusFlags & paInputUnderflow)
	++stream->underflow;
    if (statusFlags & paInputOverflow)
	++stream->overflow;

    /* Calculate number of samples we've got */
    nsamples = frameCount * stream->channel_count + stream->rec_buf_count;

    if (nsamples >= stream->samples_per_frame) 
    {
	/* If buffer is not empty, combine the buffer with the just incoming
	 * samples, then call put_frame.
	 */
	if (stream->rec_buf_count) {
	    unsigned chunk_count = 0;
	    pjmedia_frame frame;
	
	    chunk_count = stream->samples_per_frame - stream->rec_buf_count;
	    pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count,
				 (pj_int16_t*)input, chunk_count);

	    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
	    frame.buf = (void*) stream->rec_buf;
	    frame.size = stream->samples_per_frame * stream->bytes_per_sample;
	    frame.timestamp.u64 = stream->rec_timestamp.u64;
	    frame.bit_info = 0;

	    status = (*stream->rec_cb)(stream->user_data, &frame);

	    input = (pj_int16_t*) input + chunk_count;
	    nsamples -= stream->samples_per_frame;
	    stream->rec_buf_count = 0;
	    stream->rec_timestamp.u64 += stream->samples_per_frame /
					 stream->channel_count;
	}

	/* Give all frames we have */
	while (nsamples >= stream->samples_per_frame && status == 0) {
	    pjmedia_frame frame;

	    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
	    frame.buf = (void*) input;
	    frame.size = stream->samples_per_frame * stream->bytes_per_sample;
	    frame.timestamp.u64 = stream->rec_timestamp.u64;
	    frame.bit_info = 0;

	    status = (*stream->rec_cb)(stream->user_data, &frame);

	    input = (pj_int16_t*) input + stream->samples_per_frame;
	    nsamples -= stream->samples_per_frame;
	    stream->rec_timestamp.u64 += stream->samples_per_frame /
					 stream->channel_count;
	}

	/* Store the remaining samples into the buffer */
	if (nsamples && status == 0) {
	    stream->rec_buf_count = nsamples;
	    pjmedia_copy_samples(stream->rec_buf, (pj_int16_t*)input, 
			         nsamples);
	}

    } else {
	/* Not enough samples, let's just store them in the buffer */
	pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count,
			     (pj_int16_t*)input, 
			     frameCount * stream->channel_count);
	stream->rec_buf_count += frameCount * stream->channel_count;
    }

    if (status==0) 
	return paContinue;

on_break:
    stream->rec_thread_exited = 1;
    return paAbort;
}
示例#12
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);
}
示例#13
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;
}
示例#14
0
/* callbacks to get data */
int bdimad_PlaybackCallback(void *buffer, int samples, void *user_data)
{
    pj_status_t status = PJ_SUCCESS;
    pjmedia_frame frame;
    struct bd_stream *strm = (struct bd_stream*)user_data;
    unsigned nsamples_req = samples * strm->channel_count;

    if(!strm->go) 
	goto on_break;

    /* Known cases of callback's thread:
     * - The thread may be changed in the middle of a session, e.g: in MacOS 
     *   it happens when plugging/unplugging headphone.
     * - The same thread may be reused in consecutive sessions. The first
     *   session will leave TLS set, but release the TLS data address,
     *   so the second session must re-register the callback's thread.
     */
    if (strm->play_thread_initialized == 0 || !pj_thread_is_registered()) 
    {
	pj_bzero(strm->play_thread_desc, sizeof(pj_thread_desc));
	status = pj_thread_register("bd_PlaybackCallback", 
				    strm->play_thread_desc,
				    &strm->play_thread);
	if (status != PJ_SUCCESS)
	    goto on_break;

	strm->play_thread_initialized = 1;
	PJ_LOG(5,(THIS_FILE, "Player thread started"));
    }

    /*
    PLAY
    */
    if(strm->fmt_id == PJMEDIA_FORMAT_L16) {
	/* Check if any buffered samples */
	if (strm->play_buf_count) {
	    /* samples buffered >= requested by sound device */
	    if (strm->play_buf_count >= nsamples_req) {
		pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, 
				     nsamples_req);
		strm->play_buf_count -= nsamples_req;
		pjmedia_move_samples(strm->play_buf, 
				     strm->play_buf + nsamples_req,
				     strm->play_buf_count);		

		return nsamples_req;
	    }

	    /* samples buffered < requested by sound device */
	    pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, 
				 strm->play_buf_count);
	    nsamples_req -= strm->play_buf_count;
	    buffer = (pj_int16_t*)buffer + strm->play_buf_count;
	    strm->play_buf_count = 0;
	}

	/* Fill output buffer as requested */
	while (nsamples_req && status == 0) {
	    if (nsamples_req >= strm->samples_per_frame) {		
		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
		frame.buf = buffer;
		frame.size = strm->bytes_per_frame;
		frame.timestamp.u64 = strm->timestampPlayback.u64;
		frame.bit_info = 0;

		status = (*strm->play_cb)(strm->user_data, &frame);
		if (status != PJ_SUCCESS)
		    return 0;

		if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
		    pj_bzero(frame.buf, frame.size);

		nsamples_req -= strm->samples_per_frame;
		buffer = (pj_int16_t*)buffer + strm->samples_per_frame;
	    } else {		
		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
		frame.buf = strm->play_buf;
		frame.size = strm->bytes_per_frame;
		frame.timestamp.u64 = strm->timestampPlayback.u64;
		frame.bit_info = 0;

		status = (*strm->play_cb)(strm->user_data, &frame);
		if (status != PJ_SUCCESS)
		    return 0;

		if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
		    pj_bzero(frame.buf, frame.size);

		pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, 
				     nsamples_req);
		strm->play_buf_count = strm->samples_per_frame - 
				       nsamples_req;
		pjmedia_move_samples(strm->play_buf, 
				     strm->play_buf+nsamples_req,
				     strm->play_buf_count);
		nsamples_req = 0;
	    }

	    strm->timestampPlayback.u64 += strm->samples_per_frame /
					   strm->channel_count;
	}
    } else {
        pj_assert(!"Frame type not supported");
    }

    if(status != PJ_SUCCESS) {
        return 0;
    }

    strm->timestampPlayback.u64 += strm->param.samples_per_frame / 
				   strm->param.channel_count;

    if (status == 0)
	return samples;

on_break:
    strm->play_thread_exited = 1;
    return 0;
}
示例#15
0
/* callbacks to set data */
void bdimad_CaptureCallback(void *buffer, int samples, void *user_data)
{
    pj_status_t status = PJ_SUCCESS;
    pjmedia_frame frame;
    unsigned nsamples;    

    struct bd_stream *strm = (struct bd_stream*)user_data;    

    if(!strm->go) 
	goto on_break;

    /* Known cases of callback's thread:
     * - The thread may be changed in the middle of a session, e.g: in MacOS 
     *   it happens when plugging/unplugging headphone.
     * - The same thread may be reused in consecutive sessions. The first
     *   session will leave TLS set, but release the TLS data address,
     *   so the second session must re-register the callback's thread.
     */
    if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered()) 
    {
	pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
	status = pj_thread_register("bd_CaptureCallback", 
				    strm->rec_thread_desc, 
				    &strm->rec_thread);
	if (status != PJ_SUCCESS)
	    goto on_break;

	strm->rec_thread_initialized = 1;
	PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
    }
    
    /* Calculate number of samples we've got */
    nsamples = samples * strm->channel_count + strm->rec_buf_count;

    /*
    RECORD
    */
    if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
	if (nsamples >= strm->samples_per_frame) {
	    /* If buffer is not empty, combine the buffer with the just incoming
	     * samples, then call put_frame.
	     */
	    if (strm->rec_buf_count) {
		unsigned chunk_count = 0;		

		chunk_count = strm->samples_per_frame - strm->rec_buf_count;
		pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
				     (pj_int16_t*)buffer, chunk_count);

		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
		frame.buf = (void*) strm->rec_buf;
		frame.size = strm->bytes_per_frame;
		frame.timestamp.u64 = strm->timestampCapture.u64;
		frame.bit_info = 0;

		status = (*strm->rec_cb)(strm->user_data, &frame);

		buffer = (pj_int16_t*) buffer + chunk_count;
		nsamples -= strm->samples_per_frame;
		strm->rec_buf_count = 0;
		strm->timestampCapture.u64 += strm->samples_per_frame /
					      strm->channel_count;
	    }

	    /* Give all frames we have */
	    while (nsamples >= strm->samples_per_frame && status == 0) {		
		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
		frame.buf = (void*) buffer;
		frame.size = strm->bytes_per_frame;
		frame.timestamp.u64 = strm->timestampCapture.u64;
		frame.bit_info = 0;

		status = (*strm->rec_cb)(strm->user_data, &frame);

		buffer = (pj_int16_t*) buffer + strm->samples_per_frame;
		nsamples -= strm->samples_per_frame;
		strm->timestampCapture.u64 += strm->samples_per_frame /
					      strm->channel_count;
	    }

	    /* Store the remaining samples into the buffer */
	    if (nsamples && status == 0) {
		strm->rec_buf_count = nsamples;
		pjmedia_copy_samples(strm->rec_buf, (pj_int16_t*)buffer, 
				     nsamples);
	    }

	} else {
	    /* Not enough samples, let's just store them in the buffer */
	    pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
				 (pj_int16_t*)buffer, 
				 samples * strm->channel_count);
	    strm->rec_buf_count += samples * strm->channel_count;	
	}
    }  else {
        pj_assert(!"Frame type not supported");
    }
    
    strm->timestampCapture.u64 += strm->param.samples_per_frame / 
				  strm->param.channel_count;

    if (status==0)
	return;

on_break:
    strm->rec_thread_exited = 1;    
}