duplicator-monitor-capture.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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. /* clang-format off */
  12. #define TEXT_MONITOR_CAPTURE obs_module_text("MonitorCapture")
  13. #define TEXT_CAPTURE_CURSOR obs_module_text("CaptureCursor")
  14. #define TEXT_COMPATIBILITY obs_module_text("Compatibility")
  15. #define TEXT_MONITOR obs_module_text("Monitor")
  16. #define TEXT_PRIMARY_MONITOR obs_module_text("PrimaryMonitor")
  17. /* clang-format on */
  18. #define RESET_INTERVAL_SEC 3.0f
  19. struct duplicator_capture {
  20. obs_source_t *source;
  21. int monitor;
  22. bool capture_cursor;
  23. bool showing;
  24. long x;
  25. long y;
  26. int rot;
  27. uint32_t width;
  28. uint32_t height;
  29. gs_duplicator_t *duplicator;
  30. float reset_timeout;
  31. struct cursor_data cursor_data;
  32. };
  33. /* ------------------------------------------------------------------------- */
  34. static inline void update_settings(struct duplicator_capture *capture,
  35. obs_data_t *settings)
  36. {
  37. capture->monitor = (int)obs_data_get_int(settings, "monitor");
  38. capture->capture_cursor = obs_data_get_bool(settings, "capture_cursor");
  39. obs_enter_graphics();
  40. gs_duplicator_destroy(capture->duplicator);
  41. capture->duplicator = NULL;
  42. capture->width = 0;
  43. capture->height = 0;
  44. capture->x = 0;
  45. capture->y = 0;
  46. capture->rot = 0;
  47. capture->reset_timeout = 0.0f;
  48. obs_leave_graphics();
  49. }
  50. /* ------------------------------------------------------------------------- */
  51. static const char *duplicator_capture_getname(void *unused)
  52. {
  53. UNUSED_PARAMETER(unused);
  54. return TEXT_MONITOR_CAPTURE;
  55. }
  56. static void duplicator_capture_destroy(void *data)
  57. {
  58. struct duplicator_capture *capture = data;
  59. obs_enter_graphics();
  60. gs_duplicator_destroy(capture->duplicator);
  61. cursor_data_free(&capture->cursor_data);
  62. obs_leave_graphics();
  63. bfree(capture);
  64. }
  65. static void duplicator_capture_defaults(obs_data_t *settings)
  66. {
  67. obs_data_set_default_int(settings, "monitor", 0);
  68. obs_data_set_default_bool(settings, "capture_cursor", true);
  69. }
  70. static void duplicator_capture_update(void *data, obs_data_t *settings)
  71. {
  72. struct duplicator_capture *mc = data;
  73. update_settings(mc, settings);
  74. }
  75. static void *duplicator_capture_create(obs_data_t *settings,
  76. obs_source_t *source)
  77. {
  78. struct duplicator_capture *capture;
  79. capture = bzalloc(sizeof(struct duplicator_capture));
  80. capture->source = source;
  81. update_settings(capture, settings);
  82. return capture;
  83. }
  84. static void reset_capture_data(struct duplicator_capture *capture)
  85. {
  86. struct gs_monitor_info monitor_info = {0};
  87. gs_texture_t *texture = gs_duplicator_get_texture(capture->duplicator);
  88. gs_get_duplicator_monitor_info(capture->monitor, &monitor_info);
  89. capture->width = gs_texture_get_width(texture);
  90. capture->height = gs_texture_get_height(texture);
  91. capture->x = monitor_info.x;
  92. capture->y = monitor_info.y;
  93. capture->rot = monitor_info.rotation_degrees;
  94. }
  95. static void free_capture_data(struct duplicator_capture *capture)
  96. {
  97. gs_duplicator_destroy(capture->duplicator);
  98. cursor_data_free(&capture->cursor_data);
  99. capture->duplicator = NULL;
  100. capture->width = 0;
  101. capture->height = 0;
  102. capture->x = 0;
  103. capture->y = 0;
  104. capture->rot = 0;
  105. capture->reset_timeout = 0.0f;
  106. }
  107. static void duplicator_capture_tick(void *data, float seconds)
  108. {
  109. struct duplicator_capture *capture = data;
  110. /* completely shut down monitor capture if not in use, otherwise it can
  111. * sometimes generate system lag when a game is in fullscreen mode */
  112. if (!obs_source_showing(capture->source)) {
  113. if (capture->showing) {
  114. obs_enter_graphics();
  115. free_capture_data(capture);
  116. obs_leave_graphics();
  117. capture->showing = false;
  118. }
  119. return;
  120. /* always try to load the capture immediately when the source is first
  121. * shown */
  122. } else if (!capture->showing) {
  123. capture->reset_timeout = RESET_INTERVAL_SEC;
  124. }
  125. obs_enter_graphics();
  126. if (!capture->duplicator) {
  127. capture->reset_timeout += seconds;
  128. if (capture->reset_timeout >= RESET_INTERVAL_SEC) {
  129. capture->duplicator =
  130. gs_duplicator_create(capture->monitor);
  131. capture->reset_timeout = 0.0f;
  132. }
  133. }
  134. if (!!capture->duplicator) {
  135. if (capture->capture_cursor)
  136. cursor_capture(&capture->cursor_data);
  137. if (!gs_duplicator_update_frame(capture->duplicator)) {
  138. free_capture_data(capture);
  139. } else if (capture->width == 0) {
  140. reset_capture_data(capture);
  141. }
  142. }
  143. obs_leave_graphics();
  144. if (!capture->showing)
  145. capture->showing = true;
  146. UNUSED_PARAMETER(seconds);
  147. }
  148. static uint32_t duplicator_capture_width(void *data)
  149. {
  150. struct duplicator_capture *capture = data;
  151. return capture->rot % 180 == 0 ? capture->width : capture->height;
  152. }
  153. static uint32_t duplicator_capture_height(void *data)
  154. {
  155. struct duplicator_capture *capture = data;
  156. return capture->rot % 180 == 0 ? capture->height : capture->width;
  157. }
  158. static void draw_cursor(struct duplicator_capture *capture)
  159. {
  160. cursor_draw(&capture->cursor_data, -capture->x, -capture->y, 1.0f, 1.0f,
  161. capture->rot % 180 == 0 ? capture->width : capture->height,
  162. capture->rot % 180 == 0 ? capture->height : capture->width);
  163. }
  164. static void duplicator_capture_render(void *data, gs_effect_t *effect)
  165. {
  166. struct duplicator_capture *capture = data;
  167. gs_texture_t *texture;
  168. int rot;
  169. if (!capture->duplicator)
  170. return;
  171. texture = gs_duplicator_get_texture(capture->duplicator);
  172. if (!texture)
  173. return;
  174. effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
  175. rot = capture->rot;
  176. while (gs_effect_loop(effect, "Draw")) {
  177. if (rot != 0) {
  178. float x = 0.0f;
  179. float y = 0.0f;
  180. switch (rot) {
  181. case 90:
  182. x = (float)capture->height;
  183. break;
  184. case 180:
  185. x = (float)capture->width;
  186. y = (float)capture->height;
  187. break;
  188. case 270:
  189. y = (float)capture->width;
  190. break;
  191. }
  192. gs_matrix_push();
  193. gs_matrix_translate3f(x, y, 0.0f);
  194. gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD((float)rot));
  195. }
  196. obs_source_draw(texture, 0, 0, 0, 0, false);
  197. if (rot != 0)
  198. gs_matrix_pop();
  199. }
  200. if (capture->capture_cursor) {
  201. effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
  202. while (gs_effect_loop(effect, "Draw")) {
  203. draw_cursor(capture);
  204. }
  205. }
  206. }
  207. static bool get_monitor_props(obs_property_t *monitor_list, int monitor_idx)
  208. {
  209. struct dstr monitor_desc = {0};
  210. struct gs_monitor_info info;
  211. if (!gs_get_duplicator_monitor_info(monitor_idx, &info))
  212. return false;
  213. dstr_catf(&monitor_desc, "%s %d: %ldx%ld @ %ld,%ld", TEXT_MONITOR,
  214. monitor_idx + 1, info.cx, info.cy, info.x, info.y);
  215. obs_property_list_add_int(monitor_list, monitor_desc.array,
  216. monitor_idx);
  217. dstr_free(&monitor_desc);
  218. return true;
  219. }
  220. static obs_properties_t *duplicator_capture_properties(void *unused)
  221. {
  222. int monitor_idx = 0;
  223. UNUSED_PARAMETER(unused);
  224. obs_properties_t *props = obs_properties_create();
  225. obs_property_t *monitors = obs_properties_add_list(
  226. props, "monitor", TEXT_MONITOR, OBS_COMBO_TYPE_LIST,
  227. OBS_COMBO_FORMAT_INT);
  228. obs_properties_add_bool(props, "capture_cursor", TEXT_CAPTURE_CURSOR);
  229. obs_enter_graphics();
  230. while (get_monitor_props(monitors, monitor_idx++))
  231. ;
  232. obs_leave_graphics();
  233. return props;
  234. }
  235. struct obs_source_info duplicator_capture_info = {
  236. .id = "monitor_capture",
  237. .type = OBS_SOURCE_TYPE_INPUT,
  238. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
  239. OBS_SOURCE_DO_NOT_DUPLICATE,
  240. .get_name = duplicator_capture_getname,
  241. .create = duplicator_capture_create,
  242. .destroy = duplicator_capture_destroy,
  243. .video_render = duplicator_capture_render,
  244. .video_tick = duplicator_capture_tick,
  245. .update = duplicator_capture_update,
  246. .get_width = duplicator_capture_width,
  247. .get_height = duplicator_capture_height,
  248. .get_defaults = duplicator_capture_defaults,
  249. .get_properties = duplicator_capture_properties,
  250. .icon_type = OBS_ICON_TYPE_DESKTOP_CAPTURE,
  251. };