From 9366f64e335ee222835900c496c282f6193303e6 Mon Sep 17 00:00:00 2001 From: Sauw Ming Date: Tue, 11 Feb 2025 14:07:32 +0800 Subject: [PATCH] Audio and video stream refactoring --- pjmedia/include/pjmedia/stream.h | 67 +- pjmedia/include/pjmedia/stream_common.h | 326 +++++ pjmedia/include/pjmedia/vid_stream.h | 45 +- pjmedia/src/pjmedia/stream.c | 1522 ++++++----------------- pjmedia/src/pjmedia/stream_common.c | 526 ++++++++ pjmedia/src/pjmedia/stream_imp_common.c | 599 +++++++++ pjmedia/src/pjmedia/stream_info.c | 239 +--- pjmedia/src/pjmedia/vid_stream.c | 1155 ++++------------- pjmedia/src/pjmedia/vid_stream_info.c | 226 +--- pjsip/src/pjsua-lib/pjsua_media.c | 815 +++++------- 10 files changed, 2427 insertions(+), 3093 deletions(-) create mode 100755 pjmedia/src/pjmedia/stream_imp_common.c diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h index dc90d62022..43af19b000 100644 --- a/pjmedia/include/pjmedia/stream.h +++ b/pjmedia/include/pjmedia/stream.h @@ -78,79 +78,20 @@ PJ_BEGIN_DECL */ /** - * Opaque declaration for media channel. - * Media channel is unidirectional flow of media from sender to - * receiver. - */ -typedef struct pjmedia_channel pjmedia_channel; - -/** - * This structure describes media stream information. Each media stream + * This structure describes audio stream information. Each audio stream * corresponds to one "m=" line in SDP session descriptor, and it has * its own RTP/RTCP socket pair. */ typedef struct pjmedia_stream_info { - pjmedia_type type; /**< Media type (audio, video) */ - pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */ - pjmedia_dir dir; /**< Media direction. */ - pj_sockaddr local_addr; /**< Local RTP address */ - pj_sockaddr rem_addr; /**< Remote RTP address */ - pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If - sin_family is zero, the RTP address - will be calculated from RTP. */ - pj_bool_t rtcp_mux; /**< Use RTP and RTCP multiplexing. */ -#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - pj_bool_t rtcp_xr_enabled; - /**< Specify whether RTCP XR is enabled.*/ - pj_uint32_t rtcp_xr_interval; /**< RTCP XR interval. */ - pj_sockaddr rtcp_xr_dest;/** +#include #include #include #include +#include PJ_BEGIN_DECL +/***************************************************************************** + * + * COMMON MEDIA STREAM + * + *****************************************************************************/ + +/* Tracing jitter buffer operations in a stream session to a CSV file. + * The trace will contain JB operation timestamp, frame info, RTP info, and + * the JB state right after the operation. + */ +#define PJMEDIA_STREAM_TRACE_JB 0 + +/* Forward declarations. */ +typedef struct pjmedia_stream_info_common pjmedia_stream_info_common; +typedef struct pjmedia_channel pjmedia_channel; + +/** + * 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. + */ +typedef struct pjmedia_stream_common +{ + pjmedia_endpt *endpt; /**< Media endpoint. */ + pj_grp_lock_t *grp_lock; /**< Group lock. */ + pjmedia_stream_info_common *si; /**< Creation parameter. */ + pjmedia_port port; /**< Port interface. */ + pjmedia_channel *enc; /**< Encoding channel. */ + pjmedia_channel *dec; /**< Decoding channel. */ + pj_pool_t *own_pool; /**< Only created if not given */ + + pjmedia_dir dir; /**< Stream direction. */ + void *user_data; /**< User data. */ + pj_str_t name; /**< Stream name */ + pj_str_t cname; /**< SDES CNAME */ + + pjmedia_transport *transport; /**< Stream transport. */ + + pj_int16_t *enc_buf; /**< Encoding buffer, when enc's + ptime is different than dec. + Otherwise it's NULL. */ + + unsigned frame_size; /**< Size of encoded base frame.*/ + + pj_mutex_t *jb_mutex; + pjmedia_jbuf *jb; /**< Jitter buffer. */ + char jb_last_frm; /**< Last frame type from jb */ + unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/ + + pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */ + + pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */ + pj_timestamp rtcp_fb_last_tx;/**< Last RTCP-FB tx time. */ + pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */ + pj_bool_t initial_rr; /**< Initial RTCP RR sent */ + pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/ + void *out_rtcp_pkt; /**< Outgoing RTCP packet. */ + unsigned out_rtcp_pkt_size; + /**< Outgoing RTCP packet size. */ + +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + pj_uint32_t rtcp_xr_last_tx; /**< RTCP XR tx time + in timestamp. */ + pj_uint32_t rtcp_xr_interval; /**< Interval, in timestamp. */ + pj_sockaddr rtcp_xr_dest; /**< Additional remote RTCP XR + dest. If sin_family is + zero, it will be ignored*/ + unsigned rtcp_xr_dest_len; /**< Length of RTCP XR dest + address */ +#endif + +#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 + pj_bool_t use_ka; /**< Stream keep-alive with non- + codec-VAD mechanism is + enabled? */ + unsigned ka_interval; /**< The keepalive sending + interval */ + pj_time_val last_frm_ts_sent; /**< Time of last sending + packet */ + unsigned start_ka_count; /**< The number of keep-alive + to be sent after it is + created */ + unsigned start_ka_interval;/**< The keepalive sending + interval after the stream + is created */ + pj_timestamp last_start_ka_tx; /**< Timestamp of the last + keepalive sent */ +#endif + + pj_sockaddr rem_rtp_addr; /**< Remote RTP address */ + unsigned rem_rtp_flag; /**< Indicator flag about + packet from this addr. + 0=no pkt, 1=good ssrc, + 2=bad ssrc pkts */ + pj_sockaddr rtp_src_addr; /**< Actual packet src addr. */ + unsigned rtp_src_cnt; /**< How many pkt from + this addr. */ + +#if defined(PJMEDIA_STREAM_TRACE_JB) && PJMEDIA_STREAM_TRACE_JB != 0 + pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/ + char *trace_jb_buf; /**< Jitter tracing buffer. */ +#endif + + pj_uint32_t rtp_rx_last_ts; /**< Last received RTP + timestamp */ + pj_uint32_t rtp_tx_err_cnt; /**< The number of RTP + send() error */ + pj_uint32_t rtcp_tx_err_cnt; /**< The number of RTCP + send() error */ + + /* RTCP Feedback */ + pj_bool_t send_rtcp_fb_nack; /**< Send NACK? */ + int pending_rtcp_fb_nack; /**< Any pending NACK? */ + pjmedia_rtcp_fb_nack rtcp_fb_nack; /**< TX NACK state. */ + int rtcp_fb_nack_cap_idx; /**< RX NACK cap idx. */ +} pjmedia_stream_common; + + +/** + * Media channel. + * Media channel is unidirectional flow of media from sender to + * receiver. + */ +typedef struct pjmedia_channel +{ + pjmedia_stream_common *stream; /**< Parent stream. */ + pjmedia_dir dir; /**< Channel direction. */ + pjmedia_port port; /**< Port interface. */ + unsigned pt; /**< Payload type. */ + pj_bool_t paused; /**< Paused?. */ + void *buf; /**< Output buffer. */ + unsigned buf_size; /**< Size of output buffer. */ + pjmedia_rtp_session rtp; /**< RTP session. */ +} pjmedia_channel; + + /** * This structure describes rtp/rtcp session information of the media stream. */ @@ -54,6 +194,89 @@ typedef struct pjmedia_stream_rtp_sess_info } pjmedia_stream_rtp_sess_info; + +/** + * Get the stream statistics. See also + * #pjmedia_stream_get_stat_jbuf() + * + * @param stream The media stream. + * @param stat Media stream statistics. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_get_stat( const pjmedia_stream_common *stream, + pjmedia_rtcp_stat *stat); + + +/** + * Reset the stream statistics. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_reset_stat(pjmedia_stream_common *stream); + + +/** + * Send RTCP SDES for the media stream. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_send_rtcp_sdes( pjmedia_stream_common *stream ); + +/** + * Send RTCP BYE for the media stream. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_send_rtcp_bye( pjmedia_stream_common *stream ); + + +/** + * Get the RTP session information of the media stream. This function can be + * useful for app with custom media transport to inject/filter some + * outgoing/incoming proprietary packets into normal audio RTP traffics. + * This will return the original pointer to the internal states of the stream, + * and generally it is not advisable for app to modify them. + * + * @param stream The media stream. + * + * @param session_info The stream session info. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjmedia_stream_common_get_rtp_session_info(pjmedia_stream_common *stream, + pjmedia_stream_rtp_sess_info *session_info); + + +/* Internal function. */ + +/* Internal: * Send RTCP SDES for the media stream. */ +pj_status_t pjmedia_stream_send_rtcp(pjmedia_stream_common *c_strm, + pj_bool_t with_sdes, + pj_bool_t with_bye, + pj_bool_t with_xr, + pj_bool_t with_fb, + pj_bool_t with_fb_nack, + pj_bool_t with_fb_pli); + + +/***************************************************************************** + * + * COMMON MEDIA STREAM INFORMATION + * + *****************************************************************************/ + #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /** @@ -95,8 +318,111 @@ typedef struct pjmedia_stream_ka_config PJ_DECL(void) pjmedia_stream_ka_config_default(pjmedia_stream_ka_config *cfg); +#define PJ_DECL_STREAM_INFO_KA_MEMBER() \ + pj_bool_t use_ka; /**< Stream keep-alive and NAT hole punch \ + (see #PJMEDIA_STREAM_ENABLE_KA) \ + is enabled? */ \ + pjmedia_stream_ka_config ka_cfg; \ + /**< Stream send kep-alive settings. */ \ + +#else + +#define PJ_DECL_STREAM_INFO_KA_MEMBER() + #endif + +/** + * This structure describes the common media stream information. + */ +#define PJ_DECL_STREAM_INFO_COMMON_MEMBER() \ + pjmedia_type type; /**< Media type (audio, video) */ \ + pjmedia_tp_proto proto; /**< Transport protocol (RTP/AVP, etc.) */ \ + pjmedia_dir dir; /**< Media direction. */ \ + pj_sockaddr local_addr; /**< Local RTP address */ \ + pj_sockaddr rem_addr; /**< Remote RTP address */ \ + pj_sockaddr rem_rtcp; /**< Optional remote RTCP address. If \ + sin_family is zero, the RTP address \ + will be calculated from RTP. */ \ + pj_bool_t rtcp_mux; /**< Use RTP and RTCP multiplexing. */ \ +\ + pj_bool_t rtcp_xr_enabled; \ + /**< Specify whether RTCP XR is enabled.*/ \ + pj_uint32_t rtcp_xr_interval; /**< RTCP XR interval. */ \ + pj_sockaddr rtcp_xr_dest;/** -# define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1) -# define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD) -#endif +/* Enable/disable trace. */ +#define TRACE_JB PJMEDIA_STREAM_TRACE_JB +/* Optional path/prefix for the CSV filename. */ +#define TRACE_JB_PATH_PREFIX "" #ifndef PJMEDIA_STREAM_SIZE # define PJMEDIA_STREAM_SIZE 4000 @@ -77,20 +70,6 @@ /* Number of send error before repeat the report. */ #define SEND_ERR_COUNT_TO_REPORT 50 -/** - * 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 out_pkt_size; /**< Size of output buffer. */ - void *out_pkt; /**< Output buffer. */ - pjmedia_rtp_session rtp; /**< RTP session. */ -}; - struct dtmf { @@ -110,21 +89,10 @@ struct dtmf */ struct pjmedia_stream { - pjmedia_endpt *endpt; /**< Media endpoint. */ - pj_grp_lock_t *grp_lock; /**< Group lock. */ + pjmedia_stream_common base; + pjmedia_codec_mgr *codec_mgr; /**< Codec manager instance. */ pjmedia_stream_info si; /**< Creation parameter. */ - pjmedia_port port; /**< Port interface. */ - pjmedia_channel *enc; /**< Encoding channel. */ - pjmedia_channel *dec; /**< Decoding channel. */ - - pj_pool_t *own_pool; /**< Only created if not given */ - - pjmedia_dir dir; /**< Stream direction. */ - void *user_data; /**< User data. */ - pj_str_t cname; /**< SDES CNAME */ - - pjmedia_transport *transport; /**< Stream transport. */ pjmedia_codec *codec; /**< Codec instance being used. */ pjmedia_codec_param codec_param; /**< Codec param. */ @@ -162,21 +130,8 @@ struct pjmedia_stream 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 */ - unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/ unsigned soft_start_cnt;/**< Stream soft start counter */ - 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. */ - pj_bool_t initial_rr; /**< Initial RTCP RR sent */ - pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/ - void *out_rtcp_pkt; /**< Outgoing RTCP packet. */ - unsigned out_rtcp_pkt_size; - /**< Outgoing RTCP packet size. */ pj_int16_t *zero_frame; /**< Zero frame buffer. */ /* RFC 2833 DTMF transmission queue: */ @@ -224,59 +179,6 @@ struct pjmedia_stream checking */ #endif -#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - pj_uint32_t rtcp_xr_last_tx; /**< RTCP XR tx time - in timestamp. */ - pj_uint32_t rtcp_xr_interval; /**< Interval, in timestamp. */ - pj_sockaddr rtcp_xr_dest; /**< Additional remote RTCP XR - dest. If sin_family is - zero, it will be ignored*/ - unsigned rtcp_xr_dest_len; /**< Length of RTCP XR dest - address */ -#endif - -#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 - pj_bool_t use_ka; /**< Stream keep-alive with non- - codec-VAD mechanism is - enabled? */ - unsigned ka_interval; /**< The keepalive sending - interval */ - pj_time_val last_frm_ts_sent; /**< Time of last sending - packet */ - unsigned start_ka_count; /**< The number of keep-alive - to be sent after it is - created */ - unsigned start_ka_interval;/**< The keepalive sending - interval after the stream - is created */ -#endif - - pj_sockaddr rem_rtp_addr; /**< Remote RTP address */ - unsigned rem_rtp_flag; /**< Indicator flag about - packet from this addr. - 0=no pkt, 1=good ssrc, - 2=bad ssrc pkts */ - unsigned rtp_src_cnt; /**< How many pkt from - this addr. */ - -#if TRACE_JB - pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/ - char *trace_jb_buf; /**< Jitter tracing buffer. */ -#endif - - pj_uint32_t rtp_rx_last_ts; /**< Last received RTP - timestamp */ - pj_uint32_t rtp_tx_err_cnt; /**< The number of RTP - send() error */ - pj_uint32_t rtcp_tx_err_cnt; /**< The number of RTCP - send() error */ - - /* RTCP Feedback */ - pj_bool_t send_rtcp_fb_nack; /**< Send NACK? */ - pjmedia_rtcp_fb_nack rtcp_fb_nack; /**< TX NACK state. */ - int rtcp_fb_nack_cap_idx; /**< RX NACK cap idx. */ - - }; @@ -295,225 +197,8 @@ static void on_rx_rtcp( void *data, void *pkt, pj_ssize_t bytes_read); -static pj_status_t send_rtcp(pjmedia_stream *stream, - pj_bool_t with_sdes, - pj_bool_t with_bye, - pj_bool_t with_xr, - pj_bool_t with_fb); - -static void stream_on_destroy(void *arg); - -#if TRACE_JB - -PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len) -{ - pj_time_val now; - pj_parsed_time ptime; - char *p = *buf; - - if (len < 14) - return -1; - - pj_gettimeofday(&now); - pj_time_decode(&now, &ptime); - p += pj_utoa_pad(ptime.hour, p, 2, '0'); - *p++ = ':'; - p += pj_utoa_pad(ptime.min, p, 2, '0'); - *p++ = ':'; - p += pj_utoa_pad(ptime.sec, p, 2, '0'); - *p++ = '.'; - p += pj_utoa_pad(ptime.msec, p, 3, '0'); - *p++ = ','; - - *buf = p; - - return 0; -} - -PJ_INLINE(int) trace_jb_print_state(pjmedia_stream *stream, - char **buf, pj_ssize_t len) -{ - char *p = *buf; - char *endp = *buf + len; - pjmedia_jb_state state; - - pjmedia_jbuf_get_state(stream->jb, &state); - - len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d", - state.size, state.burst, state.prefetch); - if ((len < 0) || (len >= endp-p)) - return -1; - - p += len; - *buf = p; - return 0; -} - -static void trace_jb_get(pjmedia_stream *stream, pjmedia_jb_frame_type ft, - pj_size_t fsize) -{ - char *p = stream->trace_jb_buf; - char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; - pj_ssize_t len = 0; - const char* ft_st; - - if (!TRACE_JB_OPENED(stream)) - return; - - /* Print timestamp. */ - if (trace_jb_print_timestamp(&p, endp-p)) - goto on_insuff_buffer; - - /* Print frame type and size */ - switch(ft) { - case PJMEDIA_JB_MISSING_FRAME: - ft_st = "missing"; - break; - case PJMEDIA_JB_NORMAL_FRAME: - ft_st = "normal"; - break; - case PJMEDIA_JB_ZERO_PREFETCH_FRAME: - ft_st = "prefetch"; - break; - case PJMEDIA_JB_ZERO_EMPTY_FRAME: - ft_st = "empty"; - break; - default: - ft_st = "unknown"; - break; - } - - /* Print operation, size, frame count, frame type */ - len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st); - if ((len < 0) || (len >= endp-p)) - goto on_insuff_buffer; - p += len; - - /* Print JB state */ - if (trace_jb_print_state(stream, &p, endp-p)) - goto on_insuff_buffer; - - /* Print end of line */ - if (endp-p < 2) - goto on_insuff_buffer; - *p++ = '\n'; - - /* Write and flush */ - len = p - stream->trace_jb_buf; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); - return; - -on_insuff_buffer: - pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); -} - -static void trace_jb_put(pjmedia_stream *stream, const pjmedia_rtp_hdr *hdr, - unsigned payloadlen, unsigned frame_cnt) -{ - char *p = stream->trace_jb_buf; - char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; - pj_ssize_t len = 0; - - if (!TRACE_JB_OPENED(stream)) - return; - - /* Print timestamp. */ - if (trace_jb_print_timestamp(&p, endp-p)) - goto on_insuff_buffer; - - /* Print operation, size, frame count, RTP info */ - len = pj_ansi_snprintf(p, endp-p, - "PUT,%d,%d,,%d,%d,%d,", - payloadlen, frame_cnt, - pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m); - if ((len < 0) || (len >= endp-p)) - goto on_insuff_buffer; - p += len; - - /* Print JB state */ - if (trace_jb_print_state(stream, &p, endp-p)) - goto on_insuff_buffer; - - /* Print end of line */ - if (endp-p < 2) - goto on_insuff_buffer; - *p++ = '\n'; - - /* Write and flush */ - len = p - stream->trace_jb_buf; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); - return; - -on_insuff_buffer: - pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); -} - -#endif /* TRACE_JB */ - -#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 -/* - * Send keep-alive packet using non-codec frame. - */ -static void send_keep_alive_packet(pjmedia_stream *stream) -{ -#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP - - /* Keep-alive packet is empty RTP */ - pj_status_t status; - void *pkt; - int pkt_len; - - TRC_((stream->port.info.name.ptr, - "Sending keep-alive (RTCP and empty RTP)")); - - /* Send RTP */ - status = pjmedia_rtp_encode_rtp( &stream->enc->rtp, - stream->enc->pt, 0, - 1, - 0, - (const void**)&pkt, - &pkt_len); - pj_assert(status == PJ_SUCCESS); - - pj_memcpy(stream->enc->out_pkt, pkt, pkt_len); - pjmedia_transport_send_rtp(stream->transport, stream->enc->out_pkt, - pkt_len); - - /* Send RTCP */ - send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE); - - /* Update stats in case the stream is paused */ - stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq); - -#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER - - /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ - int pkt_len; - const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT; - - TRC_((stream->port.info.name.ptr, - "Sending keep-alive (custom RTP/RTCP packets)")); - - /* Send to RTP port */ - pj_memcpy(stream->enc->out_pkt, str_ka.ptr, str_ka.slen); - pkt_len = str_ka.slen; - pjmedia_transport_send_rtp(stream->transport, stream->enc->out_pkt, - pkt_len); - - /* Send to RTCP port */ - pjmedia_transport_send_rtcp(stream->transport, stream->enc->out_pkt, - pkt_len); - -#else - - PJ_UNUSED_ARG(stream); - -#endif -} -#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */ +#include "stream_imp_common.c" /* * play_callback() @@ -524,7 +209,8 @@ static void send_keep_alive_packet(pjmedia_stream *stream) static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; - pjmedia_channel *channel = stream->dec; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_channel *channel = c_strm->dec; unsigned samples_count, samples_per_frame, samples_required; pj_int16_t *p_out_samp; pj_status_t status; @@ -538,11 +224,11 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) if (stream->soft_start_cnt) { if (stream->soft_start_cnt == PJMEDIA_STREAM_SOFT_START) { - PJ_LOG(4,(stream->port.info.name.ptr, + PJ_LOG(4,(c_strm->port.info.name.ptr, "Resetting jitter buffer in stream playback start")); - pj_mutex_lock( stream->jb_mutex ); - pjmedia_jbuf_reset(stream->jb); - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); + pjmedia_jbuf_reset(c_strm->jb); + pj_mutex_unlock( c_strm->jb_mutex ); } --stream->soft_start_cnt; frame->type = PJMEDIA_FRAME_TYPE_NONE; @@ -554,9 +240,9 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) */ /* Lock jitter buffer mutex first */ - pj_mutex_lock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); - samples_required = PJMEDIA_PIA_SPF(&stream->port.info); + samples_required = PJMEDIA_PIA_SPF(&c_strm->port.info); samples_per_frame = stream->dec_ptime * stream->codec_param.info.clock_rate * stream->codec_param.info.channel_cnt / @@ -566,7 +252,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) for (samples_count=0; samples_count < samples_required;) { char frame_type; - pj_size_t frame_size = channel->out_pkt_size; + pj_size_t frame_size = channel->buf_size; pj_uint32_t bit_info; if (stream->dec_buf && stream->dec_buf_pos < stream->dec_buf_count) { @@ -584,11 +270,11 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) } /* Get frame from jitter buffer. */ - pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size, + pjmedia_jbuf_get_frame2(c_strm->jb, channel->buf, &frame_size, &frame_type, &bit_info); #if TRACE_JB - trace_jb_get(stream, frame_type, frame_size); + trace_jb_get(c_strm, frame_type, frame_size); #endif if (frame_type == PJMEDIA_JB_MISSING_FRAME) { @@ -618,15 +304,15 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_required - samples_count); } - if (frame_type != stream->jb_last_frm) { + if (frame_type != c_strm->jb_last_frm) { /* Report changing frame type event */ - PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost%s!", + PJ_LOG(5,(c_strm->port.info.name.ptr, "Frame lost%s!", (status == PJ_SUCCESS? ", recovered":""))); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } samples_count += samples_per_frame; @@ -640,7 +326,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) */ //Using this "if" will only invoke PLC for the first packet //lost and not the subsequent ones. - //if (frame_type != stream->jb_last_frm) { + //if (frame_type != c_strm->jb_last_frm) { if (1) { /* Activate PLC to smoothen the missing frame */ if (stream->codec->op->recover && @@ -674,19 +360,19 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_count = samples_required; } - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - PJ_LOG(5,(stream->port.info.name.ptr, + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer empty (prefetch=%d)%s", jb_state.prefetch, with_plc)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } break; @@ -728,19 +414,19 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_count = samples_required; } - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - PJ_LOG(5,(stream->port.info.name.ptr, + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer is bufferring (prefetch=%d)%s", jb_state.prefetch, with_plc)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } break; @@ -752,7 +438,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) stream->plc_cnt = 0; /* Decode */ - frame_in.buf = channel->out_pkt; + frame_in.buf = channel->buf; frame_in.size = frame_size; frame_in.bit_info = bit_info; frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; /* ignored */ @@ -789,17 +475,17 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) sizeof(pj_int16_t); } - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { /* Report changing frame type event */ - PJ_LOG(5,(stream->port.info.name.ptr, + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer starts returning normal frames " "(after %d empty/lost)", - stream->jb_last_frm_cnt)); + c_strm->jb_last_frm_cnt)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } if (!use_dec_buf) samples_count += samples_per_frame; @@ -808,7 +494,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) /* Unlock jitter buffer mutex. */ - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_unlock( c_strm->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). @@ -832,7 +518,8 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; - pjmedia_channel *channel = stream->dec; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_channel *channel = c_strm->dec; pjmedia_frame_ext *f = (pjmedia_frame_ext*)frame; unsigned samples_per_frame, samples_required; pj_status_t status; @@ -847,7 +534,7 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) * until we have enough frames according to codec's ptime. */ - samples_required = PJMEDIA_PIA_SPF(&stream->port.info); + samples_required = PJMEDIA_PIA_SPF(&c_strm->port.info); samples_per_frame = stream->codec_param.info.frm_ptime * stream->codec_param.info.clock_rate * stream->codec_param.info.channel_cnt / @@ -859,29 +546,29 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) while (f->samples_cnt < samples_required) { char frame_type; - pj_size_t frame_size = channel->out_pkt_size; + pj_size_t frame_size = channel->buf_size; pj_uint32_t bit_info; /* Lock jitter buffer mutex first */ - pj_mutex_lock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); /* Get frame from jitter buffer. */ - pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size, + pjmedia_jbuf_get_frame2(c_strm->jb, channel->buf, &frame_size, &frame_type, &bit_info); #if TRACE_JB - trace_jb_get(stream, frame_type, frame_size); + trace_jb_get(c_strm, frame_type, frame_size); #endif /* Unlock jitter buffer mutex. */ - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_unlock( c_strm->jb_mutex ); if (frame_type == PJMEDIA_JB_NORMAL_FRAME) { /* Got "NORMAL" frame from jitter buffer */ pjmedia_frame frame_in; /* Decode */ - frame_in.buf = channel->out_pkt; + frame_in.buf = channel->buf; frame_in.size = frame_size; frame_in.bit_info = bit_info; frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; @@ -895,17 +582,17 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) (pj_uint16_t)samples_per_frame); } - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { /* Report changing frame type event */ - PJ_LOG(5,(stream->port.info.name.ptr, + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer starts returning normal frames " "(after %d empty/lost)", - stream->jb_last_frm_cnt)); + c_strm->jb_last_frm_cnt)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } } else { @@ -923,48 +610,48 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) } if (frame_type == PJMEDIA_JB_MISSING_FRAME) { - if (frame_type != stream->jb_last_frm) { + if (frame_type != c_strm->jb_last_frm) { /* Report changing frame type event */ - PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost!")); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Frame lost!")); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) { - if (frame_type != stream->jb_last_frm) { + if (frame_type != c_strm->jb_last_frm) { pjmedia_jb_state jb_state; /* Report changing frame type event */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - PJ_LOG(5,(stream->port.info.name.ptr, + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer empty (prefetch=%d)", jb_state.prefetch)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } } else { /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */ pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME); - if (stream->jb_last_frm != frame_type) { + if (c_strm->jb_last_frm != frame_type) { pjmedia_jb_state jb_state; /* Report changing frame type event */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - PJ_LOG(5,(stream->port.info.name.ptr, + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + PJ_LOG(5,(c_strm->port.info.name.ptr, "Jitter buffer is bufferring (prefetch=%d)", jb_state.prefetch)); - stream->jb_last_frm = frame_type; - stream->jb_last_frm_cnt = 1; + c_strm->jb_last_frm = frame_type; + c_strm->jb_last_frm_cnt = 1; } else { - stream->jb_last_frm_cnt++; + c_strm->jb_last_frm_cnt++; } } } @@ -981,6 +668,7 @@ static void create_dtmf_payload(pjmedia_stream *stream, struct pjmedia_frame *frame_out, int forced_last, int *first, int *last) { + pjmedia_stream_common *c_strm = &stream->base; pjmedia_rtp_dtmf_event *event; struct dtmf *digit = &stream->tx_dtmf_buf[0]; unsigned duration = 0; @@ -1010,7 +698,7 @@ static void create_dtmf_payload(pjmedia_stream *stream, event = (pjmedia_rtp_dtmf_event*) frame_out->buf; if (digit->duration == 0) { - PJ_LOG(4,(stream->port.info.name.ptr, "Sending DTMF digit id %c", + PJ_LOG(4,(c_strm->port.info.name.ptr, "Sending DTMF digit id %c", digitmap[digit->event])); *first = 1; } @@ -1034,13 +722,13 @@ static void create_dtmf_payload(pjmedia_stream *stream, *last = 1; /* Prepare next digit. */ - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); pj_array_erase(stream->tx_dtmf_buf, sizeof(stream->tx_dtmf_buf[0]), stream->tx_dtmf_count, 0); --stream->tx_dtmf_count; - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); } } @@ -1048,197 +736,6 @@ static void create_dtmf_payload(pjmedia_stream *stream, } -static pj_status_t build_rtcp_fb(pjmedia_stream *stream, void *buf, - pj_size_t *length) -{ - pj_status_t status; - - /* Generic NACK */ - if (stream->send_rtcp_fb_nack && stream->rtcp_fb_nack.pid >= 0) - { - status = pjmedia_rtcp_fb_build_nack(&stream->rtcp, buf, length, 1, - &stream->rtcp_fb_nack); - if (status != PJ_SUCCESS) - return status; - - /* Reset Packet ID */ - stream->rtcp_fb_nack.pid = -1; - } - - return PJ_SUCCESS; -} - - -/** - * Publish transport error event. - */ -static void publish_tp_event(pjmedia_event_type event_type, - pj_status_t status, - pj_bool_t is_rtp, - pjmedia_dir dir, - pjmedia_stream *stream) -{ - pjmedia_event ev; - pj_timestamp ts_now; - - pj_get_timestamp(&ts_now); - pj_bzero(&ev.data.med_tp_err, sizeof(ev.data.med_tp_err)); - - /* Publish event. */ - pjmedia_event_init(&ev, event_type, - &ts_now, stream); - ev.data.med_tp_err.type = PJMEDIA_TYPE_AUDIO; - ev.data.med_tp_err.is_rtp = is_rtp; - ev.data.med_tp_err.dir = dir; - ev.data.med_tp_err.status = status; - - pjmedia_event_publish(NULL, stream, &ev, 0); -} - - -static pj_status_t send_rtcp(pjmedia_stream *stream, - pj_bool_t with_sdes, - pj_bool_t with_bye, - pj_bool_t with_xr, - pj_bool_t with_fb) -{ - void *sr_rr_pkt; - pj_uint8_t *pkt; - int len, max_len; - pj_status_t status; - - /* We need to prevent data race since there is only a single instance - * of rtcp packet buffer. And to avoid deadlock with media transport, - * we use the transport's group lock. - */ - if (stream->transport->grp_lock) - pj_grp_lock_acquire(stream->transport->grp_lock); - - /* Build RTCP RR/SR packet */ - pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len); - -#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0) - with_xr = PJ_FALSE; -#endif - - if (with_sdes || with_bye || with_xr || with_fb) { - pkt = (pj_uint8_t*) stream->out_rtcp_pkt; - pj_memcpy(pkt, sr_rr_pkt, len); - max_len = stream->out_rtcp_pkt_size; - } else { - pkt = (pj_uint8_t*)sr_rr_pkt; - max_len = len; - } - - /* RTCP FB must be sent in compound (i.e: with RR/SR and SDES) */ - if (with_fb) - with_sdes = PJ_TRUE; - - /* Build RTCP SDES packet */ - if (with_sdes) { - pjmedia_rtcp_sdes sdes; - pj_size_t sdes_len; - - pj_bzero(&sdes, sizeof(sdes)); - sdes.cname = stream->cname; - sdes_len = max_len - len; - status = pjmedia_rtcp_build_rtcp_sdes(&stream->rtcp, pkt+len, - &sdes_len, &sdes); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error generating RTCP SDES")); - } else { - len += (int)sdes_len; - } - } - - if (with_fb) { - pj_size_t fb_len = max_len - len; - status = build_rtcp_fb(stream, pkt+len, &fb_len); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error generating RTCP FB")); - } else { - len += (int)fb_len; - } - } - - /* Build RTCP XR packet */ -#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - if (with_xr) { - int i; - pjmedia_jb_state jb_state; - void *xr_pkt; - int xr_len; - - /* Update RTCP XR with current JB states */ - pjmedia_jbuf_get_state(stream->jb, &jb_state); - - i = jb_state.avg_delay; - status = pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, - PJMEDIA_RTCP_XR_INFO_JB_NOM, i); - pj_assert(status == PJ_SUCCESS); - - i = jb_state.max_delay; - status = pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, - PJMEDIA_RTCP_XR_INFO_JB_MAX, i); - pj_assert(status == PJ_SUCCESS); - - pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0, - &xr_pkt, &xr_len); - - if (xr_len + len <= max_len) { - pj_memcpy(pkt+len, xr_pkt, xr_len); - len += xr_len; - - /* Send the RTCP XR to third-party destination if specified */ - if (stream->rtcp_xr_dest_len) { - pjmedia_transport_send_rtcp2(stream->transport, - &stream->rtcp_xr_dest, - stream->rtcp_xr_dest_len, - xr_pkt, xr_len); - } - - } else { - PJ_PERROR(4,(stream->port.info.name.ptr, PJ_ETOOBIG, - "Error generating RTCP-XR")); - } - } -#endif - - /* Build RTCP BYE packet */ - if (with_bye) { - pj_size_t bye_len; - - bye_len = max_len - len; - status = pjmedia_rtcp_build_rtcp_bye(&stream->rtcp, pkt+len, - &bye_len, NULL); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error generating RTCP BYE")); - } else { - len += (int)bye_len; - } - } - - /* Send! */ - status = pjmedia_transport_send_rtcp(stream->transport, pkt, len); - if (status != PJ_SUCCESS) { - if (stream->rtcp_tx_err_cnt++ == 0) { - LOGERR_((stream->port.info.name.ptr, status, - "Error sending RTCP")); - } - if (stream->rtcp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - stream->rtcp_tx_err_cnt = 0; - } - } - - if (stream->transport->grp_lock) - pj_grp_lock_release(stream->transport->grp_lock); - - return status; -} - /** * check_tx_rtcp() * @@ -1247,38 +744,40 @@ static pj_status_t send_rtcp(pjmedia_stream *stream, */ static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp) { + pjmedia_stream_common *c_strm = &stream->base; + /* 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) { + if (c_strm->rtcp_last_tx == 0) { - stream->rtcp_last_tx = timestamp; + c_strm->rtcp_last_tx = timestamp; - } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) { + } else if (timestamp - c_strm->rtcp_last_tx >= c_strm->rtcp_interval) { pj_bool_t with_xr = PJ_FALSE; pj_status_t status; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - if (stream->rtcp.xr_enabled) { - if (stream->rtcp_xr_last_tx == 0) { - stream->rtcp_xr_last_tx = timestamp; - } else if (timestamp - stream->rtcp_xr_last_tx >= - stream->rtcp_xr_interval) + if (c_strm->rtcp.xr_enabled) { + if (c_strm->rtcp_xr_last_tx == 0) { + c_strm->rtcp_xr_last_tx = timestamp; + } else if (timestamp - c_strm->rtcp_xr_last_tx >= + c_strm->rtcp_xr_interval) { with_xr = PJ_TRUE; /* Update last tx RTCP XR */ - stream->rtcp_xr_last_tx = timestamp; + c_strm->rtcp_xr_last_tx = timestamp; } } #endif - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE, - with_xr, PJ_FALSE); + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, PJ_FALSE, + with_xr, PJ_FALSE, PJ_FALSE, PJ_FALSE); if (status == PJ_SUCCESS) { - stream->rtcp_last_tx = timestamp; + c_strm->rtcp_last_tx = timestamp; } } } @@ -1291,6 +790,8 @@ static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp) static void rebuffer(pjmedia_stream *stream, pjmedia_frame *frame) { + pjmedia_stream_common *c_strm = &stream->base; + /* How many samples are needed */ unsigned count; @@ -1301,8 +802,8 @@ static void rebuffer(pjmedia_stream *stream, /* 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, + pj_memmove(c_strm->enc_buf, + c_strm->enc_buf + stream->enc_buf_pos, (stream->enc_buf_count << 1)); } stream->enc_buf_pos = 0; @@ -1316,17 +817,17 @@ static void rebuffer(pjmedia_stream *stream, if (frame->size) { /* Handle case when there is no port transmitting to this port */ if (frame->buf) { - pj_memcpy(stream->enc_buf + stream->enc_buf_count, + pj_memcpy(c_strm->enc_buf + stream->enc_buf_count, frame->buf, frame->size); } else { - pj_bzero(stream->enc_buf + stream->enc_buf_count, frame->size); + pj_bzero(c_strm->enc_buf + stream->enc_buf_count, frame->size); } stream->enc_buf_count += ((unsigned)frame->size >> 1); } /* How many samples are needed */ count = stream->codec_param.info.enc_ptime * - PJMEDIA_PIA_SRATE(&stream->port.info) / + PJMEDIA_PIA_SRATE(&c_strm->port.info) / stream->codec_param.info.enc_ptime_denum / 1000; @@ -1334,7 +835,7 @@ static void rebuffer(pjmedia_stream *stream, if (stream->enc_buf_count >= count) { frame->type = PJMEDIA_FRAME_TYPE_AUDIO; - frame->buf = stream->enc_buf; + frame->buf = c_strm->enc_buf; frame->size = (count << 1); stream->enc_buf_pos = count; @@ -1354,7 +855,8 @@ static pj_status_t put_frame_imp( pjmedia_port *port, pjmedia_frame *frame ) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; - pjmedia_channel *channel = stream->enc; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_channel *channel = c_strm->enc; pj_status_t status = 0; pjmedia_frame frame_out; unsigned ts_len, rtp_ts_len; @@ -1367,26 +869,26 @@ static pj_status_t put_frame_imp( pjmedia_port *port, /* If the interval since last sending packet is greater than * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet. */ - if (stream->use_ka) + if (c_strm->use_ka) { pj_uint32_t dtx_duration, ka_interval; pj_time_val now, tmp; pj_gettimeofday(&now); tmp = now; - PJ_TIME_VAL_SUB(tmp, stream->last_frm_ts_sent); + PJ_TIME_VAL_SUB(tmp, c_strm->last_frm_ts_sent); dtx_duration = PJ_TIME_VAL_MSEC(tmp); - if (stream->start_ka_count) { - ka_interval = stream->start_ka_interval; + if (c_strm->start_ka_count) { + ka_interval = c_strm->start_ka_interval; } else { - ka_interval = stream->ka_interval * 1000; + ka_interval = c_strm->ka_interval * 1000; } if (dtx_duration > ka_interval) { - send_keep_alive_packet(stream); - stream->last_frm_ts_sent = now; + send_keep_alive_packet(c_strm); + c_strm->last_frm_ts_sent = now; - if (stream->start_ka_count) - stream->start_ka_count--; + if (c_strm->start_ka_count) + c_strm->start_ka_count--; } } #endif @@ -1396,8 +898,8 @@ static pj_status_t put_frame_imp( pjmedia_port *port, ts_len = ((unsigned)frame->size >> 1) / stream->codec_param.info.channel_cnt; else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) - ts_len = PJMEDIA_PIA_SPF(&stream->port.info) / - PJMEDIA_PIA_CCNT(&stream->port.info); + ts_len = PJMEDIA_PIA_SPF(&c_strm->port.info) / + PJMEDIA_PIA_CCNT(&c_strm->port.info); else ts_len = 0; @@ -1422,13 +924,13 @@ static pj_status_t put_frame_imp( pjmedia_port *port, NULL, NULL); /* Update RTCP stats with last RTP timestamp. */ - stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(channel->rtp.out_hdr.ts); + c_strm->rtcp.stat.rtp_tx_last_ts = pj_ntohl(channel->rtp.out_hdr.ts); /* Check if now is the time to transmit RTCP SR/RR report. * We only do this when the decoder is paused, * because otherwise check_tx_rtcp() will be handled by on_rx_rtp(). */ - if (stream->dec->paused) { + if (c_strm->dec->paused) { check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts)); } @@ -1439,7 +941,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port, stream->tx_duration += ts_len; /* Init frame_out buffer. */ - frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr); + frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr); frame_out.size = 0; /* If we have DTMF digits in the queue, transmit the digits. @@ -1485,8 +987,8 @@ static pj_status_t put_frame_imp( pjmedia_port *port, */ } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO && frame->buf == NULL && - stream->port.info.fmt.id == PJMEDIA_FORMAT_L16 && - (stream->dir & PJMEDIA_DIR_ENCODING)) + c_strm->port.info.fmt.id == PJMEDIA_FORMAT_L16 && + (c_strm->dir & PJMEDIA_DIR_ENCODING)) { pjmedia_frame silence_frame; @@ -1494,15 +996,15 @@ static pj_status_t put_frame_imp( pjmedia_port *port, silence_frame.buf = stream->zero_frame; silence_frame.size = stream->enc_samples_per_pkt * 2; silence_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; - silence_frame.timestamp.u32.lo = pj_ntohl(stream->enc->rtp.out_hdr.ts); + silence_frame.timestamp.u32.lo = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); /* Encode! */ status = pjmedia_codec_encode( stream->codec, &silence_frame, - channel->out_pkt_size - + channel->buf_size - sizeof(pjmedia_rtp_hdr), &frame_out); if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "Codec encode() error")); return status; } @@ -1521,11 +1023,11 @@ static pj_status_t put_frame_imp( pjmedia_port *port, { /* Encode! */ status = pjmedia_codec_encode( stream->codec, frame, - channel->out_pkt_size - + channel->buf_size - sizeof(pjmedia_rtp_hdr), &frame_out); if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "Codec encode() error")); return status; } @@ -1549,7 +1051,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port, } if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "RTP encode_rtp() error")); return status; } @@ -1558,14 +1060,14 @@ static pj_status_t put_frame_imp( pjmedia_port *port, * 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) { + if (c_strm->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")); + PJ_LOG(5,(c_strm->port.info.name.ptr,"Starting silence")); stream->is_streaming = PJ_FALSE; } @@ -1574,7 +1076,7 @@ static pj_status_t put_frame_imp( pjmedia_port *port, /* Copy RTP header to the beginning of packet */ - pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr)); + pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Special case for DTMF: timestamp remains constant for * the same event, and is only updated after a complete event @@ -1587,37 +1089,37 @@ static pj_status_t put_frame_imp( pjmedia_port *port, /* Set RTP marker bit if currently not streaming */ if (stream->is_streaming == PJ_FALSE) { - pjmedia_rtp_hdr *rtp = (pjmedia_rtp_hdr*) channel->out_pkt; + pjmedia_rtp_hdr *rtp = (pjmedia_rtp_hdr*) channel->buf; rtp->m = 1; - PJ_LOG(5,(stream->port.info.name.ptr,"Starting talksprut..")); + PJ_LOG(5,(c_strm->port.info.name.ptr,"Starting talksprut..")); } stream->is_streaming = PJ_TRUE; /* Send the RTP packet to the transport. */ - status = pjmedia_transport_send_rtp(stream->transport, channel->out_pkt, + status = pjmedia_transport_send_rtp(c_strm->transport, channel->buf, frame_out.size + sizeof(pjmedia_rtp_hdr)); if (status != PJ_SUCCESS) { - if (stream->rtp_tx_err_cnt++ == 0) { - LOGERR_((stream->port.info.name.ptr, status, "Error sending RTP")); + if (c_strm->rtp_tx_err_cnt++ == 0) { + LOGERR_((c_strm->port.info.name.ptr, status, "Error sending RTP")); } - if (stream->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - stream->rtp_tx_err_cnt = 0; + if (c_strm->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { + c_strm->rtp_tx_err_cnt = 0; } return PJ_SUCCESS; } /* Update stat */ - pjmedia_rtcp_tx_rtp(&stream->rtcp, (unsigned)frame_out.size); - stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts); - stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq); + pjmedia_rtcp_tx_rtp(&c_strm->rtcp, (unsigned)frame_out.size); + c_strm->rtcp.stat.rtp_tx_last_ts = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); + c_strm->rtcp.stat.rtp_tx_last_seq = pj_ntohs(c_strm->enc->rtp.out_hdr.seq); #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Update time of last sending packet. */ - pj_gettimeofday(&stream->last_frm_ts_sent); + pj_gettimeofday(&c_strm->last_frm_ts_sent); #endif return PJ_SUCCESS; @@ -1635,6 +1137,7 @@ static pj_status_t put_frame( pjmedia_port *port, pjmedia_frame *frame ) { pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata; + pjmedia_stream_common *c_strm = &stream->base; pjmedia_frame tmp_zero_frame; unsigned samples_per_frame; @@ -1680,19 +1183,19 @@ static pj_status_t put_frame( pjmedia_port *port, */ if (stream->vad_enabled != stream->codec_param.setting.vad && (stream->tx_duration - stream->ts_vad_disabled) > - PJMEDIA_PIA_SRATE(&stream->port.info) * + PJMEDIA_PIA_SRATE(&c_strm->port.info) * PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000) { stream->codec_param.setting.vad = stream->vad_enabled; pjmedia_codec_modify(stream->codec, &stream->codec_param); - PJ_LOG(4,(stream->port.info.name.ptr,"VAD re-enabled")); + PJ_LOG(4,(c_strm->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) { + if (c_strm->enc_buf != NULL) { pjmedia_frame tmp_rebuffer_frame; pj_status_t status = PJ_SUCCESS; @@ -1767,6 +1270,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, const pj_timestamp *timestamp, const void *payload, unsigned payloadlen) { + pjmedia_stream_common *c_strm = &stream->base; pjmedia_rtp_dtmf_event *event = (pjmedia_rtp_dtmf_event*) payload; pj_uint16_t event_duration; pjmedia_stream_dtmf_event dtmf_event; @@ -1789,7 +1293,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, #else if (event->event > 15) { #endif - PJ_LOG(5,(stream->port.info.name.ptr, + PJ_LOG(5,(c_strm->port.info.name.ptr, "Ignored RTP pkt with bad DTMF event %d", event->event)); return; @@ -1843,7 +1347,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, } /* New event! */ - PJ_LOG(5,(stream->port.info.name.ptr, "Received DTMF digit %c, vol=%d", + PJ_LOG(5,(c_strm->port.info.name.ptr, "Received DTMF digit %c, vol=%d", digitmap[event->event], (event->e_vol & PJMEDIA_RTP_DTMF_EVENT_VOLUME_MASK))); @@ -1873,7 +1377,7 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, /* By convention, we use jitter buffer's mutex to access shared * DTMF variables. */ - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); if (stream->rx_dtmf_count >= PJ_ARRAY_SIZE(stream->rx_dtmf_buf)) { /* DTMF digits overflow. Discard the oldest digit. */ pj_array_erase(stream->rx_dtmf_buf, @@ -1882,188 +1386,25 @@ static void handle_incoming_dtmf( pjmedia_stream *stream, --stream->rx_dtmf_count; } stream->rx_dtmf_buf[stream->rx_dtmf_count++] = digitmap[event->event]; - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); } } /* - * This callback is called by stream transport on receipt of packets - * in the RTP socket. + * This callback is called by common stream processing on receipt of + * packets in the RTP socket (i.e. called by on_rx_rtp() in + * stream_imp_common.c) */ -static void on_rx_rtp( pjmedia_tp_cb_param *param) +static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + const void *payload, + unsigned payloadlen, + pjmedia_rtp_status seq_st, + pj_bool_t *pkt_discarded) { - pjmedia_stream *stream = (pjmedia_stream*) param->user_data; - void *pkt = param->pkt; - pj_ssize_t bytes_read = param->size; - pjmedia_channel *channel = stream->dec; - const pjmedia_rtp_hdr *hdr; - const void *payload; - unsigned payloadlen; - pjmedia_rtp_status seq_st; - pj_bool_t check_pt; - pj_status_t status; - pj_bool_t pkt_discarded = PJ_FALSE; - - /* Check for errors */ - if (bytes_read < 0) { - status = (pj_status_t)-bytes_read; - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return; - } - - LOGERR_((stream->port.info.name.ptr, status, - "Unable to receive RTP packet")); - - if (status == PJ_ESOCKETSTOP) { - /* Publish receive error event. */ - publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_TRUE, - PJMEDIA_DIR_DECODING, stream); - } - return; - } - - /* Ignore non-RTP keep-alive packets */ - if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr)) - return; - - /* Update RTP and RTCP session. */ - status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, (int)bytes_read, - &hdr, &payload, &payloadlen); - if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, "RTP decode error")); - stream->rtcp.stat.rx.discard++; - return; - } - - /* Check if multiplexing is allowed and the payload indicates RTCP. */ - if (stream->si.rtcp_mux && hdr->pt >= 64 && hdr->pt <= 95) { - on_rx_rtcp(stream, pkt, bytes_read); - return; - } - - /* See if source address of RTP packet is different than the - * configured address, and check if we need to tell the - * media transport to switch RTP remote address. - */ - if (param->src_addr) { - pj_uint32_t peer_ssrc = channel->rtp.peer_ssrc; - pj_bool_t badssrc = PJ_FALSE; - - /* Check SSRC. */ - if (!channel->rtp.has_peer_ssrc && peer_ssrc == 0) - peer_ssrc = pj_ntohl(hdr->ssrc); - - if ((stream->si.has_rem_ssrc) && (pj_ntohl(hdr->ssrc) != peer_ssrc)) { - badssrc = PJ_TRUE; - } - - if (pj_sockaddr_cmp(&stream->rem_rtp_addr, param->src_addr) == 0) { - /* We're still receiving from rem_rtp_addr. */ - stream->rtp_src_cnt = 0; - stream->rem_rtp_flag = badssrc? 2: 1; - } else { - stream->rtp_src_cnt++; - - if (stream->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { - if (stream->rem_rtp_flag == 1 || - (stream->rem_rtp_flag == 2 && badssrc)) - { - /* Only discard if: - * - we have ever received packet with good ssrc from - * remote address (rem_rtp_addr), or - * - we have ever received packet with bad ssrc from - * remote address and this packet also has bad ssrc. - */ - return; - } - if (!badssrc && stream->rem_rtp_flag != 1) - { - /* Immediately switch if we receive packet with the - * correct ssrc AND we never receive packets with - * good ssrc from rem_rtp_addr. - */ - param->rem_switch = PJ_TRUE; - } - } else { - /* Switch. We no longer receive packets from rem_rtp_addr. */ - param->rem_switch = PJ_TRUE; - } - - if (param->rem_switch) { - /* Set remote RTP address to source address */ - pj_sockaddr_cp(&stream->rem_rtp_addr, param->src_addr); - - /* Reset counter and flag */ - stream->rtp_src_cnt = 0; - stream->rem_rtp_flag = badssrc? 2: 1; - - /* Update RTCP peer ssrc */ - stream->rtcp.peer_ssrc = pj_ntohl(hdr->ssrc); - } - } - } - - /* Add ref counter to avoid premature destroy from callbacks */ - pj_grp_lock_add_ref(stream->grp_lock); - - pj_bzero(&seq_st, sizeof(seq_st)); - - /* Ignore the packet if decoder is paused */ - if (channel->paused) { - goto on_return; - } - - /* Update RTP session (also checks if RTP session can accept - * the incoming packet. - */ - check_pt = (hdr->pt != stream->rx_event_pt) && PJMEDIA_STREAM_CHECK_RTP_PT; - pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, check_pt); -#if !PJMEDIA_STREAM_CHECK_RTP_PT - if (!check_pt && hdr->pt != channel->rtp.out_pt && - hdr->pt != stream->rx_event_pt) - { - seq_st.status.flag.badpt = -1; - } -#endif - if (seq_st.status.value) { - TRC_ ((stream->port.info.name.ptr, - "RTP status: badpt=%d, badssrc=%d, dup=%d, " - "outorder=%d, probation=%d, restart=%d", - seq_st.status.flag.badpt, - seq_st.status.flag.badssrc, - seq_st.status.flag.dup, - seq_st.status.flag.outorder, - seq_st.status.flag.probation, - seq_st.status.flag.restart)); - - if (seq_st.status.flag.badpt) { - PJ_LOG(4,(stream->port.info.name.ptr, - "Bad RTP pt %d (expecting %d)", - hdr->pt, channel->rtp.out_pt)); - } - - if (!stream->si.has_rem_ssrc && seq_st.status.flag.badssrc) { - PJ_LOG(4,(stream->port.info.name.ptr, - "Changed RTP peer SSRC %d (previously %d)", - channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc)); - stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; - } - - - } - - /* Skip bad RTP packet */ - if (seq_st.status.flag.bad) { - pkt_discarded = PJ_TRUE; - goto on_return; - } - - /* Ignore if payloadlen is zero */ - if (payloadlen == 0) { - pkt_discarded = PJ_TRUE; - goto on_return; - } + pjmedia_stream *stream = (pjmedia_stream*) c_strm; + pj_status_t status = PJ_SUCCESS; /* Handle incoming DTMF. */ if (hdr->pt == stream->rx_event_pt) { @@ -2086,10 +1427,10 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) /* Put "good" packet to jitter buffer, or reset the jitter buffer * when RTP session is restarted. */ - pj_mutex_lock( stream->jb_mutex ); + pj_mutex_lock( c_strm->jb_mutex ); if (seq_st.status.flag.restart) { - status = pjmedia_jbuf_reset(stream->jb); - PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset")); + status = pjmedia_jbuf_reset(c_strm->jb); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Jitter buffer reset")); } else { /* * Packets may contain more than one frames, while the jitter @@ -2110,11 +1451,11 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) status = pjmedia_codec_parse(stream->codec, (void*)payload, payloadlen, &ts, &count, frames); if (status != PJ_SUCCESS) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "Codec parse() error")); count = 0; } else if (count == 0) { - PJ_LOG(2, (stream->port.info.name.ptr, "codec parsed 0 frames")); + PJ_LOG(2, (c_strm->port.info.name.ptr, "codec parsed 0 frames")); } else if (stream->detect_ptime_change && frames[0].bit_info > 0xFFFF) { @@ -2140,7 +1481,7 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) dec_ptime_denum; stream->dec_ptime = (pj_uint16_t)dec_ptime; stream->dec_ptime_denum = (pj_uint8_t)dec_ptime_denum; - pjmedia_jbuf_set_ptime2(stream->jb, stream->dec_ptime, + pjmedia_jbuf_set_ptime2(c_strm->jb, stream->dec_ptime, stream->dec_ptime_denum); pjmedia_rtcp_session_setting_default(&setting); @@ -2149,15 +1490,15 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) stream->dec_ptime * 1000 / stream->dec_ptime_denum, stream->codec_param.info.channel_cnt); - pjmedia_rtcp_update(&stream->rtcp, &setting); + pjmedia_rtcp_update(&c_strm->rtcp, &setting); - PJ_LOG(4, (stream->port.info.name.ptr, "codec decode " + PJ_LOG(4, (c_strm->port.info.name.ptr, "codec decode " "ptime change detected: %d/%d -> %d/%d", old_ptime, old_ptime_denum, dec_ptime, dec_ptime_denum)); /* Reset jitter buffer after ptime changed */ - pjmedia_jbuf_reset(stream->jb); + pjmedia_jbuf_reset(c_strm->jb); } #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) @@ -2172,21 +1513,21 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) /* Make sure the detection performed only on two consecutive * packets with valid RTP sequence and no wrapped timestamp. */ - if (seq_st.diff == 1 && stream->rtp_rx_last_ts && - ts.u64 > stream->rtp_rx_last_ts && + if (seq_st.diff == 1 && c_strm->rtp_rx_last_ts && + ts.u64 > c_strm->rtp_rx_last_ts && stream->rtp_rx_last_cnt > 0) { unsigned peer_frm_ts_diff; unsigned frm_ts_span; /* Calculate actual frame timestamp span */ - frm_ts_span = PJMEDIA_PIA_SPF(&stream->port.info) / + frm_ts_span = PJMEDIA_PIA_SPF(&c_strm->port.info) / stream->codec_param.setting.frm_per_pkt/ - PJMEDIA_PIA_CCNT(&stream->port.info); + PJMEDIA_PIA_CCNT(&c_strm->port.info); /* Get remote frame timestamp span */ peer_frm_ts_diff = - ((pj_uint32_t)ts.u64-stream->rtp_rx_last_ts) / + ((pj_uint32_t)ts.u64-c_strm->rtp_rx_last_ts) / stream->rtp_rx_last_cnt; /* Possibilities remote's samples per frame for G.722 @@ -2211,12 +1552,12 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) stream->rtp_rx_ts_len_per_frame)); /* Reset jitter buffer once detection done */ - pjmedia_jbuf_reset(stream->jb); + pjmedia_jbuf_reset(c_strm->jb); } } } - stream->rtp_rx_last_ts = (pj_uint32_t)ts.u64; + c_strm->rtp_rx_last_ts = (pj_uint32_t)ts.u64; stream->rtp_rx_last_cnt = count; } @@ -2246,18 +1587,18 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) pj_bool_t discarded; ext_seq = (unsigned)(frames[i].timestamp.u64 / ts_span); - pjmedia_jbuf_put_frame2(stream->jb, frames[i].buf, frames[i].size, + pjmedia_jbuf_put_frame2(c_strm->jb, frames[i].buf, frames[i].size, frames[i].bit_info, ext_seq, &discarded); if (discarded) - pkt_discarded = PJ_TRUE; + *pkt_discarded = PJ_TRUE; } #if TRACE_JB - trace_jb_put(stream, hdr, payloadlen, count); + trace_jb_put(c_strm, hdr, payloadlen, count); #endif } - pj_mutex_unlock( stream->jb_mutex ); + pj_mutex_unlock( c_strm->jb_mutex ); /* Check if now is the time to transmit RTCP SR/RR report. @@ -2265,98 +1606,19 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) * if the encoder is paused, * because otherwise check_tx_rtcp() will be handled by put_frame() */ - if (stream->dir == PJMEDIA_DIR_DECODING || stream->enc->paused) { + if (c_strm->dir == PJMEDIA_DIR_DECODING || c_strm->enc->paused) { check_tx_rtcp(stream, pj_ntohl(hdr->ts)); } if (status != 0) { - LOGERR_((stream->port.info.name.ptr, status, + LOGERR_((c_strm->port.info.name.ptr, status, "Jitter buffer put() error")); - pkt_discarded = PJ_TRUE; + *pkt_discarded = PJ_TRUE; goto on_return; } on_return: - /* Update RTCP session */ - if (stream->rtcp.peer_ssrc == 0) - stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; - - pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq), - pj_ntohl(hdr->ts), payloadlen, pkt_discarded); - - /* RTCP-FB generic NACK */ - if (stream->rtcp.received >= 10 && seq_st.diff > 1 && - stream->send_rtcp_fb_nack && pj_ntohs(hdr->seq) >= seq_st.diff) - { - pj_uint16_t nlost, first_seq; - - /* Report only one NACK (last 17 losts) */ - nlost = PJ_MIN(seq_st.diff - 1, 17); - first_seq = pj_ntohs(hdr->seq) - nlost; - - pj_bzero(&stream->rtcp_fb_nack, sizeof(stream->rtcp_fb_nack)); - stream->rtcp_fb_nack.pid = first_seq; - while (--nlost) { - stream->rtcp_fb_nack.blp <<= 1; - stream->rtcp_fb_nack.blp |= 1; - } - - /* Send it immediately */ - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, - PJ_FALSE, PJ_FALSE, PJ_TRUE); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error sending RTCP FB generic NACK")); - } else { - stream->initial_rr = PJ_TRUE; - } - } - - /* Send RTCP RR and SDES after we receive some RTP packets */ - if (stream->rtcp.received >= 10 && !stream->initial_rr) { - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, - PJ_FALSE, PJ_FALSE, PJ_FALSE); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, - "Error sending initial RTCP RR")); - } else { - stream->initial_rr = PJ_TRUE; - } - } - - pj_grp_lock_dec_ref(stream->grp_lock); -} - - -/* - * This callback is called by stream transport on receipt of packets - * in the RTCP socket. - */ -static void on_rx_rtcp( void *data, - void *pkt, - pj_ssize_t bytes_read) -{ - pjmedia_stream *stream = (pjmedia_stream*) data; - pj_status_t status; - - /* Check for errors */ - if (bytes_read < 0) { - status = (pj_status_t)-bytes_read; - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return; - } - LOGERR_((stream->port.info.name.ptr, status, - "Unable to receive RTCP packet")); - - if (status == PJ_ESOCKETSTOP) { - /* Publish receive error event. */ - publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_FALSE, - PJMEDIA_DIR_DECODING, stream); - } - return; - } - - pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read); + return status; } @@ -2370,6 +1632,7 @@ static pj_status_t create_channel( pj_pool_t *pool, const pjmedia_stream_info *param, pjmedia_channel **p_channel) { + pjmedia_stream_common *c_strm = &stream->base; pjmedia_channel *channel; pj_status_t status; @@ -2380,7 +1643,7 @@ static pj_status_t create_channel( pj_pool_t *pool, /* Init channel info. */ - channel->stream = stream; + channel->stream = c_strm; channel->dir = dir; channel->paused = 1; channel->pt = pt; @@ -2392,31 +1655,31 @@ static pj_status_t create_channel( pj_pool_t *pool, unsigned max_rx_based_size; unsigned max_bps_based_size; - /* out_pkt buffer is used for sending and receiving, so lets calculate - * its size based on both. For receiving, we have stream->frame_size, + /* buf buffer is used for sending and receiving, so lets calculate + * its size based on both. For receiving, we have c_strm->frame_size, * which is used in configuring jitter buffer frame length. * For sending, it is based on codec max_bps info. */ - max_rx_based_size = stream->frame_size; + max_rx_based_size = c_strm->frame_size; max_bps_based_size = stream->codec_param.info.max_bps * PJMEDIA_MAX_FRAME_DURATION_MS / 8 / 1000; - channel->out_pkt_size = PJ_MAX(max_rx_based_size, max_bps_based_size); + channel->buf_size = PJ_MAX(max_rx_based_size, max_bps_based_size); /* Also include RTP header size (for sending) */ - channel->out_pkt_size += sizeof(pjmedia_rtp_hdr); + channel->buf_size += sizeof(pjmedia_rtp_hdr); - if (channel->out_pkt_size > PJMEDIA_MAX_MTU - + if (channel->buf_size > PJMEDIA_MAX_MTU - PJMEDIA_STREAM_RESV_PAYLOAD_LEN) { - channel->out_pkt_size = PJMEDIA_MAX_MTU - + channel->buf_size = PJMEDIA_MAX_MTU - PJMEDIA_STREAM_RESV_PAYLOAD_LEN; } } else { return PJ_ENOTSUP; } - channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size); - PJ_ASSERT_RETURN(channel->out_pkt != NULL, PJ_ENOMEM); + channel->buf = pj_pool_alloc(pool, channel->buf_size); + PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM); @@ -2449,19 +1712,20 @@ static pj_status_t stream_event_cb(pjmedia_event *event, void *user_data) { pjmedia_stream *stream = (pjmedia_stream*)user_data; + pjmedia_stream_common *c_strm = &stream->base; /* Set RTCP FB capability in the event */ if (event->type==PJMEDIA_EVENT_RX_RTCP_FB && - event->epub==&stream->rtcp) + event->epub==&c_strm->rtcp) { pjmedia_event_rx_rtcp_fb_data *data = (pjmedia_event_rx_rtcp_fb_data*) &event->data.rx_rtcp_fb; /* Application not configured to listen to NACK, discard this event */ - if (stream->rtcp_fb_nack_cap_idx < 0) + if (c_strm->rtcp_fb_nack_cap_idx < 0) return PJ_SUCCESS; - data->cap = stream->si.loc_rtcp_fb.caps[stream->rtcp_fb_nack_cap_idx]; + data->cap = stream->si.loc_rtcp_fb.caps[c_strm->rtcp_fb_nack_cap_idx]; } /* Republish events */ @@ -2483,6 +1747,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, { enum { M = 32 }; pjmedia_stream *stream; + pjmedia_stream_common *c_strm; pj_str_t name; unsigned jb_init, jb_max, jb_min_pre, jb_max_pre; pjmedia_audio_format_detail *afd; @@ -2506,10 +1771,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream = PJ_POOL_ZALLOC_T(pool, pjmedia_stream); PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); - stream->own_pool = own_pool; + c_strm = &stream->base; + c_strm->own_pool = own_pool; /* Duplicate stream info */ pj_memcpy(&stream->si, info, sizeof(*info)); + c_strm->si = (pjmedia_stream_info_common *)&stream->si; pj_strdup(pool, &stream->si.fmt.encoding_name, &info->fmt.encoding_name); if (info->param) stream->si.param = pjmedia_codec_param_clone(pool, info->param); @@ -2525,57 +1792,57 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* 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_info_init(&c_strm->port.info, &name, PJMEDIA_SIG_PORT_STREAM, info->fmt.clock_rate, info->fmt.channel_cnt, 16, 80); - afd = pjmedia_format_get_audio_format_detail(&stream->port.info.fmt, 1); + afd = pjmedia_format_get_audio_format_detail(&c_strm->port.info.fmt, 1); //No longer there in 2.0 - //pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name); + //pj_strdup(pool, &c_strm->port.info.encoding_name, &info->fmt.encoding_name); afd->clock_rate = info->fmt.clock_rate; afd->channel_count = info->fmt.channel_cnt; - stream->port.port_data.pdata = stream; + c_strm->port.port_data.pdata = stream; /* Init stream: */ - stream->endpt = endpt; + c_strm->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-500 + (pj_rand()%1000)) * + c_strm->dir = info->dir; + c_strm->user_data = user_data; + c_strm->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) * info->fmt.clock_rate / 1000; - stream->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; + c_strm->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; 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; - stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; - stream->rtcp_fb_nack.pid = -1; + c_strm->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; + c_strm->rtcp_fb_nack.pid = -1; stream->soft_start_cnt = PJMEDIA_STREAM_SOFT_START; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 - stream->use_ka = info->use_ka; - stream->ka_interval = info->ka_cfg.ka_interval; - stream->start_ka_count = info->ka_cfg.start_count; - stream->start_ka_interval = info->ka_cfg.start_interval; + c_strm->use_ka = info->use_ka; + c_strm->ka_interval = info->ka_cfg.ka_interval; + c_strm->start_ka_count = info->ka_cfg.start_count; + c_strm->start_ka_interval = info->ka_cfg.start_interval; #endif - stream->cname = info->cname; - if (stream->cname.slen == 0) { + c_strm->cname = info->cname; + if (c_strm->cname.slen == 0) { /* Build random RTCP CNAME. CNAME has user@host format */ - stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); + c_strm->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); pj_create_random_string(p, 5); p += 5; *p++ = '@'; *p++ = 'p'; *p++ = 'j'; pj_create_random_string(p, 6); p += 6; *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g'; - stream->cname.slen = p - stream->cname.ptr; + c_strm->cname.slen = p - c_strm->cname.ptr; } /* Create mutex to protect jitter buffer: */ - status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex); + status = pj_mutex_create_simple(pool, NULL, &c_strm->jb_mutex); if (status != PJ_SUCCESS) goto err_cleanup; @@ -2648,14 +1915,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, afd->frame_time_usec = stream->codec_param.info.frm_ptime * stream->codec_param.setting.frm_per_pkt * 1000 / stream->codec_param.info.frm_ptime_denum; - stream->port.info.fmt.id = stream->codec_param.info.fmt_id; + c_strm->port.info.fmt.id = stream->codec_param.info.fmt_id; if (stream->codec_param.info.fmt_id == PJMEDIA_FORMAT_L16) { /* Raw format */ afd->avg_bps = afd->max_bps = afd->clock_rate * afd->channel_count * afd->bits_per_sample; - stream->port.put_frame = &put_frame; - stream->port.get_frame = &get_frame; + c_strm->port.put_frame = &put_frame; + c_strm->port.get_frame = &get_frame; } else { /* Encoded format */ afd->avg_bps = stream->codec_param.info.avg_bps; @@ -2666,14 +1933,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->codec_param.info.frm_ptime * stream->codec_param.setting.frm_per_pkt) % 8000 != 0) { - ++stream->port.info.bytes_per_frame; + ++c_strm->port.info.bytes_per_frame; } - stream->port.info.format.bitrate = stream->codec_param.info.avg_bps; - stream->port.info.format.vad = (stream->codec_param.setting.vad != 0); + c_strm->port.info.format.bitrate = stream->codec_param.info.avg_bps; + c_strm->port.info.format.vad = (stream->codec_param.setting.vad != 0); */ - stream->port.put_frame = &put_frame; - stream->port.get_frame = &get_frame_ext; + c_strm->port.put_frame = &put_frame; + c_strm->port.get_frame = &get_frame_ext; } /* If encoder and decoder's ptime are asymmetric, then we need to @@ -2718,7 +1985,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Allocate buffer */ stream->enc_buf_size = afd->clock_rate * ptime / 1000 / 1000; - stream->enc_buf = (pj_int16_t*) + c_strm->enc_buf = (pj_int16_t*) pj_pool_alloc(pool, stream->enc_buf_size * 2); } else { @@ -2732,14 +1999,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->codec_param.setting.vad = 0; stream->ts_vad_disabled = 0; pjmedia_codec_modify(stream->codec, &stream->codec_param); - PJ_LOG(4,(stream->port.info.name.ptr,"VAD temporarily disabled")); + PJ_LOG(4,(c_strm->port.info.name.ptr,"VAD temporarily disabled")); } /* Get the frame size */ if (stream->codec_param.info.max_rx_frame_size > 0) { - stream->frame_size = stream->codec_param.info.max_rx_frame_size; + c_strm->frame_size = stream->codec_param.info.max_rx_frame_size; } else { - stream->frame_size = stream->codec_param.info.max_bps * + c_strm->frame_size = stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime / stream->codec_param.info.frm_ptime_denum / 8 / 1000; @@ -2747,7 +2014,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->codec_param.info.frm_ptime / stream->codec_param.info.frm_ptime_denum) % 8000 != 0) { - ++stream->frame_size; + ++c_strm->frame_size; } } @@ -2769,7 +2036,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) stream->rtp_rx_check_cnt = 50; stream->has_g722_mpeg_bug = PJ_FALSE; - stream->rtp_rx_last_ts = 0; + c_strm->rtp_rx_last_ts = 0; stream->rtp_rx_last_cnt = 0; stream->rtp_tx_ts_len_per_pkt = stream->enc_samples_per_pkt / stream->codec_param.info.channel_cnt; @@ -2844,24 +2111,24 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } /* Create jitter buffer */ - status = pjmedia_jbuf_create(pool, &stream->port.info.name, - stream->frame_size, + status = pjmedia_jbuf_create(pool, &c_strm->port.info.name, + c_strm->frame_size, stream->codec_param.info.frm_ptime, - jb_max, &stream->jb); + jb_max, &c_strm->jb); if (status != PJ_SUCCESS) goto err_cleanup; /* Set up jitter buffer */ - pjmedia_jbuf_set_ptime2(stream->jb, stream->codec_param.info.frm_ptime, + pjmedia_jbuf_set_ptime2(c_strm->jb, stream->codec_param.info.frm_ptime, stream->codec_param.info.frm_ptime_denum); - pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre); - pjmedia_jbuf_set_discard(stream->jb, info->jb_discard_algo); + pjmedia_jbuf_set_adaptive( c_strm->jb, jb_init, jb_min_pre, jb_max_pre); + pjmedia_jbuf_set_discard(c_strm->jb, info->jb_discard_algo); /* Create decoder channel: */ status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, - info->rx_pt, info, &stream->dec); + info->rx_pt, info, &c_strm->dec); if (status != PJ_SUCCESS) goto err_cleanup; @@ -2869,7 +2136,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Create encoder channel: */ status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, - info->tx_pt, info, &stream->enc); + info->tx_pt, info, &c_strm->enc); if (status != PJ_SUCCESS) goto err_cleanup; @@ -2880,9 +2147,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pjmedia_rtcp_session_setting rtcp_setting; pjmedia_rtcp_session_setting_default(&rtcp_setting); - rtcp_setting.name = stream->port.info.name.ptr; + rtcp_setting.name = c_strm->port.info.name.ptr; rtcp_setting.ssrc = info->ssrc; - rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts); + rtcp_setting.rtp_ts_base = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); rtcp_setting.clock_rate = info->fmt.clock_rate; rtcp_setting.samples_per_frame = PJMEDIA_AFD_SPF(afd); @@ -2895,41 +2162,41 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } #endif - pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting); + pjmedia_rtcp_init2(&c_strm->rtcp, &rtcp_setting); if (info->rtp_seq_ts_set) { - stream->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; - stream->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; + c_strm->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; + c_strm->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; } /* Subscribe to RTCP events */ pjmedia_event_subscribe(NULL, &stream_event_cb, stream, - &stream->rtcp); + &c_strm->rtcp); } /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES, * BYE, and XR. */ - stream->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + + c_strm->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + - (4 + (unsigned)stream->cname.slen) + + (4 + (unsigned)c_strm->cname.slen) + 32; #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) if (info->rtcp_xr_enabled) { - stream->out_rtcp_pkt_size += sizeof(pjmedia_rtcp_xr_pkt); + c_strm->out_rtcp_pkt_size += sizeof(pjmedia_rtcp_xr_pkt); } #endif - if (stream->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) - stream->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; + if (c_strm->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) + c_strm->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; - stream->out_rtcp_pkt = pj_pool_alloc(pool, stream->out_rtcp_pkt_size); + c_strm->out_rtcp_pkt = pj_pool_alloc(pool, c_strm->out_rtcp_pkt_size); pj_bzero(&att_param, sizeof(att_param)); att_param.stream = stream; att_param.media_type = PJMEDIA_TYPE_AUDIO; att_param.user_data = stream; pj_sockaddr_cp(&att_param.rem_addr, &info->rem_addr); - pj_sockaddr_cp(&stream->rem_rtp_addr, &info->rem_addr); + pj_sockaddr_cp(&c_strm->rem_rtp_addr, &info->rem_addr); if (stream->si.rtcp_mux) { pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr); } else if (pj_sockaddr_has_addr(&info->rem_rtcp)) { @@ -2941,24 +2208,24 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Create group lock & attach handler */ status = pj_grp_lock_create_w_handler(pool, NULL, stream, - &stream_on_destroy, - &stream->grp_lock); + &on_destroy, + &c_strm->grp_lock); if (status != PJ_SUCCESS) goto err_cleanup; /* Add ref */ - pj_grp_lock_add_ref(stream->grp_lock); - stream->port.grp_lock = stream->grp_lock; + pj_grp_lock_add_ref(c_strm->grp_lock); + c_strm->port.grp_lock = c_strm->grp_lock; /* Only attach transport when stream is ready. */ - stream->transport = tp; + c_strm->transport = tp; status = pjmedia_transport_attach2(tp, &att_param); if (status != PJ_SUCCESS) goto err_cleanup; /* Also add ref the transport group lock */ - if (stream->transport->grp_lock) - pj_grp_lock_add_ref(stream->transport->grp_lock); + if (c_strm->transport->grp_lock) + pj_grp_lock_add_ref(c_strm->transport->grp_lock); #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) @@ -2966,39 +2233,39 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (info->rtcp_xr_enabled) { int i; - pjmedia_rtcp_enable_xr(&stream->rtcp, PJ_TRUE); + pjmedia_rtcp_enable_xr(&c_strm->rtcp, PJ_TRUE); /* Set RTCP XR TX interval */ if (info->rtcp_xr_interval != 0) - stream->rtcp_xr_interval = info->rtcp_xr_interval; + c_strm->rtcp_xr_interval = info->rtcp_xr_interval; else - stream->rtcp_xr_interval = (PJMEDIA_RTCP_INTERVAL + + c_strm->rtcp_xr_interval = (PJMEDIA_RTCP_INTERVAL + (pj_rand() % 8000)) * info->fmt.clock_rate / 1000; /* Additional third-party RTCP XR destination */ if (info->rtcp_xr_dest.addr.sa_family != 0) { - stream->rtcp_xr_dest_len = pj_sockaddr_get_len(&info->rtcp_xr_dest); - pj_memcpy(&stream->rtcp_xr_dest, &info->rtcp_xr_dest, - stream->rtcp_xr_dest_len); + c_strm->rtcp_xr_dest_len = pj_sockaddr_get_len(&info->rtcp_xr_dest); + pj_memcpy(&c_strm->rtcp_xr_dest, &info->rtcp_xr_dest, + c_strm->rtcp_xr_dest_len); } /* jitter buffer adaptive info */ i = PJMEDIA_RTCP_XR_JB_ADAPTIVE; - pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, + pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_JBA, i); /* Jitter buffer aggressiveness info (estimated) */ i = 7; - pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, + pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_JBR, i); /* Jitter buffer absolute maximum delay */ i = jb_max * stream->codec_param.info.frm_ptime / stream->codec_param.info.frm_ptime_denum; - pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, + pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX, i); @@ -3011,7 +2278,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, #else i = PJMEDIA_RTCP_XR_PLC_DIS; #endif - pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, + pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, PJMEDIA_RTCP_XR_INFO_CONF_PLC, i); } @@ -3026,8 +2293,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (rfi->caps[i].type == PJMEDIA_RTCP_FB_NACK && rfi->caps[i].param.slen == 0) { - stream->send_rtcp_fb_nack = PJ_TRUE; - PJ_LOG(4,(stream->port.info.name.ptr, + c_strm->send_rtcp_fb_nack = PJ_TRUE; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Send RTCP-FB generic NACK")); break; } @@ -3035,7 +2302,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } /* Check if we should process incoming RTCP-FB */ - stream->rtcp_fb_nack_cap_idx = -1; + c_strm->rtcp_fb_nack_cap_idx = -1; if (stream->si.loc_rtcp_fb.cap_count) { pjmedia_rtcp_fb_info *lfi = &stream->si.loc_rtcp_fb; unsigned i; @@ -3044,8 +2311,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (lfi->caps[i].type == PJMEDIA_RTCP_FB_NACK && lfi->caps[i].param.slen == 0) { - stream->rtcp_fb_nack_cap_idx = i; - PJ_LOG(4,(stream->port.info.name.ptr, + c_strm->rtcp_fb_nack_cap_idx = i; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Receive RTCP-FB generic NACK")); break; } @@ -3064,14 +2331,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } /* Send RTCP SDES */ - if (!stream->rtcp_sdes_bye_disabled) { + if (!c_strm->rtcp_sdes_bye_disabled) { pjmedia_stream_send_rtcp_sdes(stream); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* NAT hole punching by sending KA packet via RTP transport. */ - if (stream->use_ka) - send_keep_alive_packet(stream); + if (c_strm->use_ka) + send_keep_alive_packet(c_strm); #endif #if TRACE_JB @@ -3081,25 +2348,25 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, pj_ansi_snprintf(trace_name, sizeof(trace_name), TRACE_JB_PATH_PREFIX "%s.csv", - stream->port.info.name.ptr); + c_strm->port.info.name.ptr); status = pj_file_open(pool, trace_name, PJ_O_WRONLY, - &stream->trace_jb_fd); + &c_strm->trace_jb_fd); if (status != PJ_SUCCESS) { - stream->trace_jb_fd = TRACE_JB_INVALID_FD; + c_strm->trace_jb_fd = TRACE_JB_INVALID_FD; PJ_PERROR(3,(THIS_FILE, status, "Failed creating RTP trace file '%s'", trace_name)); } else { - stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); + c_strm->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); /* Print column header */ - len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE, + len = pj_ansi_snprintf(c_strm->trace_jb_buf, PJ_LOG_MAX_SIZE, "Time, Operation, Size, Frame Count, " "Frame type, RTP Seq, RTP TS, RTP M, " "JB size, JB burst level, JB prefetch\n"); if (len < 1 || len >= PJ_LOG_MAX_SIZE) len = PJ_LOG_MAX_SIZE-1; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); + pj_file_write(c_strm->trace_jb_fd, c_strm->trace_jb_buf, &len); + pj_file_flush(c_strm->trace_jb_fd); } } #endif @@ -3107,7 +2374,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Success! */ *p_stream = stream; - PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->port.info.name.ptr)); + PJ_LOG(5,(THIS_FILE, "Stream %s created", c_strm->port.info.name.ptr)); return PJ_SUCCESS; @@ -3118,42 +2385,16 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, } -static void stream_on_destroy(void *arg) +static void on_stream_destroy(void *arg) { pjmedia_stream* stream = (pjmedia_stream*)arg; - /* This function may be called when stream is partly initialized. */ - - /* Release ref to transport */ - if (stream->transport && stream->transport->grp_lock) - pj_grp_lock_dec_ref(stream->transport->grp_lock); - /* Free codec. */ if (stream->codec) { pjmedia_codec_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; - } - - /* Destroy jitter buffer */ - if (stream->jb) - pjmedia_jbuf_destroy(stream->jb); - -#if TRACE_JB - if (TRACE_JB_OPENED(stream)) { - pj_file_close(stream->trace_jb_fd); - stream->trace_jb_fd = TRACE_JB_INVALID_FD; - } -#endif - - PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroyed")); - pj_pool_safe_release(&stream->own_pool); } @@ -3162,24 +2403,27 @@ static void stream_on_destroy(void *arg) */ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); pj_status_t status; PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); - PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroying")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Stream destroying")); /* Stop the streaming */ - if (stream->enc) - stream->port.put_frame = NULL; - if (stream->dec) - stream->port.get_frame = NULL; + if (c_strm->enc) + c_strm->port.put_frame = NULL; + if (c_strm->dec) + c_strm->port.get_frame = NULL; /* Send RTCP BYE (also SDES & XR) */ - if (stream->transport && !stream->rtcp_sdes_bye_disabled) { + if (c_strm->transport && !c_strm->rtcp_sdes_bye_disabled) { #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - send_rtcp(stream, PJ_TRUE, PJ_TRUE, stream->rtcp.xr_enabled, PJ_FALSE); + send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, c_strm->rtcp.xr_enabled, + PJ_FALSE, PJ_FALSE, PJ_FALSE); #else - send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE); + send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_FALSE); #endif } @@ -3187,16 +2431,16 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) * RFC 2833 RTP packet with 'End' flag set. */ if (stream->tx_dtmf_count && stream->tx_dtmf_buf[0].duration != 0 && - stream->transport && stream->jb_mutex) + c_strm->transport && c_strm->jb_mutex) { pjmedia_frame frame_out; - pjmedia_channel *channel = stream->enc; + pjmedia_channel *channel = c_strm->enc; int first=0, last=0; void *rtphdr; int rtphdrlen; pj_bzero(&frame_out, sizeof(frame_out)); - frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr); + frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr); frame_out.size = 0; create_dtmf_payload(stream, &frame_out, 1, &first, &last); @@ -3212,38 +2456,38 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) &rtphdrlen); if (status == PJ_SUCCESS) { /* Copy RTP header to the beginning of packet */ - pj_memcpy(channel->out_pkt, rtphdr, sizeof(pjmedia_rtp_hdr)); + pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Send the RTP packet to the transport. */ - status = pjmedia_transport_send_rtp(stream->transport, - channel->out_pkt, + status = pjmedia_transport_send_rtp(c_strm->transport, + channel->buf, frame_out.size + sizeof(pjmedia_rtp_hdr)); } if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->port.info.name.ptr, status, + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, "Error sending RTP/DTMF end packet")); } } /* Unsubscribe from RTCP session events */ pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, - &stream->rtcp); + &c_strm->rtcp); /* Detach from transport * MUST NOT hold stream mutex while detaching from transport, as * it may cause deadlock. See ticket #460 for the details. */ - if (stream->transport) { - pjmedia_transport_detach(stream->transport, stream); - //stream->transport = NULL; + if (c_strm->transport) { + pjmedia_transport_detach(c_strm->transport, stream); + //c_strm->transport = NULL; } - if (stream->grp_lock) { - pj_grp_lock_dec_ref(stream->grp_lock); + if (c_strm->grp_lock) { + pj_grp_lock_dec_ref(c_strm->grp_lock); } else { - stream_on_destroy(stream); + on_destroy(stream); } return PJ_SUCCESS; @@ -3255,7 +2499,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) */ PJ_DEF(char) pjmedia_stream_get_last_jb_frame_type(pjmedia_stream *stream) { - return stream->jb_last_frm; + pjmedia_stream_common *c_strm = &stream->base; + return c_strm->jb_last_frm; } @@ -3265,7 +2510,8 @@ PJ_DEF(char) pjmedia_stream_get_last_jb_frame_type(pjmedia_stream *stream) PJ_DEF(pj_status_t) pjmedia_stream_get_port( pjmedia_stream *stream, pjmedia_port **p_port ) { - *p_port = &stream->port; + pjmedia_stream_common *c_strm = &stream->base; + *p_port = &c_strm->port; return PJ_SUCCESS; } @@ -3275,7 +2521,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_port( pjmedia_stream *stream, */ PJ_DEF(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st) { - return st->transport; + return st->base.transport; } @@ -3284,23 +2530,24 @@ PJ_DEF(pjmedia_transport*) pjmedia_stream_get_transport(pjmedia_stream *st) */ PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); - PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP); + PJ_ASSERT_RETURN(stream && c_strm->enc && c_strm->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")); + if (c_strm->enc && (c_strm->dir & PJMEDIA_DIR_ENCODING)) { + c_strm->enc->paused = 0; + //pjmedia_snd_stream_start(c_strm->enc->snd_stream); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream started")); } else { - PJ_LOG(4,(stream->port.info.name.ptr, "Encoder stream paused")); + PJ_LOG(4,(c_strm->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")); + if (c_strm->dec && (c_strm->dir & PJMEDIA_DIR_DECODING)) { + c_strm->dec->paused = 0; + //pjmedia_snd_stream_start(c_strm->dec->snd_stream); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream started")); } else { - PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; @@ -3334,10 +2581,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_info( const pjmedia_stream *stream, 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; + return pjmedia_stream_common_get_stat((pjmedia_stream_common *)stream, + stat); } @@ -3346,11 +2591,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream, */ PJ_DEF(pj_status_t) pjmedia_stream_reset_stat(pjmedia_stream *stream) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - pjmedia_rtcp_init_stat(&stream->rtcp.stat); - - return PJ_SUCCESS; + return pjmedia_stream_common_reset_stat((pjmedia_stream_common *)stream); } @@ -3361,10 +2602,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_reset_stat(pjmedia_stream *stream) PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream, pjmedia_rtcp_xr_stat *stat) { + const pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); - if (stream->rtcp.xr_enabled) { - pj_memcpy(stat, &stream->rtcp.xr_session.stat, sizeof(pjmedia_rtcp_xr_stat)); + if (c_strm->rtcp.xr_enabled) { + pj_memcpy(stat, &c_strm->rtcp.xr_session.stat, sizeof(pjmedia_rtcp_xr_stat)); return PJ_SUCCESS; } return PJ_ENOTFOUND; @@ -3377,8 +2620,10 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr( const pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_get_stat_jbuf(const pjmedia_stream *stream, pjmedia_jb_state *state) { + const pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); - return pjmedia_jbuf_get_state(stream->jb, state); + return pjmedia_jbuf_get_state(c_strm->jb, state); } /* @@ -3387,22 +2632,24 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_stat_jbuf(const pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + 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_ENCODING) && c_strm->enc) { + c_strm->enc->paused = 1; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream paused")); } - if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { - stream->dec->paused = 1; + if ((dir & PJMEDIA_DIR_DECODING) && c_strm->dec) { + c_strm->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_mutex_lock( c_strm->jb_mutex ); + pjmedia_jbuf_reset(c_strm->jb); + pj_mutex_unlock( c_strm->jb_mutex ); - PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream paused")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; @@ -3415,17 +2662,19 @@ PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, PJ_DEF(pj_status_t) pjmedia_stream_resume( pjmedia_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + 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_ENCODING) && c_strm->enc) { + c_strm->enc->paused = 0; + PJ_LOG(4,(c_strm->port.info.name.ptr, "Encoder stream resumed")); } - if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { - stream->dec->paused = 0; + if ((dir & PJMEDIA_DIR_DECODING) && c_strm->dec) { + c_strm->dec->paused = 0; stream->soft_start_cnt = PJMEDIA_STREAM_SOFT_START; - PJ_LOG(4,(stream->port.info.name.ptr, "Decoder stream resumed")); + PJ_LOG(4,(c_strm->port.info.name.ptr, "Decoder stream resumed")); } return PJ_SUCCESS; @@ -3444,6 +2693,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf2( pjmedia_stream *stream, const pj_str_t *digit_char, unsigned duration) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); pj_status_t status = PJ_SUCCESS; /* By convention we use jitter buffer mutex to access DTMF @@ -3456,7 +2706,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf2( pjmedia_stream *stream, return PJMEDIA_RTP_EREMNORFC2833; } - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); if (stream->tx_dtmf_count+digit_char->slen >= (long)PJ_ARRAY_SIZE(stream->tx_dtmf_buf)) @@ -3514,7 +2764,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_dial_dtmf2( pjmedia_stream *stream, } on_return: - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); return status; } @@ -3536,12 +2786,14 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream, char *digits, unsigned *size) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream && digits && size, PJ_EINVAL); /* By convention, we use jitter buffer's mutex to access DTMF * digits resources. */ - pj_mutex_lock(stream->jb_mutex); + pj_mutex_lock(c_strm->jb_mutex); if (stream->rx_dtmf_count < *size) *size = stream->rx_dtmf_count; @@ -3556,7 +2808,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_dtmf( pjmedia_stream *stream, } } - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); return PJ_SUCCESS; } @@ -3571,17 +2823,19 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream, int digit), void *user_data) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + 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); + pj_mutex_lock(c_strm->jb_mutex); stream->dtmf_cb = cb; stream->dtmf_cb_user_data = user_data; - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); return PJ_SUCCESS; } @@ -3592,17 +2846,19 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_event_callback(pjmedia_stream *strea const pjmedia_stream_dtmf_event *event), void *user_data) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + 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); + pj_mutex_lock(c_strm->jb_mutex); stream->dtmf_event_cb = cb; stream->dtmf_event_cb_user_data = user_data; - pj_mutex_unlock(stream->jb_mutex); + pj_mutex_unlock(c_strm->jb_mutex); return PJ_SUCCESS; } @@ -3613,9 +2869,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_event_callback(pjmedia_stream *strea PJ_DEF(pj_status_t) pjmedia_stream_send_rtcp_sdes( pjmedia_stream *stream ) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - return send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE); + return pjmedia_stream_common_send_rtcp_sdes( + (pjmedia_stream_common *) stream); } /* @@ -3624,13 +2879,8 @@ pjmedia_stream_send_rtcp_sdes( pjmedia_stream *stream ) PJ_DEF(pj_status_t) pjmedia_stream_send_rtcp_bye( pjmedia_stream *stream ) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - if (stream->enc && stream->transport) { - return send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE); - } - - return PJ_SUCCESS; + return pjmedia_stream_common_send_rtcp_bye( + (pjmedia_stream_common *) stream); } @@ -3641,8 +2891,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_get_rtp_session_info(pjmedia_stream *stream, pjmedia_stream_rtp_sess_info *session_info) { - session_info->rx_rtp = &stream->dec->rtp; - session_info->tx_rtp = &stream->enc->rtp; - session_info->rtcp = &stream->rtcp; - return PJ_SUCCESS; + return pjmedia_stream_common_get_rtp_session_info( + (pjmedia_stream_common *)stream, session_info); } diff --git a/pjmedia/src/pjmedia/stream_common.c b/pjmedia/src/pjmedia/stream_common.c index 95bb1a64fe..4402c7baa7 100644 --- a/pjmedia/src/pjmedia/stream_common.c +++ b/pjmedia/src/pjmedia/stream_common.c @@ -16,10 +16,278 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include +#include #define THIS_FILE "stream_common.c" +#define LOGERR_(expr) PJ_PERROR(4,expr); + +/* Number of send error before repeat the report. */ +#define SEND_ERR_COUNT_TO_REPORT 50 + +static const pj_str_t ID_IN = { "IN", 2 }; +static const pj_str_t ID_IP4 = { "IP4", 3}; +static const pj_str_t ID_IP6 = { "IP6", 3}; + +/* + * Create stream info from SDP media line. + */ +PJ_DEF(pj_status_t) pjmedia_stream_info_common_from_sdp( + pjmedia_stream_info_common *si, + pj_pool_t *pool, + pjmedia_endpt *endpt, + const pjmedia_sdp_session *local, + const pjmedia_sdp_session *remote, + unsigned stream_idx) +{ + const pj_str_t STR_INACTIVE = { "inactive", 8 }; + const pj_str_t STR_SENDONLY = { "sendonly", 8 }; + const pj_str_t STR_RECVONLY = { "recvonly", 8 }; + + const pjmedia_sdp_attr *attr; + const pjmedia_sdp_media *local_m; + const pjmedia_sdp_media *rem_m; + const pjmedia_sdp_conn *local_conn; + const pjmedia_sdp_conn *rem_conn; + int rem_af, local_af; + unsigned i; + pj_status_t status; + + + /* Validate arguments: */ + PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); + PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); + PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); + + /* Keep SDP shortcuts */ + local_m = local->media[stream_idx]; + rem_m = remote->media[stream_idx]; + + local_conn = local_m->conn ? local_m->conn : local->conn; + if (local_conn == NULL) + return PJMEDIA_SDP_EMISSINGCONN; + + rem_conn = rem_m->conn ? rem_m->conn : remote->conn; + if (rem_conn == NULL) + return PJMEDIA_SDP_EMISSINGCONN; + + /* Reset: */ + pj_bzero(si, sizeof(*si)); + +#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR + /* Set default RTCP XR enabled/disabled */ + si->rtcp_xr_enabled = PJ_TRUE; +#endif + + /* Media type: */ + si->type = pjmedia_get_type(&local_m->desc.media); + + /* Transport protocol */ + + /* At this point, transport type must be compatible, + * the transport instance will do more validation later. + */ + status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, + &local_m->desc.transport); + if (status != PJ_SUCCESS) + return PJMEDIA_SDPNEG_EINVANSTP; + + /* Get the transport protocol */ + si->proto = pjmedia_sdp_transport_get_proto(&local_m->desc.transport); + + /* Just return success if stream is not RTP/AVP compatible */ + if (!PJMEDIA_TP_PROTO_HAS_FLAG(si->proto, PJMEDIA_TP_PROTO_RTP_AVP)) + return PJ_SUCCESS; + + /* Check address family in remote SDP */ + rem_af = pj_AF_UNSPEC(); + if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { + if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { + rem_af = pj_AF_INET(); + } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { + rem_af = pj_AF_INET6(); + } + } + + if (rem_af==pj_AF_UNSPEC()) { + /* Unsupported address family */ + return PJ_EAFNOTSUP; + } + + /* Set remote address: */ + status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, + rem_m->desc.port); + if (status == PJ_ERESOLVE && rem_af == pj_AF_INET()) { + /* Handle special case in NAT64 scenario where for some reason, server + * puts IPv6 (literal or FQDN) in SDP answer while indicating "IP4" + * in its address type, let's retry resolving using AF_INET6. + */ + status = pj_sockaddr_init(pj_AF_INET6(), &si->rem_addr, + &rem_conn->addr, rem_m->desc.port); + } + if (status != PJ_SUCCESS) { + /* Invalid IP address. */ + return PJMEDIA_EINVALIDIP; + } + + /* Check address family of local info */ + local_af = pj_AF_UNSPEC(); + if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { + if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { + local_af = pj_AF_INET(); + } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { + local_af = pj_AF_INET6(); + } + } + + if (local_af==pj_AF_UNSPEC()) { + /* Unsupported address family */ + return PJ_SUCCESS; + } + + /* Set remote address: */ + status = pj_sockaddr_init(local_af, &si->local_addr, &local_conn->addr, + local_m->desc.port); + if (status != PJ_SUCCESS) { + /* Invalid IP address. */ + return PJMEDIA_EINVALIDIP; + } + + /* Local and remote address family must match, except when ICE is used + * by both sides (see also ticket #1952). + */ + if (local_af != rem_af) { + const pj_str_t STR_ICE_CAND = { "candidate", 9 }; + if (pjmedia_sdp_media_find_attr(rem_m, &STR_ICE_CAND, NULL)==NULL || + pjmedia_sdp_media_find_attr(local_m, &STR_ICE_CAND, NULL)==NULL) + { + return PJ_EAFNOTSUP; + } + } + + /* Media direction: */ + if (local_m->desc.port == 0 || + pj_sockaddr_has_addr(&si->local_addr)==PJ_FALSE || + pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || + pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) + { + /* Inactive stream. */ + + si->dir = PJMEDIA_DIR_NONE; + + } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { + + /* Send only stream. */ + + si->dir = PJMEDIA_DIR_ENCODING; + + } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { + + /* Recv only stream. */ + + si->dir = PJMEDIA_DIR_DECODING; + + } else { + + /* Send and receive stream. */ + + si->dir = PJMEDIA_DIR_ENCODING_DECODING; + + } + + /* No need to do anything else if stream is rejected */ + if (local_m->desc.port == 0) { + return PJ_SUCCESS; + } + + /* Check if "rtcp-mux" is present in the SDP. */ + attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, + "rtcp-mux", NULL); + if (attr) + si->rtcp_mux = PJ_TRUE; + + /* If "rtcp" attribute is present in the SDP, set the RTCP address + * from that attribute. Otherwise, calculate from RTP address. + */ + attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, + "rtcp", NULL); + if (attr) { + pjmedia_sdp_rtcp_attr rtcp; + status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); + if (status == PJ_SUCCESS) { + if (rtcp.addr.slen) { + status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, + (pj_uint16_t)rtcp.port); + if (status != PJ_SUCCESS) + return PJMEDIA_EINVALIDIP; + } else { + pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, + (pj_uint16_t)rtcp.port); + pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), + pj_sockaddr_get_addr(&si->rem_addr), + pj_sockaddr_get_addr_len(&si->rem_addr)); + } + } + } + + if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { + int rtcp_port; + + pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); + rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; + pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); + } + + /* Check if "ssrc" attribute is present in the SDP. */ + for (i = 0; i < rem_m->attr_count; i++) { + if (pj_strcmp2(&rem_m->attr[i]->name, "ssrc") == 0) { + pjmedia_sdp_ssrc_attr ssrc; + + status = pjmedia_sdp_attr_get_ssrc( + (const pjmedia_sdp_attr *)rem_m->attr[i], &ssrc); + if (status == PJ_SUCCESS) { + si->has_rem_ssrc = PJ_TRUE; + si->rem_ssrc = ssrc.ssrc; + if (ssrc.cname.slen > 0) { + pj_strdup(pool, &si->rem_cname, &ssrc.cname); + break; + } + } + } + } + + /* Leave SSRC to random. */ + si->ssrc = pj_rand(); + + /* Set default jitter buffer parameter. */ + si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; + si->jb_discard_algo = PJMEDIA_JB_DISCARD_PROGRESSIVE; + + if (pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_AUDIO || + pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_VIDEO) + { + /* Get local RTCP-FB info */ + if (pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_AUDIO || + pjmedia_get_type(&local_m->desc.media) == PJMEDIA_TYPE_VIDEO) + status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, + stream_idx, si->rx_pt, + &si->loc_rtcp_fb); + if (status != PJ_SUCCESS) + return status; + + /* Get remote RTCP-FB info */ + status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, + stream_idx, si->tx_pt, + &si->rem_rtcp_fb); + if (status != PJ_SUCCESS) + return status; + } + + return status; +} + #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 PJ_DEF(void) @@ -149,3 +417,261 @@ PJ_DECL(pj_status_t) pjmedia_stream_info_parse_fmtp_data(pj_pool_t *pool, return PJ_SUCCESS; } +/* + * Get stream statistics. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_get_stat( const pjmedia_stream_common *c_strm, + pjmedia_rtcp_stat *stat) +{ + PJ_ASSERT_RETURN(c_strm && stat, PJ_EINVAL); + + pj_memcpy(stat, &c_strm->rtcp.stat, sizeof(pjmedia_rtcp_stat)); + return PJ_SUCCESS; +} + +/* + * Reset the stream statistics in the middle of a stream session. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_reset_stat(pjmedia_stream_common *c_strm) +{ + PJ_ASSERT_RETURN(c_strm, PJ_EINVAL); + + pjmedia_rtcp_init_stat(&c_strm->rtcp.stat); + + return PJ_SUCCESS; +} + +/* + * Send RTCP SDES. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_send_rtcp_sdes( pjmedia_stream_common *stream ) +{ + PJ_ASSERT_RETURN(stream, PJ_EINVAL); + + return pjmedia_stream_send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_FALSE, PJ_FALSE); +} + +/* + * Send RTCP BYE. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_send_rtcp_bye( pjmedia_stream_common *c_strm ) +{ + PJ_ASSERT_RETURN(c_strm, PJ_EINVAL); + + if (c_strm->enc && c_strm->transport) { + return pjmedia_stream_send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, PJ_FALSE, + PJ_FALSE, PJ_FALSE, PJ_FALSE); + } + + return PJ_SUCCESS; +} + +/** + * Get RTP session information from stream. + */ +PJ_DEF(pj_status_t) +pjmedia_stream_common_get_rtp_session_info(pjmedia_stream_common *c_strm, + pjmedia_stream_rtp_sess_info *session_info) +{ + session_info->rx_rtp = &c_strm->dec->rtp; + session_info->tx_rtp = &c_strm->enc->rtp; + session_info->rtcp = &c_strm->rtcp; + return PJ_SUCCESS; +} + +static pj_status_t build_rtcp_fb(pjmedia_stream_common *c_strm, void *buf, + pj_size_t *length) +{ + pj_status_t status; + + /* Generic NACK */ + if (c_strm->send_rtcp_fb_nack && c_strm->rtcp_fb_nack.pid >= 0) + { + status = pjmedia_rtcp_fb_build_nack(&c_strm->rtcp, buf, length, 1, + &c_strm->rtcp_fb_nack); + if (status != PJ_SUCCESS) + return status; + + /* Reset Packet ID */ + c_strm->rtcp_fb_nack.pid = -1; + } + + return PJ_SUCCESS; +} + +pj_status_t pjmedia_stream_send_rtcp(pjmedia_stream_common *c_strm, + pj_bool_t with_sdes, + pj_bool_t with_bye, + pj_bool_t with_xr, + pj_bool_t with_fb, + pj_bool_t with_fb_nack, + pj_bool_t with_fb_pli) +{ + void *sr_rr_pkt; + pj_uint8_t *pkt; + int len, max_len; + pj_status_t status; + + /* We need to prevent data race since there is only a single instance + * of rtcp packet buffer. And to avoid deadlock with media transport, + * we use the transport's group lock. + */ + if (c_strm->transport->grp_lock) + pj_grp_lock_acquire(c_strm->transport->grp_lock); + + /* Build RTCP RR/SR packet */ + pjmedia_rtcp_build_rtcp(&c_strm->rtcp, &sr_rr_pkt, &len); + +#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0) + with_xr = PJ_FALSE; +#endif + + if (with_sdes || with_bye || with_xr || with_fb || with_fb_nack || + with_fb_pli) + { + pkt = (pj_uint8_t*) c_strm->out_rtcp_pkt; + pj_memcpy(pkt, sr_rr_pkt, len); + max_len = c_strm->out_rtcp_pkt_size; + } else { + pkt = (pj_uint8_t*)sr_rr_pkt; + max_len = len; + } + + /* Build RTCP SDES packet, forced if also send RTCP-FB */ + with_sdes = with_sdes || with_fb_pli || with_fb_nack; + + /* Build RTCP SDES packet */ + if (with_sdes) { + pjmedia_rtcp_sdes sdes; + pj_size_t sdes_len; + + pj_bzero(&sdes, sizeof(sdes)); + sdes.cname = c_strm->cname; + sdes_len = max_len - len; + status = pjmedia_rtcp_build_rtcp_sdes(&c_strm->rtcp, pkt+len, + &sdes_len, &sdes); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP SDES")); + } else { + len += (int)sdes_len; + } + } + + if (with_fb) { + pj_size_t fb_len = max_len - len; + status = build_rtcp_fb(c_strm, pkt+len, &fb_len); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP FB")); + } else { + len += (int)fb_len; + } + } + + /* Build RTCP XR packet */ +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + if (with_xr) { + int i; + pjmedia_jb_state jb_state; + void *xr_pkt; + int xr_len; + + /* Update RTCP XR with current JB states */ + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); + + i = jb_state.avg_delay; + status = pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, + PJMEDIA_RTCP_XR_INFO_JB_NOM, i); + pj_assert(status == PJ_SUCCESS); + + i = jb_state.max_delay; + status = pjmedia_rtcp_xr_update_info(&c_strm->rtcp.xr_session, + PJMEDIA_RTCP_XR_INFO_JB_MAX, i); + pj_assert(status == PJ_SUCCESS); + + pjmedia_rtcp_build_rtcp_xr(&c_strm->rtcp.xr_session, 0, + &xr_pkt, &xr_len); + + if (xr_len + len <= max_len) { + pj_memcpy(pkt+len, xr_pkt, xr_len); + len += xr_len; + + /* Send the RTCP XR to third-party destination if specified */ + if (c_strm->rtcp_xr_dest_len) { + pjmedia_transport_send_rtcp2(c_strm->transport, + &c_strm->rtcp_xr_dest, + c_strm->rtcp_xr_dest_len, + xr_pkt, xr_len); + } + + } else { + PJ_PERROR(4,(c_strm->port.info.name.ptr, PJ_ETOOBIG, + "Error generating RTCP-XR")); + } + } +#endif + + /* Build RTCP BYE packet */ + if (with_bye) { + pj_size_t bye_len; + + bye_len = max_len - len; + status = pjmedia_rtcp_build_rtcp_bye(&c_strm->rtcp, pkt+len, + &bye_len, NULL); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP BYE")); + } else { + len += (int)bye_len; + } + } + + /* Build RTCP-FB generic NACK packet */ + if (with_fb_nack && c_strm->rtcp_fb_nack.pid >= 0) { + pj_size_t fb_len = max_len - len; + status = pjmedia_rtcp_fb_build_nack(&c_strm->rtcp, pkt+len, &fb_len, + 1, &c_strm->rtcp_fb_nack); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP-FB NACK")); + } else { + len += (int)fb_len; + } + } + + /* Build RTCP-FB PLI packet */ + if (with_fb_pli) { + pj_size_t fb_len = max_len - len; + status = pjmedia_rtcp_fb_build_pli(&c_strm->rtcp, pkt+len, &fb_len); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error generating RTCP-FB PLI")); + } else { + len += (int)fb_len; + PJ_LOG(5,(c_strm->name.ptr, "Sending RTCP-FB PLI packet")); + } + } + + /* Send! */ + status = pjmedia_transport_send_rtcp(c_strm->transport, pkt, len); + if (status != PJ_SUCCESS) { + if (c_strm->rtcp_tx_err_cnt++ == 0) { + LOGERR_((c_strm->port.info.name.ptr, status, + "Error sending RTCP")); + } + if (c_strm->rtcp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { + c_strm->rtcp_tx_err_cnt = 0; + } + } + + if (c_strm->transport->grp_lock) + pj_grp_lock_release(c_strm->transport->grp_lock); + + return status; +} diff --git a/pjmedia/src/pjmedia/stream_imp_common.c b/pjmedia/src/pjmedia/stream_imp_common.c new file mode 100755 index 0000000000..7b38541c2a --- /dev/null +++ b/pjmedia/src/pjmedia/stream_imp_common.c @@ -0,0 +1,599 @@ +/* + * Copyright (C) 2025 Teluu Inc. (http://www.teluu.com) + * + * 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 + */ + +/* Prototypes. */ +/* Specific stream implementation's RX RTP handler. */ +static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + const void *payload, + unsigned payloadlen, + pjmedia_rtp_status seq_st, + pj_bool_t *pkt_discarded); + +/* Specific stream implementation's destroy handler. */ +static void on_stream_destroy(void *arg); + +#if TRACE_JB + +#include + +#define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1) +#define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD) + +PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len) +{ + pj_time_val now; + pj_parsed_time ptime; + char *p = *buf; + + if (len < 14) + return -1; + + pj_gettimeofday(&now); + pj_time_decode(&now, &ptime); + p += pj_utoa_pad(ptime.hour, p, 2, '0'); + *p++ = ':'; + p += pj_utoa_pad(ptime.min, p, 2, '0'); + *p++ = ':'; + p += pj_utoa_pad(ptime.sec, p, 2, '0'); + *p++ = '.'; + p += pj_utoa_pad(ptime.msec, p, 3, '0'); + *p++ = ','; + + *buf = p; + + return 0; +} + +PJ_INLINE(int) trace_jb_print_state(pjmedia_stream_common *c_strm, + char **buf, pj_ssize_t len) +{ + char *p = *buf; + char *endp = *buf + len; + pjmedia_jb_state state; + + pjmedia_jbuf_get_state(c_strm->jb, &state); + + len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d", + state.size, state.burst, state.prefetch); + if ((len < 0) || (len >= endp-p)) + return -1; + + p += len; + *buf = p; + return 0; +} + +static void trace_jb_get(pjmedia_stream_common *c_strm, pjmedia_jb_frame_type ft, + pj_size_t fsize) +{ + char *p = c_strm->trace_jb_buf; + char *endp = c_strm->trace_jb_buf + PJ_LOG_MAX_SIZE; + pj_ssize_t len = 0; + const char* ft_st; + + if (!TRACE_JB_OPENED(c_strm)) + return; + + /* Print timestamp. */ + if (trace_jb_print_timestamp(&p, endp-p)) + goto on_insuff_buffer; + + /* Print frame type and size */ + switch(ft) { + case PJMEDIA_JB_MISSING_FRAME: + ft_st = "missing"; + break; + case PJMEDIA_JB_NORMAL_FRAME: + ft_st = "normal"; + break; + case PJMEDIA_JB_ZERO_PREFETCH_FRAME: + ft_st = "prefetch"; + break; + case PJMEDIA_JB_ZERO_EMPTY_FRAME: + ft_st = "empty"; + break; + default: + ft_st = "unknown"; + break; + } + + /* Print operation, size, frame count, frame type */ + len = pj_ansi_snprintf(p, endp-p, "GET,%zu,1,%s,,,,", fsize, ft_st); + if ((len < 0) || (len >= endp-p)) + goto on_insuff_buffer; + p += len; + + /* Print JB state */ + if (trace_jb_print_state(c_strm, &p, endp-p)) + goto on_insuff_buffer; + + /* Print end of line */ + if (endp-p < 2) + goto on_insuff_buffer; + *p++ = '\n'; + + /* Write and flush */ + len = p - c_strm->trace_jb_buf; + pj_file_write(c_strm->trace_jb_fd, c_strm->trace_jb_buf, &len); + pj_file_flush(c_strm->trace_jb_fd); + return; + +on_insuff_buffer: + pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); +} + +static void trace_jb_put(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + unsigned payloadlen, unsigned frame_cnt) +{ + char *p = c_strm->trace_jb_buf; + char *endp = c_strm->trace_jb_buf + PJ_LOG_MAX_SIZE; + pj_ssize_t len = 0; + + if (!TRACE_JB_OPENED(c_strm)) + return; + + /* Print timestamp. */ + if (trace_jb_print_timestamp(&p, endp-p)) + goto on_insuff_buffer; + + /* Print operation, size, frame count, RTP info */ + len = pj_ansi_snprintf(p, endp-p, + "PUT,%d,%d,,%d,%d,%d,", + payloadlen, frame_cnt, + pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m); + if ((len < 0) || (len >= endp-p)) + goto on_insuff_buffer; + p += len; + + /* Print JB state */ + if (trace_jb_print_state(c_strm, &p, endp-p)) + goto on_insuff_buffer; + + /* Print end of line */ + if (endp-p < 2) + goto on_insuff_buffer; + *p++ = '\n'; + + /* Write and flush */ + len = p - c_strm->trace_jb_buf; + pj_file_write(c_strm->trace_jb_fd, c_strm->trace_jb_buf, &len); + pj_file_flush(c_strm->trace_jb_fd); + return; + +on_insuff_buffer: + pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); +} + +#endif /* TRACE_JB */ + + +static pj_status_t send_rtcp(pjmedia_stream_common *c_strm, + pj_bool_t with_sdes, + pj_bool_t with_bye, + pj_bool_t with_xr, + pj_bool_t with_fb, + pj_bool_t with_fb_nack, + pj_bool_t with_fb_pli) +{ + return pjmedia_stream_send_rtcp(c_strm, with_sdes, with_bye, + with_xr, with_fb, with_fb_nack, + with_fb_pli); +} + + +#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 +/* + * Send keep-alive packet using non-codec frame. + */ +static void send_keep_alive_packet(pjmedia_stream_common *c_strm) +{ +#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP + + /* Keep-alive packet is empty RTP */ + pj_status_t status; + void *pkt; + int pkt_len; + + if (!c_strm->transport) + return; + + TRC_((c_strm->port.info.name.ptr, + "Sending keep-alive (RTCP and empty RTP)")); + + /* Send RTP */ + status = pjmedia_rtp_encode_rtp( &c_strm->enc->rtp, + c_strm->enc->pt, 0, + 1, + 0, + (const void**)&pkt, + &pkt_len); + pj_assert(status == PJ_SUCCESS); + + pj_memcpy(c_strm->enc->buf, pkt, pkt_len); + pjmedia_transport_send_rtp(c_strm->transport, c_strm->enc->buf, + pkt_len); + + /* Send RTCP */ + send_rtcp(c_strm, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_FALSE); + + /* Update stats in case the stream is paused */ + c_strm->rtcp.stat.rtp_tx_last_seq = pj_ntohs(c_strm->enc->rtp.out_hdr.seq); + +#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER + + /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ + pjmedia_channel *channel = c_strm->enc; + int pkt_len; + const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT; + + TRC_((c_strm->port.info.name.ptr, + "Sending keep-alive (custom RTP/RTCP packets)")); + + /* Send to RTP port */ + pj_memcpy(c_strm->enc->buf, str_ka.ptr, str_ka.slen); + pkt_len = str_ka.slen; + pjmedia_transport_send_rtp(c_strm->transport, c_strm->enc->buf, + pkt_len); + + /* Send to RTCP port */ + pjmedia_transport_send_rtcp(c_strm->transport, c_strm->enc->buf, + pkt_len); + +#else + + PJ_UNUSED_ARG(stream); + +#endif +} +#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */ + +/** + * Publish transport error event. + */ +static void publish_tp_event(pjmedia_event_type event_type, + pj_status_t status, + pj_bool_t is_rtp, + pjmedia_dir dir, + pjmedia_stream_common *stream) +{ + pjmedia_event ev; + pj_timestamp ts_now; + + pj_get_timestamp(&ts_now); + pj_bzero(&ev.data.med_tp_err, sizeof(ev.data.med_tp_err)); + + /* Publish event. */ + pjmedia_event_init(&ev, event_type, + &ts_now, stream); + ev.data.med_tp_err.type = stream->si->type; + ev.data.med_tp_err.is_rtp = is_rtp; + ev.data.med_tp_err.dir = dir; + ev.data.med_tp_err.status = status; + + pjmedia_event_publish(NULL, stream, &ev, 0); +} + +/* + * This callback is called by stream transport on receipt of packets + * in the RTCP socket. + */ +static void on_rx_rtcp( void *data, + void *pkt, + pj_ssize_t bytes_read) +{ + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)data; + pj_status_t status; + + /* Check for errors */ + if (bytes_read < 0) { + status = (pj_status_t)-bytes_read; + if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return; + } + LOGERR_((c_strm->port.info.name.ptr, status, + "Unable to receive RTCP packet")); + + if (status == PJ_ESOCKETSTOP) { + /* Publish receive error event. */ + publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_FALSE, + PJMEDIA_DIR_DECODING, c_strm); + } + return; + } + + pjmedia_rtcp_rx_rtcp(&c_strm->rtcp, pkt, bytes_read); +} + +/* + * This callback is called by stream transport on receipt of packets + * in the RTP socket. + */ +static void on_rx_rtp( pjmedia_tp_cb_param *param) +{ +#ifdef AUDIO_STREAM + pjmedia_stream *stream = (pjmedia_stream*) param->user_data; +#endif + pjmedia_stream_common *c_strm = (pjmedia_stream_common *) + param->user_data; + void *pkt = param->pkt; + pj_ssize_t bytes_read = param->size; + pjmedia_channel *channel = c_strm->dec; + const pjmedia_rtp_hdr *hdr; + const void *payload; + unsigned payloadlen; + pjmedia_rtp_status seq_st; + pj_bool_t check_pt; + pj_status_t status; + pj_bool_t pkt_discarded = PJ_FALSE; + + /* Check for errors */ + if (bytes_read < 0) { + status = (pj_status_t)-bytes_read; + if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return; + } + + LOGERR_((c_strm->port.info.name.ptr, status, + "Unable to receive RTP packet")); + + if (status == PJ_ESOCKETSTOP) { + /* Publish receive error event. */ + publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_TRUE, + PJMEDIA_DIR_DECODING, c_strm); + } + return; + } + + /* Ignore non-RTP keep-alive packets */ + if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr)) + return; + + /* Update RTP and RTCP session. */ + status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, (int)bytes_read, + &hdr, &payload, &payloadlen); + if (status != PJ_SUCCESS) { + LOGERR_((c_strm->port.info.name.ptr, status, "RTP decode error")); + c_strm->rtcp.stat.rx.discard++; + return; + } + + /* Check if multiplexing is allowed and the payload indicates RTCP. */ + if (c_strm->si->rtcp_mux && hdr->pt >= 64 && hdr->pt <= 95) { + on_rx_rtcp(c_strm, pkt, bytes_read); + return; + } + + /* See if source address of RTP packet is different than the + * configured address, and check if we need to tell the + * media transport to switch RTP remote address. + */ + if (param->src_addr) { + pj_uint32_t peer_ssrc = channel->rtp.peer_ssrc; + pj_bool_t badssrc = PJ_FALSE; + + /* Check SSRC. */ + if (!channel->rtp.has_peer_ssrc && peer_ssrc == 0) + peer_ssrc = pj_ntohl(hdr->ssrc); + + if ((c_strm->si->has_rem_ssrc) && (pj_ntohl(hdr->ssrc) != peer_ssrc)) { + badssrc = PJ_TRUE; + } + + if (pj_sockaddr_cmp(&c_strm->rem_rtp_addr, param->src_addr) == 0) { + /* We're still receiving from rem_rtp_addr. */ + c_strm->rtp_src_cnt = 0; + c_strm->rem_rtp_flag = badssrc? 2: 1; + } else { + c_strm->rtp_src_cnt++; + + if (c_strm->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { + if (c_strm->rem_rtp_flag == 1 || + (c_strm->rem_rtp_flag == 2 && badssrc)) + { + /* Only discard if: + * - we have ever received packet with good ssrc from + * remote address (rem_rtp_addr), or + * - we have ever received packet with bad ssrc from + * remote address and this packet also has bad ssrc. + */ + return; + } + if (!badssrc && c_strm->rem_rtp_flag != 1) + { + /* Immediately switch if we receive packet with the + * correct ssrc AND we never receive packets with + * good ssrc from rem_rtp_addr. + */ + param->rem_switch = PJ_TRUE; + } + } else { + /* Switch. We no longer receive packets from rem_rtp_addr. */ + param->rem_switch = PJ_TRUE; + } + + if (param->rem_switch) { + /* Set remote RTP address to source address */ + pj_sockaddr_cp(&c_strm->rem_rtp_addr, param->src_addr); + + /* Reset counter and flag */ + c_strm->rtp_src_cnt = 0; + c_strm->rem_rtp_flag = badssrc? 2: 1; + + /* Update RTCP peer ssrc */ + c_strm->rtcp.peer_ssrc = pj_ntohl(hdr->ssrc); + } + } + } + + /* Add ref counter to avoid premature destroy from callbacks */ + pj_grp_lock_add_ref(c_strm->grp_lock); + + /* Ignore the packet if decoder is paused */ + if (channel->paused) + goto on_return; + + /* Update RTP session (also checks if RTP session can accept + * the incoming packet. + */ + pj_bzero(&seq_st, sizeof(seq_st)); + check_pt = PJMEDIA_STREAM_CHECK_RTP_PT; +#ifdef AUDIO_STREAM + check_pt = check_pt && hdr->pt != stream->rx_event_pt; +#endif + pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, check_pt); +#if !PJMEDIA_STREAM_CHECK_RTP_PT + if (!check_pt && hdr->pt != channel->rtp.out_pt) { +#ifdef AUDIO_STREAM + if (hdr->pt != stream->rx_event_pt) +#endif + seq_st.status.flag.badpt = -1; + } +#endif + if (seq_st.status.value) { + TRC_ ((c_strm->port.info.name.ptr, + "RTP status: badpt=%d, badssrc=%d, dup=%d, " + "outorder=%d, probation=%d, restart=%d", + seq_st.status.flag.badpt, + seq_st.status.flag.badssrc, + seq_st.status.flag.dup, + seq_st.status.flag.outorder, + seq_st.status.flag.probation, + seq_st.status.flag.restart)); + + if (seq_st.status.flag.badpt) { + PJ_LOG(4,(c_strm->port.info.name.ptr, + "Bad RTP pt %d (expecting %d)", + hdr->pt, channel->rtp.out_pt)); + } + + if (!c_strm->si->has_rem_ssrc && seq_st.status.flag.badssrc) { + PJ_LOG(4,(c_strm->port.info.name.ptr, + "Changed RTP peer SSRC %d (previously %d)", + channel->rtp.peer_ssrc, c_strm->rtcp.peer_ssrc)); + c_strm->rtcp.peer_ssrc = channel->rtp.peer_ssrc; + } + + + } + + /* Skip bad RTP packet */ + if (seq_st.status.flag.bad) { + pkt_discarded = PJ_TRUE; + goto on_return; + } + + /* Ignore if payloadlen is zero */ + if (payloadlen == 0) { + pkt_discarded = PJ_TRUE; + goto on_return; + } + + /* Pass it to specific stream for further processing. */ + on_stream_rx_rtp(c_strm, hdr, payload, payloadlen, seq_st, + &pkt_discarded); + +on_return: + /* Update RTCP session */ + if (c_strm->rtcp.peer_ssrc == 0) + c_strm->rtcp.peer_ssrc = channel->rtp.peer_ssrc; + + pjmedia_rtcp_rx_rtp2(&c_strm->rtcp, pj_ntohs(hdr->seq), + pj_ntohl(hdr->ts), payloadlen, pkt_discarded); + + /* RTCP-FB generic NACK */ + if (c_strm->rtcp.received >= 10 && seq_st.diff > 1 && + c_strm->send_rtcp_fb_nack && pj_ntohs(hdr->seq) >= seq_st.diff) + { + pj_uint16_t nlost, first_seq; + + /* Report only one NACK (last 17 losts) */ + nlost = PJ_MIN(seq_st.diff - 1, 17); + first_seq = pj_ntohs(hdr->seq) - nlost; + + pj_bzero(&c_strm->rtcp_fb_nack, sizeof(c_strm->rtcp_fb_nack)); + c_strm->rtcp_fb_nack.pid = first_seq; + while (--nlost) { + c_strm->rtcp_fb_nack.blp <<= 1; + c_strm->rtcp_fb_nack.blp |= 1; + } + + /* Send it immediately */ + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, + PJ_FALSE, PJ_FALSE, PJ_TRUE, PJ_FALSE, PJ_FALSE); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error sending RTCP FB generic NACK")); + } else { + c_strm->initial_rr = PJ_TRUE; + } + } + + /* Send RTCP RR and SDES after we receive some RTP packets */ + if (c_strm->rtcp.received >= 10 && !c_strm->initial_rr) { + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, + PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_FALSE); + if (status != PJ_SUCCESS) { + PJ_PERROR(4,(c_strm->port.info.name.ptr, status, + "Error sending initial RTCP RR")); + } else { + c_strm->initial_rr = PJ_TRUE; + } + } + + pj_grp_lock_dec_ref(c_strm->grp_lock); +} + +/* Common stream destroy handler. */ +static void on_destroy(void *arg) +{ + pjmedia_stream_common *c_strm = (pjmedia_stream_common *)arg; + + /* This function may be called when stream is partly initialized. */ + + /* Call specific stream destroy handler. */ + on_stream_destroy(arg); + + /* Release ref to transport */ + if (c_strm->transport && c_strm->transport->grp_lock) + pj_grp_lock_dec_ref(c_strm->transport->grp_lock); + + /* Free mutex */ + if (c_strm->jb_mutex) { + pj_mutex_destroy(c_strm->jb_mutex); + c_strm->jb_mutex = NULL; + } + + /* Destroy jitter buffer */ + if (c_strm->jb) { + pjmedia_jbuf_destroy(c_strm->jb); + c_strm->jb = NULL; + } + +#if TRACE_JB + if (TRACE_JB_OPENED(c_strm)) { + pj_file_close(c_strm->trace_jb_fd); + c_strm->trace_jb_fd = TRACE_JB_INVALID_FD; + } +#endif + + PJ_LOG(4,(c_strm->port.info.name.ptr, "Stream destroyed")); + pj_pool_safe_release(&c_strm->own_pool); +} diff --git a/pjmedia/src/pjmedia/stream_info.c b/pjmedia/src/pjmedia/stream_info.c index 0a474a6e50..0b948c49e6 100644 --- a/pjmedia/src/pjmedia/stream_info.c +++ b/pjmedia/src/pjmedia/stream_info.c @@ -22,10 +22,6 @@ #include #include -static const pj_str_t ID_IN = { "IN", 2 }; -static const pj_str_t ID_IP4 = { "IP4", 3}; -static const pj_str_t ID_IP6 = { "IP6", 3}; -//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 }; @@ -357,38 +353,21 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( const pjmedia_sdp_session *remote, unsigned stream_idx) { - const pj_str_t STR_INACTIVE = { "inactive", 8 }; - const pj_str_t STR_SENDONLY = { "sendonly", 8 }; - const pj_str_t STR_RECVONLY = { "recvonly", 8 }; - + pjmedia_stream_info_common *csi = (pjmedia_stream_info_common *)si; pjmedia_codec_mgr *mgr; - const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; - const pjmedia_sdp_conn *local_conn; - const pjmedia_sdp_conn *rem_conn; - int rem_af, local_af; - unsigned i; pj_status_t status; - - /* Validate arguments: */ - PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); - PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); - PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); + status = pjmedia_stream_info_common_from_sdp(csi, pool, endpt, local, + remote, stream_idx); + if (status != PJ_SUCCESS) + return status; /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; - local_conn = local_m->conn ? local_m->conn : local->conn; - if (local_conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; - - rem_conn = rem_m->conn ? rem_m->conn : remote->conn; - if (rem_conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; - /* Media type must be audio */ if (pjmedia_get_type(&local_m->desc.media) != PJMEDIA_TYPE_AUDIO) return PJMEDIA_EINVALIMEDIATYPE; @@ -396,193 +375,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( /* Get codec manager. */ mgr = pjmedia_endpt_get_codec_mgr(endpt); - /* Reset: */ - - pj_bzero(si, sizeof(*si)); - -#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR - /* Set default RTCP XR enabled/disabled */ - si->rtcp_xr_enabled = PJ_TRUE; -#endif - - /* Media type: */ - si->type = PJMEDIA_TYPE_AUDIO; - - /* Transport protocol */ - - /* At this point, transport type must be compatible, - * the transport instance will do more validation later. - */ - status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, - &local_m->desc.transport); - if (status != PJ_SUCCESS) - return PJMEDIA_SDPNEG_EINVANSTP; - - /* Get the transport protocol */ - si->proto = pjmedia_sdp_transport_get_proto(&local_m->desc.transport); - - /* Just return success if stream is not RTP/AVP compatible */ - if (!PJMEDIA_TP_PROTO_HAS_FLAG(si->proto, PJMEDIA_TP_PROTO_RTP_AVP)) - return PJ_SUCCESS; - - /* Check address family in remote SDP */ - rem_af = pj_AF_UNSPEC(); - if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { - if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { - rem_af = pj_AF_INET(); - } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { - rem_af = pj_AF_INET6(); - } - } - - if (rem_af==pj_AF_UNSPEC()) { - /* Unsupported address family */ - return PJ_EAFNOTSUP; - } - - /* Set remote address: */ - status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, - rem_m->desc.port); - if (status == PJ_ERESOLVE && rem_af == pj_AF_INET()) { - /* Handle special case in NAT64 scenario where for some reason, server - * puts IPv6 (literal or FQDN) in SDP answer while indicating "IP4" - * in its address type, let's retry resolving using AF_INET6. - */ - status = pj_sockaddr_init(pj_AF_INET6(), &si->rem_addr, - &rem_conn->addr, rem_m->desc.port); - } - if (status != PJ_SUCCESS) { - /* Invalid IP address. */ - return PJMEDIA_EINVALIDIP; - } - - /* Check address family of local info */ - local_af = pj_AF_UNSPEC(); - if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { - if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { - local_af = pj_AF_INET(); - } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { - local_af = pj_AF_INET6(); - } - } - - if (local_af==pj_AF_UNSPEC()) { - /* Unsupported address family */ - return PJ_SUCCESS; - } - - /* Set remote address: */ - status = pj_sockaddr_init(local_af, &si->local_addr, &local_conn->addr, - local_m->desc.port); - if (status != PJ_SUCCESS) { - /* Invalid IP address. */ - return PJMEDIA_EINVALIDIP; - } - - /* Local and remote address family must match, except when ICE is used - * by both sides (see also ticket #1952). - */ - if (local_af != rem_af) { - const pj_str_t STR_ICE_CAND = { "candidate", 9 }; - if (pjmedia_sdp_media_find_attr(rem_m, &STR_ICE_CAND, NULL)==NULL || - pjmedia_sdp_media_find_attr(local_m, &STR_ICE_CAND, NULL)==NULL) - { - return PJ_EAFNOTSUP; - } - } - - /* Media direction: */ - - if (local_m->desc.port == 0 || - pj_sockaddr_has_addr(&si->local_addr)==PJ_FALSE || - pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || - pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) - { - /* Inactive stream. */ - - si->dir = PJMEDIA_DIR_NONE; - - } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { - - /* Send only stream. */ - - si->dir = PJMEDIA_DIR_ENCODING; - - } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { - - /* Recv only stream. */ - - si->dir = PJMEDIA_DIR_DECODING; - - } else { - - /* Send and receive stream. */ - - si->dir = PJMEDIA_DIR_ENCODING_DECODING; - - } - - /* No need to do anything else if stream is rejected */ - if (local_m->desc.port == 0) { - return PJ_SUCCESS; - } - - /* Check if "rtcp-mux" is present in the SDP. */ - attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, - "rtcp-mux", NULL); - if (attr) - si->rtcp_mux = PJ_TRUE; - - /* If "rtcp" attribute is present in the SDP, set the RTCP address - * from that attribute. Otherwise, calculate from RTP address. - */ - attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, - "rtcp", NULL); - if (attr) { - pjmedia_sdp_rtcp_attr rtcp; - status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); - if (status == PJ_SUCCESS) { - if (rtcp.addr.slen) { - status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, - (pj_uint16_t)rtcp.port); - if (status != PJ_SUCCESS) - return PJMEDIA_EINVALIDIP; - } else { - pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, - (pj_uint16_t)rtcp.port); - pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), - pj_sockaddr_get_addr(&si->rem_addr), - pj_sockaddr_get_addr_len(&si->rem_addr)); - } - } - } - - if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { - int rtcp_port; - - pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); - rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; - pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); - } - - /* Check if "ssrc" attribute is present in the SDP. */ - for (i = 0; i < rem_m->attr_count; i++) { - if (pj_strcmp2(&rem_m->attr[i]->name, "ssrc") == 0) { - pjmedia_sdp_ssrc_attr ssrc; - - status = pjmedia_sdp_attr_get_ssrc( - (const pjmedia_sdp_attr *)rem_m->attr[i], &ssrc); - if (status == PJ_SUCCESS) { - si->has_rem_ssrc = PJ_TRUE; - si->rem_ssrc = ssrc.ssrc; - if (ssrc.cname.slen > 0) { - pj_strdup(pool, &si->rem_cname, &ssrc.cname); - break; - } - } - } - } - /* Get the payload number for receive channel. */ /* Previously we used to rely on fmt[0] being the selected codec, @@ -605,27 +397,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( /* Get codec info and param */ status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m); - if (status != PJ_SUCCESS) - return status; - - /* Leave SSRC to random. */ - si->ssrc = pj_rand(); - - /* Set default jitter buffer parameter. */ - si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; - si->jb_discard_algo = PJMEDIA_JB_DISCARD_PROGRESSIVE; - - /* Get local RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, stream_idx, - si->rx_pt, &si->loc_rtcp_fb); - if (status != PJ_SUCCESS) - return status; - - /* Get remote RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, stream_idx, - si->tx_pt, &si->rem_rtcp_fb); - if (status != PJ_SUCCESS) - return status; return status; } diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index f3a74b85ff..c5ce745a3b 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -46,18 +46,10 @@ #define TRACE_RC 0 -/* Tracing jitter buffer operations in a stream session to a CSV file. - * The trace will contain JB operation timestamp, frame info, RTP info, and - * the JB state right after the operation. - */ -#define TRACE_JB 0 /* Enable/disable trace. */ -#define TRACE_JB_PATH_PREFIX "" /* Optional path/prefix - for the CSV filename. */ -#if TRACE_JB -# include -# define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1) -# define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD) -#endif +/* Enable/disable trace. */ +#define TRACE_JB PJMEDIA_STREAM_TRACE_JB +/* Optional path/prefix for the CSV filename. */ +#define TRACE_JB_PATH_PREFIX "" #ifndef PJMEDIA_VSTREAM_SIZE # define PJMEDIA_VSTREAM_SIZE 16000 @@ -80,18 +72,7 @@ /** * Media channel. */ -typedef struct pjmedia_vid_channel -{ - pjmedia_vid_stream *stream; /**< Parent stream. */ - pjmedia_dir dir; /**< Channel direction. */ - pjmedia_port port; /**< Port interface. */ - unsigned pt; /**< Payload type. */ - pj_bool_t paused; /**< Paused?. */ - void *buf; /**< Output buffer. */ - unsigned buf_size; /**< Size of output buffer. */ - pjmedia_rtp_session rtp; /**< RTP session. */ -} pjmedia_vid_channel; - +typedef pjmedia_channel pjmedia_vid_channel; /** * This structure describes media stream. @@ -102,35 +83,12 @@ typedef struct pjmedia_vid_channel */ struct pjmedia_vid_stream { - pj_pool_t *own_pool; /**< Internal pool. */ - pjmedia_endpt *endpt; /**< Media endpoint. */ + pjmedia_stream_common base; + pjmedia_vid_codec_mgr *codec_mgr; /**< Codec manager. */ pjmedia_vid_stream_info info; /**< Stream info. */ - pj_grp_lock_t *grp_lock; /**< Stream lock. */ - - pjmedia_vid_channel *enc; /**< Encoding channel. */ - pjmedia_vid_channel *dec; /**< Decoding channel. */ - - pjmedia_dir dir; /**< Stream direction. */ - void *user_data; /**< User data. */ - pj_str_t name; /**< Stream name */ - pj_str_t cname; /**< SDES CNAME */ - pjmedia_transport *transport; /**< Stream transport. */ - - pjmedia_jbuf *jb; /**< Jitter buffer. */ - char jb_last_frm; /**< Last frame type from jb */ - unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/ - - pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */ pj_timestamp rtcp_last_tx; /**< Last RTCP tx time. */ - pj_timestamp rtcp_fb_last_tx;/**< Last RTCP-FB tx time. */ - pj_uint32_t rtcp_interval; /**< Interval, in msec. */ - pj_bool_t initial_rr; /**< Initial RTCP RR sent */ - pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/ - void *out_rtcp_pkt; /**< Outgoing RTCP packet. */ - unsigned out_rtcp_pkt_size; - /**< Outgoing RTCP packet size. */ unsigned dec_max_size; /**< Size of decoded/raw picture*/ pjmedia_ratio dec_max_fps; /**< Max fps of decoding dir. */ @@ -157,55 +115,13 @@ struct pjmedia_vid_stream /**< Timestamp of the last keyframe. */ - -#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 - pj_bool_t use_ka; /**< Stream keep-alive with non- - codec-VAD mechanism is - enabled? */ - unsigned ka_interval; /**< The keepalive sending - interval */ - pj_time_val last_frm_ts_sent; /**< Time of last sending - packet */ - unsigned start_ka_count; /**< The number of keep-alive - to be sent after it is - created */ - unsigned start_ka_interval;/**< The keepalive sending - interval after the stream - is created */ - pj_timestamp last_start_ka_tx; /**< Timestamp of the last - keepalive sent */ -#endif - -#if TRACE_JB - pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/ - char *trace_jb_buf; /**< Jitter tracing buffer. */ -#endif - pjmedia_vid_codec *codec; /**< Codec instance being used. */ pj_uint32_t last_dec_ts; /**< Last decoded timestamp. */ int last_dec_seq; /**< Last decoded sequence. */ - pj_uint32_t rtp_tx_err_cnt;/**< The number of RTP - send() error */ - pj_uint32_t rtcp_tx_err_cnt;/**< The number of RTCP - send() error */ pj_timestamp ts_freq; /**< Timestamp frequency. */ - pj_sockaddr rem_rtp_addr; /**< Remote RTP address */ - unsigned rem_rtp_flag; /**< Indicator flag about - packet from this addr. - 0=no pkt, 1=good ssrc pkts, - 2=bad ssrc pkts */ - pj_sockaddr rtp_src_addr; /**< Actual packet src addr. */ - unsigned rtp_src_cnt; /**< How many pkt from this addr*/ - - /* RTCP Feedback */ - pj_bool_t send_rtcp_fb_nack; /**< Send NACK? */ - int pending_rtcp_fb_nack; /**< Any pending NACK? */ - int rtcp_fb_nack_cap_idx; /**< RX NACK cap idx. */ - pjmedia_rtcp_fb_nack rtcp_fb_nack; /**< TX NACK state. */ - pj_bool_t send_rtcp_fb_pli; /**< Send PLI? */ int pending_rtcp_fb_pli; /**< Any pending PLI? */ int rtcp_fb_pli_cap_idx; /**< RX PLI cap idx. */ @@ -223,13 +139,6 @@ struct pjmedia_vid_stream static pj_status_t decode_frame(pjmedia_vid_stream *stream, pjmedia_frame *frame); - -static pj_status_t send_rtcp(pjmedia_vid_stream *stream, - pj_bool_t with_sdes, - pj_bool_t with_bye, - pj_bool_t with_fb_nack, - pj_bool_t with_fb_pli); - static void on_rx_rtcp( void *data, void *pkt, pj_ssize_t bytes_read); @@ -237,155 +146,7 @@ static void on_rx_rtcp( void *data, static void on_destroy(void *arg); -#if TRACE_JB - -PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len) -{ - pj_time_val now; - pj_parsed_time ptime; - char *p = *buf; - - if (len < 14) - return -1; - - pj_gettimeofday(&now); - pj_time_decode(&now, &ptime); - p += pj_utoa_pad(ptime.hour, p, 2, '0'); - *p++ = ':'; - p += pj_utoa_pad(ptime.min, p, 2, '0'); - *p++ = ':'; - p += pj_utoa_pad(ptime.sec, p, 2, '0'); - *p++ = '.'; - p += pj_utoa_pad(ptime.msec, p, 3, '0'); - *p++ = ','; - - *buf = p; - - return 0; -} - -PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream, - char **buf, pj_ssize_t len) -{ - char *p = *buf; - char *endp = *buf + len; - pjmedia_jb_state state; - - pjmedia_jbuf_get_state(stream->jb, &state); - - len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d", - state.size, state.burst, state.prefetch); - if ((len < 0) || (len >= endp-p)) - return -1; - - p += len; - *buf = p; - return 0; -} - -static void trace_jb_get(pjmedia_vid_stream *stream, pjmedia_jb_frame_type ft, - pj_size_t fsize) -{ - char *p = stream->trace_jb_buf; - char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; - pj_ssize_t len = 0; - const char* ft_st; - - if (!TRACE_JB_OPENED(stream)) - return; - - /* Print timestamp. */ - if (trace_jb_print_timestamp(&p, endp-p)) - goto on_insuff_buffer; - - /* Print frame type and size */ - switch(ft) { - case PJMEDIA_JB_MISSING_FRAME: - ft_st = "missing"; - break; - case PJMEDIA_JB_NORMAL_FRAME: - ft_st = "normal"; - break; - case PJMEDIA_JB_ZERO_PREFETCH_FRAME: - ft_st = "prefetch"; - break; - case PJMEDIA_JB_ZERO_EMPTY_FRAME: - ft_st = "empty"; - break; - default: - ft_st = "unknown"; - break; - } - - /* Print operation, size, frame count, frame type */ - len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st); - if ((len < 0) || (len >= endp-p)) - goto on_insuff_buffer; - p += len; - - /* Print JB state */ - if (trace_jb_print_state(stream, &p, endp-p)) - goto on_insuff_buffer; - - /* Print end of line */ - if (endp-p < 2) - goto on_insuff_buffer; - *p++ = '\n'; - - /* Write and flush */ - len = p - stream->trace_jb_buf; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); - return; - -on_insuff_buffer: - pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); -} - -static void trace_jb_put(pjmedia_vid_stream *stream, - const pjmedia_rtp_hdr *hdr, - unsigned payloadlen, unsigned frame_cnt) -{ - char *p = stream->trace_jb_buf; - char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE; - pj_ssize_t len = 0; - - if (!TRACE_JB_OPENED(stream)) - return; - - /* Print timestamp. */ - if (trace_jb_print_timestamp(&p, endp-p)) - goto on_insuff_buffer; - - /* Print operation, size, frame count, RTP info */ - len = pj_ansi_snprintf(p, endp-p, - "PUT,%d,%d,,%d,%d,%d,", - payloadlen, frame_cnt, - pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m); - if ((len < 0) || (len >= endp-p)) - goto on_insuff_buffer; - p += len; - - /* Print JB state */ - if (trace_jb_print_state(stream, &p, endp-p)) - goto on_insuff_buffer; - - /* Print end of line */ - if (endp-p < 2) - goto on_insuff_buffer; - *p++ = '\n'; - - /* Write and flush */ - len = p - stream->trace_jb_buf; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); - return; - -on_insuff_buffer: - pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!"); -} - -#endif /* TRACE_JB */ +#include "stream_imp_common.c" static void dump_port_info(const pjmedia_vid_channel *chan, const char *event_name) @@ -411,6 +172,7 @@ static pj_status_t stream_event_cb(pjmedia_event *event, void *user_data) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*)user_data; + pjmedia_stream_common *c_strm = &stream->base; if (event->epub == stream->codec) { /* This is codec event */ @@ -439,7 +201,7 @@ static pj_status_t stream_event_cb(pjmedia_event *event, default: break; } - } else if (event->epub == &stream->rtcp && + } else if (event->epub == &c_strm->rtcp && event->type==PJMEDIA_EVENT_RX_RTCP_FB) { /* This is RX RTCP-FB event */ @@ -449,13 +211,13 @@ static pj_status_t stream_event_cb(pjmedia_event *event, /* Check if configured to listen to the RTCP-FB type */ if (data->cap.type == PJMEDIA_RTCP_FB_NACK) { if (data->cap.param.slen == 0 && - stream->rtcp_fb_nack_cap_idx >= 0) + c_strm->rtcp_fb_nack_cap_idx >= 0) { /* Generic NACK */ /* Update event data capability before republishing */ data->cap = stream->info.loc_rtcp_fb.caps[ - stream->rtcp_fb_nack_cap_idx]; + c_strm->rtcp_fb_nack_cap_idx]; } else if (pj_strcmp2(&data->cap.param, "pli") == 0 && stream->rtcp_fb_pli_cap_idx >= 0) @@ -478,207 +240,6 @@ static pj_status_t stream_event_cb(pjmedia_event *event, PJMEDIA_EVENT_PUBLISH_POST_EVENT); } - -/** - * Publish transport error event. - */ -static void publish_tp_event(pjmedia_event_type event_type, - pj_status_t status, - pj_bool_t is_rtp, - pjmedia_dir dir, - pjmedia_vid_stream *stream) -{ - pjmedia_event ev; - pj_timestamp ts_now; - - pj_get_timestamp(&ts_now); - pj_bzero(&ev.data.med_tp_err, sizeof(ev.data.med_tp_err)); - - /* Publish event. */ - pjmedia_event_init(&ev, event_type, - &ts_now, stream); - ev.data.med_tp_err.type = PJMEDIA_TYPE_VIDEO; - ev.data.med_tp_err.is_rtp = is_rtp; - ev.data.med_tp_err.dir = dir; - ev.data.med_tp_err.status = status; - - pjmedia_event_publish(NULL, stream, &ev, 0); -} - -#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 -/* - * Send keep-alive packet using non-codec frame. - */ -static void send_keep_alive_packet(pjmedia_vid_stream *stream) -{ -#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP - - /* Keep-alive packet is empty RTP */ - pjmedia_vid_channel *channel = stream->enc; - pj_status_t status; - void *pkt; - int pkt_len; - - if (!stream->transport) - return; - - TRC_((channel->port.info.name.ptr, - "Sending keep-alive (RTCP and empty RTP)")); - - /* Send RTP */ - status = pjmedia_rtp_encode_rtp( &stream->enc->rtp, - stream->enc->pt, 0, - 1, - 0, - (const void**)&pkt, - &pkt_len); - pj_assert(status == PJ_SUCCESS); - - pj_memcpy(stream->enc->buf, pkt, pkt_len); - pjmedia_transport_send_rtp(stream->transport, stream->enc->buf, - pkt_len); - - /* Send RTCP */ - send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE); - - /* Update stats in case the stream is paused */ - stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq); - -#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER - - /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ - pjmedia_vid_channel *channel = stream->enc; - int pkt_len; - const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT; - - TRC_((channel->port.info.name.ptr, - "Sending keep-alive (custom RTP/RTCP packets)")); - - /* Send to RTP port */ - pj_memcpy(stream->enc->buf, str_ka.ptr, str_ka.slen); - pkt_len = str_ka.slen; - pjmedia_transport_send_rtp(stream->transport, stream->enc->buf, - pkt_len); - - /* Send to RTCP port */ - pjmedia_transport_send_rtcp(stream->transport, stream->enc->buf, - pkt_len); - -#else - - PJ_UNUSED_ARG(stream); - -#endif -} -#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */ - - -static pj_status_t send_rtcp(pjmedia_vid_stream *stream, - pj_bool_t with_sdes, - pj_bool_t with_bye, - pj_bool_t with_fb_nack, - pj_bool_t with_fb_pli) -{ - void *sr_rr_pkt; - pj_uint8_t *pkt; - int len, max_len; - pj_status_t status; - - - /* To avoid deadlock with media transport, we use the transport's - * group lock. - */ - if (stream->transport->grp_lock) - pj_grp_lock_acquire( stream->transport->grp_lock ); - - /* Build RTCP RR/SR packet */ - pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len); - - if (with_sdes || with_bye || with_fb_nack || with_fb_pli) { - pkt = (pj_uint8_t*) stream->out_rtcp_pkt; - pj_memcpy(pkt, sr_rr_pkt, len); - max_len = stream->out_rtcp_pkt_size; - } else { - pkt = (pj_uint8_t*)sr_rr_pkt; - max_len = len; - } - - /* Build RTCP SDES packet, forced if also send RTCP-FB */ - with_sdes = with_sdes || with_fb_pli || with_fb_nack; - if (with_sdes) { - pjmedia_rtcp_sdes sdes; - pj_size_t sdes_len; - - pj_bzero(&sdes, sizeof(sdes)); - sdes.cname = stream->cname; - sdes_len = max_len - len; - status = pjmedia_rtcp_build_rtcp_sdes(&stream->rtcp, pkt+len, - &sdes_len, &sdes); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error generating RTCP SDES")); - } else { - len += (int)sdes_len; - } - } - - /* Build RTCP BYE packet */ - if (with_bye) { - pj_size_t bye_len = max_len - len; - status = pjmedia_rtcp_build_rtcp_bye(&stream->rtcp, pkt+len, - &bye_len, NULL); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error generating RTCP BYE")); - } else { - len += (int)bye_len; - } - } - - /* Build RTCP-FB generic NACK packet */ - if (with_fb_nack && stream->rtcp_fb_nack.pid >= 0) { - pj_size_t fb_len = max_len - len; - status = pjmedia_rtcp_fb_build_nack(&stream->rtcp, pkt+len, &fb_len, - 1, &stream->rtcp_fb_nack); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error generating RTCP-FB NACK")); - } else { - len += (int)fb_len; - } - } - - /* Build RTCP-FB PLI packet */ - if (with_fb_pli) { - pj_size_t fb_len = max_len - len; - status = pjmedia_rtcp_fb_build_pli(&stream->rtcp, pkt+len, &fb_len); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error generating RTCP-FB PLI")); - } else { - len += (int)fb_len; - PJ_LOG(5,(stream->name.ptr, "Sending RTCP-FB PLI packet")); - } - } - - /* Send! */ - status = pjmedia_transport_send_rtcp(stream->transport, pkt, len); - if (status != PJ_SUCCESS) { - if (stream->rtcp_tx_err_cnt++ == 0) { - LOGERR_((stream->name.ptr, status, "Error sending RTCP")); - } - if (stream->rtcp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - stream->rtcp_tx_err_cnt = 0; - } - } - - if (stream->transport->grp_lock) - pj_grp_lock_release( stream->transport->grp_lock ); - - return status; -} - - /** * check_tx_rtcp() * @@ -688,6 +249,7 @@ static pj_status_t send_rtcp(pjmedia_vid_stream *stream, */ static void check_tx_rtcp(pjmedia_vid_stream *stream) { + pjmedia_stream_common *c_strm = &stream->base; pj_timestamp now; pj_bool_t early; @@ -695,10 +257,10 @@ static void check_tx_rtcp(pjmedia_vid_stream *stream) * elapsed timestamp from previous RTCP-FB >= PJMEDIA_RTCP_FB_INTERVAL). */ pj_get_timestamp(&now); - early = ((stream->pending_rtcp_fb_pli || stream->pending_rtcp_fb_nack) + early = ((stream->pending_rtcp_fb_pli || c_strm->pending_rtcp_fb_nack) && - (stream->rtcp_fb_last_tx.u64 == 0 || - pj_elapsed_msec(&stream->rtcp_fb_last_tx, &now) >= + (c_strm->rtcp_fb_last_tx.u64 == 0 || + pj_elapsed_msec(&c_strm->rtcp_fb_last_tx, &now) >= PJMEDIA_RTCP_FB_INTERVAL)); /* First check, unless RTCP is 'urgent', just init rtcp_last_tx. */ @@ -709,226 +271,49 @@ static void check_tx_rtcp(pjmedia_vid_stream *stream) /* Build & send RTCP */ if (early || - pj_elapsed_msec(&stream->rtcp_last_tx, &now) >= stream->rtcp_interval) + pj_elapsed_msec(&stream->rtcp_last_tx, &now) >= c_strm->rtcp_interval) { pj_status_t status; - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE, - stream->pending_rtcp_fb_nack, + status = send_rtcp(c_strm, !c_strm->rtcp_sdes_bye_disabled, PJ_FALSE, + PJ_FALSE, PJ_FALSE, c_strm->pending_rtcp_fb_nack, stream->pending_rtcp_fb_pli); if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, + PJ_PERROR(4,(c_strm->name.ptr, status, "Error sending RTCP")); } stream->rtcp_last_tx = now; if (early) - stream->rtcp_fb_last_tx = now; + c_strm->rtcp_fb_last_tx = now; if (stream->pending_rtcp_fb_pli) stream->pending_rtcp_fb_pli--; - if (stream->pending_rtcp_fb_nack) - stream->pending_rtcp_fb_nack--; - } -} - - -#if 0 -static void dump_bin(const char *buf, unsigned len) -{ - unsigned i; - - PJ_LOG(3,(THIS_FILE, "begin dump")); - for (i=0; ipending_rtcp_fb_nack) + c_strm->pending_rtcp_fb_nack--; } - PJ_LOG(3,(THIS_FILE, "end dump")); } -#endif - /* - * This callback is called by stream transport on receipt of packets - * in the RTP socket. + * This callback is called by common stream processing on receipt of + * packets in the RTP socket (i.e. called by on_rx_rtp() in + * stream_imp_common.c) */ -static void on_rx_rtp( pjmedia_tp_cb_param *param) +static pj_status_t on_stream_rx_rtp(pjmedia_stream_common *c_strm, + const pjmedia_rtp_hdr *hdr, + const void *payload, + unsigned payloadlen, + pjmedia_rtp_status seq_st, + pj_bool_t *pkt_discarded) { - pjmedia_vid_stream *stream = (pjmedia_vid_stream*) param->user_data; - void *pkt = param->pkt; - pj_ssize_t bytes_read = param->size; - pjmedia_vid_channel *channel = stream->dec; - const pjmedia_rtp_hdr *hdr; - const void *payload; - unsigned payloadlen; - pjmedia_rtp_status seq_st; - pj_status_t status; + pjmedia_vid_stream *stream = (pjmedia_vid_stream*) c_strm; + pjmedia_vid_channel *channel = c_strm->dec; + pj_status_t status = PJ_SUCCESS; long ts_diff; - pj_bool_t pkt_discarded = PJ_FALSE; - - /* Check for errors */ - if (bytes_read < 0) { - status = (pj_status_t)-bytes_read; - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return; - } - - LOGERR_((channel->port.info.name.ptr, status, - "Unable to receive RTP packet")); - if (status == PJ_ESOCKETSTOP) { - /* Publish receive error event. */ - publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_TRUE, - PJMEDIA_DIR_DECODING, stream); - } - return; - } - - /* Ignore keep-alive packets */ - if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr)) - return; - - /* Update RTP and RTCP session. */ - status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, (int)bytes_read, - &hdr, &payload, &payloadlen); - if (status != PJ_SUCCESS) { - LOGERR_((channel->port.info.name.ptr, status, "RTP decode error")); - stream->rtcp.stat.rx.discard++; - return; - } - - /* Check if multiplexing is allowed and the payload indicates RTCP. */ - if (stream->info.rtcp_mux && hdr->pt >= 64 && hdr->pt <= 95) { - on_rx_rtcp(stream, pkt, bytes_read); - return; - } - - /* Add ref counter to avoid premature destroy from callbacks */ - pj_grp_lock_add_ref(stream->grp_lock); - - /* Ignore the packet if decoder is paused */ - if (channel->paused) - goto on_return; - - /* Update RTP session (also checks if RTP session can accept - * the incoming packet. - */ - pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, - PJMEDIA_VID_STREAM_CHECK_RTP_PT); -#if !PJMEDIA_VID_STREAM_CHECK_RTP_PT - if (hdr->pt != channel->rtp.out_pt) { - seq_st.status.flag.badpt = -1; - } -#endif - if (seq_st.status.value) { - TRC_ ((channel->port.info.name.ptr, - "RTP status: badpt=%d, badssrc=%d, dup=%d, " - "outorder=%d, probation=%d, restart=%d", - seq_st.status.flag.badpt, - seq_st.status.flag.badssrc, - seq_st.status.flag.dup, - seq_st.status.flag.outorder, - seq_st.status.flag.probation, - seq_st.status.flag.restart)); - - if (seq_st.status.flag.badpt) { - PJ_LOG(4,(channel->port.info.name.ptr, - "Bad RTP pt %d (expecting %d)", - hdr->pt, channel->rtp.out_pt)); - } - - if (!stream->info.has_rem_ssrc && seq_st.status.flag.badssrc) { - PJ_LOG(4,(channel->port.info.name.ptr, - "Changed RTP peer SSRC %d (previously %d)", - channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc)); - stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; - } - - - } - - /* Skip bad RTP packet */ - if (seq_st.status.flag.bad) { - pkt_discarded = PJ_TRUE; - goto on_return; - } - - /* Ignore if payloadlen is zero */ - if (payloadlen == 0) { - pkt_discarded = PJ_TRUE; - goto on_return; - } - - /* See if source address of RTP packet is different than the - * configured address, and check if we need to tell the - * media transport to switch RTP remote address. - */ - if (param->src_addr) { - pj_bool_t badssrc = (stream->info.has_rem_ssrc && - seq_st.status.flag.badssrc); - - if (pj_sockaddr_cmp(&stream->rem_rtp_addr, param->src_addr) == 0) { - /* We're still receiving from rem_rtp_addr. */ - stream->rtp_src_cnt = 0; - stream->rem_rtp_flag = badssrc? 2: 1; - } else { - stream->rtp_src_cnt++; - - if (stream->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { - if (stream->rem_rtp_flag == 1 || - (stream->rem_rtp_flag == 2 && badssrc)) - { - /* Only discard if: - * - we have ever received packet with good ssrc from - * remote address (rem_rtp_addr), or - * - we have ever received packet with bad ssrc from - * remote address and this packet also has bad ssrc. - */ - pkt_discarded = PJ_TRUE; - goto on_return; - } - if (stream->info.has_rem_ssrc && !seq_st.status.flag.badssrc - && stream->rem_rtp_flag != 1) - { - /* Immediately switch if we receive packet with the - * correct ssrc AND we never receive packets with - * good ssrc from rem_rtp_addr. - */ - param->rem_switch = PJ_TRUE; - } - } else { - /* Switch. We no longer receive packets from rem_rtp_addr. */ - param->rem_switch = PJ_TRUE; - } - - if (param->rem_switch) { - /* Set remote RTP address to source address */ - pj_sockaddr_cp(&stream->rem_rtp_addr, param->src_addr); - - /* Reset counter and flag */ - stream->rtp_src_cnt = 0; - stream->rem_rtp_flag = badssrc? 2: 1; - - /* Update RTCP peer ssrc */ - stream->rtcp.peer_ssrc = pj_ntohl(hdr->ssrc); - } - } - } - - pj_grp_lock_acquire( stream->grp_lock ); + pj_grp_lock_acquire( c_strm->grp_lock ); /* Quickly see if there may be a full picture in the jitter buffer, and * decode them if so. More thorough check will be done in decode_frame(). @@ -951,7 +336,7 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) */ pj_bool_t can_decode = PJ_FALSE; - if (pjmedia_jbuf_is_full(stream->jb)) { + if (pjmedia_jbuf_is_full(c_strm->jb)) { can_decode = PJ_TRUE; } else if (stream->dec_frame.size == 0) { @@ -982,32 +367,32 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) * when RTP session is restarted. */ if (seq_st.status.flag.restart) { - status = pjmedia_jbuf_reset(stream->jb); + status = pjmedia_jbuf_reset(c_strm->jb); PJ_LOG(4,(channel->port.info.name.ptr, "Jitter buffer reset")); } else { /* Just put the payload into jitter buffer */ - pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0, + pjmedia_jbuf_put_frame3(c_strm->jb, payload, payloadlen, 0, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL); #if TRACE_JB - trace_jb_put(stream, hdr, payloadlen, count); + trace_jb_put(c_strm, hdr, payloadlen, 1 /*count*/); #endif } - pj_grp_lock_release( stream->grp_lock ); + pj_grp_lock_release( c_strm->grp_lock ); /* Check if we need to send RTCP-FB generic NACK */ - if (stream->send_rtcp_fb_nack && seq_st.diff > 1 && + if (c_strm->send_rtcp_fb_nack && seq_st.diff > 1 && pj_ntohs(hdr->seq) >= seq_st.diff) { int i; - pj_bzero(&stream->rtcp_fb_nack, sizeof(stream->rtcp_fb_nack)); - stream->rtcp_fb_nack.pid = pj_ntohs(hdr->seq) - seq_st.diff + 1; + pj_bzero(&c_strm->rtcp_fb_nack, sizeof(c_strm->rtcp_fb_nack)); + c_strm->rtcp_fb_nack.pid = pj_ntohs(hdr->seq) - seq_st.diff + 1; for (i = 0; i < (seq_st.diff - 1); ++i) { - stream->rtcp_fb_nack.blp <<= 1; - stream->rtcp_fb_nack.blp |= 1; + c_strm->rtcp_fb_nack.blp <<= 1; + c_strm->rtcp_fb_nack.blp |= 1; } - stream->pending_rtcp_fb_nack = 1; + c_strm->pending_rtcp_fb_nack = 1; } /* Check if now is the time to transmit RTCP SR/RR report. @@ -1015,75 +400,28 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) * if the encoder is paused, * because otherwise check_tx_rtcp() will be handled by put_frame() */ - if (stream->dir == PJMEDIA_DIR_DECODING || stream->enc->paused) { + if (c_strm->dir == PJMEDIA_DIR_DECODING || c_strm->enc->paused) { check_tx_rtcp(stream); } if (status != 0) { LOGERR_((channel->port.info.name.ptr, status, "Jitter buffer put() error")); - pkt_discarded = PJ_TRUE; + *pkt_discarded = PJ_TRUE; goto on_return; } on_return: - /* Update RTCP session */ - if (stream->rtcp.peer_ssrc == 0) - stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc; - - pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq), - pj_ntohl(hdr->ts), payloadlen, pkt_discarded); - - /* Send RTCP RR and SDES after we receive some RTP packets */ - if (stream->rtcp.received >= 10 && !stream->initial_rr) { - status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, - PJ_FALSE, PJ_FALSE, PJ_FALSE); - if (status != PJ_SUCCESS) { - PJ_PERROR(4,(stream->name.ptr, status, - "Error sending initial RTCP RR")); - } else { - stream->initial_rr = PJ_TRUE; - } - } - - pj_grp_lock_dec_ref(stream->grp_lock); + return status; } -/* - * This callback is called by stream transport on receipt of packets - * in the RTCP socket. - */ -static void on_rx_rtcp( void *data, - void *pkt, - pj_ssize_t bytes_read) -{ - pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data; - pj_status_t status; - - /* Check for errors */ - if (bytes_read < 0) { - status = (pj_status_t)-bytes_read; - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return; - } - LOGERR_((stream->cname.ptr, status, "Unable to receive RTCP packet")); - if (status == PJ_ESOCKETSTOP) { - /* Publish receive error event. */ - publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_FALSE, - PJMEDIA_DIR_DECODING, stream); - } - return; - } - - pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read); -} - static pj_status_t put_frame(pjmedia_port *port, pjmedia_frame *frame) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata; - pjmedia_vid_channel *channel = stream->enc; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_vid_channel *channel = c_strm->enc; pj_status_t status = 0; pjmedia_frame frame_out; unsigned rtp_ts_len; @@ -1101,7 +439,7 @@ static pj_status_t put_frame(pjmedia_port *port, /* If the interval since last sending packet is greater than * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet. */ - if (stream->use_ka) + if (c_strm->use_ka) { pj_uint32_t dtx_duration, ka_interval; pj_time_val tm_now, tmp; @@ -1109,20 +447,20 @@ static pj_status_t put_frame(pjmedia_port *port, pj_gettimeofday(&tm_now); tmp = tm_now; - PJ_TIME_VAL_SUB(tmp, stream->last_frm_ts_sent); + PJ_TIME_VAL_SUB(tmp, c_strm->last_frm_ts_sent); dtx_duration = PJ_TIME_VAL_MSEC(tmp); - if (stream->start_ka_count) { - ka_interval = stream->start_ka_interval; + if (c_strm->start_ka_count) { + ka_interval = c_strm->start_ka_interval; } else { - ka_interval = stream->ka_interval * 1000; + ka_interval = c_strm->ka_interval * 1000; } if (dtx_duration > ka_interval) { - send_keep_alive_packet(stream); - stream->last_frm_ts_sent = tm_now; + send_keep_alive_packet(c_strm); + c_strm->last_frm_ts_sent = tm_now; - if (stream->start_ka_count) - stream->start_ka_count--; + if (c_strm->start_ka_count) + c_strm->start_ka_count--; } } #endif @@ -1136,7 +474,7 @@ static pj_status_t put_frame(pjmedia_port *port, NULL, NULL); /* Update RTCP stats with last RTP timestamp. */ - stream->rtcp.stat.rtp_tx_last_ts = + c_strm->rtcp.stat.rtp_tx_last_ts = pj_ntohl(channel->rtp.out_hdr.ts); return PJ_SUCCESS; } @@ -1225,25 +563,25 @@ static pj_status_t put_frame(pjmedia_port *port, /* When the payload length is zero, we should not send anything, * but proceed the rest normally. */ - if (frame_out.size != 0 && stream->transport) { + if (frame_out.size != 0 && c_strm->transport) { /* Copy RTP header to the beginning of packet */ pj_memcpy(channel->buf, rtphdr, sizeof(pjmedia_rtp_hdr)); /* Send the RTP packet to the transport. */ - status = pjmedia_transport_send_rtp(stream->transport, + status = pjmedia_transport_send_rtp(c_strm->transport, (char*)channel->buf, frame_out.size + sizeof(pjmedia_rtp_hdr)); if (status != PJ_SUCCESS) { - if (stream->rtp_tx_err_cnt++ == 0) { + if (c_strm->rtp_tx_err_cnt++ == 0) { LOGERR_((channel->port.info.name.ptr, status, "Error sending RTP")); } - if (stream->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { - stream->rtp_tx_err_cnt = 0; + if (c_strm->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) { + c_strm->rtp_tx_err_cnt = 0; } } - pjmedia_rtcp_tx_rtp(&stream->rtcp, (unsigned)frame_out.size); + pjmedia_rtcp_tx_rtp(&c_strm->rtcp, (unsigned)frame_out.size); total_sent += frame_out.size; pkt_cnt++; } @@ -1300,7 +638,7 @@ static pj_status_t put_frame(pjmedia_port *port, pj_get_timestamp(&end_time); total_sleep = pj_elapsed_msec(&initial_time, &end_time); - PJ_LOG(5, (stream->name.ptr, "total pkt=%d size=%d sleep=%d", + PJ_LOG(5, (c_strm->name.ptr, "total pkt=%d size=%d sleep=%d", pkt_cnt, total_sent, total_sleep)); if (stream->tx_start.u64 == 0) @@ -1316,7 +654,7 @@ static pj_status_t put_frame(pjmedia_port *port, * 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 && stream->transport) { + if (c_strm->dir != PJMEDIA_DIR_DECODING && c_strm->transport) { check_tx_rtcp(stream); } @@ -1327,15 +665,15 @@ static pj_status_t put_frame(pjmedia_port *port, /* Update stat */ if (pkt_cnt) { - stream->rtcp.stat.rtp_tx_last_ts = - pj_ntohl(stream->enc->rtp.out_hdr.ts); - stream->rtcp.stat.rtp_tx_last_seq = - pj_ntohs(stream->enc->rtp.out_hdr.seq); + c_strm->rtcp.stat.rtp_tx_last_ts = + pj_ntohl(c_strm->enc->rtp.out_hdr.ts); + c_strm->rtcp.stat.rtp_tx_last_seq = + pj_ntohs(c_strm->enc->rtp.out_hdr.seq); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Update time of last sending packet. */ - pj_gettimeofday(&stream->last_frm_ts_sent); + pj_gettimeofday(&c_strm->last_frm_ts_sent); #endif return PJ_SUCCESS; @@ -1345,7 +683,8 @@ static pj_status_t put_frame(pjmedia_port *port, static pj_status_t decode_frame(pjmedia_vid_stream *stream, pjmedia_frame *frame) { - pjmedia_vid_channel *channel = stream->dec; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_vid_channel *channel = c_strm->dec; pj_uint32_t last_ts = 0, frm_ts = 0; pj_bool_t last_ts_inited = PJ_FALSE; int frm_first_seq = 0, frm_last_seq = 0; @@ -1364,12 +703,12 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, int seq; /* Peek frame from jitter buffer. */ - pjmedia_jbuf_peek_frame(stream->jb, cnt, NULL, NULL, + pjmedia_jbuf_peek_frame(c_strm->jb, cnt, NULL, NULL, &ptype, NULL, &ts, &seq); if (ptype == PJMEDIA_JB_NORMAL_FRAME) { if (stream->last_dec_ts == ts) { /* Remove any late packet (the frame has been decoded) */ - pjmedia_jbuf_remove_frame(stream->jb, 1); + pjmedia_jbuf_remove_frame(c_strm->jb, 1); continue; } @@ -1408,7 +747,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, */ for (; frm_pkt_cnt > 1; --frm_pkt_cnt) { char ptype; - pjmedia_jbuf_peek_frame(stream->jb, frm_pkt_cnt, NULL, NULL, &ptype, + pjmedia_jbuf_peek_frame(c_strm->jb, frm_pkt_cnt, NULL, NULL, &ptype, NULL, NULL, NULL); if (ptype == PJMEDIA_JB_NORMAL_FRAME) break; @@ -1419,7 +758,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, PJ_LOG(1,(channel->port.info.name.ptr, "Discarding %u frames because array is full!", frm_pkt_cnt - stream->rx_frame_cnt)); - pjmedia_jbuf_remove_frame(stream->jb, + pjmedia_jbuf_remove_frame(c_strm->jb, frm_pkt_cnt - stream->rx_frame_cnt); frm_pkt_cnt = stream->rx_frame_cnt; } @@ -1435,7 +774,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, /* We use jbuf_peek_frame() as it will returns the pointer of * the payload (no buffer and memcpy needed), just as we need. */ - pjmedia_jbuf_peek_frame(stream->jb, i, + pjmedia_jbuf_peek_frame(c_strm->jb, i, (const void**)&stream->rx_frames[i].buf, &stream->rx_frames[i].size, &ptype, NULL, NULL, &frm_last_seq); @@ -1460,7 +799,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, frame->size = 0; } - pjmedia_jbuf_remove_frame(stream->jb, frm_pkt_cnt); + pjmedia_jbuf_remove_frame(c_strm->jb, frm_pkt_cnt); } /* Learn remote frame rate after successful decoding */ @@ -1503,7 +842,7 @@ static pj_status_t decode_frame(pjmedia_vid_stream *stream, /* Update the decoding delay */ { pjmedia_jb_state jb_state; - pjmedia_jbuf_get_state(stream->jb, &jb_state); + pjmedia_jbuf_get_state(c_strm->jb, &jb_state); stream->dec_delay_cnt = ((PJMEDIA_VID_STREAM_DECODE_MIN_DELAY_MSEC * @@ -1554,7 +893,8 @@ static pj_status_t get_frame(pjmedia_port *port, pjmedia_frame *frame) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata; - pjmedia_vid_channel *channel = stream->dec; + pjmedia_stream_common *c_strm = &stream->base; + pjmedia_vid_channel *channel = c_strm->dec; /* Return no frame is channel is paused */ if (channel->paused) { @@ -1577,7 +917,7 @@ static pj_status_t get_frame(pjmedia_port *port, if (fmt_chg_data->dir == PJMEDIA_DIR_DECODING) { pjmedia_format_copy(&stream->info.codec_param->dec_fmt, &fmt_chg_data->new_fmt); - pjmedia_format_copy(&stream->dec->port.info.fmt, + pjmedia_format_copy(&c_strm->dec->port.info.fmt, &fmt_chg_data->new_fmt); /* Override the framerate to be 1.5x higher in the event @@ -1588,12 +928,12 @@ static pj_status_t get_frame(pjmedia_port *port, } else { pjmedia_format_copy(&stream->info.codec_param->enc_fmt, &fmt_chg_data->new_fmt); - pjmedia_format_copy(&stream->enc->port.info.fmt, + pjmedia_format_copy(&c_strm->enc->port.info.fmt, &fmt_chg_data->new_fmt); } dump_port_info(fmt_chg_data->dir==PJMEDIA_DIR_DECODING ? - stream->dec : stream->enc, + c_strm->dec : c_strm->enc, "changed"); pjmedia_event_publish(NULL, port, &stream->fmt_event, @@ -1608,7 +948,7 @@ static pj_status_t get_frame(pjmedia_port *port, stream->miss_keyframe_event.type = PJMEDIA_EVENT_NONE; } - pj_grp_lock_acquire( stream->grp_lock ); + pj_grp_lock_acquire( c_strm->grp_lock ); if (stream->dec_frame.size == 0) { /* Don't have frame in buffer, try to decode one */ @@ -1618,7 +958,7 @@ static pj_status_t get_frame(pjmedia_port *port, } } else { if (frame->size < stream->dec_frame.size) { - PJ_LOG(4,(stream->dec->port.info.name.ptr, + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Error: not enough buffer for decoded frame " "(supplied=%d, required=%d)", (int)frame->size, (int)stream->dec_frame.size)); @@ -1634,7 +974,7 @@ static pj_status_t get_frame(pjmedia_port *port, stream->dec_frame.size = 0; } - pj_grp_lock_release( stream->grp_lock ); + pj_grp_lock_release( c_strm->grp_lock ); return PJ_SUCCESS; } @@ -1650,6 +990,7 @@ static pj_status_t create_channel( pj_pool_t *pool, pjmedia_vid_channel **p_channel) { enum { M = 32 }; + pjmedia_stream_common *c_strm = &stream->base; pjmedia_vid_channel *channel; pj_status_t status; unsigned min_out_pkt_size; @@ -1679,19 +1020,19 @@ static pj_status_t create_channel( pj_pool_t *pool, pi = &channel->port.info; /* Init channel info. */ - channel->stream = stream; + channel->stream = c_strm; channel->dir = dir; channel->paused = 1; channel->pt = pt; /* Allocate buffer for outgoing packet. */ if (dir == PJMEDIA_DIR_ENCODING) { - channel->buf_size = sizeof(pjmedia_rtp_hdr) + stream->frame_size; + channel->buf_size = sizeof(pjmedia_rtp_hdr) + c_strm->frame_size; /* It should big enough to hold (minimally) RTCP SR with an SDES. */ min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + - (4 + (unsigned)stream->cname.slen) + + (4 + (unsigned)c_strm->cname.slen) + 32; if (channel->buf_size < min_out_pkt_size) @@ -1728,7 +1069,7 @@ static pj_status_t create_channel( pj_pool_t *pool, channel->port.port_data.pdata = stream; /* Use stream group lock */ - channel->port.grp_lock = stream->grp_lock; + channel->port.grp_lock = c_strm->grp_lock; PJ_LOG(5, (name.ptr, "%s channel created %dx%d %s%s%.*s %d/%d(~%d)fps", @@ -1761,6 +1102,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( enum { M = 32 }; pj_pool_t *own_pool = NULL; pjmedia_vid_stream *stream; + pjmedia_stream_common *c_strm; + pj_str_t name; unsigned jb_init, jb_max, jb_min_pre, jb_max_pre; int frm_ptime, chunks_per_frm; pjmedia_video_format_detail *vfd_enc, *vfd_dec; @@ -1779,15 +1122,21 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Allocate stream */ stream = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_stream); PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); - stream->own_pool = own_pool; + c_strm = &stream->base; + c_strm->own_pool = own_pool; + + /* Init stream/port name */ + name.ptr = (char*) pj_pool_alloc(pool, M); + name.slen = pj_ansi_snprintf(name.ptr, M, "vstrm%p", stream); + c_strm->port.info.name = name; /* Get codec manager */ stream->codec_mgr = pjmedia_vid_codec_mgr_instance(); PJ_ASSERT_RETURN(stream->codec_mgr, PJMEDIA_CODEC_EFAILED); /* Init stream/port name */ - stream->name.ptr = (char*) pj_pool_alloc(pool, M); - stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M, + c_strm->name.ptr = (char*) pj_pool_alloc(pool, M); + c_strm->name.slen = pj_ansi_snprintf(c_strm->name.ptr, M, "vstrm%p", stream); /* Create and initialize codec: */ @@ -1825,44 +1174,44 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( &info->codec_param->dec_fmt, PJ_TRUE); /* Init stream: */ - stream->endpt = endpt; - stream->dir = info->dir; - stream->user_data = user_data; - stream->rtcp_interval = PJMEDIA_RTCP_INTERVAL + pj_rand()%1000 - 500; - stream->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; + c_strm->endpt = endpt; + c_strm->dir = info->dir; + c_strm->user_data = user_data; + c_strm->rtcp_interval = PJMEDIA_RTCP_INTERVAL + pj_rand()%1000 - 500; + c_strm->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled; - stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; + c_strm->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 - stream->use_ka = info->use_ka; - stream->ka_interval = info->ka_cfg.ka_interval; - stream->start_ka_count = info->ka_cfg.start_count; - stream->start_ka_interval = info->ka_cfg.start_interval; + c_strm->use_ka = info->use_ka; + c_strm->ka_interval = info->ka_cfg.ka_interval; + c_strm->start_ka_count = info->ka_cfg.start_count; + c_strm->start_ka_interval = info->ka_cfg.start_interval; #endif stream->num_keyframe = info->sk_cfg.count; - stream->cname = info->cname; - if (stream->cname.slen == 0) { + c_strm->cname = info->cname; + if (c_strm->cname.slen == 0) { /* Build random RTCP CNAME. CNAME has user@host format */ - stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); + c_strm->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); pj_create_random_string(p, 5); p += 5; *p++ = '@'; *p++ = 'p'; *p++ = 'j'; pj_create_random_string(p, 6); p += 6; *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g'; - stream->cname.slen = p - stream->cname.ptr; + c_strm->cname.slen = p - c_strm->cname.ptr; } /* Create group lock */ status = pj_grp_lock_create_w_handler(pool, NULL, stream, &on_destroy, - &stream->grp_lock); + &c_strm->grp_lock); if (status != PJ_SUCCESS) goto err_cleanup; /* Add ref count of group lock */ - status = pj_grp_lock_add_ref(stream->grp_lock); + status = pj_grp_lock_add_ref(c_strm->grp_lock); if (status != PJ_SUCCESS) goto err_cleanup; @@ -1879,24 +1228,24 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( stream->codec); /* Estimate the maximum frame size */ - stream->frame_size = vfd_enc->size.w * vfd_enc->size.h * 4; + c_strm->frame_size = vfd_enc->size.w * vfd_enc->size.h * 4; #if 0 - stream->frame_size = vfd_enc->max_bps/8 * vfd_enc->fps.denum / + c_strm->frame_size = vfd_enc->max_bps/8 * vfd_enc->fps.denum / vfd_enc->fps.num; /* As the maximum frame_size is not represented directly by maximum bps * (which includes intra and predicted frames), let's increase the * frame size value for safety. */ - stream->frame_size <<= 4; + c_strm->frame_size <<= 4; #endif /* Validate the frame size */ - if (stream->frame_size == 0 || - stream->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE) + if (c_strm->frame_size == 0 || + c_strm->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE) { - stream->frame_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE; + c_strm->frame_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE; } /* Get frame length in timestamp unit */ @@ -1929,13 +1278,13 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Create decoder channel */ status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, - info->rx_pt, info, &stream->dec); + info->rx_pt, info, &c_strm->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); + info->tx_pt, info, &c_strm->enc); if (status != PJ_SUCCESS) goto err_cleanup; @@ -1945,7 +1294,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Init jitter buffer parameters: */ frm_ptime = 1000 * vfd_dec->fps.denum / vfd_dec->fps.num; - chunks_per_frm = stream->frame_size / PJMEDIA_MAX_MRU; + chunks_per_frm = c_strm->frame_size / PJMEDIA_MAX_MRU; if (chunks_per_frm < MIN_CHUNKS_PER_FRM) chunks_per_frm = MIN_CHUNKS_PER_FRM; @@ -1995,58 +1344,58 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( sizeof(stream->rx_frames[0])); /* Create jitter buffer */ - status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name, + status = pjmedia_jbuf_create(pool, &c_strm->dec->port.info.name, PJMEDIA_MAX_MRU, 1000 * vfd_enc->fps.denum / vfd_enc->fps.num, - jb_max, &stream->jb); + jb_max, &c_strm->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); - pjmedia_jbuf_set_discard(stream->jb, PJMEDIA_JB_DISCARD_NONE); + pjmedia_jbuf_set_adaptive(c_strm->jb, jb_init, jb_min_pre, jb_max_pre); + pjmedia_jbuf_set_discard(c_strm->jb, PJMEDIA_JB_DISCARD_NONE); /* Init RTCP session: */ { pjmedia_rtcp_session_setting rtcp_setting; pjmedia_rtcp_session_setting_default(&rtcp_setting); - rtcp_setting.name = stream->name.ptr; + rtcp_setting.name = c_strm->name.ptr; rtcp_setting.ssrc = info->ssrc; - rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts); + rtcp_setting.rtp_ts_base = pj_ntohl(c_strm->enc->rtp.out_hdr.ts); rtcp_setting.clock_rate = info->codec_info.clock_rate; rtcp_setting.samples_per_frame = 1; - pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting); + pjmedia_rtcp_init2(&c_strm->rtcp, &rtcp_setting); if (info->rtp_seq_ts_set) { - stream->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; - stream->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; + c_strm->rtcp.stat.rtp_tx_last_seq = info->rtp_seq; + c_strm->rtcp.stat.rtp_tx_last_ts = info->rtp_ts; } /* Subscribe to RTCP events */ pjmedia_event_subscribe(NULL, &stream_event_cb, stream, - &stream->rtcp); + &c_strm->rtcp); } /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES, * BYE, Feedback, and XR. */ - stream->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + + c_strm->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) + sizeof(pjmedia_rtcp_common) + - (4 + (unsigned)stream->cname.slen) + + (4 + (unsigned)c_strm->cname.slen) + 32 + 32; - if (stream->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) - stream->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; + if (c_strm->out_rtcp_pkt_size > PJMEDIA_MAX_MTU) + c_strm->out_rtcp_pkt_size = PJMEDIA_MAX_MTU; - stream->out_rtcp_pkt = pj_pool_alloc(pool, stream->out_rtcp_pkt_size); + c_strm->out_rtcp_pkt = pj_pool_alloc(pool, c_strm->out_rtcp_pkt_size); pj_bzero(&att_param, sizeof(att_param)); att_param.stream = stream; att_param.media_type = PJMEDIA_TYPE_VIDEO; att_param.user_data = stream; pj_sockaddr_cp(&att_param.rem_addr, &info->rem_addr); - pj_sockaddr_cp(&stream->rem_rtp_addr, &info->rem_addr); + pj_sockaddr_cp(&c_strm->rem_rtp_addr, &info->rem_addr); if (info->rtcp_mux) { pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr); } else if (pj_sockaddr_has_addr(&info->rem_rtcp)) { @@ -2061,21 +1410,21 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( if (status != PJ_SUCCESS) goto err_cleanup; - stream->transport = tp; + c_strm->transport = tp; /* Also add ref the transport group lock */ - if (stream->transport->grp_lock) - pj_grp_lock_add_ref(stream->transport->grp_lock); + if (c_strm->transport->grp_lock) + pj_grp_lock_add_ref(c_strm->transport->grp_lock); /* Send RTCP SDES */ - if (!stream->rtcp_sdes_bye_disabled) { + if (!c_strm->rtcp_sdes_bye_disabled) { pjmedia_vid_stream_send_rtcp_sdes(stream); } #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* NAT hole punching by sending KA packet via RTP transport. */ - if (stream->use_ka) - send_keep_alive_packet(stream); + if (c_strm->use_ka) + send_keep_alive_packet(c_strm); #endif #if TRACE_JB @@ -2085,32 +1434,35 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( pj_ansi_snprintf(trace_name, sizeof(trace_name), TRACE_JB_PATH_PREFIX "%s.csv", - channel->port.info.name.ptr); + c_strm->port.info.name.ptr); status = pj_file_open(pool, trace_name, PJ_O_RDWR, - &stream->trace_jb_fd); + &c_strm->trace_jb_fd); if (status != PJ_SUCCESS) { - stream->trace_jb_fd = TRACE_JB_INVALID_FD; + c_strm->trace_jb_fd = TRACE_JB_INVALID_FD; PJ_PERROR(3,(THIS_FILE, status, "Failed creating RTP trace file '%s'", trace_name)); } else { - stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); + c_strm->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE); /* Print column header */ - len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE, + len = pj_ansi_snprintf(c_strm->trace_jb_buf, PJ_LOG_MAX_SIZE, "Time, Operation, Size, Frame Count, " "Frame type, RTP Seq, RTP TS, RTP M, " "JB size, JB burst level, JB prefetch\n"); if (len < 1 || len >= PJ_LOG_MAX_SIZE) len = PJ_LOG_MAX_SIZE - 1; - pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len); - pj_file_flush(stream->trace_jb_fd); + pj_file_write(c_strm->trace_jb_fd, c_strm->trace_jb_buf, &len); + pj_file_flush(c_strm->trace_jb_fd); } + + PJ_UNUSED_ARG(trace_jb_get); } #endif /* Save the stream info */ pj_memcpy(&stream->info, info, sizeof(*info)); + c_strm->si = (pjmedia_stream_info_common *)&stream->info; stream->info.codec_param = pjmedia_vid_codec_param_clone( pool, info->codec_param); pjmedia_rtcp_fb_info_dup(pool, &stream->info.loc_rtcp_fb, @@ -2126,18 +1478,18 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( for (i = 0; i < rfi->cap_count; ++i) { if (rfi->caps[i].type == PJMEDIA_RTCP_FB_NACK) { if (rfi->caps[i].param.slen == 0) { - stream->send_rtcp_fb_nack = PJ_TRUE; - PJ_LOG(5,(stream->name.ptr, "Send RTCP-FB generic NACK")); + c_strm->send_rtcp_fb_nack = PJ_TRUE; + PJ_LOG(5,(c_strm->name.ptr, "Send RTCP-FB generic NACK")); } else if (pj_stricmp2(&rfi->caps[i].param, "pli")==0) { stream->send_rtcp_fb_pli = PJ_TRUE; - PJ_LOG(5,(stream->name.ptr, "Send RTCP-FB PLI")); + PJ_LOG(5,(c_strm->name.ptr, "Send RTCP-FB PLI")); } } } } /* Check if we should process incoming RTCP-FB */ - stream->rtcp_fb_nack_cap_idx = -1; + c_strm->rtcp_fb_nack_cap_idx = -1; stream->rtcp_fb_pli_cap_idx = -1; if (stream->info.loc_rtcp_fb.cap_count) { pjmedia_rtcp_fb_info *lfi = &stream->info.loc_rtcp_fb; @@ -2146,12 +1498,12 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( for (i = 0; i < lfi->cap_count; ++i) { if (lfi->caps[i].type == PJMEDIA_RTCP_FB_NACK) { if (lfi->caps[i].param.slen == 0) { - stream->rtcp_fb_nack_cap_idx = i; - PJ_LOG(5,(stream->name.ptr, + c_strm->rtcp_fb_nack_cap_idx = i; + PJ_LOG(5,(c_strm->name.ptr, "Receive RTCP-FB generic NACK")); } else if (pj_stricmp2(&lfi->caps[i].param, "pli")==0) { stream->rtcp_fb_pli_cap_idx = i; - PJ_LOG(5,(stream->name.ptr, "Receive RTCP-FB PLI")); + PJ_LOG(5,(c_strm->name.ptr, "Receive RTCP-FB PLI")); } } } @@ -2160,7 +1512,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( /* Success! */ *p_stream = stream; - PJ_LOG(5,(THIS_FILE, "Video stream %s created", stream->name.ptr)); + PJ_LOG(5,(THIS_FILE, "Video stream %s created", c_strm->name.ptr)); return PJ_SUCCESS; @@ -2175,22 +1527,24 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( */ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); - PJ_LOG(4,(THIS_FILE, "Destroy request on %s..", stream->name.ptr)); + PJ_LOG(4,(THIS_FILE, "Destroy request on %s..", c_strm->name.ptr)); /* Stop the streaming */ - if (stream->enc) - stream->enc->port.put_frame = NULL; - if (stream->dec) - stream->dec->port.get_frame = NULL; + if (c_strm->enc) + c_strm->enc->port.put_frame = NULL; + if (c_strm->dec) + c_strm->dec->port.get_frame = NULL; #if TRACE_RC { unsigned total_time; total_time = pj_elapsed_msec(&stream->tx_start, &stream->tx_end); - PJ_LOG(5, (stream->name.ptr, + PJ_LOG(5, (c_strm->name.ptr, "RC stat: pkt_cnt=%.2f/image, sleep=%.2fms/s, fps=%.2f", stream->rc_total_pkt*1.0/stream->rc_total_img, stream->rc_total_sleep*1000.0/total_time, @@ -2203,27 +1557,28 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, stream->codec); } - pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, &stream->rtcp); + pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, &c_strm->rtcp); /* Send RTCP BYE (also SDES) */ - if (stream->transport && !stream->rtcp_sdes_bye_disabled) { - send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE); + if (c_strm->transport && !c_strm->rtcp_sdes_bye_disabled) { + send_rtcp(c_strm, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE, + PJ_FALSE); } /* Detach from transport * MUST NOT hold stream mutex while detaching from transport, as * it may cause deadlock. See ticket #460 for the details. */ - if (stream->transport) { - pjmedia_transport_detach(stream->transport, stream); - //stream->transport = NULL; + if (c_strm->transport) { + pjmedia_transport_detach(c_strm->transport, stream); + //c_strm->transport = NULL; } /* This function may be called when stream is partly initialized, * i.e: group lock may not be created yet. */ - if (stream->grp_lock) { - return pj_grp_lock_dec_ref(stream->grp_lock); + if (c_strm->grp_lock) { + return pj_grp_lock_dec_ref(c_strm->grp_lock); } else { on_destroy(stream); } @@ -2235,16 +1590,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream ) /* * Destroy stream. */ -static void on_destroy( void *arg ) +static void on_stream_destroy( void *arg ) { pjmedia_vid_stream *stream = (pjmedia_vid_stream*)arg; - pj_assert(stream); - - PJ_LOG(4,(THIS_FILE, "Destroying %s..", stream->name.ptr)); - - /* Release ref to transport */ - if (stream->transport && stream->transport->grp_lock) - pj_grp_lock_dec_ref(stream->transport->grp_lock); /* Free codec. */ if (stream->codec) { @@ -2252,24 +1600,6 @@ static void on_destroy( void *arg ) pjmedia_vid_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); stream->codec = NULL; } - - /* Free mutex */ - stream->grp_lock = NULL; - - /* Destroy jitter buffer */ - if (stream->jb) { - pjmedia_jbuf_destroy(stream->jb); - stream->jb = NULL; - } - -#if TRACE_JB - if (TRACE_JB_OPENED(stream)) { - pj_file_close(stream->trace_jb_fd); - stream->trace_jb_fd = TRACE_JB_INVALID_FD; - } -#endif - - pj_pool_safe_release(&stream->own_pool); } @@ -2280,13 +1610,15 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream, pjmedia_dir dir, pjmedia_port **p_port ) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING, PJ_EINVAL); if (dir == PJMEDIA_DIR_ENCODING) - *p_port = &stream->enc->port; + *p_port = &c_strm->enc->port; else - *p_port = &stream->dec->port; + *p_port = &c_strm->dec->port; return PJ_SUCCESS; } @@ -2298,7 +1630,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream, PJ_DEF(pjmedia_transport*) pjmedia_vid_stream_get_transport( pjmedia_vid_stream *st) { - return st->transport; + return st->base.transport; } @@ -2311,7 +1643,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat( { PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); - pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat)); + pj_memcpy(stat, &stream->base.rtcp.stat, sizeof(pjmedia_rtcp_stat)); return PJ_SUCCESS; } @@ -2323,7 +1655,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream) { PJ_ASSERT_RETURN(stream, PJ_EINVAL); - pjmedia_rtcp_init_stat(&stream->rtcp.stat); + pjmedia_rtcp_init_stat(&stream->base.rtcp.stat); return PJ_SUCCESS; } @@ -2337,7 +1669,7 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf( pjmedia_jb_state *state) { PJ_ASSERT_RETURN(stream && state, PJ_EINVAL); - return pjmedia_jbuf_get_state(stream->jb, state); + return pjmedia_jbuf_get_state(stream->base.jb, state); } @@ -2359,23 +1691,24 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_info( */ PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); - PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP); + PJ_ASSERT_RETURN(stream && c_strm->enc && c_strm->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->enc->port.info.name.ptr, "Encoder stream started")); + if (c_strm->enc && (c_strm->dir & PJMEDIA_DIR_ENCODING)) { + c_strm->enc->paused = 0; + //pjmedia_snd_stream_start(c_strm->enc->snd_stream); + PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream started")); } else { - PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused")); + PJ_LOG(4,(c_strm->enc->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->dec->port.info.name.ptr, "Decoder stream started")); + if (c_strm->dec && (c_strm->dir & PJMEDIA_DIR_DECODING)) { + c_strm->dec->paused = 0; + //pjmedia_snd_stream_start(c_strm->dec->snd_stream); + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream started")); } else { - PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused")); + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; @@ -2401,16 +1734,17 @@ pjmedia_vid_stream_modify_codec_param(pjmedia_vid_stream *stream, PJ_DEF(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); pj_bool_t is_running = PJ_TRUE; PJ_ASSERT_RETURN(stream, PJ_FALSE); if (dir & PJMEDIA_DIR_ENCODING) { - is_running &= (stream->enc && !stream->enc->paused); + is_running &= (c_strm->enc && !c_strm->enc->paused); } if (dir & PJMEDIA_DIR_DECODING) { - is_running &= (stream->dec && !stream->dec->paused); + is_running &= (c_strm->dec && !c_strm->dec->paused); } return is_running; @@ -2422,22 +1756,24 @@ PJ_DEF(pj_bool_t) pjmedia_vid_stream_is_running(pjmedia_vid_stream *stream, PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { - stream->enc->paused = 1; - PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused")); + if ((dir & PJMEDIA_DIR_ENCODING) && c_strm->enc) { + c_strm->enc->paused = 1; + PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream paused")); } - if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { - stream->dec->paused = 1; + if ((dir & PJMEDIA_DIR_DECODING) && c_strm->dec) { + c_strm->dec->paused = 1; /* Also reset jitter buffer */ - pj_grp_lock_acquire( stream->grp_lock ); - pjmedia_jbuf_reset(stream->jb); - pj_grp_lock_release( stream->grp_lock ); + pj_grp_lock_acquire( c_strm->grp_lock ); + pjmedia_jbuf_reset(c_strm->jb); + pj_grp_lock_release( c_strm->grp_lock ); - PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused")); + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream paused")); } return PJ_SUCCESS; @@ -2450,18 +1786,20 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream, PJ_DEF(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream, pjmedia_dir dir) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) { - stream->enc->paused = 0; + if ((dir & PJMEDIA_DIR_ENCODING) && c_strm->enc) { + c_strm->enc->paused = 0; stream->force_keyframe = PJ_TRUE; - PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream resumed")); + PJ_LOG(4,(c_strm->enc->port.info.name.ptr, "Encoder stream resumed")); } - if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) { - stream->dec->paused = 0; + if ((dir & PJMEDIA_DIR_DECODING) && c_strm->dec) { + c_strm->dec->paused = 0; stream->last_dec_seq = 0; - PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream resumed")); + PJ_LOG(4,(c_strm->dec->port.info.name.ptr, "Decoder stream resumed")); } return PJ_SUCCESS; @@ -2500,9 +1838,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_keyframe( PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_sdes( pjmedia_vid_stream *stream) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - return send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE); + return pjmedia_stream_common_send_rtcp_sdes( + (pjmedia_stream_common *) stream); } @@ -2512,13 +1849,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_sdes( PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_bye( pjmedia_vid_stream *stream) { - PJ_ASSERT_RETURN(stream, PJ_EINVAL); - - if (stream->enc && stream->transport) { - return send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE); - } - - return PJ_SUCCESS; + return pjmedia_stream_common_send_rtcp_bye( + (pjmedia_stream_common *) stream); } @@ -2528,10 +1860,13 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_bye( PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_pli( pjmedia_vid_stream *stream) { + pjmedia_stream_common *c_strm = (stream? &stream->base: NULL); + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - if (stream->transport) { - return send_rtcp(stream, PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_TRUE); + if (c_strm->transport) { + return send_rtcp(c_strm, PJ_FALSE, PJ_FALSE, PJ_FALSE, PJ_FALSE, + PJ_FALSE, PJ_TRUE); } return PJ_SUCCESS; @@ -2568,10 +1903,8 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_get_rtp_session_info(pjmedia_vid_stream *stream, pjmedia_stream_rtp_sess_info *session_info) { - session_info->rx_rtp = &stream->dec->rtp; - session_info->tx_rtp = &stream->enc->rtp; - session_info->rtcp = &stream->rtcp; - return PJ_SUCCESS; + return pjmedia_stream_common_get_rtp_session_info( + (pjmedia_stream_common *)stream, session_info); } diff --git a/pjmedia/src/pjmedia/vid_stream_info.c b/pjmedia/src/pjmedia/vid_stream_info.c index 3e9e60dab4..12bad8206c 100644 --- a/pjmedia/src/pjmedia/vid_stream_info.c +++ b/pjmedia/src/pjmedia/vid_stream_info.c @@ -23,10 +23,6 @@ #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) -static const pj_str_t ID_IN = { "IN", 2 }; -static const pj_str_t ID_IP4 = { "IP4", 3}; -static const pj_str_t ID_IP6 = { "IP6", 3}; -//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; /* @@ -180,237 +176,27 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp( const pjmedia_sdp_session *remote, unsigned stream_idx) { - const pj_str_t STR_INACTIVE = { "inactive", 8 }; - const pj_str_t STR_SENDONLY = { "sendonly", 8 }; - const pj_str_t STR_RECVONLY = { "recvonly", 8 }; - - const pjmedia_sdp_attr *attr; + pjmedia_stream_info_common *csi = (pjmedia_stream_info_common *)si; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; - const pjmedia_sdp_conn *local_conn; - const pjmedia_sdp_conn *rem_conn; - int rem_af, local_af; - unsigned i; pj_status_t status; - PJ_UNUSED_ARG(endpt); - - /* Validate arguments: */ - PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); - PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); - PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); + status = pjmedia_stream_info_common_from_sdp(csi, pool, endpt, local, + remote, stream_idx); + if (status != PJ_SUCCESS) + return status; /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; - local_conn = local_m->conn ? local_m->conn : local->conn; - if (local_conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; - - rem_conn = rem_m->conn ? rem_m->conn : remote->conn; - if (rem_conn == NULL) - return PJMEDIA_SDP_EMISSINGCONN; - - /* Media type must be video */ + /* Media type must be audio */ if (pjmedia_get_type(&local_m->desc.media) != PJMEDIA_TYPE_VIDEO) return PJMEDIA_EINVALIMEDIATYPE; - - /* Reset: */ - - pj_bzero(si, sizeof(*si)); - - /* Media type: */ - si->type = PJMEDIA_TYPE_VIDEO; - - /* Transport protocol */ - - /* At this point, transport type must be compatible, - * the transport instance will do more validation later. - */ - status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, - &local_m->desc.transport); - if (status != PJ_SUCCESS) - return PJMEDIA_SDPNEG_EINVANSTP; - - /* Get the transport protocol */ - si->proto = pjmedia_sdp_transport_get_proto(&local_m->desc.transport); - - /* Return success if transport protocol is not RTP/AVP compatible */ - if (!PJMEDIA_TP_PROTO_HAS_FLAG(si->proto, PJMEDIA_TP_PROTO_RTP_AVP)) - return PJ_SUCCESS; - - - /* Check address family in remote SDP */ - rem_af = pj_AF_UNSPEC(); - if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { - if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { - rem_af = pj_AF_INET(); - } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { - rem_af = pj_AF_INET6(); - } - } - - if (rem_af==pj_AF_UNSPEC()) { - /* Unsupported address family */ - return PJ_EAFNOTSUP; - } - - /* Set remote address: */ - status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, - rem_m->desc.port); - if (status != PJ_SUCCESS) { - /* Invalid IP address. */ - return PJMEDIA_EINVALIDIP; - } - - /* Check address family of local info */ - local_af = pj_AF_UNSPEC(); - if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { - if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { - local_af = pj_AF_INET(); - } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { - local_af = pj_AF_INET6(); - } - } - - if (local_af==pj_AF_UNSPEC()) { - /* Unsupported address family */ - return PJ_SUCCESS; - } - - /* Set remote address: */ - status = pj_sockaddr_init(local_af, &si->local_addr, &local_conn->addr, - local_m->desc.port); - if (status != PJ_SUCCESS) { - /* Invalid IP address. */ - return PJMEDIA_EINVALIDIP; - } - - /* Local and remote address family must match, except when ICE is used - * by both sides (see also ticket #1952). - */ - if (local_af != rem_af) { - const pj_str_t STR_ICE_CAND = { "candidate", 9 }; - if (pjmedia_sdp_media_find_attr(rem_m, &STR_ICE_CAND, NULL)==NULL || - pjmedia_sdp_media_find_attr(local_m, &STR_ICE_CAND, NULL)==NULL) - { - return PJ_EAFNOTSUP; - } - } - - /* Media direction: */ - - if (local_m->desc.port == 0 || - pj_sockaddr_has_addr(&si->local_addr)==PJ_FALSE || - pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || - pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) - { - /* Inactive stream. */ - - si->dir = PJMEDIA_DIR_NONE; - - } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { - - /* Send only stream. */ - - si->dir = PJMEDIA_DIR_ENCODING; - - } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { - - /* Recv only stream. */ - - si->dir = PJMEDIA_DIR_DECODING; - - } else { - - /* Send and receive stream. */ - - si->dir = PJMEDIA_DIR_ENCODING_DECODING; - - } - - /* No need to do anything else if stream is rejected */ - if (local_m->desc.port == 0) { - return PJ_SUCCESS; - } - - /* Check if "rtcp-mux" is present in the SDP. */ - attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, - "rtcp-mux", NULL); - if (attr) - si->rtcp_mux = PJ_TRUE; - - /* If "rtcp" attribute is present in the SDP, set the RTCP address - * from that attribute. Otherwise, calculate from RTP address. - */ - attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, - "rtcp", NULL); - if (attr) { - pjmedia_sdp_rtcp_attr rtcp; - status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); - if (status == PJ_SUCCESS) { - if (rtcp.addr.slen) { - pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, - (pj_uint16_t)rtcp.port); - } else { - pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, - (pj_uint16_t)rtcp.port); - pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), - pj_sockaddr_get_addr(&si->rem_addr), - pj_sockaddr_get_addr_len(&si->rem_addr)); - } - } - } - - if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { - int rtcp_port; - - pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); - rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; - pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); - } - - /* Check if "ssrc" attribute is present in the SDP. */ - for (i = 0; i < rem_m->attr_count; i++) { - if (pj_strcmp2(&rem_m->attr[i]->name, "ssrc") == 0) { - pjmedia_sdp_ssrc_attr ssrc; - - status = pjmedia_sdp_attr_get_ssrc( - (const pjmedia_sdp_attr *)rem_m->attr[i], &ssrc); - if (status == PJ_SUCCESS) { - si->has_rem_ssrc = PJ_TRUE; - si->rem_ssrc = ssrc.ssrc; - if (ssrc.cname.slen > 0) { - pj_strdup(pool, &si->rem_cname, &ssrc.cname); - break; - } - } - } - } - /* Get codec info and param */ status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m); - /* Leave SSRC to random. */ - si->ssrc = pj_rand(); - - /* Set default jitter buffer parameter. */ - si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; - - /* Get local RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, stream_idx, - si->rx_pt, &si->loc_rtcp_fb); - if (status != PJ_SUCCESS) - return status; - - /* Get remote RTCP-FB info */ - status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, stream_idx, - si->tx_pt, &si->rem_rtcp_fb); - if (status != PJ_SUCCESS) - return status; - return status; } diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index bfcd5d0b24..7c7cd11d0c 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -3913,6 +3913,333 @@ static pj_bool_t is_media_changed(const pjsua_call *call, #endif /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */ +/* Apply media update. */ +static pj_status_t apply_med_update(pjsua_call_media *call_med, + const pjmedia_sdp_session *local_sdp, + const pjmedia_sdp_session *remote_sdp, + pj_bool_t *need_renego_sdp) +{ + const char *STR_SENDRECV = "sendrecv"; + const char *STR_SENDONLY = "sendonly"; + const char *STR_RECVONLY = "recvonly"; + const char *STR_INACTIVE = "inactive"; + + pjsua_call *call = call_med->call; + pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; + pjsua_call_id call_id = call->index; + unsigned mi = call_med->idx; + pj_bool_t media_changed = PJ_FALSE; + pj_pool_t *tmp_pool = call->inv->pool_prov; + pj_status_t status = PJ_SUCCESS; + + pjmedia_stream_info asi; +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + pjmedia_vid_stream_info vsi; +#endif + pjmedia_stream_info_common *si; + pjsua_stream_info stream_info; + pj_str_t *enc_name; + + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + si = (pjmedia_stream_info_common *)&asi; + status = pjmedia_stream_info_from_sdp( + &asi, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); + stream_info.info.aud = asi; + enc_name = &asi.fmt.encoding_name; + +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + } else if (call_med->type == PJMEDIA_TYPE_VIDEO) { + si = (pjmedia_stream_info_common *)&vsi; + status = pjmedia_vid_stream_info_from_sdp( + &vsi, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); + stream_info.info.vid = vsi; + enc_name = &vsi.codec_info.encoding_name; +#endif + } + + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjmedia_stream_info_from_sdp() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + +#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + /* Enable/disable RTCP XR based on account setting. */ + si->rtcp_xr_enabled = acc->cfg.enable_rtcp_xr; +#endif + + /* Check if remote wants RTP and RTCP multiplexing, + * but we don't enable it. + */ + if (si->rtcp_mux && !call_med->enable_rtcp_mux) { + si->rtcp_mux = PJ_FALSE; + } + + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + /* Codec parameter of stream info (si->param) can be NULL if + * the stream is rejected or disabled. + */ + /* Override ptime, if this option is specified. */ + if (pjsua_var.media_cfg.ptime != 0 && asi.param) { + asi.param->setting.frm_per_pkt = (pj_uint8_t) + (pjsua_var.media_cfg.ptime / asi.param->info.frm_ptime); + if (asi.param->setting.frm_per_pkt == 0) + asi.param->setting.frm_per_pkt = 1; + } + + /* Disable VAD, if this option is specified. */ + if (pjsua_var.media_cfg.no_vad && asi.param) { + asi.param->setting.vad = 0; + } + + if (call->audio_idx==-1 && status==PJ_SUCCESS && + si->dir != PJMEDIA_DIR_NONE) + { + call->audio_idx = mi; + } + } + + if (!pjmedia_sdp_neg_was_answer_remote(call->inv->neg) && + si->dir != PJMEDIA_DIR_NONE) + { + pjmedia_dir dir = si->dir; + + if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) { + call_med->def_dir = call->opt.media_dir[mi]; + PJ_LOG(4,(THIS_FILE, "Call %d: setting audio media " + "direction #%d to %d.", + call_id, mi, call_med->def_dir)); + } + + /* If the default direction specifies we do not wish + * encoding/decoding, clear that direction. + */ + if ((call_med->def_dir & PJMEDIA_DIR_ENCODING) == 0) { + dir &= ~PJMEDIA_DIR_ENCODING; + } + if ((call_med->def_dir & PJMEDIA_DIR_DECODING) == 0) { + dir &= ~PJMEDIA_DIR_DECODING; + } + + if (dir != si->dir) { + const char *str_attr = NULL; + pjmedia_sdp_attr *attr; + pjmedia_sdp_media *m; + + if (!*need_renego_sdp) { + pjmedia_sdp_session *local_sdp_renego; + local_sdp_renego = + pjmedia_sdp_session_clone(tmp_pool, local_sdp); + local_sdp = local_sdp_renego; + *need_renego_sdp = PJ_TRUE; + } + + si->dir = dir; + m = local_sdp->media[mi]; + + /* Remove existing directions attributes */ + pjmedia_sdp_media_remove_all_attr(m, STR_SENDRECV); + pjmedia_sdp_media_remove_all_attr(m, STR_SENDONLY); + pjmedia_sdp_media_remove_all_attr(m, STR_RECVONLY); + + if (si->dir == PJMEDIA_DIR_ENCODING_DECODING) { + str_attr = STR_SENDRECV; + } else if (si->dir == PJMEDIA_DIR_ENCODING) { + str_attr = STR_SENDONLY; + } else if (si->dir == PJMEDIA_DIR_DECODING) { + str_attr = STR_RECVONLY; + } else { + str_attr = STR_INACTIVE; + } + attr = pjmedia_sdp_attr_create(tmp_pool, str_attr, NULL); + pjmedia_sdp_media_add_attr(m, attr); + } + } + + stream_info.type = call_med->type; + +#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* Check if we need to reset or maintain SRTP ROC */ + check_srtp_roc(call, mi, &stream_info, local_sdp, remote_sdp); +#endif +#endif + + /* Check if this media is changed */ + if (pjsua_var.media_cfg.no_smart_media_update || + is_media_changed(call, mi, &stream_info)) + { + media_changed = PJ_TRUE; + /* Stop the media */ + stop_media_stream(call, mi); + } else { + PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (%s) unchanged.", + call_id, mi, pjmedia_type_name(call_med->type))); + } + + /* Check if no media is active */ + if (local_sdp->media[mi]->desc.port == 0) { + + /* Update call media state and direction */ + call_med->state = PJSUA_CALL_MEDIA_NONE; + call_med->dir = PJMEDIA_DIR_NONE; + + } else if (call_med->tp) { + pjmedia_transport_info tp_info; + pjmedia_srtp_info *srtp_info; + + /* Call media direction */ + call_med->dir = si->dir; + + /* Call media state */ + if (call->local_hold || + ((call_med->dir & PJMEDIA_DIR_DECODING) == 0 && + (call_med->def_dir & PJMEDIA_DIR_DECODING) == 0)) + { + /* Local hold: Either the user holds the call, or sets + * the media direction that requests the remote party to + * stop sending media (i.e. sendonly or inactive). + */ + call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; + } else if ((call_med->dir & PJMEDIA_DIR_ENCODING) == 0 && + (call_med->def_dir & PJMEDIA_DIR_DECODING) != 0) + { + /* Remote hold: Remote doesn't want us to send media + * (recvonly or inactive) and we don't set media dir that + * locally holds the media. + */ + call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; + } else { + call_med->state = PJSUA_CALL_MEDIA_ACTIVE; + } + + if (call->inv->following_fork) { + unsigned options = (call_med->enable_rtcp_mux? + PJMEDIA_TPMED_RTCP_MUX: 0); + /* Normally media transport will automatically restart + * itself (if needed, based on info from the SDP) in + * pjmedia_transport_media_start(), however in "following + * forked media" case (see #1644), we need to explicitly + * restart it as it cannot detect fork scenario from + * the SDP only. + */ + status = pjmedia_transport_media_stop(call_med->tp); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjmedia_transport_media_stop() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + status = pjmedia_transport_media_create(call_med->tp, + tmp_pool, + options, NULL, mi); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjmedia_transport_media_create() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + } + + /* Start/restart media transport based on info in SDP */ + status = pjmedia_transport_media_start(call_med->tp, + tmp_pool, local_sdp, + remote_sdp, mi); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjmedia_transport_media_start() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + + pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); + + /* Get remote SRTP usage policy */ + pjmedia_transport_info_init(&tp_info); + pjmedia_transport_get_info(call_med->tp, &tp_info); + srtp_info = (pjmedia_srtp_info*) + pjmedia_transport_info_get_spc_info( + &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP); + if (srtp_info) { + call_med->rem_srtp_use = srtp_info->peer_use; + } + + /* Update audio channel */ + if (media_changed) { + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + status = pjsua_aud_channel_update(call_med, + call->inv->pool, &asi, + local_sdp, remote_sdp); + } else if (call_med->type == PJMEDIA_TYPE_VIDEO) { +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + status = pjsua_vid_channel_update(call_med, + call->inv->pool, &vsi, + local_sdp, remote_sdp); +#endif + } + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjsua_aud_channel_update() failed " + "for call_id %d media %d", + call_id, mi)); + return status; + } + + if (pjmedia_transport_info_get_spc_info( + &tp_info, PJMEDIA_TRANSPORT_TYPE_LOOP)) + { + pjmedia_transport_loop_disable_rx( + call_med->tp, call_med->strm.a.stream, + !acc->cfg.enable_loopback); + } + } + } + + /* Print info. */ + if (status == PJ_SUCCESS) { + char info[80]; + int info_len = 0; + int len; + const char *dir; + + switch (si->dir) { + case PJMEDIA_DIR_NONE: + dir = "inactive"; + break; + case PJMEDIA_DIR_ENCODING: + dir = "sendonly"; + break; + case PJMEDIA_DIR_DECODING: + dir = "recvonly"; + break; + case PJMEDIA_DIR_ENCODING_DECODING: + dir = "sendrecv"; + break; + default: + dir = "unknown"; + break; + } + len = pj_ansi_snprintf( info+info_len, sizeof(info)-info_len, + ", stream #%d: %.*s (%s)", mi, + (int)enc_name->slen, + enc_name->ptr, + dir); + if (len > 0) + info_len += len; + PJ_LOG(4,(THIS_FILE,"%s updated%s", + pjmedia_type_name(call_med->type), info)); + } + return status; +} + + pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp) @@ -4025,12 +4352,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Process each media stream */ for (mi=0; mi < call->med_cnt; ++mi) { - const char *STR_SENDRECV = "sendrecv"; - const char *STR_SENDONLY = "sendonly"; - const char *STR_RECVONLY = "recvonly"; - const char *STR_INACTIVE = "inactive"; pjsua_call_media *call_med = &call->media[mi]; - pj_bool_t media_changed = PJ_FALSE; if (mi >= local_sdp->media_count || mi >= remote_sdp->media_count) @@ -4060,490 +4382,15 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Apply media update action */ if (call_med->type==PJMEDIA_TYPE_AUDIO) { - pjmedia_stream_info the_si, *si = &the_si; - pjsua_stream_info stream_info; - - status = pjmedia_stream_info_from_sdp( - si, tmp_pool, pjsua_var.med_endpt, - local_sdp, remote_sdp, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_stream_info_from_sdp() failed " - "for call_id %d media %d", - call_id, mi)); + status = apply_med_update(call_med, local_sdp, remote_sdp, &need_renego_sdp); + if (status != PJ_SUCCESS) goto on_check_med_status; - } - -#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) - /* Enable/disable RTCP XR based on account setting. */ - si->rtcp_xr_enabled = acc->cfg.enable_rtcp_xr; -#endif - - /* Check if remote wants RTP and RTCP multiplexing, - * but we don't enable it. - */ - if (si->rtcp_mux && !call_med->enable_rtcp_mux) { - si->rtcp_mux = PJ_FALSE; - } - - /* Codec parameter of stream info (si->param) can be NULL if - * the stream is rejected or disabled. - */ - /* Override ptime, if this option is specified. */ - if (pjsua_var.media_cfg.ptime != 0 && si->param) { - si->param->setting.frm_per_pkt = (pj_uint8_t) - (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime); - if (si->param->setting.frm_per_pkt == 0) - si->param->setting.frm_per_pkt = 1; - } - - /* Disable VAD, if this option is specified. */ - if (pjsua_var.media_cfg.no_vad && si->param) { - si->param->setting.vad = 0; - } - - if (!pjmedia_sdp_neg_was_answer_remote(call->inv->neg) && - si->dir != PJMEDIA_DIR_NONE) - { - pjmedia_dir dir = si->dir; - - if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) { - call_med->def_dir = call->opt.media_dir[mi]; - PJ_LOG(4,(THIS_FILE, "Call %d: setting audio media " - "direction #%d to %d.", - call_id, mi, call_med->def_dir)); - } - - /* If the default direction specifies we do not wish - * encoding/decoding, clear that direction. - */ - if ((call_med->def_dir & PJMEDIA_DIR_ENCODING) == 0) { - dir &= ~PJMEDIA_DIR_ENCODING; - } - if ((call_med->def_dir & PJMEDIA_DIR_DECODING) == 0) { - dir &= ~PJMEDIA_DIR_DECODING; - } - - if (dir != si->dir) { - const char *str_attr = NULL; - pjmedia_sdp_attr *attr; - pjmedia_sdp_media *m; - - if (!need_renego_sdp) { - pjmedia_sdp_session *local_sdp_renego; - local_sdp_renego = - pjmedia_sdp_session_clone(tmp_pool, local_sdp); - local_sdp = local_sdp_renego; - need_renego_sdp = PJ_TRUE; - } - - si->dir = dir; - m = local_sdp->media[mi]; - - /* Remove existing directions attributes */ - pjmedia_sdp_media_remove_all_attr(m, STR_SENDRECV); - pjmedia_sdp_media_remove_all_attr(m, STR_SENDONLY); - pjmedia_sdp_media_remove_all_attr(m, STR_RECVONLY); - - if (si->dir == PJMEDIA_DIR_ENCODING_DECODING) { - str_attr = STR_SENDRECV; - } else if (si->dir == PJMEDIA_DIR_ENCODING) { - str_attr = STR_SENDONLY; - } else if (si->dir == PJMEDIA_DIR_DECODING) { - str_attr = STR_RECVONLY; - } else { - str_attr = STR_INACTIVE; - } - attr = pjmedia_sdp_attr_create(tmp_pool, str_attr, NULL); - pjmedia_sdp_media_add_attr(m, attr); - } - } - - stream_info.type = PJMEDIA_TYPE_AUDIO; - stream_info.info.aud = the_si; - -#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO -#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) - /* Check if we need to reset or maintain SRTP ROC */ - check_srtp_roc(call, mi, &stream_info, local_sdp, remote_sdp); -#endif -#endif - - /* Check if this media is changed */ - if (pjsua_var.media_cfg.no_smart_media_update || - is_media_changed(call, mi, &stream_info)) - { - media_changed = PJ_TRUE; - /* Stop the media */ - stop_media_stream(call, mi); - } else { - PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (audio) unchanged.", - call_id, mi)); - } - - /* Check if no media is active */ - if (local_sdp->media[mi]->desc.port == 0) { - - /* Update call media state and direction */ - call_med->state = PJSUA_CALL_MEDIA_NONE; - call_med->dir = PJMEDIA_DIR_NONE; - - } else if (call_med->tp) { - pjmedia_transport_info tp_info; - pjmedia_srtp_info *srtp_info; - - /* Call media direction */ - call_med->dir = si->dir; - - /* Call media state */ - if (call->local_hold || - ((call_med->dir & PJMEDIA_DIR_DECODING) == 0 && - (call_med->def_dir & PJMEDIA_DIR_DECODING) == 0)) - { - /* Local hold: Either the user holds the call, or sets - * the media direction that requests the remote party to - * stop sending media (i.e. sendonly or inactive). - */ - call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; - } else if ((call_med->dir & PJMEDIA_DIR_ENCODING) == 0 && - (call_med->def_dir & PJMEDIA_DIR_DECODING) != 0) - { - /* Remote hold: Remote doesn't want us to send media - * (recvonly or inactive) and we don't set media dir that - * locally holds the media. - */ - call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; - } else { - call_med->state = PJSUA_CALL_MEDIA_ACTIVE; - } - - if (call->inv->following_fork) { - unsigned options = (call_med->enable_rtcp_mux? - PJMEDIA_TPMED_RTCP_MUX: 0); - /* Normally media transport will automatically restart - * itself (if needed, based on info from the SDP) in - * pjmedia_transport_media_start(), however in "following - * forked media" case (see #1644), we need to explicitly - * restart it as it cannot detect fork scenario from - * the SDP only. - */ - status = pjmedia_transport_media_stop(call_med->tp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_transport_media_stop() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - status = pjmedia_transport_media_create(call_med->tp, - tmp_pool, - options, NULL, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_transport_media_create() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - } - - /* Start/restart media transport based on info in SDP */ - status = pjmedia_transport_media_start(call_med->tp, - tmp_pool, local_sdp, - remote_sdp, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_transport_media_start() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - - pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); - - /* Get remote SRTP usage policy */ - pjmedia_transport_info_init(&tp_info); - pjmedia_transport_get_info(call_med->tp, &tp_info); - srtp_info = (pjmedia_srtp_info*) - pjmedia_transport_info_get_spc_info( - &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP); - if (srtp_info) { - call_med->rem_srtp_use = srtp_info->peer_use; - } - - /* Update audio channel */ - if (media_changed) { - status = pjsua_aud_channel_update(call_med, - call->inv->pool, si, - local_sdp, remote_sdp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjsua_aud_channel_update() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - - if (pjmedia_transport_info_get_spc_info( - &tp_info, PJMEDIA_TRANSPORT_TYPE_LOOP)) - { - pjmedia_transport_loop_disable_rx( - call_med->tp, call_med->strm.a.stream, - !acc->cfg.enable_loopback); - } - } - } - - /* Print info. */ - if (status == PJ_SUCCESS) { - char info[80]; - int info_len = 0; - int len; - const char *dir; - - switch (si->dir) { - case PJMEDIA_DIR_NONE: - dir = "inactive"; - break; - case PJMEDIA_DIR_ENCODING: - dir = "sendonly"; - break; - case PJMEDIA_DIR_DECODING: - dir = "recvonly"; - break; - case PJMEDIA_DIR_ENCODING_DECODING: - dir = "sendrecv"; - break; - default: - dir = "unknown"; - break; - } - len = pj_ansi_snprintf( info+info_len, sizeof(info)-info_len, - ", stream #%d: %.*s (%s)", mi, - (int)si->fmt.encoding_name.slen, - si->fmt.encoding_name.ptr, - dir); - if (len > 0) - info_len += len; - PJ_LOG(4,(THIS_FILE,"Audio updated%s", info)); - } - - - if (call->audio_idx==-1 && status==PJ_SUCCESS && - si->dir != PJMEDIA_DIR_NONE) - { - call->audio_idx = mi; - } #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) } else if (call_med->type==PJMEDIA_TYPE_VIDEO) { - pjmedia_vid_stream_info the_si, *si = &the_si; - pjsua_stream_info stream_info; - - status = pjmedia_vid_stream_info_from_sdp( - si, tmp_pool, pjsua_var.med_endpt, - local_sdp, remote_sdp, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_vid_stream_info_from_sdp() failed " - "for call_id %d media %d", - call_id, mi)); + status = apply_med_update(call_med, local_sdp, remote_sdp, &need_renego_sdp); + if (status != PJ_SUCCESS) goto on_check_med_status; - } - - /* Check if remote wants RTP and RTCP multiplexing, - * but we don't enable it. - */ - if (si->rtcp_mux && !call_med->enable_rtcp_mux) { - si->rtcp_mux = PJ_FALSE; - } - - if (!pjmedia_sdp_neg_was_answer_remote(call->inv->neg) && - si->dir != PJMEDIA_DIR_NONE) - { - pjmedia_dir dir = si->dir; - - if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) { - call_med->def_dir = call->opt.media_dir[mi]; - PJ_LOG(4,(THIS_FILE, "Call %d: setting video media " - "direction #%d to %d.", - call_id, mi, call_med->def_dir)); - } - - /* If the default direction specifies we do not wish - * encoding/decoding, clear that direction. - */ - if ((call_med->def_dir & PJMEDIA_DIR_ENCODING) == 0) { - dir &= ~PJMEDIA_DIR_ENCODING; - } - if ((call_med->def_dir & PJMEDIA_DIR_DECODING) == 0) { - dir &= ~PJMEDIA_DIR_DECODING; - } - - if (dir != si->dir) { - const char *str_attr = NULL; - pjmedia_sdp_attr *attr; - pjmedia_sdp_media *m; - - if (!need_renego_sdp) { - pjmedia_sdp_session *local_sdp_renego; - local_sdp_renego = - pjmedia_sdp_session_clone(tmp_pool, local_sdp); - local_sdp = local_sdp_renego; - need_renego_sdp = PJ_TRUE; - } - - si->dir = dir; - m = local_sdp->media[mi]; - - /* Remove existing directions attributes */ - pjmedia_sdp_media_remove_all_attr(m, STR_SENDRECV); - pjmedia_sdp_media_remove_all_attr(m, STR_SENDONLY); - pjmedia_sdp_media_remove_all_attr(m, STR_RECVONLY); - - if (si->dir == PJMEDIA_DIR_ENCODING_DECODING) { - str_attr = STR_SENDRECV; - } else if (si->dir == PJMEDIA_DIR_ENCODING) { - str_attr = STR_SENDONLY; - } else if (si->dir == PJMEDIA_DIR_DECODING) { - str_attr = STR_RECVONLY; - } else { - str_attr = STR_INACTIVE; - } - attr = pjmedia_sdp_attr_create(tmp_pool, str_attr, NULL); - pjmedia_sdp_media_add_attr(m, attr); - } - } - - stream_info.type = PJMEDIA_TYPE_VIDEO; - stream_info.info.vid = the_si; - -#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO -#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) - /* Check if we need to reset or maintain SRTP ROC */ - check_srtp_roc(call, mi, &stream_info, local_sdp, remote_sdp); -#endif -#endif - - /* Check if this media is changed */ - if (is_media_changed(call, mi, &stream_info)) { - media_changed = PJ_TRUE; - /* Stop the media */ - stop_media_stream(call, mi); - } else { - PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (video) unchanged.", - call_id, mi)); - } - - /* Check if no media is active */ - if (local_sdp->media[mi]->desc.port == 0) { - - /* Update call media state and direction */ - call_med->state = PJSUA_CALL_MEDIA_NONE; - call_med->dir = PJMEDIA_DIR_NONE; - - } else if (call_med->tp) { - pjmedia_transport_info tp_info; - pjmedia_srtp_info *srtp_info; - - /* Call media direction */ - call_med->dir = si->dir; - - /* Call media state */ - if (call->local_hold || - ((call_med->dir & PJMEDIA_DIR_DECODING) == 0 && - (call_med->def_dir & PJMEDIA_DIR_DECODING) == 0)) - { - /* Local hold: Either the user holds the call, or sets - * the media direction that requests the remote party to - * stop sending media (i.e. sendonly or inactive). - */ - call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; - } else if ((call_med->dir & PJMEDIA_DIR_ENCODING) == 0 && - (call_med->def_dir & PJMEDIA_DIR_DECODING) != 0) - { - /* Remote hold: Remote doesn't want us to send media - * (recvonly or inactive) and we don't set media dir that - * locally holds the media. - */ - call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; - } else { - call_med->state = PJSUA_CALL_MEDIA_ACTIVE; - } - - /* Start/restart media transport */ - status = pjmedia_transport_media_start(call_med->tp, - tmp_pool, local_sdp, - remote_sdp, mi); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjmedia_transport_media_start() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - - pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); - - /* Get remote SRTP usage policy */ - pjmedia_transport_info_init(&tp_info); - pjmedia_transport_get_info(call_med->tp, &tp_info); - srtp_info = (pjmedia_srtp_info*) - pjmedia_transport_info_get_spc_info( - &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP); - if (srtp_info) { - call_med->rem_srtp_use = srtp_info->peer_use; - } - - /* Update video channel */ - if (media_changed) { - status = pjsua_vid_channel_update(call_med, - call->inv->pool, si, - local_sdp, remote_sdp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjsua_vid_channel_update() failed " - "for call_id %d media %d", - call_id, mi)); - goto on_check_med_status; - } - } - } - - /* Print info. */ - { - char info[80]; - int info_len = 0; - int len; - const char *dir; - - switch (si->dir) { - case PJMEDIA_DIR_NONE: - dir = "inactive"; - break; - case PJMEDIA_DIR_ENCODING: - dir = "sendonly"; - break; - case PJMEDIA_DIR_DECODING: - dir = "recvonly"; - break; - case PJMEDIA_DIR_ENCODING_DECODING: - dir = "sendrecv"; - break; - default: - dir = "unknown"; - break; - } - len = pj_ansi_snprintf( info+info_len, sizeof(info)-info_len, - ", stream #%d: %.*s (%s)", mi, - (int)si->codec_info.encoding_name.slen, - si->codec_info.encoding_name.ptr, - dir); - if (len > 0) - info_len += len; - PJ_LOG(4,(THIS_FILE,"Video updated%s", info)); - } - #endif } else { status = PJMEDIA_EUNSUPMEDIATYPE;