duplicator-monitor-capture.c 8.2 KB

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