From 6e22ac41b842e2741ec32ac372d28aedfaf4603c Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 26 Mar 2015 22:49:48 -0700 Subject: [PATCH] libobs: Swap async source frames in tick Async frames are only swapping when rendering, or when not visible. This is a flawed design due to the fact that there are certain circumstances where the source is neither visible nor currently rendering. This is what caused a memory leak when scene items were marked as invisible, because if a source has an async child source and decides not to render that source for whatever reason, the child source would not process the async frames at all, and the cache would just grow. To fix this, simply moving the async frame cycle to tick fixes the issue due to the fact that tick is always called regardless of circumstance. --- libobs/obs-internal.h | 1 + libobs/obs-source.c | 63 +++++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 06b765e68..83a645ef3 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -337,6 +337,7 @@ struct obs_source { /* async video data */ gs_texture_t *async_texture; gs_texrender_t *async_convert_texrender; + struct obs_source_frame *cur_async_frame; bool async_gpu_conversion; enum video_format async_format; enum gs_color_format async_texture_format; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 3a3a9c643..eb0271aec 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -616,12 +616,31 @@ void obs_source_deactivate(obs_source_t *source, enum view_type type) } } +static inline struct obs_source_frame *get_closest_frame(obs_source_t *source, + uint64_t sys_time); +static void remove_async_frame(obs_source_t *source, + struct obs_source_frame *frame); + void obs_source_video_tick(obs_source_t *source, float seconds) { bool now_showing, now_active; if (!source) return; + if ((source->info.output_flags & OBS_SOURCE_ASYNC) != 0) { + uint64_t sys_time = os_gettime_ns(); + + pthread_mutex_lock(&source->async_mutex); + if (source->cur_async_frame) { + remove_async_frame(source, source->cur_async_frame); + source->cur_async_frame = NULL; + } + + source->cur_async_frame = get_closest_frame(source, sys_time); + source->last_sys_timestamp = sys_time; + pthread_mutex_unlock(&source->async_mutex); + } + if (source->defer_update) obs_source_deferred_update(source); @@ -1655,20 +1674,17 @@ static inline struct obs_source_frame *cache_video(struct obs_source *source, return new_frame; } -static inline void cycle_frames(struct obs_source *source) -{ - bool not_currently_visible = !source->show_refs || !source->enabled; - - if (source->async_frames.num && not_currently_visible) - ready_async_frame(source, os_gettime_ns()); -} - void obs_source_output_video(obs_source_t *source, const struct obs_source_frame *frame) { if (!source) return; + if (!frame) { + source->async_active = false; + return; + } + struct obs_source_frame *output = !!frame ? cache_video(source, frame) : NULL; @@ -1676,12 +1692,9 @@ void obs_source_output_video(obs_source_t *source, if (output) { pthread_mutex_lock(&source->async_mutex); - cycle_frames(source); da_push_back(source->async_frames, &output); pthread_mutex_unlock(&source->async_mutex); source->async_active = true; - } else { - source->async_active = false; } } @@ -1968,9 +1981,16 @@ static bool ready_async_frame(obs_source_t *source, uint64_t sys_time) static inline struct obs_source_frame *get_closest_frame(obs_source_t *source, uint64_t sys_time) { - if (ready_async_frame(source, sys_time)) { + if (!source->async_frames.num) + return NULL; + + if (!source->last_frame_ts || ready_async_frame(source, sys_time)) { struct obs_source_frame *frame = source->async_frames.array[0]; da_erase(source->async_frames, 0); + + if (!source->last_frame_ts) + source->last_frame_ts = frame->timestamp; + return frame; } @@ -1986,35 +2006,20 @@ static inline struct obs_source_frame *get_closest_frame(obs_source_t *source, struct obs_source_frame *obs_source_get_frame(obs_source_t *source) { struct obs_source_frame *frame = NULL; - uint64_t sys_time; if (!source) return NULL; pthread_mutex_lock(&source->async_mutex); - sys_time = os_gettime_ns(); - - if (!source->async_frames.num) - goto unlock; - - if (!source->last_frame_ts) { - frame = source->async_frames.array[0]; - da_erase(source->async_frames, 0); - - source->last_frame_ts = frame->timestamp; - } else { - frame = get_closest_frame(source, sys_time); - } + frame = source->cur_async_frame; + source->cur_async_frame = NULL; /* reset timing to current system time */ if (frame) { os_atomic_inc_long(&frame->refs); } -unlock: - source->last_sys_timestamp = sys_time; - pthread_mutex_unlock(&source->async_mutex); return frame;