gpu-delay.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #include <obs-module.h>
  2. #include <util/circlebuf.h>
  3. #define S_DELAY_MS "delay_ms"
  4. #define T_DELAY_MS obs_module_text("DelayMs")
  5. struct frame {
  6. gs_texrender_t *render;
  7. uint64_t ts;
  8. };
  9. struct gpu_delay_filter_data {
  10. obs_source_t *context;
  11. struct circlebuf frames;
  12. uint64_t delay_ns;
  13. uint64_t interval_ns;
  14. uint32_t cx;
  15. uint32_t cy;
  16. bool target_valid;
  17. bool processed_frame;
  18. };
  19. static const char *gpu_delay_filter_get_name(void *unused)
  20. {
  21. UNUSED_PARAMETER(unused);
  22. return obs_module_text("GPUDelayFilter");
  23. }
  24. static void free_textures(struct gpu_delay_filter_data *f)
  25. {
  26. obs_enter_graphics();
  27. while (f->frames.size) {
  28. struct frame frame;
  29. circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
  30. gs_texrender_destroy(frame.render);
  31. }
  32. circlebuf_free(&f->frames);
  33. obs_leave_graphics();
  34. }
  35. static size_t num_frames(struct circlebuf *buf)
  36. {
  37. return buf->size / sizeof(struct frame);
  38. }
  39. static void update_interval(struct gpu_delay_filter_data *f,
  40. uint64_t new_interval_ns)
  41. {
  42. if (!f->target_valid) {
  43. free_textures(f);
  44. return;
  45. }
  46. f->interval_ns = new_interval_ns;
  47. size_t num = (size_t)(f->delay_ns / new_interval_ns);
  48. if (num > num_frames(&f->frames)) {
  49. size_t prev_num = num_frames(&f->frames);
  50. obs_enter_graphics();
  51. circlebuf_upsize(&f->frames, num * sizeof(struct frame));
  52. for (size_t i = prev_num; i < num; i++) {
  53. struct frame *frame =
  54. circlebuf_data(&f->frames, i * sizeof(*frame));
  55. frame->render =
  56. gs_texrender_create(GS_RGBA, GS_ZS_NONE);
  57. }
  58. obs_leave_graphics();
  59. } else if (num < num_frames(&f->frames)) {
  60. obs_enter_graphics();
  61. while (num_frames(&f->frames) > num) {
  62. struct frame frame;
  63. circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
  64. gs_texrender_destroy(frame.render);
  65. }
  66. obs_leave_graphics();
  67. }
  68. }
  69. static inline void check_interval(struct gpu_delay_filter_data *f)
  70. {
  71. struct obs_video_info ovi = {0};
  72. uint64_t interval_ns;
  73. obs_get_video_info(&ovi);
  74. interval_ns =
  75. (uint64_t)ovi.fps_den * 1000000000ULL / (uint64_t)ovi.fps_num;
  76. if (interval_ns != f->interval_ns)
  77. update_interval(f, interval_ns);
  78. }
  79. static inline void reset_textures(struct gpu_delay_filter_data *f)
  80. {
  81. f->interval_ns = 0;
  82. free_textures(f);
  83. check_interval(f);
  84. }
  85. static inline bool check_size(struct gpu_delay_filter_data *f)
  86. {
  87. obs_source_t *target = obs_filter_get_target(f->context);
  88. uint32_t cx;
  89. uint32_t cy;
  90. f->target_valid = !!target;
  91. if (!f->target_valid)
  92. return true;
  93. cx = obs_source_get_base_width(target);
  94. cy = obs_source_get_base_height(target);
  95. f->target_valid = !!cx && !!cy;
  96. if (!f->target_valid)
  97. return true;
  98. if (cx != f->cx || cy != f->cy) {
  99. f->cx = cx;
  100. f->cy = cy;
  101. reset_textures(f);
  102. return true;
  103. }
  104. return false;
  105. }
  106. static void gpu_delay_filter_update(void *data, obs_data_t *s)
  107. {
  108. struct gpu_delay_filter_data *f = data;
  109. f->delay_ns = (uint64_t)obs_data_get_int(s, S_DELAY_MS) * 1000000ULL;
  110. /* full reset */
  111. f->cx = 0;
  112. f->cy = 0;
  113. f->interval_ns = 0;
  114. free_textures(f);
  115. }
  116. static obs_properties_t *gpu_delay_filter_properties(void *data)
  117. {
  118. obs_properties_t *props = obs_properties_create();
  119. obs_property_t *p = obs_properties_add_int(props, S_DELAY_MS,
  120. T_DELAY_MS, 0, 500, 1);
  121. obs_property_int_set_suffix(p, " ms");
  122. UNUSED_PARAMETER(data);
  123. return props;
  124. }
  125. static void *gpu_delay_filter_create(obs_data_t *settings,
  126. obs_source_t *context)
  127. {
  128. struct gpu_delay_filter_data *f = bzalloc(sizeof(*f));
  129. f->context = context;
  130. obs_source_update(context, settings);
  131. return f;
  132. }
  133. static void gpu_delay_filter_destroy(void *data)
  134. {
  135. struct gpu_delay_filter_data *f = data;
  136. free_textures(f);
  137. bfree(f);
  138. }
  139. static void gpu_delay_filter_tick(void *data, float t)
  140. {
  141. UNUSED_PARAMETER(t);
  142. struct gpu_delay_filter_data *f = data;
  143. f->processed_frame = false;
  144. if (check_size(f))
  145. return;
  146. check_interval(f);
  147. }
  148. static void draw_frame(struct gpu_delay_filter_data *f)
  149. {
  150. struct frame frame;
  151. circlebuf_peek_front(&f->frames, &frame, sizeof(frame));
  152. gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
  153. gs_texture_t *tex = gs_texrender_get_texture(frame.render);
  154. if (tex) {
  155. gs_eparam_t *image =
  156. gs_effect_get_param_by_name(effect, "image");
  157. gs_effect_set_texture(image, tex);
  158. while (gs_effect_loop(effect, "Draw"))
  159. gs_draw_sprite(tex, 0, f->cx, f->cy);
  160. }
  161. }
  162. static void gpu_delay_filter_render(void *data, gs_effect_t *effect)
  163. {
  164. struct gpu_delay_filter_data *f = data;
  165. obs_source_t *target = obs_filter_get_target(f->context);
  166. obs_source_t *parent = obs_filter_get_parent(f->context);
  167. if (!f->target_valid || !target || !parent || !f->frames.size) {
  168. obs_source_skip_video_filter(f->context);
  169. return;
  170. }
  171. if (f->processed_frame) {
  172. draw_frame(f);
  173. return;
  174. }
  175. struct frame frame;
  176. circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
  177. gs_texrender_reset(frame.render);
  178. gs_blend_state_push();
  179. gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
  180. if (gs_texrender_begin(frame.render, f->cx, f->cy)) {
  181. uint32_t parent_flags = obs_source_get_output_flags(target);
  182. bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
  183. bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;
  184. struct vec4 clear_color;
  185. vec4_zero(&clear_color);
  186. gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
  187. gs_ortho(0.0f, (float)f->cx, 0.0f, (float)f->cy, -100.0f,
  188. 100.0f);
  189. if (target == parent && !custom_draw && !async)
  190. obs_source_default_render(target);
  191. else
  192. obs_source_video_render(target);
  193. gs_texrender_end(frame.render);
  194. }
  195. gs_blend_state_pop();
  196. circlebuf_push_back(&f->frames, &frame, sizeof(frame));
  197. draw_frame(f);
  198. f->processed_frame = true;
  199. UNUSED_PARAMETER(effect);
  200. }
  201. struct obs_source_info gpu_delay_filter = {
  202. .id = "gpu_delay",
  203. .type = OBS_SOURCE_TYPE_FILTER,
  204. .output_flags = OBS_SOURCE_VIDEO,
  205. .get_name = gpu_delay_filter_get_name,
  206. .create = gpu_delay_filter_create,
  207. .destroy = gpu_delay_filter_destroy,
  208. .update = gpu_delay_filter_update,
  209. .get_properties = gpu_delay_filter_properties,
  210. .video_tick = gpu_delay_filter_tick,
  211. .video_render = gpu_delay_filter_render,
  212. };