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

📄 stream.c

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


/**
 * check_tx_rtcp()
 *
 * This function is can be called by either put_frame() or get_frame(),
 * to transmit periodic RTCP SR/RR report.
 */
static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp)
{
    /* Note that timestamp may represent local or remote timestamp, 
     * depending on whether this function is called from put_frame()
     * or get_frame().
     */


    if (stream->rtcp_last_tx == 0) {
	
	stream->rtcp_last_tx = timestamp;

    } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
	
	pjmedia_rtcp_pkt *rtcp_pkt;
	int len;

	pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);

	(*stream->transport->op->send_rtcp)(stream->transport, 
					    rtcp_pkt, len);

	stream->rtcp_last_tx = timestamp;
    }

}


/**
 * Rebuffer the frame when encoder and decoder has different ptime
 * (such as when different iLBC modes are used by local and remote)
 */
static void rebuffer(pjmedia_stream *stream,
		     pjmedia_frame *frame)
{
    /* How many samples are needed */
    unsigned count;

    /* Normalize frame */
    if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO)
	frame->size = 0;

    /* Remove used frame from the buffer. */
    if (stream->enc_buf_pos) {
	if (stream->enc_buf_count) {
	    pj_memmove(stream->enc_buf,
		       stream->enc_buf + stream->enc_buf_pos,
		       (stream->enc_buf_count << 1));
	}
	stream->enc_buf_pos = 0;
    }

    /* Make sure we have space to store the new frame */
    pj_assert(stream->enc_buf_count + (frame->size >> 1) <
		stream->enc_buf_size);

    /* Append new frame to the buffer */
    if (frame->size) {
	pj_memcpy(stream->enc_buf + stream->enc_buf_count,
		  frame->buf, frame->size);
	stream->enc_buf_count += (frame->size >> 1);
    }

    /* How many samples are needed */
    count = stream->codec_param.info.enc_ptime * 
		stream->port.info.clock_rate / 1000;

    /* See if we have enough samples */
    if (stream->enc_buf_count >= count) {

	frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
	frame->buf = stream->enc_buf;
	frame->size = (count << 1);

	stream->enc_buf_pos = count;
	stream->enc_buf_count -= count;

    } else {
	/* We don't have enough samples */
	frame->type = PJMEDIA_FRAME_TYPE_NONE;
    }
}


/**
 * put_frame_imp()
 */
static pj_status_t put_frame_imp( pjmedia_port *port, 
				  const pjmedia_frame *frame )
{
    pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
    pjmedia_channel *channel = stream->enc;
    pj_status_t status = 0;
    pjmedia_frame frame_out;
    unsigned ts_len, samples_per_frame;
    void *rtphdr;
    int rtphdrlen;


    /* Don't do anything if stream is paused */
    if (channel->paused) {
	stream->enc_buf_pos = stream->enc_buf_count = 0;
	return PJ_SUCCESS;
    }

    /* Number of samples in the frame */
    if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO)
	ts_len = (frame->size >> 1);
    else
	ts_len = 0;

    /* Increment transmit duration */
    stream->tx_duration += ts_len;

    /* Init frame_out buffer. */
    frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr);
    frame_out.size = 0;

    /* Calculate number of samples per frame */
    samples_per_frame = stream->enc_samples_per_frame;


    /* If we have DTMF digits in the queue, transmit the digits. 
     * Otherwise encode the PCM buffer.
     */
    if (stream->tx_dtmf_count) {

	create_dtmf_payload(stream, &frame_out);

	/* Encapsulate. */
	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
					 stream->tx_event_pt, 0, 
					 frame_out.size, ts_len, 
					 (const void**)&rtphdr, 
					 &rtphdrlen);

    } else if (frame->type != PJMEDIA_FRAME_TYPE_NONE) {
	unsigned ts, codec_samples_per_frame;

	/* Repeatedly call encode if there are multiple frames to be
	 * sent.
	 */
	codec_samples_per_frame = stream->codec_param.info.enc_ptime *
				  stream->codec_param.info.clock_rate /
				  1000;
	if (codec_samples_per_frame == 0) {
	    codec_samples_per_frame = stream->codec_param.info.frm_ptime *
				      stream->codec_param.info.clock_rate /
				      1000;
	}

	for (ts=0; ts<ts_len; ts += codec_samples_per_frame) {
	    pjmedia_frame tmp_out_frame, tmp_in_frame;
	    unsigned bytes_per_sample, max_size;

	    /* Nb of bytes in PCM sample */
	    bytes_per_sample = stream->codec_param.info.pcm_bits_per_sample/8;

	    /* Split original PCM input frame into base frame size */
	    tmp_in_frame.timestamp.u64 = frame->timestamp.u64 + ts;
	    tmp_in_frame.buf = ((char*)frame->buf) + ts * bytes_per_sample;
	    tmp_in_frame.size = codec_samples_per_frame * bytes_per_sample;
	    tmp_in_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;

	    /* Set output frame position */
	    tmp_out_frame.buf = ((char*)frame_out.buf) + frame_out.size;

	    max_size = channel->out_pkt_size - sizeof(pjmedia_rtp_hdr) -
		       frame_out.size;

	    /* Encode! */
	    status = stream->codec->op->encode( stream->codec, &tmp_in_frame, 
						max_size, &tmp_out_frame);
	    if (status != PJ_SUCCESS) {
		LOGERR_((stream->port.info.name.ptr, 
			"Codec encode() error", status));
		return status;
	    }

	    /* tmp_out_frame.size may be zero for silence frame. */
	    frame_out.size += tmp_out_frame.size;

	    /* Stop processing next PCM frame when encode() returns either 
	     * CNG frame or NULL frame.
	     */
	    if (tmp_out_frame.type!=PJMEDIA_FRAME_TYPE_AUDIO || 
		tmp_out_frame.size==0) 
	    {
		break;
	    }

	}

	/* Encapsulate. */
	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
					 channel->pt, 0, 
					 frame_out.size, ts_len, 
					 (const void**)&rtphdr, 
					 &rtphdrlen);
    } else {

	/* Just update RTP session's timestamp. */
	status = pjmedia_rtp_encode_rtp( &channel->rtp, 
					 0, 0, 
					 0, ts_len, 
					 (const void**)&rtphdr, 
					 &rtphdrlen);

    }

    if (status != PJ_SUCCESS) {
	LOGERR_((stream->port.info.name.ptr, 
		"RTP encode_rtp() error", status));
	return status;
    }

    /* Check if now is the time to transmit RTCP SR/RR report. 
     * We only do this when stream direction is not "decoding only", because
     * when it is, check_tx_rtcp() will be handled by get_frame().
     */
    if (stream->dir != PJMEDIA_DIR_DECODING) {
	check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts));
    }

    /* Do nothing if we have nothing to transmit */
    if (frame_out.size == 0) {
	if (stream->is_streaming) {
	    PJ_LOG(5,(stream->port.info.name.ptr,"Starting silence"));
	    stream->is_streaming = PJ_FALSE;
	}

	return PJ_SUCCESS;
    }


    /* Copy RTP header to the beginning of packet */
    pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr));


    /* Set RTP marker bit if currently not streaming */
    if (stream->is_streaming == PJ_FALSE) {
	pjmedia_rtp_hdr *rtp = (pjmedia_rtp_hdr*) channel->out_pkt;

	rtp->m = 1;
	PJ_LOG(5,(stream->port.info.name.ptr,"Start talksprut.."));
    }

    stream->is_streaming = PJ_TRUE;

    /* Send the RTP packet to the transport. */
    (*stream->transport->op->send_rtp)(stream->transport,
				       channel->out_pkt, 
				       frame_out.size + 
					    sizeof(pjmedia_rtp_hdr));


    /* Update stat */
    pjmedia_rtcp_tx_rtp(&stream->rtcp, frame_out.size);

    return PJ_SUCCESS;
}


/**
 * put_frame()
 *
 * This callback is called by upstream component when it has PCM frame
 * to transmit. This function encodes the PCM frame, pack it into
 * RTP packet, and transmit to peer.
 */
