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

📄 conference.c

📁 一个开源的sip源代码
💻 C
📖 第 1 页 / 共 4 页
字号:

	    /* 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 = (pjmedia_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((pj_int16_t*)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, (pj_int16_t*)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 = (pj_int16_t*) 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((pj_int16_t*) 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, 
				     (const pj_int16_t*)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( (pj_int16_t*)frame->buf, 
			      (const pj_int16_t*)conf->ports[0]->mix_buf, 
			      conf->samples_per_frame);
    } else {
	pjmedia_zero_samples((pj_int16_t*)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 = (pjmedia_conf*) this_port->port_data.pdata;
    struct conf_port *port = conf->ports[this_port->port_data.ldata];
    const pj_int16_t *input = (const pj_int16_t*) 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 + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -