📄 source.c
字号:
/*
* FILE: source.c
* AUTHOR(S): Orion Hodson
*
* Layering support added by Tristan Henderson.
*
* Copyright (c) 1999-2001 University College London
* All rights reserved.
*/
#ifndef HIDE_SOURCE_STRINGS
static const char cvsid[] =
"$Id: source.c,v 1.163 2002/03/15 17:57:19 ucacoxh Exp $";
#endif /* HIDE_SOURCE_STRINGS */
#include "config_unix.h"
#include "config_win32.h"
#include "audio_types.h"
#include "codec_types.h"
#include "ts.h"
#include "playout.h"
#include "channel.h"
#include "channel_types.h"
#include "codec.h"
#include "codec_state.h"
#include "converter.h"
#include "audio_util.h"
#include "render_3D.h"
#include "repair.h"
#include "ts.h"
#include "channel_types.h"
#include "pdb.h"
#include "pktbuf.h"
#include "source.h"
#include "debug.h"
#include "util.h"
#include "net_udp.h"
#include "mix.h"
#include "rtp.h"
#include "playout_calc.h"
#include "session.h"
#include "ui_send_stats.h"
#include "auddev.h"
#include "mbus.h"
#define SKEW_ADAPT_THRESHOLD 5000
#define SOURCE_YOUNG_AGE 20
#define NO_TOGED_CONT_FOR_PLAYOUT_RECALC 3
#define SOURCE_COMPARE_WINDOW_SIZE 8
#define SOURCE_MERGE_LEN_SAMPLES SOURCE_COMPARE_WINDOW_SIZE
/* Match threshold is mean abs diff. lower score gives less noise, but less */
/* adaption..., might be better if threshold adapted with how much extra */
/* data we have buffered... */
#define MATCH_THRESHOLD 1200
/* constants for skew adjustment:
SOURCE_SKEW_SLOW - denotes source clock appears slower than ours.
SOURCE_SKEW_FAST - denotes source clock appears faster than ours.
*/
typedef enum { SOURCE_SKEW_SLOW, SOURCE_SKEW_FAST, SOURCE_SKEW_NONE } skew_t;
typedef enum { PLAYOUT_MODE_NORMAL, PLAYOUT_MODE_SPIKE } pmode_t;
typedef struct s_source {
struct s_source *next;
struct s_source *prev;
pdb_entry_t *pdbe; /* persistent database entry */
uint32_t age;
timestamp_t next_played; /* anticipated next unit */
timestamp_t talkstart; /* start of latest talkspurt */
timestamp_t last_repair;
int hold_repair;
uint32_t post_talkstart_units;
uint16_t consec_lost;
uint32_t mean_energy;
struct s_pktbuf *pktbuf;
uint32_t packets_done;
struct s_channel_state *channel_state;
struct s_codec_state_store *codec_states;
struct s_pb *channel;
struct s_pb *media;
struct s_pb_iterator *media_pos;
struct s_converter *converter;
pmode_t playout_mode; /* SPIKE, NORMAL */
timestamp_t spike_var;
/* Fine grained playout buffer adjustment variables. Used in */
/* attempts to correct for clock skew between source and local host. */
skew_t skew;
timestamp_t skew_adjust;
int16_t skew_cnt;
/* Skew stats */
int32_t samples_played;
int32_t samples_added;
/* b/w estimation variables */
uint32_t byte_count;
timestamp_t byte_count_start;
double bps;
/* Playout stats (most in pdb_entry_t) */
u_char toged_cont; /* Toged in a row */
uint16_t toged_mask; /* bitmap hist. of tog */
uint32_t magic; /* For debugging */
} source;
/* A linked list is used for sources and this is fine since we mostly expect */
/* 1 or 2 sources to be simultaneously active and so efficiency is not a */
/* killer. */
typedef struct s_source_list {
source sentinel;
uint16_t nsrcs;
} source_list;
/*****************************************************************************/
/* Source List functions. Source List is used as a container for sources */
/*****************************************************************************/
int
source_list_create(source_list **pplist)
{
source_list *plist = (source_list*)xmalloc(sizeof(source_list));
if (plist != NULL) {
*pplist = plist;
plist->sentinel.next = &plist->sentinel;
plist->sentinel.prev = &plist->sentinel;
plist->nsrcs = 0;
return TRUE;
}
return FALSE;
}
void
source_list_clear(source_list *plist)
{
assert(plist != NULL);
while(plist->sentinel.next != &plist->sentinel) {
source_remove(plist, plist->sentinel.next);
}
}
void
source_list_destroy(source_list **pplist)
{
source_list *plist = *pplist;
source_list_clear(plist);
assert(plist->nsrcs == 0);
xfree(plist);
*pplist = NULL;
}
uint32_t
source_list_source_count(source_list *plist)
{
return plist->nsrcs;
}
source*
source_list_get_source_no(source_list *plist, uint32_t n)
{
source *curr = NULL;
assert(plist != NULL);
if (n < plist->nsrcs) {
curr = plist->sentinel.next;
while(n != 0) {
curr = curr->next;
n--;
}
return curr;
}
return NULL;
}
source*
source_get_by_ssrc(source_list *plist, uint32_t ssrc)
{
source *curr = NULL, *stop = NULL;
curr = plist->sentinel.next;
stop = &plist->sentinel;
while(curr != stop) {
if (curr->pdbe->ssrc == ssrc) {
return curr;
}
curr = curr->next;
}
return NULL;
}
/*****************************************************************************/
/* Timestamp constants and initialization */
/*****************************************************************************/
static timestamp_t zero_ts; /* No time at all :-) */
static timestamp_t keep_source_ts; /* How long source kept after source goes quiet */
static timestamp_t history_ts; /* How much old audio hang onto for repair usage */
static timestamp_t bw_avg_period; /* Average period for bandwidth estimate */
static timestamp_t skew_thresh; /* Significant size b4 consider playout adapt */
static timestamp_t skew_limit; /* Upper bound, otherwise clock reset. */
static timestamp_t transit_reset; /* Period after which new transit time taken */
/* if source has been quiet. */
static timestamp_t transit_jump; /* If transit delta is bigger than this reset */
static timestamp_t spike_jump; /* Packet spike delay threshold (trigger). */
static timestamp_t spike_end; /* Value of var when spike over */
static timestamp_t repair_max_gap; /* Maximum stream gap repair is attempted for. */
static int time_constants_inited = FALSE;
static void
time_constants_init()
{
/* We use these time constants *all* the time. Initialize once */
zero_ts = ts_map32(8000, 0);
keep_source_ts = ts_map32(8000, 24000);
history_ts = ts_map32(8000, 2000);
bw_avg_period = ts_map32(8000, 8000);
skew_thresh = ts_map32(8000, 320);
skew_limit = ts_map32(8000, 4000);
transit_reset = ts_map32(8000, 80000);
transit_jump = ts_map32(8000, 12000);
spike_jump = ts_map32(8000, 3000);
spike_end = ts_map32(8000, 64);
repair_max_gap = ts_map32(8000, 1600); /* 200ms */
time_constants_inited = TRUE;
}
/*****************************************************************************/
/* Source functions. A source is an active audio source. */
/*****************************************************************************/
static void
source_validate(source *s)
{
/* More debugging code... check the invarients of the soure. */
/* This is called from all the routines here... if anything */
/* is trashing the source, this is supposed to detect it. */
assert(s != NULL);
assert(s->magic == 0xface0ff);
#ifdef DEBUG
assert(s->next != NULL);
assert(s->prev != NULL);
assert(s->pdbe != NULL);
assert(s->bps >= 0);
assert((s->skew == SOURCE_SKEW_SLOW) || (s->skew == SOURCE_SKEW_FAST) ||( s->skew == SOURCE_SKEW_NONE));
assert((s->playout_mode == PLAYOUT_MODE_NORMAL) || (s->playout_mode == PLAYOUT_MODE_SPIKE));
assert(ts_valid(s->pdbe->playout));
#endif
}
source*
source_create(source_list *plist,
uint32_t ssrc,
pdb_t *pdb)
{
source *psrc;
int success;
assert(plist != NULL);
assert(source_get_by_ssrc(plist, ssrc) == NULL);
/* Time constant initialization. Nothing to do with source creation */
/* just has to go somewhere before sources might be active, here it */
/* definitely is! */
if (time_constants_inited == FALSE) {
time_constants_init();
}
/* On with the show... */
psrc = (source*)block_alloc(sizeof(source));
if (psrc == NULL) {
return NULL;
}
memset(psrc, 0, sizeof(source));
psrc->magic = 0xface0ff;
if (pdb_item_get(pdb, ssrc, &psrc->pdbe) == FALSE) {
debug_msg("Persistent database item not found\n");
abort();
}
psrc->pdbe->first_mix = 1; /* Used to note nothing mixed anything */
psrc->toged_cont = 0; /* Reset continuous thrown on ground cnt */
psrc->toged_mask = 0;
psrc->channel_state = NULL;
psrc->skew = SOURCE_SKEW_NONE;
psrc->samples_played = 0;
psrc->samples_added = 0;
psrc->spike_var = zero_ts;
psrc->last_repair = zero_ts;
psrc->hold_repair = 0;
/* Allocate channel and media buffers */
success = pb_create(&psrc->channel,
(playoutfreeproc)channel_data_destroy);
if (!success) {
debug_msg("Failed to allocate channel buffer\n");
goto fail_create_channel;
}
success = pb_create(&psrc->media, (playoutfreeproc)media_data_destroy);
if (!success) {
debug_msg("Failed to allocate media buffer\n");
goto fail_create_media;
}
success = pb_iterator_create(psrc->media, &psrc->media_pos);
if (!success) {
debug_msg("Failed to attach iterator to media buffer\n");
goto fail_create_iterator;
}
success = codec_state_store_create(&psrc->codec_states, DECODER);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -