瀏覽代碼

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.
jp9000 9 年之前
父節點
當前提交
bc0b85cb79
共有 4 個文件被更改,包括 140 次插入16 次删除
  1. 5 1
      libobs/obs-internal.h
  2. 127 15
      libobs/obs-source.c
  3. 4 0
      libobs/obs-source.h
  4. 4 0
      libobs/obs.h

+ 5 - 1
libobs/obs-internal.h

@@ -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;

+ 127 - 15
libobs/obs-source.c

@@ -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];
+		}
+	}
+}

+ 4 - 0
libobs/obs-source.h

@@ -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
  */

+ 4 - 0
libobs/obs.h

@@ -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 */