duplicator-monitor-capture.c 8.2 KB

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