libobs: Store circular audio buffer on source itself (skip)

(Note: This commit breaks libobs compilation.  Skip if bisecting)

Removes audio lines and stores the circular buffer for the audio on the
source itself.
This commit is contained in:
jp9000 2015-12-17 04:28:35 -08:00
parent f73bbe6746
commit bc0b85cb79
4 changed files with 140 additions and 16 deletions

View file

@ -492,6 +492,7 @@ struct obs_source {
volatile bool timing_set;
volatile uint64_t timing_adjust;
uint64_t next_audio_ts_min;
uint64_t next_audio_sys_ts_min;
uint64_t last_frame_ts;
uint64_t last_sys_timestamp;
bool async_rendered;
@ -501,9 +502,12 @@ struct obs_source {
bool muted;
struct obs_source *next_audio_source;
struct obs_source **prev_next_audio_source;
uint64_t audio_ts;
struct circlebuf audio_input_buf[MAX_AUDIO_CHANNELS];
float *audio_output_buf[MAX_AUDIO_MIXES][MAX_AUDIO_CHANNELS];
struct resample_info sample_info;
audio_resampler_t *resampler;
audio_line_t *audio_line;
pthread_mutex_t audio_buf_mutex;
pthread_mutex_t audio_mutex;
struct obs_audio_data audio_data;
size_t audio_storage_size;

View file

@ -116,6 +116,22 @@ const char *obs_source_get_display_name(enum obs_source_type type,
return (info != NULL) ? info->get_name(info->type_data) : NULL;
}
static void allocate_audio_output_buffer(struct obs_source *source)
{
size_t size = sizeof(float) *
AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS * MAX_AUDIO_MIXES;
float *ptr = bzalloc(size);
for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
size_t mix_pos = mix * AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS;
for (size_t i = 0; i < MAX_AUDIO_CHANNELS; i++) {
source->audio_output_buf[mix][i] =
ptr + mix_pos + AUDIO_OUTPUT_FRAMES * i;
}
}
}
/* internal initialization */
bool obs_source_init(struct obs_source *source,
const struct obs_source_info *info)
@ -129,6 +145,7 @@ bool obs_source_init(struct obs_source *source,
pthread_mutex_init_value(&source->filter_mutex);
pthread_mutex_init_value(&source->async_mutex);
pthread_mutex_init_value(&source->audio_mutex);
pthread_mutex_init_value(&source->audio_buf_mutex);
if (pthread_mutexattr_init(&attr) != 0)
return false;
@ -136,19 +153,15 @@ bool obs_source_init(struct obs_source *source,
return false;
if (pthread_mutex_init(&source->filter_mutex, &attr) != 0)
return false;
if (pthread_mutex_init(&source->audio_buf_mutex, NULL) != 0)
return false;
if (pthread_mutex_init(&source->audio_mutex, NULL) != 0)
return false;
if (pthread_mutex_init(&source->async_mutex, NULL) != 0)
return false;
if (info && info->output_flags & OBS_SOURCE_AUDIO) {
source->audio_line = audio_output_create_line(obs->audio.audio,
source->context.name, 0xF);
if (!source->audio_line) {
blog(LOG_ERROR, "Failed to create audio line for "
"source '%s'", source->context.name);
return false;
}
allocate_audio_output_buffer(source);
pthread_mutex_lock(&obs->data.audio_sources_mutex);
@ -397,14 +410,16 @@ void obs_source_destroy(struct obs_source *source)
for (i = 0; i < MAX_AV_PLANES; i++)
bfree(source->audio_data.data[i]);
audio_line_destroy(source->audio_line);
for (i = 0; i < MAX_AUDIO_CHANNELS; i++)
circlebuf_free(&source->audio_input_buf[i]);
audio_resampler_destroy(source->resampler);
bfree(source->audio_output_buf[0][0]);
da_free(source->async_cache);
da_free(source->async_frames);
da_free(source->filters);
pthread_mutex_destroy(&source->filter_mutex);
pthread_mutex_destroy(&source->audio_buf_mutex);
pthread_mutex_destroy(&source->audio_mutex);
pthread_mutex_destroy(&source->async_mutex);
obs_context_data_free(&source->context);
@ -861,14 +876,27 @@ static inline void reset_audio_timing(obs_source_t *source, uint64_t timestamp,
source->timing_adjust = os_time - timestamp;
}
static inline void handle_ts_jump(obs_source_t *source, uint64_t expected,
static void reset_audio_data(obs_source_t *source, uint64_t os_time)
{
for (size_t i = 0; i < MAX_AUDIO_CHANNELS; i++) {
if (source->audio_input_buf[i].size)
circlebuf_pop_front(&source->audio_input_buf[i], NULL,
source->audio_input_buf[i].size);
}
source->audio_ts = os_time;
}
static void handle_ts_jump(obs_source_t *source, uint64_t expected,
uint64_t ts, uint64_t diff, uint64_t os_time)
{
blog(LOG_DEBUG, "Timestamp for source '%s' jumped by '%"PRIu64"', "
"expected value %"PRIu64", input value %"PRIu64,
source->context.name, diff, expected, ts);
pthread_mutex_lock(&source->audio_buf_mutex);
reset_audio_timing(source, ts, os_time);
pthread_mutex_unlock(&source->audio_buf_mutex);
}
static void source_signal_audio_data(obs_source_t *source,
@ -892,28 +920,77 @@ static inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)
return (ts1 < ts2) ? (ts2 - ts1) : (ts1 - ts2);
}
static void source_output_audio_line(obs_source_t *source,
static inline size_t get_buf_placement(audio_t *audio, uint64_t offset)
{
uint32_t sample_rate = audio_output_get_sample_rate(audio);
return (size_t)(offset * (uint64_t)sample_rate / 1000000000ULL);
}
static void source_output_audio_place(obs_source_t *source,
const struct audio_data *in)
{
audio_t *audio = obs->audio.audio;
size_t buf_placement;
size_t channels = audio_output_get_channels(audio);
size_t size = in->frames * sizeof(float);
if (!source->audio_ts || in->timestamp < source->audio_ts)
reset_audio_data(source, in->timestamp);
buf_placement = get_buf_placement(audio,
in->timestamp - source->audio_ts) * sizeof(float);
#if DEBUG_AUDIO == 1
blog(LOG_DEBUG, "frames: %lu, size: %lu, placement: %lu, base_ts: %llu, ts: %llu",
(unsigned long)in->frames,
(unsigned long)source->audio_input_buf[0].size,
(unsigned long)buf_placement,
source->audio_ts,
in->timestamp);
#endif
for (size_t i = 0; i < channels; i++)
circlebuf_place(&source->audio_input_buf[i], buf_placement,
in->data[i], size);
}
static inline void source_output_audio_push_back(obs_source_t *source,
const struct audio_data *in)
{
audio_t *audio = obs->audio.audio;
size_t channels = audio_output_get_channels(audio);
for (size_t i = 0; i < channels; i++)
circlebuf_push_back(&source->audio_input_buf[i],
in->data[i], in->frames * sizeof(float));
}
static void source_output_audio_data(obs_source_t *source,
const struct audio_data *data)
{
size_t sample_rate = audio_output_get_sample_rate(obs->audio.audio);
struct audio_data in = *data;
uint64_t diff;
uint64_t os_time = os_gettime_ns();
bool using_direct_ts = false;
bool push_back = false;
/* detects 'directly' set timestamps as long as they're within
* a certain threshold */
if (uint64_diff(in.timestamp, os_time) < MAX_TS_VAR) {
source->timing_adjust = 0;
source->timing_set = true;
using_direct_ts = true;
}
} else if (!source->timing_set) {
if (!source->timing_set) {
reset_audio_timing(source, in.timestamp, os_time);
} else if (source->next_audio_ts_min != 0) {
diff = uint64_diff(source->next_audio_ts_min, in.timestamp);
/* smooth audio if within threshold */
if (diff > MAX_TS_VAR)
if (diff > MAX_TS_VAR && !using_direct_ts)
handle_ts_jump(source, source->next_audio_ts_min,
in.timestamp, diff, os_time);
else if (diff < TS_SMOOTHING_THRESHOLD)
@ -928,6 +1005,11 @@ static void source_output_audio_line(obs_source_t *source,
source->present_volume * obs->audio.user_volume *
obs->audio.present_volume;
if (source->next_audio_sys_ts_min == in.timestamp)
push_back = true;
source->next_audio_sys_ts_min = source->next_audio_ts_min +
source->timing_adjust + source->sync_offset;
if (source->push_to_mute_enabled && source->push_to_mute_pressed)
source->push_to_mute_stop_time = os_time +
source->push_to_mute_delay * 1000000;
@ -948,7 +1030,15 @@ static void source_output_audio_line(obs_source_t *source,
if (muted)
in.volume = 0.0f;
audio_line_output(source->audio_line, &in);
pthread_mutex_lock(&source->audio_buf_mutex);
if (push_back)
source_output_audio_push_back(source, &in);
else
source_output_audio_place(source, &in);
pthread_mutex_unlock(&source->audio_buf_mutex);
source_signal_audio_data(source, &in, muted);
}
@ -2126,7 +2216,7 @@ void obs_source_output_audio(obs_source_t *source,
data.timestamp = output->timestamp;
pthread_mutex_lock(&source->audio_mutex);
source_output_audio_line(source, &data);
source_output_audio_data(source, &data);
pthread_mutex_unlock(&source->audio_mutex);
}
@ -3192,3 +3282,25 @@ void *obs_source_get_type_data(obs_source_t *source)
return obs_source_valid(source, "obs_source_get_type_data")
? source->info.type_data : NULL;
}
uint64_t obs_source_get_audio_timestamp(const obs_source_t *source)
{
return obs_source_valid(source, "obs_source_get_audio_timestamp") ?
source->audio_ts : 0;
}
void obs_source_get_audio_mix(const obs_source_t *source,
struct obs_source_audio_mix *audio)
{
if (!obs_source_valid(source, "obs_source_get_audio_mix"))
return;
if (!obs_ptr_valid(audio, "audio"))
return;
for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {
for (size_t ch = 0; ch < MAX_AUDIO_CHANNELS; ch++) {
audio->output[mix].data[ch] =
source->audio_output_buf[mix][ch];
}
}
}

View file

@ -108,6 +108,10 @@ enum obs_source_type {
typedef void (*obs_source_enum_proc_t)(obs_source_t *parent,
obs_source_t *child, void *param);
struct obs_source_audio_mix {
struct audio_output_data output[MAX_AUDIO_MIXES];
};
/**
* Source definition structure
*/

View file

@ -986,6 +986,10 @@ EXPORT uint32_t obs_source_get_base_width(obs_source_t *source);
/** Gets the base height for a source (not taking in to account filtering) */
EXPORT uint32_t obs_source_get_base_height(obs_source_t *source);
EXPORT uint64_t obs_source_get_audio_timestamp(const obs_source_t *source);
EXPORT void obs_source_get_audio_mix(const obs_source_t *source,
struct obs_source_audio_mix *audio);
/* ------------------------------------------------------------------------- */
/* Scenes */