static pj_status_t put_frame( pjmedia_port *port, 
			      const pjmedia_frame *frame )
{
    pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
    pjmedia_frame tmp_zero_frame;
    unsigned samples_per_frame;

    samples_per_frame = stream->enc_samples_per_frame;

    /* http://www.pjsip.org/trac/ticket/56:
     *  when input is PJMEDIA_FRAME_TYPE_NONE, feed zero PCM frame
     *  instead so that encoder can decide whether or not to transmit
     *  silence frame.
     */
    if (frame->type == PJMEDIA_FRAME_TYPE_NONE &&
	samples_per_frame <= ZERO_PCM_MAX_SIZE) 
    {
	pj_memcpy(&tmp_zero_frame, frame, sizeof(pjmedia_frame));
	frame = &tmp_zero_frame;

	tmp_zero_frame.buf = zero_frame;
	tmp_zero_frame.size = samples_per_frame * 2;
	tmp_zero_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
    }

#if 0
    // This is no longer needed because each TYPE_NONE frame will
    // be converted into zero frame above

    /* If VAD is temporarily disabled during creation, feed zero PCM frame
     * to the codec.
     */
    if (stream->vad_enabled != stream->codec_param.setting.vad &&
	stream->vad_enabled != 0 &&
	frame->type == PJMEDIA_FRAME_TYPE_NONE &&
	samples_per_frame <= ZERO_PCM_MAX_SIZE)
    {
	pj_memcpy(&tmp_in_frame, frame, sizeof(pjmedia_frame));
	frame = &tmp_in_frame;

	tmp_in_frame.buf = zero_frame;
	tmp_in_frame.size = samples_per_frame * 2;
	tmp_in_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
    }
#endif

    /* If VAD is temporarily disabled during creation, enable it
     * after transmitting for VAD_SUSPEND_SEC seconds.
     */
    if (stream->vad_enabled != stream->codec_param.setting.vad &&
	(stream->tx_duration - stream->ts_vad_disabled) > 
	stream->port.info.clock_rate * PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000)
    {
	stream->codec_param.setting.vad = stream->vad_enabled;
	stream->codec->op->modify(stream->codec, &stream->codec_param);
	PJ_LOG(4,(stream->port.info.name.ptr,"VAD re-enabled"));
    }


    /* If encoder has different ptime than decoder, then the frame must
     * be passed through the encoding buffer via rebuffer() function.
     */
    if (stream->enc_buf != NULL) {
	pjmedia_frame tmp_rebuffer_frame;
	pj_status_t status = PJ_SUCCESS;

	/* Copy original frame to temporary frame since we need 
	 * to modify it.
	 */
	pj_memcpy(&tmp_rebuffer_frame, frame, sizeof(pjmedia_frame));

	/* Loop while we have full frame in enc_buffer */
	for (;;) {
	    pj_status_t st;

	    /* Run rebuffer() */
	    rebuffer(stream, &tmp_rebuffer_frame);

	    /* Process this frame */
	    st = put_frame_imp(port, &tmp_rebuffer_frame);
	    if (st != PJ_SUCCESS)
		status = st;

	    /* If we still have full frame in the buffer, re-run
	     * rebuffer() with NULL frame.
	     */
	    if (stream->enc_buf_count >= stream->enc_samples_per_frame) {

		tmp_rebuffer_frame.type = PJMEDIA_FRAME_TYPE_NONE;

	    } else {

		/* Otherwise break */
		break;
	    }
	}

	return status;

    } else {
	return put_frame_imp(port, frame);
    }
}


#if 0
static void dump_bin(const char *buf, unsigned len)
{
    unsigned i;

    PJ_LOG(3,(THIS_FILE, "begin dump"));
    for (i=0; i<len; ++i) {
	int j;
	char bits[9];
	unsigned val = buf[i] & 0xFF;

	bits[8] = '\0';
	for (j=0; j<8; ++j) {
	    if (val & (1 << (7-j)))
		bits[j] = '1';
	    else
		bits[j] = '0';
	}

	PJ_LOG(3,(THIS_FILE, "%2d %s [%d]", i, bits, val));
    }
    PJ_LOG(3,(THIS_FILE, "end dump"));
}
#endif

/*
 * Handle incoming DTMF digits.
 */
static void handle_incoming_dtmf( pjmedia_stream *stream, 
				  const void *payload, unsigned payloadlen)
{
    pjmedia_rtp_dtmf_event *event = (pjmedia_rtp_dtmf_event*) payload;

    /* Check compiler packing. */
    pj_assert(sizeof(pjmedia_rtp_dtmf_event)==4);

⌨️ 快捷键说明

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