static void process_audio(obs_source_t *transition, obs_source_t *child, struct obs_source_audio_mix *audio, uint64_t min_ts, uint32_t mixers, size_t channels, size_t sample_rate, obs_transition_audio_mix_callback_t mix) { bool valid = child && !child->audio_pending; struct obs_source_audio_mix child_audio; uint64_t ts; size_t pos; if (!valid) return; ts = child->audio_ts; obs_source_get_audio_mix(child, &child_audio); for (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) { struct audio_output_data *output = &audio->output[mix_idx]; struct audio_output_data *input = &child_audio.output[mix_idx]; if ((mixers & (1 << mix_idx)) == 0) continue; pos = (size_t)ns_to_audio_frames(sample_rate, ts - min_ts); for (size_t ch = 0; ch < channels; ch++) { float *out = output->data[ch]; float *in = input->data[ch]; mix_child(transition, out + pos, in, AUDIO_OUTPUT_FRAMES - pos, sample_rate, ts, mix); } } }
static void add_audio_buffering(struct obs_core_audio *audio, size_t sample_rate, struct ts_info *ts, uint64_t min_ts) { struct ts_info new_ts; uint64_t offset; uint64_t frames; int ticks; if (audio->total_buffering_ticks == MAX_BUFFERING_TICKS) return; if (!audio->buffering_wait_ticks) audio->buffered_ts = ts->start; offset = ts->start - min_ts; frames = ns_to_audio_frames(sample_rate, offset); ticks = (int)((frames + AUDIO_OUTPUT_FRAMES - 1) / AUDIO_OUTPUT_FRAMES); audio->total_buffering_ticks += ticks; if (audio->total_buffering_ticks >= MAX_BUFFERING_TICKS) { ticks -= audio->total_buffering_ticks - MAX_BUFFERING_TICKS; audio->total_buffering_ticks = MAX_BUFFERING_TICKS; blog(LOG_WARNING, "Max audio buffering reached!"); } blog(LOG_INFO, "adding %d ticks of buffering, total buffering is " "now %d", ticks, audio->total_buffering_ticks); #if DEBUG_AUDIO == 1 blog(LOG_DEBUG, "min_ts (%"PRIu64") < start timestamp " "(%"PRIu64")", min_ts, ts->start); blog(LOG_DEBUG, "old buffered ts: %"PRIu64"-%"PRIu64, ts->start, ts->end); #endif new_ts.start = audio->buffered_ts - audio_frames_to_ns(sample_rate, audio->buffering_wait_ticks * AUDIO_OUTPUT_FRAMES); while (ticks--) { int cur_ticks = ++audio->buffering_wait_ticks; new_ts.end = new_ts.start; new_ts.start = audio->buffered_ts - audio_frames_to_ns( sample_rate, cur_ticks * AUDIO_OUTPUT_FRAMES); #if DEBUG_AUDIO == 1 blog(LOG_DEBUG, "add buffered ts: %"PRIu64"-%"PRIu64, new_ts.start, new_ts.end); #endif circlebuf_push_front(&audio->buffered_timestamps, &new_ts, sizeof(new_ts)); } *ts = new_ts; }
static bool scene_audio_render(void *data, uint64_t *ts_out, struct obs_source_audio_mix *audio_output, uint32_t mixers, size_t channels, size_t sample_rate) { uint64_t timestamp = 0; float *buf = NULL; struct obs_source_audio_mix child_audio; struct obs_scene *scene = data; struct obs_scene_item *item; audio_lock(scene); item = scene->first_item; while (item) { if (!obs_source_audio_pending(item->source)) { uint64_t source_ts = obs_source_get_audio_timestamp(item->source); if (!timestamp || source_ts < timestamp) timestamp = source_ts; } item = item->next; } if (!timestamp) { /* just process all pending audio actions if no audio playing, * otherwise audio actions will just never be processed */ item = scene->first_item; while (item) { process_all_audio_actions(item, sample_rate); item = item->next; } audio_unlock(scene); return false; } item = scene->first_item; while (item) { uint64_t source_ts; size_t pos, count; bool apply_buf; apply_buf = apply_scene_item_volume(item, &buf, timestamp, sample_rate); if (obs_source_audio_pending(item->source)) { item = item->next; continue; } source_ts = obs_source_get_audio_timestamp(item->source); pos = (size_t)ns_to_audio_frames(sample_rate, source_ts - timestamp); count = AUDIO_OUTPUT_FRAMES - pos; if (!apply_buf && !item->visible) { item = item->next; continue; } obs_source_get_audio_mix(item->source, &child_audio); for (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) { if ((mixers & (1 << mix)) == 0) continue; for (size_t ch = 0; ch < channels; ch++) { float *out = audio_output->output[mix].data[ch]; float *in = child_audio.output[mix].data[ch]; if (apply_buf) mix_audio_with_buf(out, in, buf, pos, count); else mix_audio(out, in, pos, count); } } item = item->next; } *ts_out = timestamp; audio_unlock(scene); free(buf); return true; }