conference.c

来自「基于sip协议的网络电话源码」· C语言 代码 · 共 1,932 行 · 第 1/4 页

C
1,932
字号
	    /* Calculate average level, and convert the sample to	     * 16bit signed integer.	     */	    itemp = unsigned2pcm(cport->mix_buf[j] / cport->src_level);	    /* Adjust the level */	    /*itemp = itemp * adj_level / NORMAL_LEVEL;*/	    itemp = (itemp * adj_level) >> 7;	    /* Clip the signal if it's too loud */	    if (itemp > 32767) itemp = 32767;	    else if (itemp < -32768) itemp = -32768;	    /* Put back in the buffer. */	    buf[j] = (pj_int16_t) itemp;	}    } else if (cport->src_level) {	/* No need to adjust signal level. */	for (j=0; j<conf->samples_per_frame; ++j) {	    buf[j] = unsigned2pcm(cport->mix_buf[j] / cport->src_level);	}    } else {	// Not necessarry. Buffer has been zeroed before.	// pjmedia_zero_samples(buf, conf->samples_per_frame);	//pj_assert(buf[0] == 0);	// This shouldn't happen. Function should've already bailed out when	// cport->src_level == 0.	pj_assert(0);    }    /* Calculate TX level if we need to do so.      * This actually is not the most correct place to calculate TX signal      * level of the port; it should calculate the level of the actual     * frame just before put_frame() is called.     * But doing so would make the code more complicated than it is     * necessary, since the purpose of level calculation mostly is just     * for VU meter display. By doing it here, it should give the acceptable     * indication of the signal level of the port.     */    if (cport->src_cnt) {	cport->tx_level = cport->src_level / cport->src_cnt;    } else {	cport->tx_level = 0;    }    /* If port has the same clock_rate and samples_per_frame settings as     * the conference bridge, transmit the frame as is.     */    if (cport->clock_rate == conf->clock_rate &&	cport->samples_per_frame == conf->samples_per_frame)    {	if (cport->port != NULL) {	    pjmedia_frame frame;	    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;	    frame.buf = (pj_int16_t*)cport->mix_buf;	    frame.size = conf->samples_per_frame * BYTES_PER_SAMPLE;	    /* No need to adjust timestamp, port has the same	     * clock rate as conference bridge 	     */	    frame.timestamp = *timestamp;	    TRACE_((THIS_FILE, "put_frame %.*s, count=%d", 			       (int)cport->name.slen, cport->name.ptr,			       frame.size / BYTES_PER_SAMPLE));	    return pjmedia_port_put_frame(cport->port, &frame);	} else	    return PJ_SUCCESS;    }    /* If it has different clock_rate, must resample. */    if (cport->clock_rate != conf->clock_rate) {	unsigned dst_count;	pjmedia_resample_run( cport->tx_resample, buf, 			      cport->tx_buf + cport->tx_buf_count );	dst_count = (unsigned)(conf->samples_per_frame * 1.0 *			       cport->clock_rate / conf->clock_rate);	cport->tx_buf_count += dst_count;    } else {	/* Same clock rate.	 * Just copy the samples to tx_buffer.	 */	pjmedia_copy_samples( cport->tx_buf + cport->tx_buf_count,			      buf, conf->samples_per_frame );	cport->tx_buf_count += conf->samples_per_frame;    }    /* Transmit while we have enough frame in the tx_buf. */    status = PJ_SUCCESS;    ts = 0;    while (cport->tx_buf_count >= cport->samples_per_frame &&	   status == PJ_SUCCESS)     {		TRACE_((THIS_FILE, "write_port %.*s: count=%d", 			   (int)cport->name.slen, cport->name.ptr,			   cport->samples_per_frame));	if (cport->port) {	    pjmedia_frame frame;	    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;	    frame.buf = cport->tx_buf;	    frame.size = cport->samples_per_frame * BYTES_PER_SAMPLE;	    /* Adjust timestamp as port may have different clock rate	     * than the bridge.	     */	    frame.timestamp.u64 = timestamp->u64 * cport->clock_rate /				  conf->clock_rate;	    /* Add timestamp for individual frame */	    frame.timestamp.u64 += ts;	    ts += cport->samples_per_frame;	    TRACE_((THIS_FILE, "put_frame %.*s, count=%d", 			       (int)cport->name.slen, cport->name.ptr,			       frame.size / BYTES_PER_SAMPLE));	    status = pjmedia_port_put_frame(cport->port, &frame);	} else	    status = PJ_SUCCESS;	cport->tx_buf_count -= cport->samples_per_frame;	if (cport->tx_buf_count) {	    pjmedia_copy_samples(cport->tx_buf, 				 cport->tx_buf + cport->samples_per_frame,				 cport->tx_buf_count);	}	TRACE_((THIS_FILE, " tx_buf count now is %d", 			   cport->tx_buf_count));    }    return status;}/* * Player callback. */static pj_status_t get_frame(pjmedia_port *this_port, 			     pjmedia_frame *frame){    pjmedia_conf *conf = this_port->port_data.pdata;    pjmedia_frame_type speaker_frame_type = PJMEDIA_FRAME_TYPE_NONE;    unsigned ci, cj, i, j;        TRACE_((THIS_FILE, "- clock -"));    /* Check that correct size is specified. */    pj_assert(frame->size == conf->samples_per_frame *			     conf->bits_per_sample / 8);    /* Must lock mutex */    pj_mutex_lock(conf->mutex);    /* Reset port source count. We will only reset port's mix     * buffer when we have someone transmitting to it.     */    for (i=0, ci=0; i<conf->max_ports && ci < conf->port_cnt; ++i) {	struct conf_port *conf_port = conf->ports[i];	/* Skip empty slot. */	if (!conf_port)	    continue;	++ci;	/* Reset sources */	conf_port->src_level = 0;	conf_port->src_cnt = 0;    }    /* Get frames from all ports, and "mix" the signal      * to mix_buf of all listeners of the port.     */    for (i=0, ci=0; i<conf->max_ports && ci<conf->port_cnt; ++i) {	struct conf_port *conf_port = conf->ports[i];	pj_int32_t level;	/* Skip empty port. */	if (!conf_port)	    continue;	/* Var "ci" is to count how many ports have been visited so far. */	++ci;	/* Skip if we're not allowed to receive from this port. */	if (conf_port->rx_setting == PJMEDIA_PORT_DISABLE) {	    conf_port->rx_level = 0;	    continue;	}	/* Also skip if this port doesn't have listeners. */	if (conf_port->listener_cnt == 0) {	    conf_port->rx_level = 0;	    continue;	}	/* Get frame from this port. 	 * For port zero (sound port) and passive ports, get the frame  from 	 * the rx_buffer instead.	 */	if (conf_port->port == NULL) {	    pj_int16_t *snd_buf;	    if (conf_port->snd_read_pos == conf_port->snd_write_pos) {		conf_port->snd_read_pos = 		    (conf_port->snd_write_pos+RX_BUF_COUNT-RX_BUF_COUNT/2) % 			RX_BUF_COUNT;	    }	    /* Skip if this port is muted/disabled. */	    if (conf_port->rx_setting != PJMEDIA_PORT_ENABLE) {		conf_port->rx_level = 0;		continue;	    }	    snd_buf = conf_port->snd_buf[conf_port->snd_read_pos];	    pjmedia_copy_samples(frame->buf, snd_buf, conf->samples_per_frame);	    conf_port->snd_read_pos = (conf_port->snd_read_pos+1) % RX_BUF_COUNT;	} else {	    pj_status_t status;	    pjmedia_frame_type frame_type;	    status = read_port(conf, conf_port, frame->buf, 			       conf->samples_per_frame, &frame_type);	    	    if (status != PJ_SUCCESS) {		/* bennylp: why do we need this????		 * Also see comments on similar issue with write_port().		PJ_LOG(4,(THIS_FILE, "Port %.*s get_frame() returned %d. "				     "Port is now disabled",				     (int)conf_port->name.slen,				     conf_port->name.ptr,				     status));		conf_port->rx_setting = PJMEDIA_PORT_DISABLE;		 */		continue;	    }	    /* Check that the port is not removed when we call get_frame() */	    if (conf->ports[i] == NULL)		continue;	}	/* If we need to adjust the RX level from this port, adjust the level	 * and calculate the average level at the same time.	 * Otherwise just calculate the averate level.	 */	if (conf_port->rx_adj_level != NORMAL_LEVEL) {	    pj_int16_t *input = frame->buf;	    pj_int32_t adj = conf_port->rx_adj_level;	    level = 0;	    for (j=0; j<conf->samples_per_frame; ++j) {		pj_int32_t itemp;		/* For the level adjustment, we need to store the sample to		 * a temporary 32bit integer value to avoid overflowing the		 * 16bit sample storage.		 */		itemp = input[j];		/*itemp = itemp * adj / NORMAL_LEVEL;*/		itemp = (itemp * adj) >> 7;		/* Clip the signal if it's too loud */		if (itemp > 32767) itemp = 32767;		else if (itemp < -32768) itemp = -32768;		input[j] = (pj_int16_t) itemp;		if (itemp >=0 ) level += itemp;		else level -= itemp;	    }	    level /= conf->samples_per_frame;	} else {	    level = pjmedia_calc_avg_signal(frame->buf, 					    conf->samples_per_frame);	}	/* Apply simple AGC to the level, to avoid dramatic change in the	 * level thus causing noise because the signal now is not aligned	 * with the signal from the previous frame.	 */	if (level >= conf_port->last_level) {	    level = (conf_port->last_level * ATTACK_A + level * ATTACK_B) / 		    (ATTACK_A + ATTACK_B);	} else {	    level = (conf_port->last_level * DECAY_A + level * DECAY_B) / 		    (DECAY_A + DECAY_B);	}	conf_port->last_level = level;	/* Convert level to 8bit complement ulaw */	level = pjmedia_linear2ulaw(level) ^ 0xff;	/* Put this level to port's last RX level. */	conf_port->rx_level = level;	/* Skip processing frame if level is zero */	if (level == 0)	    continue;	/* Convert the buffer to unsigned 16bit value */	for (j=0; j<conf->samples_per_frame; ++j)	    conf->uns_buf[j] = pcm2unsigned(((pj_int16_t*)frame->buf)[j]);	/* Add the signal to all listeners. */	for (cj=0; cj < conf_port->listener_cnt; ++cj) 	{	    struct conf_port *listener;	    pj_uint32_t *mix_buf;	    unsigned k;	    listener = conf->ports[conf_port->listener_slots[cj]];	    /* Skip if this listener doesn't want to receive audio */	    if (listener->tx_setting != PJMEDIA_PORT_ENABLE)		continue;	    /* Mix the buffer. If this is the first source for target port,	     * zero the mix buffer of target port first.	     */	    mix_buf = listener->mix_buf;	    if (listener->src_level == 0) {		pj_bzero( mix_buf, conf->samples_per_frame*sizeof(mix_buf[0]));	    }	    /* A little bit of optimization:	     *  When "conf_port" is the only transmitter to "listener",	     *  just add copy the frame directly from the original	     *  16bit frame (avoiding unsigned2pcm() conversion).	     *  But write_port() needs to be aware of this trick!	     */	    if (listener->transmitter_cnt == 1) {		pjmedia_copy_samples((pj_int16_t*)mix_buf, 				     frame->buf, conf->samples_per_frame);		listener->src_level = level;	    } else {		for (k=0; k<conf->samples_per_frame; ++k)		    mix_buf[k] += (conf->uns_buf[k] * level);		listener->src_level += level;	    }	    listener->src_cnt++;	}    }    /* Time for all ports to transmit whetever they have in their     * buffer.      */    for (i=0, ci=0; i<conf->max_ports && ci<conf->port_cnt; ++i) {	struct conf_port *conf_port = conf->ports[i];	pjmedia_frame_type frm_type;	pj_status_t status;	if (!conf_port)	    continue;	/* Var "ci" is to count how many ports have been visited. */	++ci;	status = write_port( conf, conf_port, &frame->timestamp,			     &frm_type);	if (status != PJ_SUCCESS) {	    /* bennylp: why do we need this????	       One thing for sure, put_frame()/write_port() may return	       non-successfull status on Win32 if there's temporary glitch	       on network interface, so disabling the port here does not	       sound like a good idea.	    PJ_LOG(4,(THIS_FILE, "Port %.*s put_frame() returned %d. "				 "Port is now disabled",				 (int)conf_port->name.slen,				 conf_port->name.ptr,				 status));	    conf_port->tx_setting = PJMEDIA_PORT_DISABLE;	    */	    continue;	}	/* Set the type of frame to be returned to sound playback	 * device.	 */	if (i == 0)	    speaker_frame_type = frm_type;    }    /* Return sound playback frame. */    if (conf->ports[0]->src_level) {	TRACE_((THIS_FILE, "write to audio, count=%d", 			   conf->samples_per_frame));	pjmedia_copy_samples( frame->buf, (pj_int16_t*)conf->ports[0]->mix_buf, 			      conf->samples_per_frame);    } else {	pjmedia_zero_samples( frame->buf, conf->samples_per_frame );     }    /* MUST set frame type */    frame->type = speaker_frame_type;    pj_mutex_unlock(conf->mutex);#ifdef REC_FILE    if (fhnd_rec == NULL)	fhnd_rec = fopen(REC_FILE, "wb");    if (fhnd_rec)	fwrite(frame->buf, frame->size, 1, fhnd_rec);#endif    return PJ_SUCCESS;}/* * get_frame() for passive port */static pj_status_t get_frame_pasv(pjmedia_port *this_port, 				  pjmedia_frame *frame){    pj_assert(0);    PJ_UNUSED_ARG(this_port);    PJ_UNUSED_ARG(frame);    return -1;}/* * Recorder callback. */static pj_status_t put_frame(pjmedia_port *this_port, 			     const pjmedia_frame *frame){    pjmedia_conf *conf = this_port->port_data.pdata;    struct conf_port *port = conf->ports[this_port->port_data.ldata];    const pj_int16_t *input = frame->buf;    pj_int16_t *target_snd_buf;    /* Check for correct size. */    PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *				     conf->bits_per_sample / 8,		      PJMEDIA_ENCSAMPLESPFRAME);    /* Skip if this port is muted/disabled. */    if (port->rx_setting != PJMEDIA_PORT_ENABLE) {	return PJ_SUCCESS;    }    /* Skip if no port is listening to the microphone */    if (port->listener_cnt == 0) {	return PJ_SUCCESS;    }    /* Determine which rx_buffer to fill in */    target_snd_buf = port->snd_buf[port->snd_write_pos];        /* Copy samples from audio device to target rx_buffer */    pjmedia_copy_samples(target_snd_buf, input, conf->samples_per_frame);    /* Switch buffer */    port->snd_write_pos = (port->snd_write_pos+1)%RX_BUF_COUNT;    return PJ_SUCCESS;}

⌨️ 快捷键说明

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