test-desktop.m 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #include <stdlib.h>
  2. #include <obs.h>
  3. #include <util/threading.h>
  4. #include <pthread.h>
  5. #import <CoreGraphics/CGDisplayStream.h>
  6. #import <Cocoa/Cocoa.h>
  7. struct display_capture {
  8. samplerstate_t sampler;
  9. effect_t draw_effect;
  10. texture_t tex;
  11. unsigned display;
  12. uint32_t width, height;
  13. os_event_t disp_finished;
  14. CGDisplayStreamRef disp;
  15. IOSurfaceRef current, prev;
  16. pthread_mutex_t mutex;
  17. };
  18. static void destroy_display_stream(struct display_capture *dc)
  19. {
  20. if (dc->disp) {
  21. CGDisplayStreamStop(dc->disp);
  22. os_event_wait(dc->disp_finished);
  23. }
  24. if (dc->tex) {
  25. texture_destroy(dc->tex);
  26. dc->tex = NULL;
  27. }
  28. if (dc->current) {
  29. IOSurfaceDecrementUseCount(dc->current);
  30. CFRelease(dc->current);
  31. dc->current = NULL;
  32. }
  33. if (dc->prev) {
  34. IOSurfaceDecrementUseCount(dc->prev);
  35. CFRelease(dc->prev);
  36. dc->prev = NULL;
  37. }
  38. if (dc->disp) {
  39. CFRelease(dc->disp);
  40. dc->disp = NULL;
  41. }
  42. os_event_destroy(dc->disp_finished);
  43. }
  44. static void display_capture_destroy(void *data)
  45. {
  46. struct display_capture *dc = data;
  47. if (!dc)
  48. return;
  49. pthread_mutex_lock(&dc->mutex);
  50. gs_entercontext(obs_graphics());
  51. destroy_display_stream(dc);
  52. if (dc->sampler)
  53. samplerstate_destroy(dc->sampler);
  54. if (dc->draw_effect)
  55. effect_destroy(dc->draw_effect);
  56. gs_leavecontext();
  57. pthread_mutex_destroy(&dc->mutex);
  58. bfree(dc);
  59. }
  60. static bool init_display_stream(struct display_capture *dc)
  61. {
  62. if (dc->display >= [NSScreen screens].count)
  63. return false;
  64. NSScreen *screen = [NSScreen screens][dc->display];
  65. NSRect frame = [screen convertRectToBacking:screen.frame];
  66. dc->width = frame.size.width;
  67. dc->height = frame.size.height;
  68. NSNumber *screen_num = screen.deviceDescription[@"NSScreenNumber"];
  69. CGDirectDisplayID disp_id = (CGDirectDisplayID)screen_num.pointerValue;
  70. NSDictionary *dict = @{
  71. (__bridge NSString*)kCGDisplayStreamSourceRect:
  72. (__bridge NSDictionary*)CGRectCreateDictionaryRepresentation(
  73. CGRectMake(0, 0, dc->width, dc->height)),
  74. (__bridge NSString*)kCGDisplayStreamQueueDepth: @5
  75. };
  76. os_event_init(&dc->disp_finished, OS_EVENT_TYPE_MANUAL);
  77. dc->disp = CGDisplayStreamCreateWithDispatchQueue(disp_id,
  78. dc->width, dc->height, 'BGRA',
  79. (__bridge CFDictionaryRef)dict,
  80. dispatch_queue_create(NULL, NULL),
  81. ^(CGDisplayStreamFrameStatus status, uint64_t
  82. displayTime, IOSurfaceRef frameSurface,
  83. CGDisplayStreamUpdateRef updateRef)
  84. {
  85. UNUSED_PARAMETER(displayTime);
  86. UNUSED_PARAMETER(updateRef);
  87. if (status ==
  88. kCGDisplayStreamFrameStatusStopped) {
  89. os_event_signal(dc->disp_finished);
  90. return;
  91. }
  92. if (!frameSurface ||
  93. pthread_mutex_trylock(&dc->mutex))
  94. return;
  95. if (dc->current) {
  96. IOSurfaceDecrementUseCount(dc->current);
  97. CFRelease(dc->current);
  98. dc->current = NULL;
  99. }
  100. dc->current = frameSurface;
  101. CFRetain(dc->current);
  102. IOSurfaceIncrementUseCount(dc->current);
  103. pthread_mutex_unlock(&dc->mutex);
  104. }
  105. );
  106. return !CGDisplayStreamStart(dc->disp);
  107. }
  108. static void *display_capture_create(obs_data_t settings,
  109. obs_source_t source)
  110. {
  111. UNUSED_PARAMETER(source);
  112. UNUSED_PARAMETER(settings);
  113. struct display_capture *dc = bzalloc(sizeof(struct display_capture));
  114. gs_entercontext(obs_graphics());
  115. struct gs_sampler_info info = {
  116. .filter = GS_FILTER_LINEAR,
  117. .address_u = GS_ADDRESS_CLAMP,
  118. .address_v = GS_ADDRESS_CLAMP,
  119. .address_w = GS_ADDRESS_CLAMP,
  120. .max_anisotropy = 1,
  121. };
  122. dc->sampler = gs_create_samplerstate(&info);
  123. if (!dc->sampler)
  124. goto fail;
  125. char *effect_file = obs_find_plugin_file("test-input/draw_rect.effect");
  126. dc->draw_effect = gs_create_effect_from_file(effect_file, NULL);
  127. bfree(effect_file);
  128. if (!dc->draw_effect)
  129. goto fail;
  130. gs_leavecontext();
  131. dc->display = obs_data_getint(settings, "display");
  132. pthread_mutex_init(&dc->mutex, NULL);
  133. if (!init_display_stream(dc))
  134. goto fail;
  135. return dc;
  136. fail:
  137. gs_leavecontext();
  138. display_capture_destroy(dc);
  139. return NULL;
  140. }
  141. static void display_capture_video_render(void *data, effect_t effect)
  142. {
  143. UNUSED_PARAMETER(effect);
  144. struct display_capture *dc = data;
  145. pthread_mutex_lock(&dc->mutex);
  146. if (dc->current && dc->prev != dc->current) {
  147. if (dc->tex)
  148. texture_rebind_iosurface(dc->tex, dc->current);
  149. else
  150. dc->tex = gs_create_texture_from_iosurface(
  151. dc->current);
  152. if (dc->prev) {
  153. IOSurfaceDecrementUseCount(dc->prev);
  154. CFRelease(dc->prev);
  155. }
  156. dc->prev = dc->current;
  157. dc->current = NULL;
  158. }
  159. if (!dc->tex) goto fail;
  160. gs_load_samplerstate(dc->sampler, 0);
  161. technique_t tech = effect_gettechnique(dc->draw_effect, "Default");
  162. effect_settexture(dc->draw_effect,
  163. effect_getparambyidx(dc->draw_effect, 1),
  164. dc->tex);
  165. technique_begin(tech);
  166. technique_beginpass(tech, 0);
  167. gs_draw_sprite(dc->tex, 0, 0, 0);
  168. technique_endpass(tech);
  169. technique_end(tech);
  170. fail:
  171. pthread_mutex_unlock(&dc->mutex);
  172. }
  173. static const char *display_capture_getname(const char *locale)
  174. {
  175. UNUSED_PARAMETER(locale);
  176. return "Display Capture";
  177. }
  178. static uint32_t display_capture_getwidth(void *data)
  179. {
  180. struct display_capture *dc = data;
  181. return dc->width;
  182. }
  183. static uint32_t display_capture_getheight(void *data)
  184. {
  185. struct display_capture *dc = data;
  186. return dc->height;
  187. }
  188. static void display_capture_defaults(obs_data_t settings)
  189. {
  190. obs_data_set_default_int(settings, "display", 0);
  191. }
  192. static void display_capture_update(void *data, obs_data_t settings)
  193. {
  194. struct display_capture *dc = data;
  195. unsigned display = obs_data_getint(settings, "display");
  196. if (dc->display == display)
  197. return;
  198. gs_entercontext(obs_graphics());
  199. destroy_display_stream(dc);
  200. dc->display = display;
  201. init_display_stream(dc);
  202. gs_leavecontext();
  203. }
  204. static obs_properties_t display_capture_properties(char const *locale)
  205. {
  206. UNUSED_PARAMETER(locale);
  207. obs_properties_t props = obs_properties_create();
  208. obs_property_t list = obs_properties_add_list(props,
  209. "display", "Display",
  210. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
  211. for (unsigned i = 0; i < [NSScreen screens].count; i++)
  212. {
  213. char buf[10];
  214. sprintf(buf, "%u", i);
  215. obs_property_list_add_item(list, buf, buf);
  216. }
  217. return props;
  218. }
  219. struct obs_source_info display_capture_info = {
  220. .id = "display_capture",
  221. .type = OBS_SOURCE_TYPE_INPUT,
  222. .getname = display_capture_getname,
  223. .create = display_capture_create,
  224. .destroy = display_capture_destroy,
  225. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW,
  226. .video_render = display_capture_video_render,
  227. .getwidth = display_capture_getwidth,
  228. .getheight = display_capture_getheight,
  229. .defaults = display_capture_defaults,
  230. .properties = display_capture_properties,
  231. .update = display_capture_update,
  232. };