From 64d1bed1542041370f054e693dcb131dac889618 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 12 Apr 2012 13:41:50 +0000 Subject: Re #1476: Initial version of send rate control in video stream, added simple blocking method (block application thread to make send delay when delay is needed). git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4043 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia/vid_stream.h | 57 +++++++++++++++++++++++++++++++++ pjmedia/src/pjmedia/vid_stream.c | 61 ++++++++++++++++++++++++++++++++++-- pjsip/include/pjsua-lib/pjsua.h | 7 +++++ pjsip/src/pjsua-lib/pjsua_core.c | 1 + pjsip/src/pjsua-lib/pjsua_vid.c | 3 ++ 5 files changed, 127 insertions(+), 2 deletions(-) diff --git a/pjmedia/include/pjmedia/vid_stream.h b/pjmedia/include/pjmedia/vid_stream.h index 634709a0..c812d37d 100644 --- a/pjmedia/include/pjmedia/vid_stream.h +++ b/pjmedia/include/pjmedia/vid_stream.h @@ -73,6 +73,51 @@ PJ_BEGIN_DECL */ +/** + * Enumeration of video stream sending rate control. + */ +typedef enum pjmedia_vid_stream_rc_method +{ + /** + * No sending rate control. All outgoing RTP packets will be transmitted + * immediately right after encoding process is done. + */ + PJMEDIA_VID_STREAM_RC_NONE = 0, + + /** + * Simple blocking. Each outgoing RTP packet transmission may be delayed + * to avoid peak bandwidth that is much higher than specified. The thread + * invoking the video stream put_frame(), e.g: video capture device thread, + * will be blocked whenever transmission delay takes place. + */ + PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING = 1 + +} pjmedia_vid_stream_rc_method; + + +/** + * Structure of configuration settings for video stream sending rate control. + */ +typedef struct pjmedia_vid_stream_rc_config +{ + /** + * Rate control method. + * + * Default: PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING. + */ + pjmedia_vid_stream_rc_method method; + + /** + * Upstream/outgoing bandwidth. If this is set to zero, the video stream + * will use codec maximum bitrate setting. + * + * Default: 0 (follow codec maximum bitrate). + */ + unsigned bandwidth; + +} pjmedia_vid_stream_rc_config; + + /** * This structure describes video stream information. Each video stream * corresponds to one "m=" line in SDP session descriptor, and it has @@ -117,6 +162,9 @@ typedef struct pjmedia_vid_stream_info pj_bool_t rtcp_sdes_bye_disabled; /**< Disable automatic sending of RTCP SDES and BYE. */ + + pjmedia_vid_stream_rc_config rc_cfg; + /**< Stream send rate control settings. */ } pjmedia_vid_stream_info; @@ -145,6 +193,15 @@ pjmedia_vid_stream_info_from_sdp(pjmedia_vid_stream_info *si, unsigned stream_idx); +/** + * Initialize the video stream rate control with default settings. + * + * @param cfg Video stream rate control structure to be initialized. + */ +PJ_DECL(void) +pjmedia_vid_stream_rc_config_default(pjmedia_vid_stream_rc_config *cfg); + + /* * Opaque declaration for video stream. */ diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c index a813758e..af5a08dc 100644 --- a/pjmedia/src/pjmedia/vid_stream.c +++ b/pjmedia/src/pjmedia/vid_stream.c @@ -153,8 +153,11 @@ struct pjmedia_vid_stream #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 last_dec_ts; /**< Last decoded timestamp. */ + int last_dec_seq; /**< Last decoded sequence. */ + + + pj_timestamp ts_freq; /**< Timestamp frequency. */ }; /* Prototypes */ @@ -768,6 +771,8 @@ static pj_status_t put_frame(pjmedia_port *port, pj_bool_t has_more_data = PJ_FALSE; pj_size_t total_sent = 0; pjmedia_vid_encode_opt enc_opt; + unsigned pkt_cnt = 0; + pj_timestamp initial_time; #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 /* If the interval since last sending packet is greater than @@ -826,6 +831,8 @@ static pj_status_t put_frame(pjmedia_port *port, &rtphdrlen); return status; } + + pj_get_timestamp(&initial_time); /* Loop while we have frame to send */ for (;;) { @@ -864,6 +871,7 @@ static pj_status_t put_frame(pjmedia_port *port, pjmedia_rtcp_tx_rtp(&stream->rtcp, frame_out.size); total_sent += frame_out.size; + pkt_cnt++; if (!has_more_data) break; @@ -885,8 +893,41 @@ static pj_status_t put_frame(pjmedia_port *port, /* Ignore this error (?) */ break; } + + /* Send rate control */ + if (stream->info.rc_cfg.method==PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING) + { + pj_timestamp now, next_send_ts, total_send_ts; + + total_send_ts.u64 = total_sent * stream->ts_freq.u64 * 8 / + stream->info.rc_cfg.bandwidth; + next_send_ts = initial_time; + pj_add_timestamp(&next_send_ts, &total_send_ts); + + pj_get_timestamp(&now); + if (pj_cmp_timestamp(&now, &next_send_ts) < 0) { + unsigned ms_sleep; + ms_sleep = pj_elapsed_msec(&now, &next_send_ts); + + if (ms_sleep > 10) + ms_sleep = 10; + + pj_thread_sleep(ms_sleep); + } + } } +#if 0 + /* Trace log for rate control */ + { + pj_timestamp end_time; + pj_get_timestamp(&end_time); + PJ_LOG(5, (stream->name.ptr, "total pkt=%d size=%d sleep=%d", + pkt_cnt, total_sent, + pj_elapsed_msec(&initial_time, &end_time))); + } +#endif + /* Check if now is the time to transmit RTCP SR/RR report. * We only do this when stream direction is not "decoding only", because * when it is, check_tx_rtcp() will be handled by get_frame(). @@ -1413,6 +1454,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_create( stream->frame_ts_len = info->codec_info.clock_rate * vfd_enc->fps.denum / vfd_enc->fps.num; + /* Initialize send rate states */ + pj_get_timestamp_freq(&stream->ts_freq); + if (info->rc_cfg.bandwidth == 0) + info->rc_cfg.bandwidth = vfd_enc->max_bps * 150 / 100; + /* Override the initial framerate in the decoding direction. This initial * value will be used by the renderer to configure its clock, and setting * it to a bit higher value can avoid the possibility of high latency @@ -1867,4 +1913,15 @@ PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_bye( } +/* + * Initialize the video stream rate control with default settings. + */ +PJ_DEF(void) +pjmedia_vid_stream_rc_config_default(pjmedia_vid_stream_rc_config *cfg) +{ + pj_bzero(cfg, sizeof(*cfg)); + cfg->method = PJMEDIA_VID_STREAM_RC_SIMPLE_BLOCKING; +} + + #endif /* PJMEDIA_HAS_VIDEO */ diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 98942e3a..73dc9291 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -2881,6 +2881,13 @@ typedef struct pjsua_acc_config */ pjmedia_vid_dev_index vid_rend_dev; + /** + * Specify the send rate control for video stream. + * + * Default: see #pjmedia_vid_stream_rc_config + */ + pjmedia_vid_stream_rc_config vid_stream_rc_cfg; + /** * Media transport config. */ diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 89f2b13d..aeb85ebf 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -218,6 +218,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) cfg->ka_data = pj_str("\r\n"); cfg->vid_cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; cfg->vid_rend_dev = PJMEDIA_VID_DEFAULT_RENDER_DEV; + pjmedia_vid_stream_rc_config_default(&cfg->vid_stream_rc_cfg); pjsua_transport_config_default(&cfg->rtp_cfg); cfg->use_srtp = pjsua_var.ua_cfg.use_srtp; cfg->srtp_secure_signaling = pjsua_var.ua_cfg.srtp_secure_signaling; diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index 24f0919f..b8ece7a8 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -747,6 +747,9 @@ pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med, si->rtp_seq = call_med->rtp_tx_seq; si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set; + /* Set rate control config from account setting */ + si->rc_cfg = acc->cfg.vid_stream_rc_cfg; + #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 /* Enable/disable stream keep-alive and NAT hole punch. */ si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka; -- cgit v1.2.3