فهرست منبع

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 10 سال پیش
والد
کامیت
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 */