bool obs_transition_audio_render(obs_source_t *transition, uint64_t *ts_out, struct obs_source_audio_mix *audio, uint32_t mixers, size_t channels, size_t sample_rate, obs_transition_audio_mix_callback_t mix_a, obs_transition_audio_mix_callback_t mix_b) { obs_source_t *sources[2]; struct transition_state state = {0}; bool stopped = false; uint64_t min_ts; float t; if (!transition_valid(transition, "obs_transition_audio_render")) return false; lock_transition(transition); sources[0] = transition->transition_sources[0]; sources[1] = transition->transition_sources[1]; min_ts = calc_min_ts(sources); if (min_ts) { t = calc_time(transition, min_ts); if (t >= 1.0f && transition->transitioning_audio) stopped = stop_audio(transition); sources[0] = transition->transition_sources[0]; sources[1] = transition->transition_sources[1]; min_ts = calc_min_ts(sources); if (min_ts) copy_transition_state(transition, &state); } else if (transition->transitioning_audio) { stopped = stop_audio(transition); } unlock_transition(transition); if (min_ts) { if (state.transitioning_audio) { if (state.s[0]) process_audio(transition, state.s[0], audio, min_ts, mixers, channels, sample_rate, mix_a); if (state.s[1]) process_audio(transition, state.s[1], audio, min_ts, mixers, channels, sample_rate, mix_b); } else if (state.s[0]) { memcpy(audio->output[0].data[0], state.s[0]->audio_output_buf[0][0], TOTAL_AUDIO_SIZE); } obs_source_release(state.s[0]); obs_source_release(state.s[1]); } if (stopped) obs_source_dosignal(transition, "source_transition_stop", "transition_stop"); *ts_out = min_ts; return !!min_ts; }
bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in, uint64_t *out_ts, uint32_t mixers, struct audio_output_data *mixes) { struct obs_core_data *data = &obs->data; struct obs_core_audio *audio = &obs->audio; struct obs_source *source; size_t sample_rate = audio_output_get_sample_rate(audio->audio); size_t channels = audio_output_get_channels(audio->audio); struct ts_info ts = {start_ts_in, end_ts_in}; size_t audio_size; uint64_t min_ts; da_resize(audio->render_order, 0); da_resize(audio->root_nodes, 0); circlebuf_push_back(&audio->buffered_timestamps, &ts, sizeof(ts)); circlebuf_peek_front(&audio->buffered_timestamps, &ts, sizeof(ts)); min_ts = ts.start; audio_size = AUDIO_OUTPUT_FRAMES * sizeof(float); #if DEBUG_AUDIO == 1 blog(LOG_DEBUG, "ts %llu-%llu", ts.start, ts.end); #endif /* ------------------------------------------------ */ /* build audio render order * NOTE: these are source channels, not audio channels */ for (uint32_t i = 0; i < MAX_CHANNELS; i++) { obs_source_t *source = obs_get_output_source(i); if (source) { obs_source_enum_active_tree(source, push_audio_tree, audio); push_audio_tree(NULL, source, audio); da_push_back(audio->root_nodes, &source); obs_source_release(source); } } pthread_mutex_lock(&data->audio_sources_mutex); source = data->first_audio_source; while (source) { push_audio_tree(NULL, source, audio); source = (struct obs_source*)source->next_audio_source; } pthread_mutex_unlock(&data->audio_sources_mutex); /* ------------------------------------------------ */ /* render audio data */ for (size_t i = 0; i < audio->render_order.num; i++) { obs_source_t *source = audio->render_order.array[i]; obs_source_audio_render(source, mixers, channels, sample_rate, audio_size); } /* ------------------------------------------------ */ /* get minimum audio timestamp */ pthread_mutex_lock(&data->audio_sources_mutex); calc_min_ts(data, sample_rate, &min_ts); pthread_mutex_unlock(&data->audio_sources_mutex); /* ------------------------------------------------ */ /* if a source has gone backward in time, buffer */ if (min_ts < ts.start) add_audio_buffering(audio, sample_rate, &ts, min_ts); /* ------------------------------------------------ */ /* mix audio */ if (!audio->buffering_wait_ticks) { for (size_t i = 0; i < audio->root_nodes.num; i++) { obs_source_t *source = audio->root_nodes.array[i]; if (source->audio_pending) continue; pthread_mutex_lock(&source->audio_buf_mutex); if (source->audio_output_buf[0][0] && source->audio_ts) mix_audio(mixes, source, channels, sample_rate, &ts); pthread_mutex_unlock(&source->audio_buf_mutex); } } /* ------------------------------------------------ */ /* discard audio */ pthread_mutex_lock(&data->audio_sources_mutex); source = data->first_audio_source; while (source) { pthread_mutex_lock(&source->audio_buf_mutex); discard_audio(audio, source, channels, sample_rate, &ts); pthread_mutex_unlock(&source->audio_buf_mutex); source = (struct obs_source*)source->next_audio_source; } pthread_mutex_unlock(&data->audio_sources_mutex); /* ------------------------------------------------ */ /* release audio sources */ release_audio_sources(audio); circlebuf_pop_front(&audio->buffered_timestamps, NULL, sizeof(ts)); *out_ts = ts.start; if (audio->buffering_wait_ticks) { audio->buffering_wait_ticks--; return false; } UNUSED_PARAMETER(param); return true; }