123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- //
- // 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;
- if (gs_get_device_type() == GS_DEVICE_OPENGL) {
- capture_info->effect = obs_get_base_effect(OBS_EFFECT_DEFAULT_RECT);
- } else {
- capture_info->effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
- }
- 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;
- }
|