⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sound_port.c

📁 一个开源的sip源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: sound_port.c 1296 2007-05-23 14:25:32Z bennylp $ */
/* 
 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include <pjmedia/sound_port.h>
#include <pjmedia/echo.h>
#include <pjmedia/errno.h>
#include <pjmedia/plc.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/rand.h>
#include <pj/string.h>	    /* pj_memset() */

//#define SIMULATE_LOST_PCT   20
#define AEC_TAIL	    128	    /* default AEC length in ms */
#define AEC_SUSPEND_LIMIT   5	    /* seconds of no activity	*/

#define THIS_FILE	    "sound_port.c"

enum
{
    PJMEDIA_PLC_ENABLED	    = 1,
};

//#define DEFAULT_OPTIONS	PJMEDIA_PLC_ENABLED
#define DEFAULT_OPTIONS	    0


struct pjmedia_snd_port
{
    int			 rec_id;
    int			 play_id;
    pjmedia_snd_stream	*snd_stream;
    pjmedia_dir		 dir;
    pjmedia_port	*port;
    unsigned		 options;

    pjmedia_echo_state	*ec_state;
    unsigned		 aec_tail_len;

    pj_bool_t		 ec_suspended;
    unsigned		 ec_suspend_count;
    unsigned		 ec_suspend_limit;

    pjmedia_plc		*plc;

    unsigned		 clock_rate;
    unsigned		 channel_count;
    unsigned		 samples_per_frame;
    unsigned		 bits_per_sample;
};

/*
 * The callback called by sound player when it needs more samples to be
 * played.
 */
static pj_status_t play_cb(/* in */   void *user_data,
			   /* in */   pj_uint32_t timestamp,
			   /* out */  void *output,
			   /* out */  unsigned size)
{
    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
    pjmedia_port *port;
    pjmedia_frame frame;
    pj_status_t status;

    /* We're risking accessing the port without holding any mutex.
     * It's possible that port is disconnected then destroyed while
     * we're trying to access it.
     * But in the name of performance, we'll try this approach until
     * someone complains when it crashes.
     */
    port = snd_port->port;
    if (port == NULL)
	goto no_frame;

    frame.buf = output;
    frame.size = size;
    frame.timestamp.u32.hi = 0;
    frame.timestamp.u32.lo = timestamp;

    status = pjmedia_port_get_frame(port, &frame);
    if (status != PJ_SUCCESS)
	goto no_frame;

    if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
	goto no_frame;

    /* Must supply the required samples */
    pj_assert(frame.size == size);

#ifdef SIMULATE_LOST_PCT
    /* Simulate packet lost */
    if (pj_rand() % 100 < SIMULATE_LOST_PCT) {
	PJ_LOG(4,(THIS_FILE, "Frame dropped"));
	goto no_frame;
    }
#endif

    if (snd_port->plc)
	pjmedia_plc_save(snd_port->plc, (pj_int16_t*) output);

    if (snd_port->ec_state) {
	if (snd_port->ec_suspended) {
	    snd_port->ec_suspended = PJ_FALSE;
	    //pjmedia_echo_state_reset(snd_port->ec_state);
	    PJ_LOG(4,(THIS_FILE, "EC activated"));
	}
	snd_port->ec_suspend_count = 0;
	pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)output);
    }


    return PJ_SUCCESS;

no_frame:

    if (snd_port->ec_state && !snd_port->ec_suspended) {
	++snd_port->ec_suspend_count;
	if (snd_port->ec_suspend_count > snd_port->ec_suspend_limit) {
	    snd_port->ec_suspended = PJ_TRUE;
	    PJ_LOG(4,(THIS_FILE, "EC suspended because of inactivity"));
	}
	if (snd_port->ec_state) {
	    /* To maintain correct delay in EC */
	    pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)output);
	}
    }

    /* Apply PLC */
    if (snd_port->plc) {

	pjmedia_plc_generate(snd_port->plc, (pj_int16_t*) output);
#ifdef SIMULATE_LOST_PCT
	PJ_LOG(4,(THIS_FILE, "Lost frame generated"));
#endif
    } else {
	pj_bzero(output, size);
    }


    return PJ_SUCCESS;
}


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

    /* We're risking accessing the port without holding any mutex.
     * It's possible that port is disconnected then destroyed while
     * we're trying to access it.
     * But in the name of performance, we'll try this approach until
     * someone complains when it crashes.
     */
    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*) input, 0);
    }

    frame.buf = (void*)input;
    frame.size = size;
    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
    frame.timestamp.u32.lo = timestamp;

    pjmedia_port_put_frame(port, &frame);

    return PJ_SUCCESS;
}

/*
 * Start the sound stream.
 * This may be called even when the sound stream has already been started.
 */
static pj_status_t start_sound_device( pj_pool_t *pool,
				       pjmedia_snd_port *snd_port )
{
    pj_status_t status;

    /* Check if sound has been started. */
    if (snd_port->snd_stream != NULL)
	return PJ_SUCCESS;

    /* Open sound stream. */
    if (snd_port->dir == PJMEDIA_DIR_CAPTURE) {
	status = pjmedia_snd_open_rec( snd_port->rec_id, 
				       snd_port->clock_rate,
				       snd_port->channel_count,
				       snd_port->samples_per_frame,
				       snd_port->bits_per_sample,
				       &rec_cb,
				       snd_port,
				       &snd_port->snd_stream);

    } else if (snd_port->dir == PJMEDIA_DIR_PLAYBACK) {
	status = pjmedia_snd_open_player( snd_port->play_id, 
					  snd_port->clock_rate,
					  snd_port->channel_count,
					  snd_port->samples_per_frame,
					  snd_port->bits_per_sample,
					  &play_cb,
					  snd_port,
					  &snd_port->snd_stream);

    } else if (snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
	status = pjmedia_snd_open( snd_port->rec_id, 
				   snd_port->play_id,
				   snd_port->clock_rate,
				   snd_port->channel_count,
				   snd_port->samples_per_frame,
				   snd_port->bits_per_sample,
				   &rec_cb,
				   &play_cb,
				   snd_port,
				   &snd_port->snd_stream);
    } else {
	pj_assert(!"Invalid dir");
	status = PJ_EBUG;
    }

    if (status != PJ_SUCCESS)
	return status;


#ifdef SIMULATE_LOST_PCT
    snd_port->options |= PJMEDIA_PLC_ENABLED;
#endif

    /* If we have player components, allocate buffer to save the last
     * frame played to the speaker. The last frame is used for packet
     * lost concealment (PLC) algorithm.
     */
    if ((snd_port->dir & PJMEDIA_DIR_PLAYBACK) &&
	(snd_port->options & PJMEDIA_PLC_ENABLED)) 
    {
	status = pjmedia_plc_create(pool, snd_port->clock_rate, 
				    snd_port->samples_per_frame * 
					snd_port->channel_count,
				    0, &snd_port->plc);
	if (status != PJ_SUCCESS) {
	    PJ_LOG(4,(THIS_FILE, "Unable to create PLC"));
	    snd_port->plc = NULL;
	}
    }

    /* Inactivity limit before EC is suspended. */
    snd_port->ec_suspend_limit = AEC_SUSPEND_LIMIT *
				 (snd_port->clock_rate / 
				  snd_port->samples_per_frame);

    /* Start sound stream. */
    status = pjmedia_snd_stream_start(snd_port->snd_stream);
    if (status != PJ_SUCCESS) {
	pjmedia_snd_stream_close(snd_port->snd_stream);
	snd_port->snd_stream = NULL;
	return status;
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -