| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 | 
							- #include <obs-module.h>
 
- #include <util/deque.h>
 
- #include <util/util_uint64.h>
 
- /* NOTE: Delaying audio shouldn't be necessary because the audio subsystem will
 
-  * automatically sync audio to video frames */
 
- /* #define DELAY_AUDIO */
 
- #ifndef SEC_TO_NSEC
 
- #define SEC_TO_NSEC 1000000000ULL
 
- #endif
 
- #ifndef MSEC_TO_NSEC
 
- #define MSEC_TO_NSEC 1000000ULL
 
- #endif
 
- #define SETTING_DELAY_MS "delay_ms"
 
- #define TEXT_DELAY_MS obs_module_text("DelayMs")
 
- struct async_delay_data {
 
- 	obs_source_t *context;
 
- 	/* contains struct obs_source_frame* */
 
- 	struct deque video_frames;
 
- #ifdef DELAY_AUDIO
 
- 	/* stores the audio data */
 
- 	struct deque audio_frames;
 
- 	struct obs_audio_data audio_output;
 
- #endif
 
- 	uint64_t last_video_ts;
 
- 	uint64_t last_audio_ts;
 
- 	uint64_t interval;
 
- 	uint64_t samplerate;
 
- 	bool video_delay_reached;
 
- 	bool audio_delay_reached;
 
- 	bool reset_video;
 
- 	bool reset_audio;
 
- };
 
- static const char *async_delay_filter_name(void *unused)
 
- {
 
- 	UNUSED_PARAMETER(unused);
 
- 	return obs_module_text("AsyncDelayFilter");
 
- }
 
- static void free_video_data(struct async_delay_data *filter, obs_source_t *parent)
 
- {
 
- 	while (filter->video_frames.size) {
 
- 		struct obs_source_frame *frame;
 
- 		deque_pop_front(&filter->video_frames, &frame, sizeof(struct obs_source_frame *));
 
- 		obs_source_release_frame(parent, frame);
 
- 	}
 
- }
 
- #ifdef DELAY_AUDIO
 
- static inline void free_audio_packet(struct obs_audio_data *audio)
 
- {
 
- 	for (size_t i = 0; i < MAX_AV_PLANES; i++)
 
- 		bfree(audio->data[i]);
 
- 	memset(audio, 0, sizeof(*audio));
 
- }
 
- static void free_audio_data(struct async_delay_data *filter)
 
- {
 
- 	while (filter->audio_frames.size) {
 
- 		struct obs_audio_data audio;
 
- 		deque_pop_front(&filter->audio_frames, &audio, sizeof(struct obs_audio_data));
 
- 		free_audio_packet(&audio);
 
- 	}
 
- }
 
- #endif
 
- static void async_delay_filter_update(void *data, obs_data_t *settings)
 
- {
 
- 	struct async_delay_data *filter = data;
 
- 	uint64_t new_interval = (uint64_t)obs_data_get_int(settings, SETTING_DELAY_MS) * MSEC_TO_NSEC;
 
- 	if (new_interval < filter->interval)
 
- 		free_video_data(filter, obs_filter_get_parent(filter->context));
 
- 	filter->reset_audio = true;
 
- 	filter->reset_video = true;
 
- 	filter->interval = new_interval;
 
- 	filter->video_delay_reached = false;
 
- 	filter->audio_delay_reached = false;
 
- }
 
- static void *async_delay_filter_create(obs_data_t *settings, obs_source_t *context)
 
- {
 
- 	struct async_delay_data *filter = bzalloc(sizeof(*filter));
 
- 	struct obs_audio_info oai;
 
- 	filter->context = context;
 
- 	async_delay_filter_update(filter, settings);
 
- 	obs_get_audio_info(&oai);
 
- 	filter->samplerate = oai.samples_per_sec;
 
- 	return filter;
 
- }
 
- static void async_delay_filter_destroy(void *data)
 
- {
 
- 	struct async_delay_data *filter = data;
 
- 	deque_free(&filter->video_frames);
 
- #ifdef DELAY_AUDIO
 
- 	free_audio_packet(&filter->audio_output);
 
- 	deque_free(&filter->audio_frames);
 
- #endif
 
- 	bfree(data);
 
- }
 
- static obs_properties_t *async_delay_filter_properties(void *data)
 
- {
 
- 	obs_properties_t *props = obs_properties_create();
 
- 	obs_property_t *p = obs_properties_add_int(props, SETTING_DELAY_MS, TEXT_DELAY_MS, 0, 20000, 1);
 
- 	obs_property_int_set_suffix(p, " ms");
 
- 	UNUSED_PARAMETER(data);
 
- 	return props;
 
- }
 
