duplicator-monitor-capture.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. #include <windows.h>
  2. #include <obs-module.h>
  3. #include <util/dstr.h>
  4. #include "cursor-capture.h"
  5. #define do_log(level, format, ...) \
  6. blog(level, "[duplicator-monitor-capture: '%s'] " format, \
  7. obs_source_get_name(capture->source), ##__VA_ARGS__)
  8. #define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
  9. #define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
  10. #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
  11. #define TEXT_MONITOR_CAPTURE obs_module_text("MonitorCapture")
  12. #define TEXT_CAPTURE_CURSOR obs_module_text("CaptureCursor")
  13. #define TEXT_COMPATIBILITY obs_module_text("Compatibility")
  14. #define TEXT_MONITOR obs_module_text("Monitor")
  15. #define TEXT_PRIMARY_MONITOR obs_module_text("PrimaryMonitor")
  16. #define RESET_INTERVAL_SEC 3.0f
  17. struct duplicator_capture {
  18. obs_source_t *source;
  19. int monitor;
  20. bool capture_cursor;
  21. long x;
  22. long y;
  23. int rot;
  24. uint32_t width;
  25. uint32_t height;
  26. gs_duplicator_t *duplicator;
  27. float reset_timeout;
  28. struct cursor_data cursor_data;
  29. };
  30. /* ------------------------------------------------------------------------- */
  31. static inline void update_settings(struct duplicator_capture *capture,
  32. obs_data_t *settings)
  33. {
  34. capture->monitor = (int)obs_data_get_int(settings, "monitor");
  35. capture->capture_cursor = obs_data_get_bool(settings, "capture_cursor");
  36. obs_enter_graphics();
  37. gs_duplicator_destroy(capture->duplicator);
  38. capture->duplicator = gs_duplicator_create(capture->monitor);
  39. capture->width = 0;
  40. capture->height = 0;
  41. capture->x = 0;
  42. capture->y = 0;
  43. capture->rot = 0;
  44. capture->reset_timeout = 0.0f;
  45. obs_leave_graphics();
  46. }
  47. /* ------------------------------------------------------------------------- */
  48. static const char *duplicator_capture_getname(void)
  49. {
  50. return TEXT_MONITOR_CAPTURE;
  51. }
  52. static void duplicator_capture_destroy(void *data)
  53. {
  54. struct duplicator_capture *capture = data;
  55. obs_enter_graphics();
  56. gs_duplicator_destroy(capture->duplicator);
  57. cursor_data_free(&capture->cursor_data);
  58. obs_leave_graphics();
  59. bfree(capture);
  60. }
  61. static void duplicator_capture_defaults(obs_data_t *settings)
  62. {
  63. obs_data_set_default_int(settings, "monitor", 0);
  64. obs_data_set_default_bool(settings, "capture_cursor", true);
  65. }
  66. static void duplicator_capture_update(void *data, obs_data_t *settings)
  67. {
  68. struct duplicator_capture *mc = data;
  69. update_settings(mc, settings);
  70. }
  71. static void *duplicator_capture_create(obs_data_t *settings,
  72. obs_source_t *source)
  73. {
  74. struct duplicator_capture *capture;
  75. capture = bzalloc(sizeof(struct duplicator_capture));
  76. capture->source = source;
  77. update_settings(capture, settings);
  78. return capture;
  79. }
  80. static void reset_capture_data(struct duplicator_capture *capture)
  81. {
  82. struct gs_monitor_info monitor_info = {0};
  83. gs_texture_t *texture = gs_duplicator_get_texture(capture->duplicator);
  84. gs_get_duplicator_monitor_info(capture->monitor, &monitor_info);
  85. capture->width = gs_texture_get_width(texture);
  86. capture->height = gs_texture_get_height(texture);
  87. capture->x = monitor_info.x;
  88. capture->y = monitor_info.y;
  89. capture->rot = monitor_info.rotation_degrees;
  90. }
  91. static void duplicator_capture_tick(void *data, float seconds)
  92. {
  93. struct duplicator_capture *capture = data;
  94. if (!obs_source_showing(capture->source))
  95. return;
  96. obs_enter_graphics();
  97. if (!capture->duplicator) {
  98. capture->reset_timeout += seconds;
  99. if (capture->reset_timeout >= RESET_INTERVAL_SEC) {
  100. capture->duplicator =
  101. gs_duplicator_create(capture->monitor);
  102. capture->reset_timeout = 0.0f;
  103. }
  104. }
  105. if (!!capture->duplicator) {
  106. if (capture->capture_cursor)
  107. cursor_capture(&capture->cursor_data);
  108. if (!gs_duplicator_update_frame(capture->duplicator)) {
  109. gs_duplicator_destroy(capture->duplicator);
  110. capture->duplicator = NULL;
  111. capture->width = 0;
  112. capture->height = 0;
  113. capture->x = 0;
  114. capture->y = 0;
  115. capture->rot = 0;
  116. capture->reset_timeout = 0.0f;
  117. } else if (capture->width == 0) {
  118. reset_capture_data(capture);
  119. }
  120. }
  121. obs_leave_graphics();
  122. UNUSED_PARAMETER(seconds);
  123. }
  124. static uint32_t duplicator_capture_width(void *data)
  125. {
  126. struct duplicator_capture *capture = data;
  127. return capture->rot % 180 == 0 ? capture->width : capture->height;
  128. }
  129. static uint32_t duplicator_capture_height(void *data)
  130. {
  131. struct duplicator_capture *capture = data;
  132. return capture->rot % 180 == 0 ? capture->height : capture->width;
  133. }
  134. static void draw_cursor(struct duplicator_capture *capture)
  135. {
  136. cursor_draw(&capture->cursor_data, -capture->x, -capture->y,
  137. 1.0f, 1.0f,
  138. capture->rot % 180 == 0 ? capture->width : capture->height,
  139. capture->rot % 180 == 0 ? capture->height : capture->width);
  140. }
  141. static void duplicator_capture_render(void *data, gs_effect_t *effect)
  142. {
  143. struct duplicator_capture *capture = data;
  144. gs_texture_t *texture;
  145. int rot;
  146. if (!capture->duplicator)
  147. return;
  148. texture = gs_duplicator_get_texture(capture->duplicator);
  149. if (!texture)
  150. return;
  151. effect = obs_get_opaque_effect();
  152. rot = capture->rot;
  153. while (gs_effect_loop(effect, "Draw")) {
  154. if (rot != 0) {
  155. float x = 0.0f;
  156. float y = 0.0f;
  157. switch (rot) {
  158. case 90:
  159. x = (float)capture->height;
  160. break;
  161. case 180:
  162. x = (float)capture->width;
  163. y = (float)capture->height;
  164. break;
  165. case 270:
  166. y = (float)capture->width;
  167. break;
  168. }
  169. gs_matrix_push();
  170. gs_matrix_translate3f(x, y, 0.0f);
  171. gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD((float)rot));
  172. }
  173. obs_source_draw(texture, 0, 0, 0, 0, false);
  174. if (rot != 0)
  175. gs_matrix_pop();
  176. }
  177. if (capture->capture_cursor) {
  178. effect = obs_get_default_effect();
  179. while (gs_effect_loop(effect, "Draw")) {
  180. draw_cursor(capture);
  181. }
  182. }
  183. }
  184. static bool get_monitor_props(obs_property_t *monitor_list, int monitor_idx)
  185. {
  186. struct dstr monitor_desc = {0};
  187. struct gs_monitor_info info;
  188. if (!gs_get_duplicator_monitor_info(monitor_idx, &info))
  189. return false;
  190. dstr_catf(&monitor_desc, "%s %d: %ldx%ld @ %ld,%ld",
  191. TEXT_MONITOR, monitor_idx,
  192. info.cx, info.cy, info.x, info.y);
  193. obs_property_list_add_int(monitor_list, monitor_desc.array,
  194. monitor_idx);
  195. dstr_free(&monitor_desc);
  196. return true;
  197. }
  198. static obs_properties_t *duplicator_capture_properties(void *unused)
  199. {
  200. int monitor_idx = 0;
  201. UNUSED_PARAMETER(unused);
  202. obs_properties_t *props = obs_properties_create();
  203. obs_property_t *monitors = obs_properties_add_list(props,
  204. "monitor", TEXT_MONITOR,
  205. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
  206. obs_properties_add_bool(props, "capture_cursor", TEXT_CAPTURE_CURSOR);
  207. obs_enter_graphics();
  208. while (get_monitor_props(monitors, monitor_idx++));
  209. obs_leave_graphics();
  210. return props;
  211. }
  212. struct obs_source_info duplicator_capture_info = {
  213. .id = "monitor_capture",
  214. .type = OBS_SOURCE_TYPE_INPUT,
  215. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
  216. .get_name = duplicator_capture_getname,
  217. .create = duplicator_capture_create,
  218. .destroy = duplicator_capture_destroy,
  219. .video_render = duplicator_capture_render,
  220. .video_tick = duplicator_capture_tick,
  221. .update = duplicator_capture_update,
  222. .get_width = duplicator_capture_width,
  223. .get_height = duplicator_capture_height,
  224. .get_defaults = duplicator_capture_defaults,
  225. .get_properties = duplicator_capture_properties
  226. };