📄 source.c
字号:
source_update_toged(src, 0);
}
/* Signal the playout delay to the video tool, so it can lip */
/* sync with us. */
if (adjust_playout && sp->sync_on) {
mbus_qmsgf(sp->mbus_engine, sp->mbus_video_addr, FALSE, "rtp.source.playout", "\"%08lx\" %d",
src->pdbe->ssrc, timestamp_to_ms(ts_abs_diff(playout, now)));
}
/* Update persistent database fields... */
if (e->last_seq > p->seq) {
e->misordered++;
}
e->last_seq = p->seq;
e->last_ts = p->ts;
e->last_arr = now;
e->last_last_transit = e->last_transit;
e->last_transit = transit;
/* This would be a good place to log a histogram of loss */
/* lengths, right? llhist[p->seq - e->last_seq]++ after a */
/* check that this is not the first packet in a talkspurt. */
/* We could then feed it back to the sender in our reception */
/* reports, where it could be used to adapt the redundancy */
/* offset, for example. [csp] */
#ifdef SOURCE_LOG_PLAYOUT
source_playout_log(src, p->ts, now);
#endif /* SOURCE_LOG_PLAYOUT */
src->packets_done++;
xfree(p);
}
source_validate(src);
}
int
source_add_packet (source *src,
rtp_packet *pckt)
{
source_validate(src);
src->byte_count += pckt->data_len;
return pktbuf_enqueue(src->pktbuf, pckt);
}
static void
source_update_bps(source *src, timestamp_t now)
{
timestamp_t delta;
source_validate(src);
if (!ts_valid(src->byte_count_start)) {
src->byte_count_start = now;
src->byte_count = 0;
src->bps = 0.0;
source_validate(src);
return;
}
delta = ts_sub(now, src->byte_count_start);
if (ts_gt(delta, bw_avg_period)) {
double this_est;
this_est = 8.0 * src->byte_count * 1000.0/ timestamp_to_ms(delta);
if (src->bps == 0.0) {
src->bps = this_est;
} else {
src->bps += (this_est - src->bps)/2.0;
}
src->byte_count = 0;
src->byte_count_start = now;
}
source_validate(src);
}
double
source_get_bps(source *src)
{
return src->bps;
}
static int16_t
find_local_match(sample *buffer, uint16_t wstart, uint16_t wlen, uint16_t sstart, uint16_t send, uint16_t channels)
{
uint16_t i,j, i_min = sstart;
uint32_t score = 0, score_min = 0xffffffff;
for (i = sstart; i < send; i += channels) {
score = 0;
for(j = 0; j < wlen; j += channels) {
score += abs((int32_t)buffer[wstart + j] - (int32_t)buffer[i + j]);
}
if (score <= score_min) {
score_min = score;
i_min = i;
}
}
if (score_min / wlen < MATCH_THRESHOLD) {
return i_min / channels;
}
return -1;
}
/* recommend_skew_adjust_dur examines a frame to determine how much audio */
/* to insert or drop. Argument drop is boolean to indicate whether */
/* dropping samples (TRUE) or inserting (FALSE). */
static int32_t
recommend_skew_adjust_dur(media_data *md, int drop, timestamp_t *adjust)
{
int16_t matchlen;
uint32_t rate;
uint16_t channels, samples;
sample *buffer;
int16_t i;
i = md->nrep - 1;
while(i >= 0) {
if (codec_get_native_info(md->rep[i]->id, &rate, &channels)) {
break;
}
i--;
}
assert(i != -1);
buffer = (sample*)md->rep[i]->data;
samples = md->rep[i]->data_len / (sizeof(sample) * channels);
if (drop) {
/* match with first samples of frame start just past
* search window and finish at end of frame
*/
matchlen = find_local_match((sample*)md->rep[i]->data, /* buffer */
0, /* window start */
(uint16_t)(SOURCE_COMPARE_WINDOW_SIZE * channels), /* window len */
(uint16_t)(SOURCE_COMPARE_WINDOW_SIZE * channels), /* search area start */
(uint16_t)((samples - SOURCE_COMPARE_WINDOW_SIZE) * channels), /* search area len */
channels);
if (matchlen == -1) {
return FALSE;
}
} else {
/* match with last samples of frame. Start at the
* start of frame and finish just before search window.
*/
matchlen = find_local_match((sample*)md->rep[i]->data, /* buffer */
(uint16_t)((samples - SOURCE_COMPARE_WINDOW_SIZE) * channels), /* wstart */
(uint16_t)(SOURCE_COMPARE_WINDOW_SIZE * channels), /* wlen */
0, /* sstart */
(uint16_t)((samples - 2 * SOURCE_COMPARE_WINDOW_SIZE) * channels), /* slen */
channels);
/* Want to measure from where frames will overlap. */
if (matchlen == -1) {
return FALSE;
}
matchlen += SOURCE_COMPARE_WINDOW_SIZE;
}
assert(matchlen >= 0);
assert(matchlen <= samples);
*adjust = ts_map32(rate, matchlen);
return TRUE;
}
static void
conceal_dropped_samples(media_data *md, timestamp_t drop_dur)
{
/* We are dropping drop_dur samples and want signal to be */
/* continuous. So we blend samples that would have been played if */
/* they weren't dropped with where signal continues after the drop. */
uint32_t drop_samples;
uint32_t rate;
uint16_t channels;
int32_t i;
sample *new_start, *buf;
for (i = md->nrep - 1; i >= 0; i--) {
if (codec_get_native_info(md->rep[i]->id, &rate, &channels)) {
break;
}
}
assert(i != -1);
buf = (sample*)md->rep[i]->data;
drop_dur = ts_convert(rate, drop_dur);
drop_samples = channels * drop_dur.ticks;
new_start = buf + drop_samples;
audio_blend(buf, new_start, new_start, SOURCE_MERGE_LEN_SAMPLES, channels);
xmemchk();
}
/* Source conceal_inserted_samples blends end of omd with overlap in imd */
/* just before insert takes over. Aims to provide transparent transitition */
/* between added block and old block. */
static void
conceal_inserted_samples(media_data *omd, media_data *imd, timestamp_t insert_dur)
{
uint32_t rate;
uint16_t channels;
uint32_t dst_samples, src_samples, skip;
int32_t i;
sample *dst, *src;
assert(omd != NULL);
assert(imd != NULL);
for (i = omd->nrep - 1; i >= 0; i--) {
if (codec_get_native_info(omd->rep[i]->id, &rate, &channels)) {
break;
}
}
assert(i >= 0);
for (i = imd->nrep - 1; i >= 0; i--) {
if (codec_get_native_info(imd->rep[i]->id, &rate, &channels)) {
break;
}
}
assert(i >= 0);
dst_samples = omd->rep[i]->data_len / sizeof(sample);
dst = ((sample*)omd->rep[i]->data) + dst_samples - SOURCE_MERGE_LEN_SAMPLES * channels;
src_samples = imd->rep[i]->data_len / sizeof(sample);
skip = insert_dur.ticks * channels - SOURCE_MERGE_LEN_SAMPLES;
if (skip > src_samples - SOURCE_MERGE_LEN_SAMPLES * channels) {
debug_msg("Clipping insert length\n");
skip = src_samples - SOURCE_MERGE_LEN_SAMPLES * channels;
}
src = ((sample*)imd->rep[i]->data) + skip;
xmemchk();
audio_blend(dst, src, dst, channels * SOURCE_MERGE_LEN_SAMPLES, channels);
xmemchk();
}
/* source_check_buffering is supposed to check amount of audio buffered */
/* corresponds to what we expect from playout so we can think about skew */
/* adjustment. */
int
source_check_buffering(source *src)
{
timestamp_t actual, desired, diff;
source_validate(src);
if (src->post_talkstart_units < 20) {
/* If the source is new(ish) then not enough audio will be */
/* in the playout buffer because it hasn't arrived yet. */
return FALSE;
}
actual = source_get_audio_buffered(src);
desired = source_get_playout_delay(src);
diff = ts_abs_diff(actual, desired);
if (ts_gt(actual, desired) && ts_gt(diff, skew_thresh)) {
src->skew_adjust = diff;
/* We're accumulating audio, their clock faster */
src->skew = SOURCE_SKEW_FAST;
src->skew_cnt++;
source_validate(src);
return TRUE;
} else if (ts_gt(desired, actual)) {
/* We're short of audio, so their clock is slower */
/* Lower bound is much harder than upper bound */
/* since mixer will dry up / repair will start to */
/* be invoked as we decode units late. */
src->skew_adjust = diff;
src->skew = SOURCE_SKEW_SLOW;
source_validate(src);
return TRUE;
}
src->skew = SOURCE_SKEW_NONE;
src->skew_adjust = zero_ts;
source_validate(src);
return FALSE;
}
/* source_skew_adapt exists to shift playout units if source clock appears */
/* to be fast or slow. The media_data unit is here so that it can be */
/* examined to see if it is low energy and adjustment would be okay. Might */
/* want to be more sophisticated and put a silence detector in rather than */
/* static threshold. */
/* */
/* Returns what adaption type occurred. */
static skew_t
source_skew_adapt(source *src, media_data *md, timestamp_t playout)
{
uint32_t i = 0, e = 0, samples = 0;
uint32_t rate;
uint16_t channels;
timestamp_t adjustment, frame_dur;
source_validate(src);
assert(src);
assert(md);
assert(src->skew != SOURCE_SKEW_NONE);
for(i = 0; i < md->nrep; i++) {
if (codec_get_native_info(md->rep[i]->id, &rate, &channels)) {
samples = md->rep[i]->data_len / (channels * sizeof(sample));
e = audio_avg_energy((sample*)md->rep[i]->data, samples * channels, channels);
src->mean_energy = (15 * src->mean_energy + e)/16;
frame_dur = ts_map32(rate, samples);
break;
}
}
if (i == md->nrep) {
/* don't adapt if unit has not been decoded (error) or */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -