stream.c

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

C
1,670
字号
    /* Create RTP and RTCP sessions: */    status = pjmedia_rtp_session_init(&channel->rtp, pt, param->ssrc);    if (status != PJ_SUCCESS)	return status;    /* Done. */    *p_channel = channel;    return PJ_SUCCESS;}/* * Create media stream. */PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,					   pj_pool_t *pool,					   const pjmedia_stream_info *info,					   pjmedia_transport *tp,					   void *user_data,					   pjmedia_stream **p_stream){    enum { M = 32 };    pjmedia_stream *stream;    pj_str_t name;    unsigned jb_init, jb_max, jb_min_pre, jb_max_pre;    pj_status_t status;    PJ_ASSERT_RETURN(pool && info && p_stream, PJ_EINVAL);    /* Allocate the media stream: */    stream = pj_pool_zalloc(pool, sizeof(pjmedia_stream));    PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);    /* Init stream/port name */    name.ptr = pj_pool_alloc(pool, M);    name.slen = pj_ansi_snprintf(name.ptr, M, "strm%p", stream);    /* Init some port-info. Some parts of the info will be set later     * once we have more info about the codec.     */    pjmedia_port_info_init(&stream->port.info, &name,			   PJMEDIA_PORT_SIGNATURE('S', 'T', 'R', 'M'),			   info->fmt.clock_rate, info->fmt.channel_cnt,			   16, 80);    /* Init port. */    pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);    stream->port.info.clock_rate = info->fmt.clock_rate;    stream->port.info.channel_count = info->fmt.channel_cnt;    stream->port.port_data.pdata = stream;    stream->port.put_frame = &put_frame;    stream->port.get_frame = &get_frame;    /* Init stream: */    stream->endpt = endpt;    stream->codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);    stream->dir = info->dir;    stream->user_data = user_data;    stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL + (pj_rand() % 8000)) * 			    info->fmt.clock_rate / 1000;    stream->tx_event_pt = info->tx_event_pt ? info->tx_event_pt : -1;    stream->rx_event_pt = info->rx_event_pt ? info->rx_event_pt : -1;    stream->last_dtmf = -1;    /* Create mutex to protect jitter buffer: */    status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex);    if (status != PJ_SUCCESS)	goto err_cleanup;    /* Create and initialize codec: */    status = pjmedia_codec_mgr_alloc_codec( stream->codec_mgr,					    &info->fmt, &stream->codec);    if (status != PJ_SUCCESS)	goto err_cleanup;    /* Get codec param: */    if (info->param)	stream->codec_param = *info->param;    else {	status = pjmedia_codec_mgr_get_default_param(stream->codec_mgr, 						     &info->fmt, 						     &stream->codec_param);	if (status != PJ_SUCCESS)	    goto err_cleanup;    }    /* Check for invalid frame per packet. */    if (stream->codec_param.setting.frm_per_pkt < 1)	stream->codec_param.setting.frm_per_pkt = 1;    /* Set additional info. */    stream->port.info.bits_per_sample = 16;    stream->port.info.samples_per_frame = info->fmt.clock_rate * 					  stream->codec_param.info.frm_ptime *					  stream->codec_param.setting.frm_per_pkt /					  1000;    stream->port.info.bytes_per_frame = stream->codec_param.info.avg_bps/8 * 					stream->codec_param.info.frm_ptime *					stream->codec_param.setting.frm_per_pkt /					1000;    /* Open the codec: */    status = stream->codec->op->open(stream->codec, &stream->codec_param);    if (status != PJ_SUCCESS)	goto err_cleanup;    /* If encoder and decoder's ptime are asymmetric, then we need to     * create buffer on the encoder side. This could happen for example     * with iLBC      */    if (stream->codec_param.info.enc_ptime!=0 &&	stream->codec_param.info.enc_ptime!=stream->codec_param.info.frm_ptime)    {	unsigned ptime;	stream->enc_samples_per_frame = stream->codec_param.info.enc_ptime *					stream->port.info.clock_rate / 1000;	/* Set buffer size as twice the largest ptime value between	 * stream's ptime, encoder ptime, or decoder ptime.	 */	ptime = stream->port.info.samples_per_frame * 1000 /		stream->port.info.clock_rate;	if (stream->codec_param.info.enc_ptime > ptime)	    ptime = stream->codec_param.info.enc_ptime;	if (stream->codec_param.info.frm_ptime > ptime)	    ptime = stream->codec_param.info.frm_ptime;	ptime <<= 1;	/* Allocate buffer */	stream->enc_buf_size = stream->port.info.clock_rate * ptime / 1000;	stream->enc_buf = pj_pool_alloc(pool, stream->enc_buf_size * 2);    } else {	stream->enc_samples_per_frame = stream->port.info.samples_per_frame;    }    /* Initially disable the VAD in the stream, to help traverse NAT better */    stream->vad_enabled = stream->codec_param.setting.vad;    if (stream->vad_enabled) {	stream->codec_param.setting.vad = 0;	stream->ts_vad_disabled = 0;	stream->codec->op->modify(stream->codec, &stream->codec_param);	PJ_LOG(4,(stream->port.info.name.ptr,"VAD temporarily disabled"));    }    /* Get the frame size: */    stream->frame_size = ((stream->codec_param.info.avg_bps + 7) / 8) * 			  stream->codec_param.info.frm_ptime / 1000;    /* Init RTCP session: */    pjmedia_rtcp_init(&stream->rtcp, stream->port.info.name.ptr,		      info->fmt.clock_rate, 		      stream->port.info.samples_per_frame, 		      info->ssrc);    /* Init jitter buffer parameters: */    if (info->jb_max > 0)	jb_max = info->jb_max;    else	jb_max = 360 / stream->codec_param.info.frm_ptime;    if (info->jb_min_pre >= 0)	jb_min_pre = info->jb_min_pre;    else	jb_min_pre = 60 / stream->codec_param.info.frm_ptime;    if (info->jb_max_pre > 0)	jb_max_pre = info->jb_max_pre;    else	jb_max_pre = 240 / stream->codec_param.info.frm_ptime;    if (info->jb_init >= 0)	jb_init = info->jb_init;    else	jb_init = (jb_min_pre + jb_max_pre) / 2;    /* Create jitter buffer */    status = pjmedia_jbuf_create(pool, &stream->port.info.name,				 stream->frame_size, 				 stream->codec_param.info.frm_ptime,				 jb_max, &stream->jb);    if (status != PJ_SUCCESS)	goto err_cleanup;    /* Set up jitter buffer */    pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre);    /* Create decoder channel: */    status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, 			     info->fmt.pt, info, &stream->dec);    if (status != PJ_SUCCESS)	goto err_cleanup;    /* Create encoder channel: */    status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, 			     info->tx_pt, info, &stream->enc);    if (status != PJ_SUCCESS)	goto err_cleanup;    /* Only attach transport when stream is ready. */    status = pjmedia_transport_attach(tp, stream, &info->rem_addr, 				      &info->rem_rtcp, sizeof(info->rem_addr),                                       &on_rx_rtp, &on_rx_rtcp);    if (status != PJ_SUCCESS)	goto err_cleanup;    stream->transport = tp;    /* Success! */    *p_stream = stream;    PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->port.info.name.ptr));    return PJ_SUCCESS;err_cleanup:    pjmedia_stream_destroy(stream);    return status;}/* * Destroy stream. */PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ){    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);    /* This function may be called when stream is partly initialized. */    if (stream->jb_mutex)	pj_mutex_lock(stream->jb_mutex);    /* Detach from transport */    if (stream->transport) {	(*stream->transport->op->detach)(stream->transport, stream);	stream->transport = NULL;    }    /* Free codec. */    if (stream->codec) {	stream->codec->op->close(stream->codec);	pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec);	stream->codec = NULL;    }    /* Free mutex */        if (stream->jb_mutex) {	pj_mutex_destroy(stream->jb_mutex);	stream->jb_mutex = NULL;    }    return PJ_SUCCESS;}/* * Get the port interface. */PJ_DEF(pj_status_t) pjmedia_stream_get_port( pjmedia_stream *stream,					     pjmedia_port **p_port ){    *p_port = &stream->port;    return PJ_SUCCESS;}/* * Get the transport object */PJ_DEF(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st){    return st->transport;}/* * Start stream. */PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream){    PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP);    if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) {	stream->enc->paused = 0;	//pjmedia_snd_stream_start(stream->enc->snd_stream);	PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream started"));    } else {	PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused"));    }    if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) {	stream->dec->paused = 0;	//pjmedia_snd_stream_start(stream->dec->snd_stream);	PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream started"));    } else {	PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused"));    }    return PJ_SUCCESS;}/* * Get stream statistics. */PJ_DEF(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream,					     pjmedia_rtcp_stat *stat){    PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);    pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat));    return PJ_SUCCESS;}/* * Pause stream. */PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream,					  pjmedia_dir dir){    PJ_ASSERT_RETURN(stream, PJ_EINVAL);    if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {	stream->enc->paused = 1;	PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused"));    }    if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {	stream->dec->paused = 1;	/* Also reset jitter buffer */	pj_mutex_lock( stream->jb_mutex );	pjmedia_jbuf_reset(stream->jb);	pj_mutex_unlock( stream->jb_mutex );	PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused"));    }    return PJ_SUCCESS;}/* * Resume stream */PJ_DEF(pj_status_t) pjmedia_stream_resume( pjmedia_stream *stream,					   pjmedia_dir dir){    PJ_ASSERT_RETURN(stream, PJ_EINVAL);    if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {	stream->enc->paused = 0;	PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream resumed"));    }    if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {	stream->dec->paused = 0;	PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream resumed"));    }    return PJ_SUCCESS;}/* * Dial DTMF */PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf( pjmedia_stream *stream,					      const pj_str_t *digit_char){    pj_status_t status = PJ_SUCCESS;    /* By convention we use jitter buffer mutex to access DTMF     * queue.     */    PJ_ASSERT_RETURN(stream && digit_char, PJ_EINVAL);    /* Check that remote can receive DTMF events. */    if (stream->tx_event_pt < 0) {	return PJMEDIA_RTP_EREMNORFC2833;    }        pj_mutex_lock(stream->jb_mutex);        if (stream->tx_dtmf_count+digit_char->slen >=	PJ_ARRAY_SIZE(stream->tx_dtmf_buf))    {	status = PJ_ETOOMANY;    } else {	int i;	/* convert ASCII digits into payload type first, to make sure	 * that all digits are valid. 	 */	for (i=0; i<digit_char->slen; ++i) {	    unsigned pt;	    int dig = pj_tolower(digit_char->ptr[i]);	    if (dig >= '0' && dig <= '9')	    {		pt = dig - '0';	    } 	    else if (dig >= 'a' && dig <= 'd')	    {		pt = dig - 'a' + 12;	    }	    else if (dig == '*')	    {		pt = 10;	    }	    else if (dig == '#')	    {		pt = 11;	    }	    else	    {		status = PJMEDIA_RTP_EINDTMF;		break;	    }	    stream->tx_dtmf_buf[stream->tx_dtmf_count+i].event = pt;	}	if (status != PJ_SUCCESS)	    goto on_return;	/* Init start_ts and end_ts only for the first digit.	 * Subsequent digits are initialized on the fly.	 */	if (stream->tx_dtmf_count ==0) {	    pj_uint32_t start_ts;	    start_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);	    stream->tx_dtmf_buf[0].start_ts = start_ts;	}	/* Increment digit count only if all digits are valid. */	stream->tx_dtmf_count += digit_char->slen;    }on_return:    pj_mutex_unlock(stream->jb_mutex);    return status;}/* * See if we have DTMF digits in the rx buffer. */PJ_DEF(pj_bool_t) pjmedia_stream_check_dtmf(pjmedia_stream *stream){    return stream->rx_dtmf_count != 0;}/* * Retrieve incoming DTMF digits from the stream's DTMF buffer. */PJ_DEF(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream,					     char *digits,					     unsigned *size){    PJ_ASSERT_RETURN(stream && digits && size, PJ_EINVAL);    pj_assert(sizeof(stream->rx_dtmf_buf[0]) == 0);    /* By convention, we use jitter buffer's mutex to access DTMF     * digits resources.     */    pj_mutex_lock(stream->jb_mutex);    if (stream->rx_dtmf_count < *size)	*size = stream->rx_dtmf_count;    if (*size) {	pj_memcpy(digits, stream->rx_dtmf_buf, *size);	stream->rx_dtmf_count -= *size;	if (stream->rx_dtmf_count) {	    pj_memmove(stream->rx_dtmf_buf,		       &stream->rx_dtmf_buf[*size],		       stream->rx_dtmf_count);	}    }    pj_mutex_unlock(stream->jb_mutex);    return PJ_SUCCESS;}/* * Set callback to be called upon receiving DTMF digits. */PJ_DEF(pj_status_t)pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream,				 void (*cb)(pjmedia_stream*, 					    void *user_data, 					    int digit), 				 void *user_data){    PJ_ASSERT_RETURN(stream, PJ_EINVAL);    /* By convention, we use jitter buffer's mutex to access DTMF     * digits resources.     */    pj_mutex_lock(stream->jb_mutex);    stream->dtmf_cb = cb;    stream->dtmf_cb_user_data = user_data;    pj_mutex_unlock(stream->jb_mutex);    return PJ_SUCCESS;}

⌨️ 快捷键说明

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