📄 source.c
字号:
/* signal has too much energy */
source_validate(src);
return SOURCE_SKEW_NONE;
}
/* When we are making the adjustment we must shift playout buffers */
/* and timestamps that the source decode process uses. Must be */
/* careful with last repair because it is not valid if no repair has */
/* taken place. */
if (src->skew == SOURCE_SKEW_FAST && src->skew_cnt > 3) {
/* source is fast so we need to bring units forward.
* Should only move forward at most a single unit
* otherwise we might discard something we have not
* classified. */
if (ts_gt(skew_limit, src->skew_adjust)) {
if (recommend_skew_adjust_dur(md, TRUE, &adjustment) == FALSE) {
/* No suitable adjustment found, and */
/* adjustment is not urgent so bail here... */
source_validate(src);
return src->skew;
}
} else {
/* Things are really skewed. We're more than */
/* skew_limit off of where we ought to be. Just */
/* drop a frame and don't worry. */
debug_msg("Dropping Frame\n");
adjustment = ts_div(src->pdbe->frame_dur, 2);
}
if (ts_gt(adjustment, src->skew_adjust) || adjustment.ticks == 0) {
/* adjustment needed is greater than adjustment */
/* period that best matches dropable by signal */
/* matching. */
source_validate(src);
return SOURCE_SKEW_NONE;
}
debug_msg("dropping %d / %d samples\n", adjustment.ticks, src->skew_adjust.ticks);
pb_shift_forward(src->media, adjustment);
pb_shift_forward(src->channel, adjustment);
src->samples_added += adjustment.ticks;
src->pdbe->transit = ts_sub(src->pdbe->transit, adjustment);
src->skew_cnt = 0;
/* avg_transit and last_transit are fine. Difference in */
/* avg_transit and transit triggered this adjustment. */
if (ts_valid(src->last_repair)) {
src->last_repair = ts_sub(src->last_repair, adjustment);
}
src->next_played = ts_sub(src->next_played, adjustment);
/* Remove skew adjustment from estimate of skew outstanding */
if (ts_gt(src->skew_adjust, adjustment)) {
src->skew_adjust = ts_sub(src->skew_adjust, adjustment);
} else {
src->skew = SOURCE_SKEW_NONE;
}
conceal_dropped_samples(md, adjustment);
xmemchk();
return SOURCE_SKEW_FAST;
} else if (src->skew == SOURCE_SKEW_SLOW) {
media_data *fmd;
timestamp_t insert_playout;
xmemchk();
if (recommend_skew_adjust_dur(md, FALSE, &adjustment) == FALSE) {
debug_msg("bad match\n");
source_validate(src);
return src->skew;
}
debug_msg("Insert %d samples\n", adjustment.ticks);
pb_shift_units_back_after(src->media, playout, adjustment);
pb_shift_units_back_after(src->channel, playout, adjustment);
src->pdbe->transit = ts_add(src->pdbe->transit, adjustment);
/* Insert a unit: buffer looks like current frame -> gap of adjustment -> next frame */
media_data_dup(&fmd, md);
insert_playout = ts_add(playout, adjustment);
xmemchk();
if (pb_add(src->media, (u_char*)fmd, sizeof(media_data), insert_playout) == TRUE) {
xmemchk();
conceal_inserted_samples(md, fmd, adjustment);
xmemchk();
} else {
debug_msg("Buffer push back: insert failed\n");
media_data_destroy(&fmd, sizeof(media_data));
}
if (ts_gt(adjustment, src->skew_adjust)) {
src->skew_adjust = zero_ts;
} else {
src->skew_adjust = ts_sub(src->skew_adjust, adjustment);
}
src->samples_added -= adjustment.ticks;
debug_msg("Playout buffer shift back %d samples.\n", adjustment.ticks);
xmemchk();
src->skew = SOURCE_SKEW_NONE;
source_validate(src);
return SOURCE_SKEW_SLOW;
}
source_validate(src);
return SOURCE_SKEW_NONE;
}
static int
source_repair(source *src,
repair_id_t r,
timestamp_t fill_ts)
{
media_data* fill_md, *prev_md;
timestamp_t prev_ts;
uint32_t success, prev_len;
source_validate(src);
/* We repair one unit at a time since it may be all we need */
if (pb_iterator_retreat(src->media_pos) == FALSE) {
/* New packet when source still active, but dry, e.g. new talkspurt */
timestamp_t start, end;
debug_msg("Repair not possible no previous unit!\n");
if (pb_get_start_ts(pb_iterator_get_playout_buffer(src->media_pos),
&start) &&
pb_get_end_ts(pb_iterator_get_playout_buffer(src->media_pos),
&end)) {
debug_msg("Range available [%d - %d] want %d\n",
timestamp_to_ms(start),
timestamp_to_ms(end),
timestamp_to_ms(fill_ts));
}
source_validate(src);
return FALSE;
}
pb_iterator_get_at(src->media_pos,
(u_char**)&prev_md,
&prev_len,
&prev_ts);
media_data_create(&fill_md, 1);
repair(r,
src->consec_lost,
src->codec_states,
prev_md,
fill_md->rep[0]);
success = pb_add(src->media,
(u_char*)fill_md,
sizeof(media_data),
fill_ts);
if (success) {
src->consec_lost++;
src->last_repair = fill_ts;
/* Advance to unit we just added */
pb_iterator_advance(src->media_pos);
debug_msg("Repair added %d\n", timestamp_to_ms(fill_ts));
} else {
/* This should only ever fail at when source changes
* sample rate in less time than playout buffer
* timeout. This should be a very very rare event...
*/
debug_msg("Repair add data failed %d.\n", timestamp_to_ms(fill_ts));
media_data_destroy(&fill_md, sizeof(media_data));
src->consec_lost = 0;
src->hold_repair += 2;
source_validate(src);
return FALSE;
}
source_validate(src);
return TRUE;
}
static int
source_repair_required(source *src, timestamp_t playout)
{
timestamp_t gap;
/* Repair any gap in the audio stream. Conditions for repair: */
/* (a) playout point of unit is further away than expected. */
/* (b) playout point is not too far away (repair burns cycles) */
/* (c) playout does not correspond to new talkspurt (don't */
/* fill between end of last talkspurt and start of next). */
/* NB Use post_talkstart_units as talkspurts maybe longer */
/* than timestamp wrap period and want to repair even if */
/* timestamps wrap. */
/* (d) not start of a talkspurt. */
/* (e) don't have a hold on. */
gap = ts_sub(playout, src->next_played);
if ((ts_gt(gap, zero_ts) && ts_gt(repair_max_gap, gap)) &&
((ts_gt(src->next_played, src->talkstart) &&
ts_gt(playout, src->talkstart)) || src->post_talkstart_units > 100) &&
(src->hold_repair == 0)) {
return TRUE;
}
/* Repair not needed, just maintain loss related variables */
if (src->hold_repair) {
src->hold_repair--;
}
src->consec_lost = 0;
return FALSE;
}
void
source_process(session_t *sp,
source *src,
timestamp_t start_ts, /* Real-world time */
timestamp_t end_ts) /* Real-world time + cushion */
{
media_data *md;
coded_unit *cu;
codec_state *cs;
uint32_t md_len;
timestamp_t playout, step;
uint32_t sample_rate;
uint16_t channels;
int i;
/* Note: src->hold_repair is used to stop repair occuring.
* Occasionally, there is a race condition when the playout
* point is recalculated causing overlap, and when playout
* buffer shift occurs in middle of a loss.
*/
session_validate(sp);
/* The call to source_process_packets() calculates the desired playout */
/* point for each packet and inserts it into the channel decoder input */
/* buffer (src->channel) at the correct time interval. */
source_process_packets(sp, src, start_ts);
if (src->packets_done == 0) {
return;
}
source_validate(src);
/* Split channel coder units up into media units. This takes units from */
/* the channel decoder input buffer (src->channel) and, after decoding, */
/* adds them to the media buffer (src->media). The channel decoder may keep */
/* the units for some time in-between these two buffers e.g. if there is j */
/* a block interleaver, output will not start until a complete block has */
/* been read in. Any intermediate buffer is hidden within the channel */
/* decoder, and is not visible here. */
if (pb_node_count(src->channel)) {
channel_decoder_decode(src->channel_state, src->channel, src->media, end_ts);
}
source_validate(src);
/* The following loop pulls data out of the media buffer (src->media) when */
/* it's time to play it out. It then repairs any gaps in the audio stream, */
/* decodes anything still in encoded form, performs skew adaptation and */
/* mixes the data ready for playout. */
while (ts_gt(end_ts, src->next_played) && pb_iterator_advance(src->media_pos)) {
pb_iterator_get_at(src->media_pos, (u_char**)&md, &md_len, &playout);
if (source_repair_required(src, playout)) {
if (source_repair(src, sp->repair, src->next_played)) {
/* Repair moves media buffer iterator to start of repaired */
/* frames, need to get media iterator position */
int success;
debug_msg("Repair succeeded (% 2d got % 6d exp % 6d talks % 6d)\n",
src->consec_lost,
playout.ticks,
src->next_played.ticks,
src->talkstart.ticks);
success = pb_iterator_get_at(src->media_pos,
(u_char**)&md, &md_len,
&playout);
assert(success);
assert(ts_eq(playout, src->next_played));
}
}
/* At this point, md is the media data at the current playout point. */
/* There may be multiple representations of the data, for example if */
/* we are receiving a stream using redundancy. */
assert(md != NULL);
assert(md_len == sizeof(media_data));
assert(md->nrep < MAX_MEDIA_UNITS && md->nrep > 0);
for(i = 0; i < md->nrep; i++) {
assert(md->rep[i] != NULL);
assert(codec_is_native_coding(md->rep[i]->id) || codec_id_is_valid(md->rep[i]->id));
}
if (ts_gt(playout, end_ts)) {
/* This playout point is after now so stop */
pb_iterator_retreat(src->media_pos);
break;
}
assert(md != NULL);
assert(md_len == sizeof(media_data));
assert(md->nrep < MAX_MEDIA_UNITS && md->nrep > 0);
for(i = 0; i < md->nrep; i++) {
assert(md->rep[i] != NULL);
assert(codec_is_native_coding(md->rep[i]->id) || codec_id_is_valid(md->rep[i]->id));
}
if (!codec_is_native_coding(md->rep[md->nrep - 1]->id)) {
/* If we've got to here, we have no native coding for this unit */
/* We need to decode this unit, may not have to when repair has */
/* been used. */
for(i = 0; i < md->nrep; i++) {
/* If there is a native coding this unit has already */
/* been decoded and this would be a bug. */
assert(code
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -