| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 | 
							- //
 
- //  plugin-main.m
 
- //  mac-avcapture
 
- //
 
- //  Created by Patrick Heyer on 2023-03-07.
 
- //
 
- #import "plugin-main.h"
 
- #pragma mark av-capture API
 
- const char *av_capture_get_text(const char *text_id)
 
- {
 
-     return obs_module_text(text_id);
 
- }
 
- static void *av_capture_create(obs_data_t *settings, obs_source_t *source)
 
- {
 
-     OBSAVCaptureInfo *capture_data = bzalloc(sizeof(OBSAVCaptureInfo));
 
-     capture_data->isFastPath = false;
 
-     capture_data->settings = settings;
 
-     capture_data->source = source;
 
-     capture_data->videoFrame = bzalloc(sizeof(OBSAVCaptureVideoFrame));
 
-     capture_data->audioFrame = bzalloc(sizeof(OBSAVCaptureAudioFrame));
 
-     OBSAVCapture *capture = [[OBSAVCapture alloc] initWithCaptureInfo:capture_data];
 
-     return (void *) CFBridgingRetain(capture);
 
- }
 
- static void *av_fast_capture_create(obs_data_t *settings, obs_source_t *source)
 
- {
 
-     OBSAVCaptureInfo *capture_info = bzalloc(sizeof(OBSAVCaptureInfo));
 
-     capture_info->isFastPath = true;
 
-     capture_info->settings = settings;
 
-     capture_info->source = source;
 
-     capture_info->effect = obs_get_base_effect(OBS_EFFECT_DEFAULT_RECT);
 
-     capture_info->frameSize = CGRectZero;
 
-     if (!capture_info->effect) {
 
-         return NULL;
 
-     }
 
-     pthread_mutex_init(&capture_info->mutex, NULL);
 
-     OBSAVCapture *capture = [[OBSAVCapture alloc] initWithCaptureInfo:capture_info];
 
-     return (void *) CFBridgingRetain(capture);
 
- }
 
- static const char *av_capture_get_name(void *av_capture __unused)
 
- {
 
-     return obs_module_text("AVCapture");
 
- }
 
- static const char *av_fast_capture_get_name(void *av_capture __unused)
 
- {
 
-     return obs_module_text("AVCapture_Fast");
 
- }
 
- static void av_capture_set_defaults(obs_data_t *settings)
 
- {
 
-     obs_data_set_default_string(settings, "device", "");
 
-     obs_data_set_default_bool(settings, "use_preset", true);
 
-     obs_data_set_default_string(settings, "preset", AVCaptureSessionPresetHigh.UTF8String);
 
-     obs_data_set_default_bool(settings, "enable_audio", true);
 
- }
 
- static void av_fast_capture_set_defaults(obs_data_t *settings)
 
- {
 
-     obs_data_set_default_string(settings, "device", "");
 
-     obs_data_set_default_bool(settings, "use_preset", false);
 
-     obs_data_set_default_bool(settings, "enable_audio", true);
 
- }
 
- static obs_properties_t *av_capture_properties(void *av_capture)
 
