gpu-delay.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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 = circlebuf_data(&f->frames,
  54. i * sizeof(*frame));
  55. frame->render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
  56. }
  57. obs_leave_graphics();
  58. } else if (num < num_frames(&f->frames)) {
  59. obs_enter_graphics();
  60. while (num_frames(&f->frames) > num) {
  61. struct frame frame;
  62. circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
  63. gs_texrender_destroy(frame.render);
  64. }
  65. obs_leave_graphics();
  66. }
  67. }
  68. static inline void check_interval(struct gpu_delay_filter_data *f)
  69. {
  70. struct obs_video_info ovi = {0};
  71. uint64_t interval_ns;
  72. obs_get_video_info(&ovi);
  73. interval_ns = (uint64_t)ovi.fps_den * 1000000000ULL /
  74. (uint64_t)ovi.fps_num;
  75. if (interval_ns != f->interval_ns)
  76. update_interval(f, interval_ns);
  77. }
  78. static inline void reset_textures(struct gpu_delay_filter_data *f)
  79. {
  80. f->interval_ns = 0;
  81. free_textures(f);
  82. check_interval(f);
  83. }
  84. static inline bool check_size(struct gpu_delay_filter_data *f)
  85. {
  86. obs_source_t *target = obs_filter_get_target(f->context);
  87. uint32_t cx;
  88. uint32_t cy;
  89. f->target_valid = !!target;
  90. if (!f->target_valid)
  91. return true;
  92. cx = obs_source_get_base_width(target);
  93. cy = obs_source_get_base_height(target);
  94. f->target_valid = !!cx && !!cy;
  95. if (!f->target_valid)
  96. return true;
  97. if (cx != f->cx || cy != f->cy) {
  98. f->cx = cx;
  99. f->cy = cy;
  100. reset_textures(f);
  101. return true;
  102. }
  103. return false;
  104. }
  105. static void gpu_delay_filter_update(void *data, obs_data_t *s)
  106. {
  107. struct gpu_delay_filter_data *f = data;
  108. f->delay_ns = (uint64_t)obs_data_get_int(s, S_DELAY_MS) * 1000000ULL;
  109. /* full reset */
  110. f->cx = 0;
  111. f->cy = 0;
  112. f->interval_ns = 0;
  113. free_textures(f);
  114. }
  115. static obs_properties_t *gpu_delay_filter_properties(void *data)
  116. {
  117. obs_properties_t *props = obs_properties_create();
  118. obs_properties_add_int(props, S_DELAY_MS, T_DELAY_MS, 0, 500, 1);
  119. UNUSED_PARAMETER(data);
  120. return props;
  121. }
  122. static void *gpu_delay_filter_create(obs_data_t *settings, obs_source_t *context)
  123. {
  124. struct gpu_delay_filter_data *f = bzalloc(sizeof(*f));
  125. f->context = context;
  126. obs_source_update(context, settings);
  127. return f;
  128. }
  129. static void gpu_delay_filter_destroy(void *data)
  130. {
  131. struct gpu_delay_filter_data *f = data;
  132. free_textures(f);
  133. bfree(f);
  134. }
  135. static void gpu_delay_filter_tick(void *data, float t)
  136. {
  137. UNUSED_PARAMETER(t);
  138. struct gpu_delay_filter_data *f = data;
  139. f->processed_frame = false;
  140. if (check_size(f))
  141. return;
  142. check_interval(f);
  143. }
  144. static void draw_frame(struct gpu_delay_filter_data *f)
  145. {
  146. struct frame frame;
  147. circlebuf_peek_front(&f->frames, &frame, sizeof(frame));
  148. gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
  149. gs_texture_t *tex = gs_texrender_get_texture(frame.render);
  150. if (tex) {
  151. gs_eparam_t *image =
  152. gs_effect_get_param_by_name(effect, "image");
  153. gs_effect_set_texture(image, tex);
  154. while (gs_effect_loop(effect, "Draw"))
  155. gs_draw_sprite(tex, 0, f->cx, f->cy);
  156. }
  157. }
  158. static void gpu_delay_filter_render(void *data, gs_effect_t *effect)
  159. {
  160. struct gpu_delay_filter_data *f = data;
  161. obs_source_t *target = obs_filter_get_target(f->context);
  162. obs_source_t *parent = obs_filter_get_parent(f->context);
  163. if (!f->target_valid || !target || !parent || !f->frames.size) {
  164. obs_source_skip_video_filter(f->context);
  165. return;
  166. }
  167. if (f->processed_frame) {
  168. draw_frame(f);
  169. return;
  170. }
  171. struct frame frame;
  172. circlebuf_pop_front(&f->frames, &frame, sizeof(frame));
  173. gs_texrender_reset(frame.render);
  174. gs_blend_state_push();
  175. gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
  176. if (gs_texrender_begin(frame.render, f->cx, f->cy)) {
  177. uint32_t parent_flags = obs_source_get_output_flags(target);
  178. bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;
  179. bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;
  180. struct vec4 clear_color;
  181. vec4_set(&clear_color, 0.0f, 0.0f, 0.0f, 1.0f);
  182. gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
  183. gs_ortho(0.0f, (float)f->cx, 0.0f, (float)f->cy,
  184. -100.0f, 100.0f);
  185. if (target == parent && !custom_draw && !async)
  186. obs_source_default_render(target);
  187. else
  188. obs_source_video_render(target);
  189. gs_texrender_end(frame.render);
  190. }
  191. gs_blend_state_pop();
  192. circlebuf_push_back(&f->frames, &frame, sizeof(frame));
  193. draw_frame(f);
  194. f->processed_frame = true;
  195. UNUSED_PARAMETER(effect);
  196. }
  197. struct obs_source_info gpu_delay_filter = {
  198. .id = "gpu_delay",
  199. .type = OBS_SOURCE_TYPE_FILTER,
  200. .output_flags = OBS_SOURCE_VIDEO,
  201. .get_name = gpu_delay_filter_get_name,
  202. .create = gpu_delay_filter_create,
  203. .destroy = gpu_delay_filter_destroy,
  204. .update = gpu_delay_filter_update,
  205. .get_properties = gpu_delay_filter_properties,
  206. .video_tick = gpu_delay_filter_tick,
  207. .video_render = gpu_delay_filter_render
  208. };