stream.c

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

C
1,670
字号
/* $Id: stream.c 1047 2007-03-07 11:22:14Z 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/stream.h>#include <pjmedia/errno.h>#include <pjmedia/rtp.h>#include <pjmedia/rtcp.h>#include <pjmedia/jbuf.h>#include <pj/array.h>#include <pj/assert.h>#include <pj/ctype.h>#include <pj/compat/socket.h>#include <pj/errno.h>#include <pj/ioqueue.h>#include <pj/log.h>#include <pj/os.h>#include <pj/pool.h>#include <pj/rand.h>#include <pj/sock_select.h>#include <pj/string.h>	    /* memcpy() */#define THIS_FILE			"stream.c"#define ERRLEVEL			1#define LOGERR_(expr)			stream_perror expr#define TRC_(expr)			PJ_LOG(5,expr)#define BYTES_PER_SAMPLE		2/** * Media channel. */struct pjmedia_channel{    pjmedia_stream	   *stream;	    /**< Parent stream.		    */    pjmedia_dir		    dir;	    /**< Channel direction.	    */    unsigned		    pt;		    /**< Payload type.		    */    pj_bool_t		    paused;	    /**< Paused?.		    */    unsigned		    in_pkt_size;    /**< Size of input buffer.	    */    void		   *in_pkt;	    /**< Input buffer.		    */    unsigned		    out_pkt_size;   /**< Size of output buffer.	    */    void		   *out_pkt;	    /**< Output buffer.		    */    pjmedia_rtp_session	    rtp;	    /**< RTP session.		    */};struct dtmf{    int		    event;    pj_uint32_t	    start_ts;};/** * This structure describes media stream. * A media stream is bidirectional media transmission between two endpoints. * It consists of two channels, i.e. encoding and decoding channels. * A media stream corresponds to a single "m=" line in a SDP session * description. */struct pjmedia_stream{    pjmedia_endpt	    *endpt;	    /**< Media endpoint.	    */    pjmedia_codec_mgr	    *codec_mgr;	    /**< Codec manager instance.    */    pjmedia_port	     port;	    /**< Port interface.	    */    pjmedia_channel	    *enc;	    /**< Encoding channel.	    */    pjmedia_channel	    *dec;	    /**< Decoding channel.	    */    pjmedia_dir		     dir;	    /**< Stream direction.	    */    void		    *user_data;	    /**< User data.		    */    pjmedia_transport	    *transport;	    /**< Stream transport.	    */    pjmedia_codec	    *codec;	    /**< Codec instance being used. */    pjmedia_codec_param	     codec_param;   /**< Codec param.		    */    pj_int16_t		    *enc_buf;	    /**< Encoding buffer, when enc's						 ptime is different than dec.						 Otherwise it's NULL.	    */    unsigned		     enc_samples_per_frame;    unsigned		     enc_buf_size;  /**< Encoding buffer size, in						 samples.		    */    unsigned		     enc_buf_pos;   /**< First position in buf.	    */    unsigned		     enc_buf_count; /**< Number of samples in the						 encoding buffer.	    */    unsigned		     vad_enabled;   /**< VAD enabled in param.	    */    unsigned		     frame_size;    /**< Size of encoded base frame.*/    pj_bool_t		     is_streaming;  /**< Currently streaming?. This						 is used to put RTP marker						 bit.			    */    pj_uint32_t		     ts_vad_disabled;/**< TS when VAD was disabled. */    pj_uint32_t		     tx_duration;   /**< TX duration in timestamp.  */    pj_mutex_t		    *jb_mutex;    pjmedia_jbuf	    *jb;	    /**< Jitter buffer.		    */    char		     jb_last_frm;   /**< Last frame type from jb    */    pjmedia_rtcp_session     rtcp;	    /**< RTCP for incoming RTP.	    */    pj_uint32_t		     rtcp_last_tx;  /**< RTCP tx time in timestamp  */    pj_uint32_t		     rtcp_interval; /**< Interval, in timestamp.    */    /* RFC 2833 DTMF transmission queue: */    int			     tx_event_pt;   /**< Outgoing pt for dtmf.	    */    int			     tx_dtmf_count; /**< # of digits in tx dtmf buf.*/    struct dtmf		     tx_dtmf_buf[32];/**< Outgoing dtmf queue.	    */    /* Incoming DTMF: */    int			     rx_event_pt;   /**< Incoming pt for dtmf.	    */    int			     last_dtmf;	    /**< Current digit, or -1.	    */    pj_uint32_t		     last_dtmf_dur; /**< Start ts for cur digit.    */    unsigned		     rx_dtmf_count; /**< # of digits in dtmf rx buf.*/    char		     rx_dtmf_buf[32];/**< Incoming DTMF buffer.	    */    /* DTMF callback */    void		    (*dtmf_cb)(pjmedia_stream*, void*, int);    void		     *dtmf_cb_user_data;};/* RFC 2833 digit */static const char digitmap[16] = { '0', '1', '2', '3', 				   '4', '5', '6', '7', 				   '8', '9', '*', '#',				   'A', 'B', 'C', 'D'};/* Zero PCM frame */#define ZERO_PCM_MAX_SIZE   1920    /* 40ms worth of PCM @ 48KHz */static pj_int16_t zero_frame[ZERO_PCM_MAX_SIZE];/* * Print error. */static void stream_perror(const char *sender, const char *title,			  pj_status_t status){    char errmsg[PJ_ERR_MSG_SIZE];    pj_strerror(status, errmsg, sizeof(errmsg));    PJ_LOG(4,(sender, "%s: %s [err:%d]", title, errmsg, status));}/* * play_callback() * * This callback is called by sound device's player thread when it * needs to feed the player with some frames. */static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame){    pjmedia_stream *stream = port->port_data.pdata;    pjmedia_channel *channel = stream->dec;    unsigned samples_count, samples_per_frame, samples_required;    pj_int16_t *p_out_samp;    pj_status_t status;    /* Return no frame is channel is paused */    if (channel->paused) {	frame->type = PJMEDIA_FRAME_TYPE_NONE;	return PJ_SUCCESS;    }    /* Repeat get frame from the jitter buffer and decode the frame     * until we have enough frames according to codec's ptime.     */    /* Lock jitter buffer mutex first */    pj_mutex_lock( stream->jb_mutex );    samples_required = stream->port.info.samples_per_frame;    samples_per_frame = stream->codec_param.info.frm_ptime *			stream->codec_param.info.clock_rate *			stream->codec_param.info.channel_cnt / 			1000;    p_out_samp = frame->buf;    for (samples_count=0; samples_count < samples_required; 	 samples_count += samples_per_frame)     {	char frame_type;	/* Get frame from jitter buffer. */	pjmedia_jbuf_get_frame(stream->jb, channel->out_pkt, &frame_type);		if (frame_type == PJMEDIA_JB_MISSING_FRAME) {	    	    /* Activate PLC */	    if (stream->codec->op->recover && 		stream->codec_param.setting.plc) 	    {		pjmedia_frame frame_out;		frame_out.buf = p_out_samp + samples_count;		frame_out.size = frame->size - samples_count*2;		status = (*stream->codec->op->recover)(stream->codec,						       frame_out.size,						       &frame_out);	    } else {		status = -1;	    }	    if (status != PJ_SUCCESS) {		/* Either PLC failed or PLC not supported/enabled */		pjmedia_zero_samples(p_out_samp + samples_count,				     samples_required - samples_count);		PJ_LOG(5,(stream->port.info.name.ptr,  "Frame lost!"));	    } else {		PJ_LOG(5,(stream->port.info.name.ptr, 			  "Lost frame recovered"));	    }	    	} else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {	    /* Jitter buffer is empty. If this is the first "empty" state,	     * activate PLC to smoothen the fade-out, otherwise zero	     * the frame. 	     */	    if (frame_type != stream->jb_last_frm) {		pjmedia_jb_state jb_state;		/* Activate PLC to smoothen the missing frame */		if (stream->codec->op->recover && 		    stream->codec_param.setting.plc) 		{		    pjmedia_frame frame_out;		    do {			frame_out.buf = p_out_samp + samples_count;			frame_out.size = frame->size - samples_count*2;			status = (*stream->codec->op->recover)(stream->codec,							       frame_out.size,							       &frame_out);			if (status != PJ_SUCCESS)			    break;			samples_count += samples_per_frame;		    } while (samples_count < samples_required);		} 		/* Report the state of jitter buffer */		pjmedia_jbuf_get_state(stream->jb, &jb_state);		PJ_LOG(5,(stream->port.info.name.ptr, 			  "Jitter buffer empty (prefetch=%d)", 			  jb_state.prefetch));	    }	    if (samples_count < samples_required) {		pjmedia_zero_samples(p_out_samp + samples_count,				     samples_required - samples_count);		samples_count = samples_required;	    }	    stream->jb_last_frm = frame_type;	    break;	} else if (frame_type != PJMEDIA_JB_NORMAL_FRAME) {	    pjmedia_jb_state jb_state;	    /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */	    pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME);	    /* Get the state of jitter buffer */	    pjmedia_jbuf_get_state(stream->jb, &jb_state);	    /* Always activate PLC when it's available.. */	    if (stream->codec->op->recover && 		stream->codec_param.setting.plc) 	    {		pjmedia_frame frame_out;		do {		    frame_out.buf = p_out_samp + samples_count;		    frame_out.size = frame->size - samples_count*2;		    status = (*stream->codec->op->recover)(stream->codec,							   frame_out.size,							   &frame_out);		    if (status != PJ_SUCCESS)			break;		    samples_count += samples_per_frame;		} while (samples_count < samples_required);		if (stream->jb_last_frm != frame_type) {		    PJ_LOG(5,(stream->port.info.name.ptr, 			      "Jitter buffer is bufferring with plc (prefetch=%d)",			      jb_state.prefetch));		}	    } 	    if (samples_count < samples_required) {		pjmedia_zero_samples(p_out_samp + samples_count,				     samples_required - samples_count);		samples_count = samples_required;		PJ_LOG(5,(stream->port.info.name.ptr, 			  "Jitter buffer is bufferring (prefetch=%d)..", 			  jb_state.prefetch));	    }	    stream->jb_last_frm = frame_type;	    break;	} else {	    /* Got "NORMAL" frame from jitter buffer */	    pjmedia_frame frame_in, frame_out;	    /* Decode */	    frame_in.buf = channel->out_pkt;	    frame_in.size = stream->frame_size;	    frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO;  /* ignored */	    frame_out.buf = p_out_samp + samples_count;	    frame_out.size = frame->size - samples_count*BYTES_PER_SAMPLE;	    status = stream->codec->op->decode( stream->codec, &frame_in,						frame_out.size, &frame_out);	    if (status != 0) {		LOGERR_((port->info.name.ptr, "codec decode() error", 			 status));		pjmedia_zero_samples(p_out_samp + samples_count, 				     samples_per_frame);	    }	}	stream->jb_last_frm = frame_type;    }    /* Unlock jitter buffer mutex. */    pj_mutex_unlock( stream->jb_mutex );    /* Return PJMEDIA_FRAME_TYPE_NONE if we have no frames at all     * (it can happen when jitter buffer returns PJMEDIA_JB_ZERO_EMPTY_FRAME).     */    if (samples_count == 0) {	frame->type = PJMEDIA_FRAME_TYPE_NONE;	frame->size = 0;    } else {	frame->type = PJMEDIA_FRAME_TYPE_AUDIO;	frame->size = samples_count * BYTES_PER_SAMPLE;	frame->timestamp.u64 = 0;    }    return PJ_SUCCESS;}/* * Transmit DTMF */static void create_dtmf_payload(pjmedia_stream *stream, 			  struct pjmedia_frame *frame_out){    pjmedia_rtp_dtmf_event *event;    struct dtmf *digit = &stream->tx_dtmf_buf[0];    unsigned duration;    pj_uint32_t cur_ts;    pj_assert(sizeof(pjmedia_rtp_dtmf_event) == 4);    event = frame_out->buf;    cur_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);    duration = cur_ts - digit->start_ts;    event->event = (pj_uint8_t)digit->event;    event->e_vol = 10;    event->duration = pj_htons((pj_uint16_t)duration);    if (duration >= PJMEDIA_DTMF_DURATION) {	event->e_vol |= 0x80;	/* Prepare next digit. */	pj_mutex_lock(stream->jb_mutex);	pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]),		       stream->tx_dtmf_count, 0);	--stream->tx_dtmf_count;	stream->tx_dtmf_buf[0].start_ts = cur_ts;	pj_mutex_unlock(stream->jb_mutex);	if (stream->tx_dtmf_count) {	    PJ_LOG(5,(stream->port.info.name.ptr,		      "Sending DTMF digit id %c", 		      digitmap[stream->tx_dtmf_buf[0].event]));	}    } else if (duration == 0) {	PJ_LOG(5,(stream->port.info.name.ptr, "Sending DTMF digit id %c", 		  digitmap[digit->event]));    }    frame_out->size = 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 = 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, 

⌨️ 快捷键说明

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