- {
 
-     OBSAVCapture *capture = (__bridge OBSAVCapture *) (av_capture);
 
-     OBSAVCaptureInfo *capture_info = capture.captureInfo;
 
-     AVCaptureDevice *device = capture.deviceInput.device;
 
-     NSString *effectsWarningKey = [OBSAVCapture effectsWarningForDevice:device];
 
-     obs_properties_t *properties = obs_properties_create();
 
-     // Create Properties
 
-     obs_property_t *device_list = obs_properties_add_list(properties, "device", obs_module_text("Device"),
 
-                                                           OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
 
-     obs_property_t *effects_warning;
 
-     if (effectsWarningKey) {
 
-         effects_warning = obs_properties_add_text(properties, "effects_warning",
 
-                                                   obs_module_text(effectsWarningKey.UTF8String), OBS_TEXT_INFO);
 
-         obs_property_text_set_info_type(effects_warning, OBS_TEXT_INFO_WARNING);
 
-     }
 
-     obs_property_t *use_preset = obs_properties_add_bool(properties, "use_preset", obs_module_text("UsePreset"));
 
-     obs_property_t *preset_list = obs_properties_add_list(properties, "preset", obs_module_text("Preset"),
 
-                                                           OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
 
-     obs_property_t *supported_formats = obs_properties_add_list(
 
-         properties, "supported_format", obs_module_text("InputFormat"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
 
-     obs_property_t *use_buffering = obs_properties_add_bool(properties, "buffering", obs_module_text("Buffering"));
 
-     obs_property_t *frame_rates = obs_properties_add_frame_rate(properties, "frame_rate", obs_module_text("FrameRate"));
 
-     if (capture_info) {
 
-         bool isFastPath = capture_info->isFastPath;
 
-         // Add Property Visibility and Callbacks
 
-         configure_property(device_list, true, true, properties_changed, capture);
 
-         if (effectsWarningKey) {
 
-             configure_property(effects_warning, true, true, NULL, NULL);
 
-         }
 
-         configure_property(use_preset, !isFastPath, !isFastPath, (!isFastPath) ? properties_changed_use_preset : NULL,
 
-                            capture);
 
-         configure_property(preset_list, !isFastPath, !isFastPath, (!isFastPath) ? properties_changed_preset : NULL,
 
-                            capture);
 
-         configure_property(supported_formats, true, true, properties_changed, capture);
 
-         configure_property(use_buffering, !isFastPath, !isFastPath, NULL, NULL);
 
-         configure_property(frame_rates, isFastPath, isFastPath, NULL, NULL);
 
-     }
 
-     return properties;
 
- }
 
- static void av_capture_update(void *av_capture, obs_data_t *settings)
 
- {
 
-     OBSAVCapture *capture = (__bridge OBSAVCapture *) (av_capture);
 
-     capture.captureInfo->settings = settings;
 
-     [capture updateSessionwithError:NULL];
 
- }
 
- static void av_fast_capture_tick(void *av_capture, float seconds __unused)
 
- {
 
-     OBSAVCapture *capture = (__bridge OBSAVCapture *) (av_capture);
 
-     OBSAVCaptureInfo *capture_info = capture.captureInfo;
 
-     if (!capture_info->currentSurface) {
 
-         return;
 
-     }
 
-     if (!obs_source_showing(capture_info->source)) {
 
-         return;
 
-     }
 
-     IOSurfaceRef previousSurface = capture_info->previousSurface;
 
-     if (pthread_mutex_lock(&capture_info->mutex)) {
 
-         return;
 
-     }
 
-     capture_info->previousSurface = capture_info->currentSurface;
 
-     capture_info->currentSurface = NULL;
 
-     pthread_mutex_unlock(&capture_info->mutex);
 
-     if (previousSurface == capture_info->previousSurface) {
 
-         return;
 
-     }
 
-     if (capture_info->previousSurface) {
 
-         obs_enter_graphics();
 
-         if (capture_info->texture) {
 
-             gs_texture_rebind_iosurface(capture_info->texture, capture_info->previousSurface);
 
-         } else {
 
-             capture_info->texture = gs_texture_create_from_iosurface(capture_info->previousSurface);
 
-         }
 
-         obs_leave_graphics();
 
-     }
 
-     if (previousSurface) {
 
-         IOSurfaceDecrementUseCount(previousSurface);
 
-         CFRelease(previousSurface);
 
-     }
 
- }
 
- static void av_fast_capture_render(void *av_capture, gs_effect_t *effect __unused)
 
- {
 
-     OBSAVCapture *capture = (__bridge OBSAVCapture *) (av_capture);
 
-     OBSAVCaptureInfo *capture_info = capture.captureInfo;
 
-     if (!capture_info->texture) {
 
-         return;
 
-     }
 
-     const bool linear_srgb = gs_get_linear_srgb();
 
-     const bool previous = gs_framebuffer_srgb_enabled();
 
-     gs_enable_framebuffer_srgb(linear_srgb);
 
-     gs_eparam_t *param = gs_effect_get_param_by_name(capture_info->effect, "image");
 
-     gs_effect_set_texture_srgb(param, capture_info->texture);
 
-     if (linear_srgb) {
 
-         gs_effect_set_texture_srgb(param, capture_info->texture);
 
-     } else {
 
-         gs_effect_set_texture(param, capture_info->texture);
 
-     }
 
-     while (gs_effect_loop(capture_info->effect, "Draw")) {
 
-         gs_draw_sprite(capture_info->texture, 0, 0, 0);
 
-     }
 
-     gs_enable_framebuffer_srgb(previous);
 
- }
 
- static UInt32 av_fast_capture_get_width(void *av_capture)
 
- {
 
-     OBSAVCapture *capture = (__bridge OBSAVCapture *) (av_capture);
 
-     OBSAVCaptureInfo *capture_info = capture.captureInfo;
 
-     CGSize frameSize = capture_info->frameSize.size;
 
-     return (UInt32) frameSize.width;
 
- }
 
- static UInt32 av_fast_capture_get_height(void *av_capture)
 
- {
 
-     OBSAVCapture *capture = (__bridge OBSAVCapture *) (av_capture);
 
-     OBSAVCaptureInfo *capture_info = capture.captureInfo;
 
-     CGSize frameSize = capture_info->frameSize.size;
 
-     return (UInt32) frameSize.height;
 
- }
 
- static void av_capture_destroy(void *av_capture)
 
- {
 
-     OBSAVCapture *capture = (__bridge OBSAVCapture *) (av_capture);
 
-     if (!capture) {
 
-         return;
 
-     }
 
-     /// It is possible that the source's serial queue is still creating this source, so perform destruction
 
-     /// synchronously on that queue to ensure the source is fully initialized before being destroyed.
 
-     dispatch_sync(capture.sessionQueue, ^{
 
-         OBSAVCaptureInfo *capture_info = capture.captureInfo;
 
-         [capture stopCaptureSession];
 
-         [capture.deviceInput.device unlockForConfiguration];
 
-         if (capture_info->isFastPath) {
 
-             pthread_mutex_destroy(&capture_info->mutex);
 
-         }
 
-         if (capture_info->videoFrame) {
 
-             bfree(capture_info->videoFrame);
 
-             capture_info->videoFrame = NULL;
 
-         }
 
-         if (capture_info->audioFrame) {
 
-             bfree(capture_info->audioFrame);
 
-             capture_info->audioFrame = NULL;
 
-         }
 
-         if (capture_info->sampleBufferDescription) {
 
-             capture_info->sampleBufferDescription = NULL;
 
-         }
 
-         bfree(capture_info);
 
-         CFBridgingRelease((__bridge CFTypeRef _Nullable)(capture));
 
-     });
 
- }
 
- #pragma mark - OBS Module API
 
- OBS_DECLARE_MODULE()
 
- OBS_MODULE_USE_DEFAULT_LOCALE("macOS-avcapture", "en-US")
 
- MODULE_EXPORT const char *obs_module_description(void)
 
- {
 
-     return "macOS AVFoundation Capture Source";
 
- }
 
- bool obs_module_load(void)
 
- {
 
-     struct obs_source_info av_capture_info = {
 
-         .id = "macos-avcapture",
 
-         .type = OBS_SOURCE_TYPE_INPUT,
 
-         .output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE,
 
-         .create = av_capture_create,
 
-         .get_name = av_capture_get_name,
 
-         .get_defaults = av_capture_set_defaults,
 
-         .get_properties = av_capture_properties,
 
-         .update = av_capture_update,
 
-         .destroy = av_capture_destroy,
 
-         .icon_type = OBS_ICON_TYPE_CAMERA,
 
-     };
 
-     obs_register_source(&av_capture_info);
 
-     struct obs_source_info av_capture_sync_info = {
 
-         .id = "macos-avcapture-fast",
 
-         .type = OBS_SOURCE_TYPE_INPUT,
 
-         .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_AUDIO | OBS_SOURCE_SRGB |
 
-                         OBS_SOURCE_DO_NOT_DUPLICATE,
 
-         .create = av_fast_capture_create,
 
-         .get_name = av_fast_capture_get_name,
 
-         .get_defaults = av_fast_capture_set_defaults,
 
-         .get_properties = av_capture_properties,
 
-         .update = av_capture_update,
 
-         .destroy = av_capture_destroy,
 
-         .video_tick = av_fast_capture_tick,
 
-         .video_render = av_fast_capture_render,
 
-         .get_width = av_fast_capture_get_width,
 
-         .get_height = av_fast_capture_get_height,
 
-         .icon_type = OBS_ICON_TYPE_CAMERA,
 
-     };
 
-     obs_register_source(&av_capture_sync_info);
 
-     return true;
 
- }
 
 
  |