conference.c
来自「基于sip协议的网络电话源码」· C语言 代码 · 共 1,932 行 · 第 1/4 页
C
1,932 行
/* $Id: conference.c 974 2007-02-19 01:13:53Z 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/conference.h>#include <pjmedia/alaw_ulaw.h>#include <pjmedia/errno.h>#include <pjmedia/port.h>#include <pjmedia/resample.h>#include <pjmedia/silencedet.h>#include <pjmedia/sound_port.h>#include <pjmedia/stream.h>#include <pj/array.h>#include <pj/assert.h>#include <pj/log.h>#include <pj/pool.h>#include <pj/string.h>/* CONF_DEBUG enables detailed operation of the conference bridge. * Beware that it prints large amounts of logs (several lines per frame). *///#define CONF_DEBUG#ifdef CONF_DEBUG# include <stdio.h># define TRACE_(x) PJ_LOG(5,x)#else# define TRACE_(x)#endif/* REC_FILE macro enables recording of the samples written to the sound * device. The file contains RAW PCM data with no header, and has the * same settings (clock rate etc) as the conference bridge. * This should only be enabled when debugging audio quality *only*. *///#define REC_FILE "confrec.pcm"#ifdef REC_FILEstatic FILE *fhnd_rec;#endif#define THIS_FILE "conference.c"#define RX_BUF_COUNT PJMEDIA_SOUND_BUFFER_COUNT#define BYTES_PER_SAMPLE 2#define SIGNATURE PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'F')#define SIGNATURE_PORT PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'P')/* Normal level is hardcodec to 128 in all over places */#define NORMAL_LEVEL 128#define SLOT_TYPE unsigned#define INVALID_SLOT ((SLOT_TYPE)-1)/* These are settings to control the adaptivity of changes in the * signal level of the ports, so that sudden change in signal level * in the port does not cause misaligned signal (which causes noise). */#if 1# define ATTACK_A 10# define ATTACK_B 2# define DECAY_A 10# define DECAY_B 2#else /* To simulate old behavior */# define ATTACK_A 0# define ATTACK_B 1# define DECAY_A 0# define DECAY_B 1#endif/* * DON'T GET CONFUSED WITH TX/RX!! * * TX and RX directions are always viewed from the conference bridge's point * of view, and NOT from the port's point of view. So TX means the bridge * is transmitting to the port, RX means the bridge is receiving from the * port. *//** * This is a port connected to conference bridge. */struct conf_port{ pj_str_t name; /**< Port name. */ pjmedia_port *port; /**< get_frame() and put_frame() */ pjmedia_port_op rx_setting; /**< Can we receive from this port */ pjmedia_port_op tx_setting; /**< Can we transmit to this port */ unsigned listener_cnt; /**< Number of listeners. */ SLOT_TYPE *listener_slots;/**< Array of listeners. */ unsigned transmitter_cnt;/**<Number of transmitters. */ pjmedia_silence_det *vad; /**< VAD for this port. */ /* Shortcut for port info. */ unsigned clock_rate; /**< Port's clock rate. */ unsigned samples_per_frame; /**< Port's samples per frame. */ /* Last level calculated from this port */ pj_int32_t last_level; /* Calculated signal levels: */ unsigned tx_level; /**< Last tx level to this port. */ unsigned rx_level; /**< Last rx level from this port. */ /* The normalized signal level adjustment. * A value of 128 (NORMAL_LEVEL) means there's no adjustment. */ unsigned tx_adj_level; /**< Adjustment for TX. */ unsigned rx_adj_level; /**< Adjustment for RX. */ /* Resample, for converting clock rate, if they're different. */ pjmedia_resample *rx_resample; pjmedia_resample *tx_resample; /* RX buffer is temporary buffer to be used when there is mismatch * between port's sample rate or ptime with conference's sample rate * or ptime. The buffer is used for sampling rate conversion AND/OR to * buffer the samples until there are enough samples to fulfill a * complete frame to be processed by the bridge. * * When both sample rate AND ptime of the port match the conference * settings, this buffer will not be created. * * This buffer contains samples at port's clock rate. * The size of this buffer is the sum between port's samples per frame * and bridge's samples per frame. */ pj_int16_t *rx_buf; /**< The RX buffer. */ unsigned rx_buf_cap; /**< Max size, in samples */ unsigned rx_buf_count; /**< # of samples in the buf. */ /* Mix buf is a temporary buffer used to calculate the average signal * received by this port from all other ports. Samples from all ports * that are transmitting to this port will be accumulated here, then * they will be divided by the source level before the samples are put * to the TX buffer of this port. * * This buffer contains samples at bridge's clock rate. * The size of this buffer is equal to samples per frame of the bridge. * * Note that the samples here are unsigned 32bit. */ unsigned src_level; /**< Sum of input levels */ unsigned src_cnt; /**< Number of sources. */ pj_uint32_t *mix_buf; /**< Total sum of signal. */ /* Tx buffer is a temporary buffer to be used when there's mismatch * between port's clock rate or ptime with conference's sample rate * or ptime. This buffer is used as the source of the sampling rate * conversion AND/OR to buffer the samples until there are enough * samples to fulfill a complete frame to be transmitted to the port. * * When both sample rate and ptime of the port match the bridge's * settings, this buffer will not be created. * * This buffer contains samples at port's clock rate. * The size of this buffer is the sum between port's samples per frame * and bridge's samples per frame. */ pj_int16_t *tx_buf; /**< Tx buffer. */ unsigned tx_buf_cap; /**< Max size, in samples. */ unsigned tx_buf_count; /**< # of samples in the buffer. */ /* Snd buffers is a special buffer for sound device port (port 0, master * port). It's not used by other ports. * * There are multiple numbers of this buffer, because we can not expect * the mic and speaker thread to run equally after one another. In most * systems, each thread will run multiple times before the other thread * gains execution time. For example, in my system, mic thread is called * three times, then speaker thread is called three times, and so on. */ int snd_write_pos, snd_read_pos; pj_int16_t *snd_buf[RX_BUF_COUNT]; /**< Buffer */};/* * Conference bridge. */struct pjmedia_conf{ unsigned options; /**< Bitmask options. */ unsigned max_ports; /**< Maximum ports. */ unsigned port_cnt; /**< Current number of ports. */ unsigned connect_cnt; /**< Total number of connections */ pjmedia_snd_port *snd_dev_port; /**< Sound device port. */ pjmedia_port *master_port; /**< Port zero's port. */ char master_name_buf[80]; /**< Port0 name buffer. */ pj_mutex_t *mutex; /**< Conference mutex. */ struct conf_port **ports; /**< Array of ports. */ pj_uint16_t *uns_buf; /**< Buf for unsigned conversion */ unsigned clock_rate; /**< Sampling rate. */ unsigned channel_count;/**< Number of channels (1=mono). */ unsigned samples_per_frame; /**< Samples per frame. */ unsigned bits_per_sample; /**< Bits per sample. */};/* Prototypes */static pj_status_t put_frame(pjmedia_port *this_port, const pjmedia_frame *frame);static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame *frame);static pj_status_t get_frame_pasv(pjmedia_port *this_port, pjmedia_frame *frame);static pj_status_t destroy_port(pjmedia_port *this_port);/* * Create port. */static pj_status_t create_conf_port( pj_pool_t *pool, pjmedia_conf *conf, pjmedia_port *port, const pj_str_t *name, struct conf_port **p_conf_port){ struct conf_port *conf_port; pj_status_t status; /* Create port. */ conf_port = pj_pool_zalloc(pool, sizeof(struct conf_port)); PJ_ASSERT_RETURN(conf_port, PJ_ENOMEM); /* Set name */ pj_strdup_with_null(pool, &conf_port->name, name); /* Default has tx and rx enabled. */ conf_port->rx_setting = PJMEDIA_PORT_ENABLE; conf_port->tx_setting = PJMEDIA_PORT_ENABLE; /* Default level adjustment is 128 (which means no adjustment) */ conf_port->tx_adj_level = NORMAL_LEVEL; conf_port->rx_adj_level = NORMAL_LEVEL; /* Create transmit flag array */ conf_port->listener_slots = pj_pool_zalloc(pool, conf->max_ports * sizeof(SLOT_TYPE)); PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM); /* Save some port's infos, for convenience. */ if (port) { conf_port->port = port; conf_port->clock_rate = port->info.clock_rate; conf_port->samples_per_frame = port->info.samples_per_frame; } else { conf_port->port = NULL; conf_port->clock_rate = conf->clock_rate; conf_port->samples_per_frame = conf->samples_per_frame; } /* Create and init vad. */ status = pjmedia_silence_det_create( pool, conf_port->clock_rate, conf_port->samples_per_frame, &conf_port->vad); if (status != PJ_SUCCESS) return status; /* Set fixed */ pjmedia_silence_det_set_fixed(conf_port->vad, 2); /* Set VAD name */ pjmedia_silence_det_set_name(conf_port->vad, conf_port->name.ptr); /* If port's clock rate is different than conference's clock rate, * create a resample sessions. */ if (conf_port->clock_rate != conf->clock_rate) { pj_bool_t high_quality; pj_bool_t large_filter; high_quality = ((conf->options & PJMEDIA_CONF_USE_LINEAR)==0); large_filter = ((conf->options & PJMEDIA_CONF_SMALL_FILTER)==0); /* Create resample for rx buffer. */ status = pjmedia_resample_create( pool, high_quality, large_filter, conf_port->clock_rate,/* Rate in */ conf->clock_rate, /* Rate out */ conf->samples_per_frame * conf_port->clock_rate / conf->clock_rate, &conf_port->rx_resample); if (status != PJ_SUCCESS) return status; /* Create resample for tx buffer. */ status = pjmedia_resample_create(pool, high_quality, large_filter, conf->clock_rate, /* Rate in */ conf_port->clock_rate, /* Rate out */ conf->samples_per_frame, &conf_port->tx_resample); if (status != PJ_SUCCESS) return status; } /* * Initialize rx and tx buffer, only when port's samples per frame or * port's clock rate is different then the conference bridge settings. */ if (conf_port->clock_rate != conf->clock_rate || conf_port->samples_per_frame != conf->samples_per_frame) { /* Create RX buffer. */ conf_port->rx_buf_cap = (unsigned)(conf_port->samples_per_frame + conf->samples_per_frame * conf_port->clock_rate * 1.0 / conf->clock_rate); conf_port->rx_buf_count = 0; conf_port->rx_buf = pj_pool_alloc(pool, conf_port->rx_buf_cap * sizeof(conf_port->rx_buf[0])); PJ_ASSERT_RETURN(conf_port->rx_buf, PJ_ENOMEM); /* Create TX buffer. */ conf_port->tx_buf_cap = conf_port->rx_buf_cap; conf_port->tx_buf_count = 0; conf_port->tx_buf = pj_pool_alloc(pool, conf_port->tx_buf_cap * sizeof(conf_port->tx_buf[0])); PJ_ASSERT_RETURN(conf_port->tx_buf, PJ_ENOMEM); } /* Create mix buffer. */ conf_port->mix_buf = pj_pool_zalloc(pool, conf->samples_per_frame * sizeof(conf_port->mix_buf[0])); PJ_ASSERT_RETURN(conf_port->mix_buf, PJ_ENOMEM); /* Done */ *p_conf_port = conf_port; return PJ_SUCCESS;}/* * Add passive port. */static pj_status_t create_pasv_port( pjmedia_conf *conf, pj_pool_t *pool, const pj_str_t *name, pjmedia_port *port, struct conf_port **p_conf_port){ struct conf_port *conf_port; unsigned i; pj_status_t status; /* Create port */ status = create_conf_port(pool, conf, port, name, &conf_port); if (status != PJ_SUCCESS) return status; /* Passive port has rx buffers. */ for (i=0; i<RX_BUF_COUNT; ++i) { conf_port->snd_buf[i] = pj_pool_zalloc(pool, conf->samples_per_frame * sizeof(conf_port->snd_buf[0][0])); if (conf_port->snd_buf[i] == NULL) { return PJ_ENOMEM; } } conf_port->snd_write_pos = 0; conf_port->snd_read_pos = 0; *p_conf_port = conf_port; return PJ_SUCCESS;}/* * Create port zero for the sound device. */static pj_status_t create_sound_port( pj_pool_t *pool, pjmedia_conf *conf ){ struct conf_port *conf_port; pj_str_t name = { "Master/sound", 12 }; pj_status_t status; status = create_pasv_port(conf, pool, &name, NULL, &conf_port); if (status != PJ_SUCCESS) return status; /* Create sound device port: */ if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) { pjmedia_snd_stream *strm; pjmedia_snd_stream_info si; /* * If capture is disabled then create player only port. * Otherwise create bidirectional sound device port. */ if (conf->options & PJMEDIA_CONF_NO_MIC) { status = pjmedia_snd_port_create_player(pool, -1, conf->clock_rate, conf->channel_count, conf->samples_per_frame, conf->bits_per_sample, 0, /* options */ &conf->snd_dev_port); } else { status = pjmedia_snd_port_create( pool, -1, -1, conf->clock_rate, conf->channel_count, conf->samples_per_frame, conf->bits_per_sample, 0, /* Options */ &conf->snd_dev_port); } if (status != PJ_SUCCESS) return status; strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port); status = pjmedia_snd_stream_get_info(strm, &si); if (status == PJ_SUCCESS) { const pjmedia_snd_dev_info *snd_dev_info; if (conf->options & PJMEDIA_CONF_NO_MIC) snd_dev_info = pjmedia_snd_get_dev_info(si.play_id); else snd_dev_info = pjmedia_snd_get_dev_info(si.rec_id); pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info->name); } } /* Add the port to the bridge */ conf->ports[0] = conf_port; conf->port_cnt++; PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0")); return PJ_SUCCESS;}/* * Create conference bridge. */PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, unsigned max_ports, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_conf **p_conf ){ pjmedia_conf *conf; const pj_str_t name = { "Conf", 4 }; pj_status_t status; /* Can only accept 16bits per sample, for now.. */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?