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 + -
显示快捷键?