diff --git a/pjmedia/include/pjmedia-codec/opus.h b/pjmedia/include/pjmedia-codec/opus.h index 9438c7daa..5621761ab 100644 --- a/pjmedia/include/pjmedia-codec/opus.h +++ b/pjmedia/include/pjmedia-codec/opus.h @@ -98,7 +98,8 @@ typedef struct pjmedia_codec_opus_config { unsigned sample_rate; /**< Sample rate in Hz. */ unsigned channel_cnt; /**< Number of channels. */ - unsigned frm_ptime; /**< Frame time in msec. */ + unsigned frm_ptime; /**< Frame ptime in msec. */ + unsigned frm_ptime_denum;/**< Frame ptime denumerator, can be zero*/ unsigned bit_rate; /**< Encoder bit rate in bps. */ unsigned packet_loss; /**< Encoder's expected packet loss pct. */ unsigned complexity; /**< Encoder complexity, 0-10(10 is highest)*/ diff --git a/pjmedia/include/pjmedia/codec.h b/pjmedia/include/pjmedia/codec.h index c69356699..743f9c41a 100644 --- a/pjmedia/include/pjmedia/codec.h +++ b/pjmedia/include/pjmedia/codec.h @@ -270,6 +270,9 @@ typedef struct pjmedia_codec_param /** * The "info" part of codec param describes the capability of the codec, * and is recommended not to be modified unless necessary. + * Note that application must be ready to handle cases when ptime + * denumerators are zero, since most codecs that only support integer + * ptime will leave these fields untouched. */ struct { unsigned clock_rate; /**< Sampling rate in Hz */ @@ -278,8 +281,12 @@ typedef struct pjmedia_codec_param pj_uint32_t max_bps; /**< Maximum bandwidth in bits/sec */ unsigned max_rx_frame_size; /**< Maximum frame size */ pj_uint16_t frm_ptime; /**< Decoder frame ptime in msec. */ + pj_uint8_t frm_ptime_denum; /**< Decoder frame ptime denum, or + zero if ptime is integer. */ pj_uint16_t enc_ptime; /**< Encoder ptime, or zero if it's equal to decoder ptime. */ + pj_uint8_t enc_ptime_denum; /**< Encoder frame ptime denum, or + zero if ptime is integer. */ pj_uint8_t pcm_bits_per_sample; /**< Bits/sample in the PCM side */ pj_uint8_t pt; /**< Payload type. */ pjmedia_format_id fmt_id; /**< Source format, it's format of @@ -355,8 +362,8 @@ typedef struct pjmedia_codec_op /** * Open the codec and initialize with the specified parameter. * Upon successful initialization, the codec may modify the parameter - * and fills in the unspecified values (such as enc_ptime, when - * encoder ptime is different than decoder ptime). + * and fills in the unspecified values (such as enc_ptime/enc_ptime_denum, + * when encoder ptime is different than decoder ptime). * * Application should call #pjmedia_codec_open() instead of * calling this function directly. @@ -404,7 +411,7 @@ typedef struct pjmedia_codec_op * Instruct the codec to inspect the specified payload/packet and * split the packet into individual base frames. Each output frames will * have ptime that is equal to basic frame ptime (i.e. the value of - * info.frm_ptime in #pjmedia_codec_param). + * info.frm_ptime/info.frm_ptime_denum in #pjmedia_codec_param). * * Application should call #pjmedia_codec_parse() instead of * calling this function directly. @@ -431,7 +438,8 @@ typedef struct pjmedia_codec_op /** * Instruct the codec to encode the specified input frame. The input * PCM samples MUST have ptime that is multiplication of base frame - * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param). + * ptime (i.e. the value of info.frm_ptime/info.frm_ptime_denum in + * #pjmedia_codec_param). * * Application should call #pjmedia_codec_encode() instead of * calling this function directly. @@ -451,7 +459,8 @@ typedef struct pjmedia_codec_op /** * Instruct the codec to decode the specified input frame. The input * frame MUST have ptime that is exactly equal to base frame - * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param). + * ptime (i.e. the value of info.frm_ptime/info.frm_ptime_denum in + * #pjmedia_codec_param). * Application can achieve this by parsing the packet into base * frames before decoding each frame. * @@ -1037,7 +1046,7 @@ PJ_INLINE(pj_status_t) pjmedia_codec_modify(pjmedia_codec *codec, * Instruct the codec to inspect the specified payload/packet and * split the packet into individual base frames. Each output frames will * have ptime that is equal to basic frame ptime (i.e. the value of - * info.frm_ptime in #pjmedia_codec_param). + * info.frm_ptime/info.frm_ptime_denum in #pjmedia_codec_param). * * @param codec The codec instance * @param pkt The input packet. @@ -1066,7 +1075,8 @@ PJ_INLINE(pj_status_t) pjmedia_codec_parse( pjmedia_codec *codec, /** * Instruct the codec to encode the specified input frame. The input * PCM samples MUST have ptime that is multiplication of base frame - * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param). + * ptime (i.e. the value of info.frm_ptime/info.frm_ptime_denum in + * #pjmedia_codec_param). * * @param codec The codec instance. * @param input The input frame. @@ -1088,7 +1098,8 @@ PJ_INLINE(pj_status_t) pjmedia_codec_encode( /** * Instruct the codec to decode the specified input frame. The input * frame MUST have ptime that is exactly equal to base frame - * ptime (i.e. the value of info.frm_ptime in #pjmedia_codec_param). + * ptime (i.e. the value of info.frm_ptime/info.frm_ptime_denum in + * #pjmedia_codec_param). * Application can achieve this by parsing the packet into base * frames before decoding each frame. * diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h index 25b01f48e..6f727494d 100644 --- a/pjmedia/include/pjmedia/jbuf.h +++ b/pjmedia/include/pjmedia/jbuf.h @@ -170,8 +170,8 @@ PJ_DECL(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, /** * Set the jitter buffer's frame duration. * - * @param jb The jitter buffer - * @param ptime Frame duration. + * @param jb The jitter buffer. + * @param ptime Frame ptime. * * @return PJ_SUCCESS on success. */ @@ -179,6 +179,20 @@ PJ_DECL(pj_status_t) pjmedia_jbuf_set_ptime( pjmedia_jbuf *jb, unsigned ptime); +/** + * Set the jitter buffer's frame duration. + * + * @param jb The jitter buffer. + * @param ptime Frame ptime. + * @param ptime_denum Frame ptime denumerator. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_jbuf_set_ptime2(pjmedia_jbuf *jb, + unsigned ptime, + unsigned ptime_denum); + + /** * Set the jitter buffer to fixed delay mode. The default behavior * is to adapt the delay with actual packet delay. diff --git a/pjmedia/src/pjmedia-codec/opus.c b/pjmedia/src/pjmedia-codec/opus.c index 03a220e10..1925c7a20 100644 --- a/pjmedia/src/pjmedia-codec/opus.c +++ b/pjmedia/src/pjmedia-codec/opus.c @@ -44,6 +44,7 @@ /* Default frame time (msec) */ #define PTIME 20 +#define PTIME_DENUM 1 /* Tracing */ #if 0 @@ -139,7 +140,9 @@ struct opus_data OpusRepacketizer *dec_packer; pjmedia_codec_opus_config cfg; unsigned enc_ptime; + unsigned enc_ptime_denum; unsigned dec_ptime; + unsigned dec_ptime_denum; pjmedia_frame dec_frame[2]; int dec_frame_index; }; @@ -152,7 +155,8 @@ static pjmedia_codec_opus_config opus_cfg = { PJMEDIA_CODEC_OPUS_DEFAULT_SAMPLE_RATE, /* Sample rate */ 1, /* Channel count */ - PTIME, /* Frame time */ + PTIME, /* Frame ptime */ + PTIME_DENUM, /* Frame ptime denum */ PJMEDIA_CODEC_OPUS_DEFAULT_BIT_RATE, /* Bit rate */ 5, /* Expected packet loss */ PJMEDIA_CODEC_OPUS_DEFAULT_COMPLEXITY, /* Complexity */ @@ -420,7 +424,9 @@ pjmedia_codec_opus_set_default_param(const pjmedia_codec_opus_config *cfg, param->info.clock_rate = opus_cfg.sample_rate = cfg->sample_rate; param->info.max_bps = opus_cfg.sample_rate * 2; opus_cfg.frm_ptime = cfg->frm_ptime; + opus_cfg.frm_ptime_denum = cfg->frm_ptime_denum; param->info.frm_ptime = (pj_uint16_t)cfg->frm_ptime; + param->info.frm_ptime_denum = (pj_uint8_t)cfg->frm_ptime_denum; /* Set channel count */ if (cfg->channel_cnt != 1 && cfg->channel_cnt != 2) @@ -503,6 +509,7 @@ static pj_status_t factory_default_attr( pjmedia_codec_factory *factory, attr->info.avg_bps = opus_cfg.bit_rate; attr->info.max_bps = opus_cfg.sample_rate * 2; attr->info.frm_ptime = (pj_uint16_t)opus_cfg.frm_ptime; + attr->info.frm_ptime_denum = (pj_uint8_t)opus_cfg.frm_ptime_denum; attr->setting.frm_per_pkt = 1; attr->info.pcm_bits_per_sample = 16; attr->setting.vad = OPUS_DEFAULT_VAD; @@ -644,6 +651,9 @@ static pj_status_t codec_open( pjmedia_codec *codec, opus_data->cfg.sample_rate = attr->info.clock_rate; opus_data->cfg.channel_cnt = attr->info.channel_cnt; opus_data->enc_ptime = opus_data->dec_ptime = attr->info.frm_ptime; + opus_data->enc_ptime_denum = attr->info.frm_ptime_denum? + attr->info.frm_ptime_denum: 1; + opus_data->dec_ptime_denum = opus_data->enc_ptime_denum; /* Allocate memory used by the codec */ if (!opus_data->enc) { @@ -757,7 +767,8 @@ static pj_status_t codec_open( pjmedia_codec *codec, PJ_LOG(4, (THIS_FILE, "Initialize Opus encoder, sample rate: %d, ch: %d, " "avg bitrate: %d%s, vad: %d, plc: %d, pkt loss: %d, " - "complexity: %d, constant bit rate: %d", + "complexity: %d, constant bit rate: %d, " + "ptime: %d/%d", opus_data->cfg.sample_rate, opus_data->cfg.channel_cnt, (auto_bit_rate? 0: attr->info.avg_bps), @@ -766,7 +777,9 @@ static pj_status_t codec_open( pjmedia_codec *codec, attr->setting.plc?1:0, opus_data->cfg.packet_loss, opus_data->cfg.complexity, - opus_data->cfg.cbr?1:0)); + opus_data->cfg.cbr?1:0, + opus_data->enc_ptime, + opus_data->enc_ptime_denum)); /* Initialize decoder */ err = opus_decoder_init (opus_data->dec, @@ -821,6 +834,11 @@ static pj_status_t codec_modify( pjmedia_codec *codec, TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); + /* Set encoder ptime */ + opus_data->enc_ptime = attr->info.frm_ptime; + opus_data->enc_ptime_denum = attr->info.frm_ptime_denum? + attr->info.frm_ptime_denum: 1; + /* Set bitrate */ opus_data->cfg.bit_rate = attr->info.avg_bps; opus_encoder_ctl(opus_data->enc, OPUS_SET_BITRATE(attr->info.avg_bps? @@ -848,7 +866,8 @@ static pj_status_t codec_modify( pjmedia_codec *codec, PJ_LOG(4, (THIS_FILE, "Modifying Opus encoder, sample rate: %d, ch: %d, " "avg bitrate: %d%s, vad: %d, plc: %d, pkt loss: %d, " - "complexity: %d, constant bit rate: %d", + "complexity: %d, constant bit rate: %d, " + "ptime: %d/%d ms", attr->info.clock_rate, attr->info.channel_cnt, (attr->info.avg_bps? attr->info.avg_bps: 0), @@ -857,7 +876,9 @@ static pj_status_t codec_modify( pjmedia_codec *codec, attr->setting.plc?1:0, attr->setting.packet_loss, attr->setting.complexity, - attr->setting.cbr?1:0)); + attr->setting.cbr?1:0, + opus_data->enc_ptime, + opus_data->enc_ptime_denum)); pj_mutex_unlock (opus_data->mutex); return PJ_SUCCESS; @@ -922,6 +943,7 @@ static pj_status_t codec_parse( pjmedia_codec *codec, if (i == 0) { int nsamples; unsigned ptime; + unsigned ptime_denum = 1; nsamples = opus_packet_get_nb_samples(frames[i].buf, frames[i].size, @@ -933,12 +955,22 @@ static pj_status_t codec_parse( pjmedia_codec *codec, return PJMEDIA_CODEC_EFAILED; } - ptime = nsamples * 1000 / opus_data->cfg.sample_rate; - if (ptime != opus_data->dec_ptime) { - PJ_LOG(4, (THIS_FILE, "Opus ptime change detected: %d ms " - "--> %d ms", - opus_data->dec_ptime, ptime)); + if ((nsamples * 1000) % opus_data->cfg.sample_rate != 0) { + /* The only non-integer ptime that Opus supports is 2.5 ms */ + ptime_denum = 2; + } + ptime = nsamples * ptime_denum * 1000 / opus_data->cfg.sample_rate; + + if (ptime * opus_data->dec_ptime_denum != + opus_data->dec_ptime * ptime_denum) + { + PJ_LOG(4, (THIS_FILE, "Opus ptime change detected: %d/%d ms " + "--> %d/%d ms", + opus_data->dec_ptime, + opus_data->dec_ptime_denum, + ptime, ptime_denum)); opus_data->dec_ptime = ptime; + opus_data->dec_ptime_denum = ptime_denum; opus_data->dec_frame_index = -1; /* Signal to the stream about ptime change. */ @@ -977,7 +1009,8 @@ static pj_status_t codec_encode( pjmedia_codec *codec, pj_mutex_lock (opus_data->mutex); samples_per_frame = (opus_data->cfg.sample_rate * - opus_data->enc_ptime) / 1000; + opus_data->enc_ptime / + opus_data->enc_ptime_denum) / 1000; frame_size = samples_per_frame * opus_data->cfg.channel_cnt * sizeof(opus_int16); @@ -1092,7 +1125,8 @@ static pj_status_t codec_decode( pjmedia_codec *codec, if (inframe->type != PJMEDIA_FRAME_TYPE_AUDIO || fec) { frm_size = PJ_MIN((unsigned)frm_size, opus_data->cfg.sample_rate * - opus_data->dec_ptime / 1000); + opus_data->dec_ptime / + opus_data->dec_ptime_denum / 1000); } decoded_samples = opus_decode( opus_data->dec, inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ? @@ -1153,7 +1187,7 @@ static pj_status_t codec_recover( pjmedia_codec *codec, /* Recover the first packet? Don't think so, fill it with zeroes. */ unsigned samples_per_frame; samples_per_frame = opus_data->cfg.sample_rate * opus_data->dec_ptime/ - 1000; + opus_data->dec_ptime_denum / 1000; output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = samples_per_frame << 1; pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame); @@ -1167,7 +1201,8 @@ static pj_status_t codec_recover( pjmedia_codec *codec, opus_data->cfg.channel_cnt); if (inframe->type != PJMEDIA_FRAME_TYPE_AUDIO) { frm_size = PJ_MIN((unsigned)frm_size, opus_data->cfg.sample_rate * - opus_data->dec_ptime/1000); + opus_data->dec_ptime / opus_data->dec_ptime_denum / + 1000); } decoded_samples = opus_decode(opus_data->dec, inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ? diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c index ed718b841..9fafa6c71 100644 --- a/pjmedia/src/pjmedia/jbuf.c +++ b/pjmedia/src/pjmedia/jbuf.c @@ -98,6 +98,7 @@ struct pjmedia_jbuf pj_str_t jb_name; /**< jitter buffer name */ pj_size_t jb_frame_size; /**< frame size */ unsigned jb_frame_ptime; /**< frame duration. */ + unsigned jb_frame_ptime_denum;/**< frame duration denumerator. */ pj_size_t jb_max_count; /**< capacity of jitter buffer, in frames */ int jb_init_prefetch; /**< Initial prefetch */ @@ -608,6 +609,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, pj_strdup_with_null(pool, &jb->jb_name, name); jb->jb_frame_size = frame_size; jb->jb_frame_ptime = ptime; + jb->jb_frame_ptime_denum = 1; jb->jb_prefetch = PJ_MIN(PJMEDIA_JB_DEFAULT_INIT_DELAY,max_count*4/5); jb->jb_min_prefetch = 0; jb->jb_max_prefetch = max_count*4/5; @@ -628,13 +630,21 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, PJ_DEF(pj_status_t) pjmedia_jbuf_set_ptime( pjmedia_jbuf *jb, unsigned ptime) +{ + return pjmedia_jbuf_set_ptime2(jb, ptime, 1); +} + +PJ_DEF(pj_status_t) pjmedia_jbuf_set_ptime2(pjmedia_jbuf *jb, + unsigned ptime, + unsigned ptime_denum) { PJ_ASSERT_RETURN(jb, PJ_EINVAL); jb->jb_frame_ptime = ptime; - jb->jb_min_shrink_gap = PJMEDIA_JBUF_DISC_MIN_GAP / ptime; - jb->jb_max_burst = (int)PJ_MAX(MAX_BURST_MSEC / ptime, - jb->jb_max_count*3/4); + jb->jb_frame_ptime_denum = ptime_denum; + jb->jb_min_shrink_gap = PJMEDIA_JBUF_DISC_MIN_GAP * ptime_denum / ptime; + jb->jb_max_burst = (int)PJ_MAX(MAX_BURST_MSEC * ptime_denum / ptime, + jb->jb_max_count*3/4); return PJ_SUCCESS; } @@ -910,7 +920,8 @@ static void jbuf_discard_progressive(pjmedia_jbuf *jb) /* Calculate current discard distance */ overflow = cur_size - burst_level; - discard_dist = T / overflow / jb->jb_frame_ptime; + discard_dist = T * jb->jb_frame_ptime_denum / overflow / + jb->jb_frame_ptime; /* Get last seq number in the JB */ last_seq = jb_framelist_origin(&jb->jb_framelist) + @@ -1161,7 +1172,8 @@ PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb, /* We've just retrieved one frame, so add one to cur_size */ cur_size = jb_framelist_eff_size(&jb->jb_framelist) + 1; pj_math_stat_update(&jb->jb_delay, - cur_size*jb->jb_frame_ptime); + cur_size * jb->jb_frame_ptime / + jb->jb_frame_ptime_denum); } } else { /* Jitter buffer is empty */ diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 25eb78b16..b3f96ca5d 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -145,6 +145,7 @@ struct pjmedia_stream decoding buffer. */ pj_uint16_t dec_ptime; /**< Decoder frame ptime in ms. */ + pj_uint8_t dec_ptime_denum;/**< Decoder ptime denum. */ pj_bool_t detect_ptime_change; /**< Detect decode ptime change */ @@ -556,6 +557,7 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_per_frame = stream->dec_ptime * stream->codec_param.info.clock_rate * stream->codec_param.info.channel_cnt / + stream->dec_ptime_denum / 1000; p_out_samp = (pj_int16_t*) frame->buf; @@ -846,6 +848,7 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) samples_per_frame = stream->codec_param.info.frm_ptime * stream->codec_param.info.clock_rate * stream->codec_param.info.channel_cnt / + stream->codec_param.info.frm_ptime_denum / 1000; pj_bzero(f, sizeof(pjmedia_frame_ext)); @@ -1300,7 +1303,9 @@ static void rebuffer(pjmedia_stream *stream, /* How many samples are needed */ count = stream->codec_param.info.enc_ptime * - PJMEDIA_PIA_SRATE(&stream->port.info) / 1000; + PJMEDIA_PIA_SRATE(&stream->port.info) / + stream->codec_param.info.enc_ptime_denum / + 1000; /* See if we have enough samples */ if (stream->enc_buf_count >= count) { @@ -2065,21 +2070,34 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) } else if (stream->detect_ptime_change && frames[0].bit_info > 0xFFFF) { - unsigned dec_ptime, old_ptime; + unsigned dec_ptime, dec_ptime_denum = 1; + pj_uint16_t old_ptime, old_ptime_denum; old_ptime = stream->dec_ptime; + old_ptime_denum = stream->dec_ptime_denum; frames[0].bit_info &= 0xFFFF; - dec_ptime = frames[0].bit_info * 1000 / + if ((frames[0].bit_info * 1000) % + stream->codec_param.info.clock_rate != 0) + { + dec_ptime_denum = 2; + } + dec_ptime = frames[0].bit_info * 1000 * dec_ptime_denum / stream->codec_param.info.clock_rate; stream->rtp_rx_ts_len_per_frame= stream->rtp_rx_ts_len_per_frame * - dec_ptime / stream->dec_ptime; + dec_ptime * + stream->dec_ptime_denum / + stream->dec_ptime / + dec_ptime_denum; stream->dec_ptime = (pj_uint16_t)dec_ptime; - pjmedia_jbuf_set_ptime(stream->jb, stream->dec_ptime); + stream->dec_ptime_denum = (pj_uint8_t)dec_ptime_denum; + pjmedia_jbuf_set_ptime2(stream->jb, stream->dec_ptime, + stream->dec_ptime_denum); PJ_LOG(4, (stream->port.info.name.ptr, "codec decode " - "ptime change detected: %d -> %d", - old_ptime, dec_ptime)); + "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); @@ -2155,11 +2173,13 @@ static void on_rx_rtp( pjmedia_tp_cb_param *param) } else { ts_span = stream->dec_ptime * stream->codec_param.info.clock_rate / + stream->dec_ptime_denum / 1000; } #else ts_span = stream->dec_ptime * stream->codec_param.info.clock_rate / + stream->dec_ptime_denum / 1000; #endif @@ -2523,6 +2543,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, if (stream->codec_param.setting.frm_per_pkt < 1) stream->codec_param.setting.frm_per_pkt = 1; + if (stream->codec_param.info.frm_ptime_denum < 1) + stream->codec_param.info.frm_ptime_denum = 1; + /* Init the codec. */ status = pjmedia_codec_init(stream->codec, pool); if (status != PJ_SUCCESS) @@ -2552,9 +2575,12 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Set additional info and callbacks. */ stream->dec_ptime = stream->codec_param.info.frm_ptime; + stream->dec_ptime_denum = PJ_MAX(stream->codec_param.info.frm_ptime_denum, + 1); afd->bits_per_sample = 16; afd->frame_time_usec = stream->codec_param.info.frm_ptime * - stream->codec_param.setting.frm_per_pkt * 1000; + 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; if (stream->codec_param.info.fmt_id == PJMEDIA_FORMAT_L16) { /* Raw format */ @@ -2588,30 +2614,43 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, * with iLBC */ if (stream->codec_param.info.enc_ptime!=0 && - stream->codec_param.info.enc_ptime!=stream->codec_param.info.frm_ptime) + stream->codec_param.info.enc_ptime * + stream->codec_param.info.frm_ptime_denum != + stream->codec_param.info.frm_ptime * + stream->codec_param.info.enc_ptime_denum) { unsigned ptime; stream->enc_samples_per_pkt = stream->codec_param.info.enc_ptime * stream->codec_param.info.channel_cnt * - afd->clock_rate / 1000; + afd->clock_rate / + stream->codec_param.info.enc_ptime_denum + / 1000; /* Set buffer size as twice the largest ptime value between * stream's ptime, encoder ptime, or decoder ptime. */ - ptime = afd->frame_time_usec / 1000; + ptime = afd->frame_time_usec; - if (stream->codec_param.info.enc_ptime > ptime) - ptime = stream->codec_param.info.enc_ptime; + if (stream->codec_param.info.enc_ptime * 1000 > + ptime * stream->codec_param.info.enc_ptime_denum) + { + ptime = stream->codec_param.info.enc_ptime * 1000 / + stream->codec_param.info.enc_ptime_denum; + } - if (stream->codec_param.info.frm_ptime > ptime) - ptime = stream->codec_param.info.frm_ptime; + if (stream->codec_param.info.frm_ptime * 1000 > + ptime * stream->codec_param.info.frm_ptime_denum) + { + ptime = stream->codec_param.info.frm_ptime * 1000 / + stream->codec_param.info.frm_ptime_denum; + } ptime <<= 1; /* Allocate buffer */ - stream->enc_buf_size = afd->clock_rate * ptime / 1000; + stream->enc_buf_size = afd->clock_rate * ptime / 1000 / 1000; stream->enc_buf = (pj_int16_t*) pj_pool_alloc(pool, stream->enc_buf_size * 2); @@ -2634,17 +2673,22 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->frame_size = stream->codec_param.info.max_rx_frame_size; } else { stream->frame_size = stream->codec_param.info.max_bps * - stream->codec_param.info.frm_ptime / 8 / 1000; + stream->codec_param.info.frm_ptime / + stream->codec_param.info.frm_ptime_denum / + 8 / 1000; if ((stream->codec_param.info.max_bps * - stream->codec_param.info.frm_ptime) % 8000 != 0) + stream->codec_param.info.frm_ptime / + stream->codec_param.info.frm_ptime_denum) % 8000 != 0) { ++stream->frame_size; } } /* How many consecutive PLC frames can be generated */ - stream->max_plc_cnt = (MAX_PLC_MSEC+stream->codec_param.info.frm_ptime-1)/ - stream->codec_param.info.frm_ptime; + stream->max_plc_cnt = (MAX_PLC_MSEC+stream->codec_param.info.frm_ptime/ + stream->codec_param.info.frm_ptime_denum-1) * + stream->codec_param.info.frm_ptime_denum / + stream->codec_param.info.frm_ptime; /* Disable PLC until a "NORMAL" frame is gotten from the jitter buffer. */ stream->plc_cnt = stream->max_plc_cnt; @@ -2687,29 +2731,50 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, #endif /* Init jitter buffer parameters: */ - if (info->jb_max >= stream->codec_param.info.frm_ptime) - jb_max = (info->jb_max + stream->codec_param.info.frm_ptime - 1) / + if (info->jb_max * stream->codec_param.info.frm_ptime_denum >= + stream->codec_param.info.frm_ptime) + { + jb_max = (info->jb_max + stream->codec_param.info.frm_ptime / + stream->codec_param.info.frm_ptime_denum - 1) * + stream->codec_param.info.frm_ptime_denum / stream->codec_param.info.frm_ptime; - else - jb_max = 500 / stream->codec_param.info.frm_ptime; + } else { + jb_max = 500 * stream->codec_param.info.frm_ptime_denum / + stream->codec_param.info.frm_ptime; + } - if (info->jb_min_pre >= stream->codec_param.info.frm_ptime) - jb_min_pre = info->jb_min_pre / stream->codec_param.info.frm_ptime; - else + if (info->jb_min_pre * stream->codec_param.info.frm_ptime_denum >= + stream->codec_param.info.frm_ptime) + { + jb_min_pre = info->jb_min_pre * + stream->codec_param.info.frm_ptime_denum / + stream->codec_param.info.frm_ptime; + } else { //jb_min_pre = 60 / stream->codec_param.info.frm_ptime; jb_min_pre = 1; + } - if (info->jb_max_pre >= stream->codec_param.info.frm_ptime) - jb_max_pre = info->jb_max_pre / stream->codec_param.info.frm_ptime; - else + if (info->jb_max_pre * stream->codec_param.info.frm_ptime_denum >= + stream->codec_param.info.frm_ptime) + { + jb_max_pre = info->jb_max_pre * + stream->codec_param.info.frm_ptime_denum / + stream->codec_param.info.frm_ptime; + } else { //jb_max_pre = 240 / stream->codec_param.info.frm_ptime; jb_max_pre = PJ_MAX(1, jb_max * 4 / 5); + } - if (info->jb_init >= stream->codec_param.info.frm_ptime) - jb_init = info->jb_init / stream->codec_param.info.frm_ptime; - else + if (info->jb_init * stream->codec_param.info.frm_ptime_denum >= + stream->codec_param.info.frm_ptime) + { + jb_init = info->jb_init * + stream->codec_param.info.frm_ptime_denum / + stream->codec_param.info.frm_ptime; + } else { //jb_init = (jb_min_pre + jb_max_pre) / 2; jb_init = 0; + } /* Create jitter buffer */ status = pjmedia_jbuf_create(pool, &stream->port.info.name, @@ -2721,6 +2786,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Set up jitter buffer */ + pjmedia_jbuf_set_ptime2(stream->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); @@ -2845,7 +2912,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, i); /* Jitter buffer absolute maximum delay */ - i = jb_max * stream->codec_param.info.frm_ptime; + 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_INFO_JB_ABS_MAX, i); diff --git a/pjsip/include/pjsua2/media.hpp b/pjsip/include/pjsua2/media.hpp index e7335ece6..bde0ad1bf 100644 --- a/pjsip/include/pjsua2/media.hpp +++ b/pjsip/include/pjsua2/media.hpp @@ -2444,8 +2444,12 @@ struct CodecParamInfo unsigned maxBps; /**< Maximum bandwidth in bits/sec */ unsigned maxRxFrameSize; /**< Maximum frame size */ unsigned frameLen; /**< Decoder frame ptime in msec. */ + unsigned frameLenDenum; /**< Decoder frame ptime denum, or + zero if ptime is integer. */ unsigned encFrameLen; /**< Encoder ptime, or zero if it's equal to decoder ptime. */ + unsigned encFrameLenDenum; /**< Encoder ptime denum, or zero + if ptime is integer. */ unsigned pcmBitsPerSample; /**< Bits/sample in the PCM side */ unsigned pt; /**< Payload type. */ pjmedia_format_id fmtId; /**< Source format, it's format of @@ -2462,7 +2466,9 @@ public: maxBps(0), maxRxFrameSize(0), frameLen(0), + frameLenDenum(0), encFrameLen(0), + encFrameLenDenum(0), pcmBitsPerSample(0), pt(0), fmtId(PJMEDIA_FORMAT_L16) @@ -2516,6 +2522,7 @@ struct CodecOpusConfig unsigned sample_rate; /**< Sample rate in Hz. */ unsigned channel_cnt; /**< Number of channels. */ unsigned frm_ptime; /**< Frame time in msec. */ + unsigned frm_ptime_denum;/**< Frame time denumerator. */ unsigned bit_rate; /**< Encoder bit rate in bps. */ unsigned packet_loss; /**< Encoder's expected packet loss pct. */ unsigned complexity; /**< Encoder complexity, 0-10(10 is highest)*/ diff --git a/pjsip/src/pjsua2/media.cpp b/pjsip/src/pjsua2/media.cpp index 402c47b6e..e5b197b44 100644 --- a/pjsip/src/pjsua2/media.cpp +++ b/pjsip/src/pjsua2/media.cpp @@ -1830,7 +1830,9 @@ void CodecParam::fromPj(const pjmedia_codec_param ¶m) info.maxBps = param.info.max_bps; info.maxRxFrameSize = param.info.max_rx_frame_size; info.frameLen = param.info.frm_ptime; + info.frameLenDenum = param.info.frm_ptime_denum; info.encFrameLen = param.info.enc_ptime; + info.encFrameLenDenum = param.info.enc_ptime_denum; info.pcmBitsPerSample = param.info.pcm_bits_per_sample; info.pt = param.info.pt; info.fmtId = param.info.fmt_id; @@ -1860,7 +1862,9 @@ pjmedia_codec_param CodecParam::toPj() const param.info.max_bps= (pj_uint32_t)info.maxBps; param.info.max_rx_frame_size = info.maxRxFrameSize; param.info.frm_ptime = (pj_uint16_t)info.frameLen; + param.info.frm_ptime_denum = (pj_uint16_t)info.frameLenDenum; param.info.enc_ptime = (pj_uint16_t)info.encFrameLen; + param.info.enc_ptime_denum = (pj_uint16_t)info.encFrameLenDenum; param.info.pcm_bits_per_sample = (pj_uint8_t)info.pcmBitsPerSample; param.info.pt = (pj_uint8_t)info.pt; param.info.fmt_id = info.fmtId; @@ -1887,6 +1891,7 @@ pjmedia_codec_opus_config CodecOpusConfig::toPj() const config.sample_rate = sample_rate; config.channel_cnt = channel_cnt; config.frm_ptime = frm_ptime; + config.frm_ptime_denum = frm_ptime_denum; config.bit_rate = bit_rate; config.packet_loss = packet_loss; config.complexity = complexity; @@ -1900,6 +1905,7 @@ void CodecOpusConfig::fromPj(const pjmedia_codec_opus_config &config) sample_rate = config.sample_rate; channel_cnt = config.channel_cnt; frm_ptime = config.frm_ptime; + frm_ptime_denum = config.frm_ptime_denum; bit_rate = config.bit_rate; packet_loss = config.packet_loss; complexity = config.complexity;