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