فهرست منبع

libobs: Use reference counting with source frames

When a frame is processed by a filter, it comes directly from the
source's video frame cache.  However, if a filter is using or processing
those frames for whatever reason, there would be no guarantee that the
frames would persist during processing, and frames could eventually be
deallocated unexpected, for example when the resolution or format
changes.

So the solution is to implement simple reference counting for the frames
so that the frames will exist until they have been released by any
source or filter that's using them.
jp9000 10 سال پیش
والد
کامیت
26206f6af4
2فایلهای تغییر یافته به همراه40 افزوده شده و 8 حذف شده
  1. 37 8
      libobs/obs-source.c
  2. 3 0
      libobs/obs.h

+ 37 - 8
libobs/obs-source.c

@@ -228,6 +228,12 @@ void obs_source_frame_init(struct obs_source_frame *frame,
 	}
 }
 
+static inline void obs_source_frame_decref(struct obs_source_frame *frame)
+{
+	if (os_atomic_dec_long(&frame->refs) == 0)
+		obs_source_frame_destroy(frame);
+}
+
 void obs_source_destroy(struct obs_source *source)
 {
 	size_t i;
@@ -256,7 +262,7 @@ void obs_source_destroy(struct obs_source *source)
 		obs_source_filter_remove(source, source->filters.array[0]);
 
 	for (i = 0; i < source->async_cache.num; i++)
-		obs_source_frame_destroy(source->async_cache.array[i].frame);
+		obs_source_frame_decref(source->async_cache.array[i].frame);
 
 	gs_enter_context(obs->video.graphics);
 	gs_texrender_destroy(source->async_convert_texrender);
@@ -1463,7 +1469,7 @@ static inline bool async_texture_changed(struct obs_source *source,
 static inline void free_async_cache(struct obs_source *source)
 {
 	for (size_t i = 0; i < source->async_cache.num; i++)
-		obs_source_frame_destroy(source->async_cache.array[i].frame);
+		obs_source_frame_decref(source->async_cache.array[i].frame);
 
 	da_resize(source->async_cache, 0);
 	da_resize(source->async_frames, 0);
@@ -1503,6 +1509,7 @@ static inline struct obs_source_frame *cache_video(struct obs_source *source,
 				frame->width, frame->height);
 		new_af.frame = new_frame;
 		new_af.used = true;
+		new_frame->refs = 1;
 
 		da_push_back(source->async_cache, &new_af);
 	}
@@ -1531,9 +1538,21 @@ void obs_source_output_video(obs_source_t *source,
 		cache_video(source, frame) : NULL;
 
 	pthread_mutex_lock(&source->filter_mutex);
+
+	if (output)
+		os_atomic_inc_long(&output->refs);
+
 	output = filter_async_video(source, output);
+
+	if (output && os_atomic_dec_long(&output->refs) == 0) {
+		obs_source_frame_destroy(output);
+		output = NULL;
+	}
+
 	pthread_mutex_unlock(&source->filter_mutex);
 
+	/* ------------------------------------------- */
+
 	if (output) {
 		pthread_mutex_lock(&source->async_mutex);
 		cycle_frames(source);
@@ -1872,6 +1891,8 @@ struct obs_source_frame *obs_source_get_frame(obs_source_t *source)
 	if (frame) {
 		source->timing_adjust = sys_time - frame->timestamp;
 		source->timing_set = true;
+
+		os_atomic_inc_long(&frame->refs);
 	}
 
 unlock:
@@ -1879,18 +1900,26 @@ unlock:
 
 	pthread_mutex_unlock(&source->async_mutex);
 
-	if (frame)
-		obs_source_addref(source);
-
 	return frame;
 }
 
 void obs_source_release_frame(obs_source_t *source,
 		struct obs_source_frame *frame)
 {
-	if (source && frame) {
-		remove_async_frame(source, frame);
-		obs_source_release(source);
+	if (!frame)
+		return;
+
+	if (!source) {
+		obs_source_frame_destroy(frame);
+	} else {
+		pthread_mutex_lock(&source->async_mutex);
+
+		if (os_atomic_dec_long(&frame->refs) == 0)
+			obs_source_frame_destroy(frame);
+		else
+			remove_async_frame(source, frame);
+
+		pthread_mutex_unlock(&source->async_mutex);
 	}
 }
 

+ 3 - 0
libobs/obs.h

@@ -228,6 +228,9 @@ struct obs_source_frame {
 	float               color_range_min[3];
 	float               color_range_max[3];
 	bool                flip;
+
+	/* used internally by libobs */
+	volatile long       refs;
 };
 
 /* ------------------------------------------------------------------------- */