- static void async_delay_filter_remove(void *data, obs_source_t *parent)
 
- {
 
- 	struct async_delay_data *filter = data;
 
- 	free_video_data(filter, parent);
 
- #ifdef DELAY_AUDIO
 
- 	free_audio_data(filter);
 
- #endif
 
- }
 
- /* due to the fact that we need timing information to be consistent in order to
 
-  * measure the current interval of data, if there is an unexpected hiccup or
 
-  * jump with the timestamps, reset the cached delay data and start again to
 
-  * ensure that the timing is consistent */
 
- static inline bool is_timestamp_jump(uint64_t ts, uint64_t prev_ts)
 
- {
 
- 	return ts < prev_ts || (ts - prev_ts) > SEC_TO_NSEC;
 
- }
 
- static struct obs_source_frame *async_delay_filter_video(void *data, struct obs_source_frame *frame)
 
- {
 
- 	struct async_delay_data *filter = data;
 
- 	obs_source_t *parent = obs_filter_get_parent(filter->context);
 
- 	struct obs_source_frame *output;
 
- 	uint64_t cur_interval;
 
- 	if (filter->reset_video || is_timestamp_jump(frame->timestamp, filter->last_video_ts)) {
 
- 		free_video_data(filter, parent);
 
- 		filter->video_delay_reached = false;
 
- 		filter->reset_video = false;
 
- 	}
 
- 	filter->last_video_ts = frame->timestamp;
 
- 	deque_push_back(&filter->video_frames, &frame, sizeof(struct obs_source_frame *));
 
- 	deque_peek_front(&filter->video_frames, &output, sizeof(struct obs_source_frame *));
 
- 	cur_interval = frame->timestamp - output->timestamp;
 
- 	if (!filter->video_delay_reached && cur_interval < filter->interval)
 
- 		return NULL;
 
- 	deque_pop_front(&filter->video_frames, NULL, sizeof(struct obs_source_frame *));
 
- 	if (!filter->video_delay_reached)
 
- 		filter->video_delay_reached = true;
 
- 	return output;
 
- }
 
- #ifdef DELAY_AUDIO
 
- static struct obs_audio_data *async_delay_filter_audio(void *data, struct obs_audio_data *audio)
 
- {
 
- 	struct async_delay_data *filter = data;
 
- 	struct obs_audio_data cached = *audio;
 
- 	uint64_t cur_interval;
 
- 	uint64_t duration;
 
- 	uint64_t end_ts;
 
- 	if (filter->reset_audio || is_timestamp_jump(audio->timestamp, filter->last_audio_ts)) {
 
- 		free_audio_data(filter);
 
- 		filter->audio_delay_reached = false;
 
- 		filter->reset_audio = false;
 
- 	}
 
- 	filter->last_audio_ts = audio->timestamp;
 
- 	duration = util_mul_div64(audio->frames, SEC_TO_NSEC, filter->samplerate);
 
- 	end_ts = audio->timestamp + duration;
 
- 	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
 
- 		if (!audio->data[i])
 
- 			break;
 
- 		cached.data[i] = bmemdup(audio->data[i], audio->frames * sizeof(float));
 
- 	}
 
- 	free_audio_packet(&filter->audio_output);
 
- 	deque_push_back(&filter->audio_frames, &cached, sizeof(cached));
 
- 	deque_peek_front(&filter->audio_frames, &cached, sizeof(cached));
 
- 	cur_interval = end_ts - cached.timestamp;
 
- 	if (!filter->audio_delay_reached && cur_interval < filter->interval)
 
- 		return NULL;
 
- 	deque_pop_front(&filter->audio_frames, NULL, sizeof(cached));
 
- 	memcpy(&filter->audio_output, &cached, sizeof(cached));
 
- 	if (!filter->audio_delay_reached)
 
- 		filter->audio_delay_reached = true;
 
- 	return &filter->audio_output;
 
- }
 
- #endif
 
- struct obs_source_info async_delay_filter = {
 
- 	.id = "async_delay_filter",
 
- 	.type = OBS_SOURCE_TYPE_FILTER,
 
- 	.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_ASYNC,
 
- 	.get_name = async_delay_filter_name,
 
- 	.create = async_delay_filter_create,
 
- 	.destroy = async_delay_filter_destroy,
 
- 	.update = async_delay_filter_update,
 
- 	.get_properties = async_delay_filter_properties,
 
- 	.filter_video = async_delay_filter_video,
 
- #ifdef DELAY_AUDIO
 
- 	.filter_audio = async_delay_filter_audio,
 
- #endif
 
- 	.filter_remove = async_delay_filter_remove,
 
- };
 
 
  |