/** * The x server was changed */ static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *settings) { UNUSED_PARAMETER(p); bool advanced = obs_data_get_bool(settings, "advanced"); int_fast32_t old_screen = obs_data_get_int(settings, "screen"); const char *server = obs_data_get_string(settings, "server"); obs_property_t *screens = obs_properties_get(props, "screen"); /* we want a real NULL here in case there is no string here */ server = (advanced && *server) ? server : NULL; obs_property_list_clear(screens); Display *dpy = XOpenDisplay(server); if (!dpy) { obs_property_set_enabled(screens, false); return true; } struct dstr screen_info; dstr_init(&screen_info); bool xinerama = xinerama_is_active(dpy); int_fast32_t count = (xinerama) ? xinerama_screen_count(dpy) : XScreenCount(dpy); for (int_fast32_t i = 0; i < count; ++i) { int_fast32_t x, y, w, h; x = y = w = h = 0; if (xinerama) xinerama_screen_geo(dpy, i, &x, &y, &w, &h); else x11_screen_geo(dpy, i, &w, &h); dstr_printf(&screen_info, "Screen %"PRIuFAST32" (%"PRIuFAST32 "x%"PRIuFAST32" @ %"PRIuFAST32 ",%"PRIuFAST32")", i, w, h, x, y); obs_property_list_add_int(screens, screen_info.array, i); } /* handle missing screen */ if (old_screen + 1 > count) { dstr_printf(&screen_info, "Screen %"PRIuFAST32" (not found)", old_screen); size_t index = obs_property_list_add_int(screens, screen_info.array, old_screen); obs_property_list_item_disable(screens, index, true); } dstr_free(&screen_info); XCloseDisplay(dpy); obs_property_set_enabled(screens, true); return true; }
/* * List dv timings for the device */ static void v4l2_dv_timing_list(int dev, obs_property_t *prop) { struct v4l2_dv_timings dvt; struct dstr buf; int index = 0; dstr_init(&buf); obs_property_list_clear(prop); obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); while (v4l2_enum_dv_timing(dev, &dvt, index) == 0) { /* i do not pretend to understand, this is from qv4l2 ... */ double h = (double) dvt.bt.height + dvt.bt.vfrontporch + dvt.bt.vsync + dvt.bt.vbackporch + dvt.bt.il_vfrontporch + dvt.bt.il_vsync + dvt.bt.il_vbackporch; double w = (double) dvt.bt.width + dvt.bt.hfrontporch + dvt.bt.hsync + dvt.bt.hbackporch; double i = (dvt.bt.interlaced) ? 2.0f : 1.0f; double rate = (double) dvt.bt.pixelclock / (w * (h / i)); dstr_printf(&buf, "%ux%u%c %.2f", dvt.bt.width, dvt.bt.height, (dvt.bt.interlaced) ? 'i' : 'p', rate); obs_property_list_add_int(prop, buf.array, index); index++; } dstr_free(&buf); }
static obs_properties_t *decklink_get_properties(void *data) { obs_properties_t *props = obs_properties_create(); obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); obs_property_set_modified_callback(list, decklink_device_changed); fill_out_devices(list); list = obs_properties_add_list(props, MODE_ID, TEXT_MODE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); list = obs_properties_add_list(props, PIXEL_FORMAT, TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); list = obs_properties_add_list(props, CHANNEL_FORMAT, TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_NONE, SPEAKERS_UNKNOWN); obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_0CH, SPEAKERS_STEREO); obs_properties_add_bool(props, BUFFERING, TEXT_BUFFERING); UNUSED_PARAMETER(data); return props; }
/* * List video standards for the device */ static void v4l2_standard_list(int dev, obs_property_t *prop) { struct v4l2_standard std; std.index = 0; obs_property_list_clear(prop); obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); while (v4l2_ioctl(dev, VIDIOC_ENUMSTD, &std) == 0) { obs_property_list_add_int(prop, (char *) std.name, std.id); std.index++; } }
/* * List formats for device */ static void v4l2_format_list(int dev, obs_property_t *prop) { struct v4l2_fmtdesc fmt; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.index = 0; struct dstr buffer; dstr_init(&buffer); obs_property_list_clear(prop); while (v4l2_ioctl(dev, VIDIOC_ENUM_FMT, &fmt) == 0) { dstr_copy(&buffer, (char *) fmt.description); if (fmt.flags & V4L2_FMT_FLAG_EMULATED) dstr_cat(&buffer, " (Emulated)"); if (v4l2_to_obs_video_format(fmt.pixelformat) != VIDEO_FORMAT_NONE) { obs_property_list_add_int(prop, buffer.array, fmt.pixelformat); blog(LOG_INFO, "Pixelformat: %s (available)", buffer.array); } else { blog(LOG_INFO, "Pixelformat: %s (unavailable)", buffer.array); } fmt.index++; } dstr_free(&buffer); }
/* * List framerates for device and resolution */ static void v4l2_framerate_list(int dev, uint_fast32_t pixelformat, uint_fast32_t width, uint_fast32_t height, obs_property_t *prop) { struct v4l2_frmivalenum frmival; frmival.pixel_format = pixelformat; frmival.width = width; frmival.height = height; frmival.index = 0; struct dstr buffer; dstr_init(&buffer); obs_property_list_clear(prop); obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); v4l2_ioctl(dev, VIDIOC_ENUM_FRAMEINTERVALS, &frmival); switch(frmival.type) { case V4L2_FRMIVAL_TYPE_DISCRETE: while (v4l2_ioctl(dev, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0) { float fps = (float) frmival.discrete.denominator / frmival.discrete.numerator; int pack = v4l2_pack_tuple(frmival.discrete.numerator, frmival.discrete.denominator); dstr_printf(&buffer, "%.2f", fps); obs_property_list_add_int(prop, buffer.array, pack); frmival.index++; } break; default: blog(LOG_INFO, "Stepwise and Continuous framerates " "are currently hardcoded"); for (const int *packed = v4l2_framerates; *packed; ++packed) { int num; int denom; v4l2_unpack_tuple(&num, &denom, *packed); float fps = (float) denom / num; dstr_printf(&buffer, "%.2f", fps); obs_property_list_add_int(prop, buffer.array, *packed); } break; } dstr_free(&buffer); }
/* * List resolutions for device and format */ static void v4l2_resolution_list(int dev, uint_fast32_t pixelformat, obs_property_t *prop) { struct v4l2_frmsizeenum frmsize; frmsize.pixel_format = pixelformat; frmsize.index = 0; struct dstr buffer; dstr_init(&buffer); obs_property_list_clear(prop); obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); v4l2_ioctl(dev, VIDIOC_ENUM_FRAMESIZES, &frmsize); switch(frmsize.type) { case V4L2_FRMSIZE_TYPE_DISCRETE: while (v4l2_ioctl(dev, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) { dstr_printf(&buffer, "%dx%d", frmsize.discrete.width, frmsize.discrete.height); obs_property_list_add_int(prop, buffer.array, v4l2_pack_tuple(frmsize.discrete.width, frmsize.discrete.height)); frmsize.index++; } break; default: blog(LOG_INFO, "Stepwise and Continuous framesizes " "are currently hardcoded"); for (const int *packed = v4l2_framesizes; *packed; ++packed) { int width; int height; v4l2_unpack_tuple(&width, &height, *packed); dstr_printf(&buffer, "%dx%d", width, height); obs_property_list_add_int(prop, buffer.array, *packed); } break; } dstr_free(&buffer); }
/* * List inputs for device */ static void v4l2_input_list(int_fast32_t dev, obs_property_t *prop) { struct v4l2_input in; memset(&in, 0, sizeof(in)); obs_property_list_clear(prop); while (v4l2_ioctl(dev, VIDIOC_ENUMINPUT, &in) == 0) { obs_property_list_add_int(prop, (char *) in.name, in.index); blog(LOG_INFO, "Found input '%s' (Index %d)", in.name, in.index); in.index++; } }
static obs_properties_t *stinger_properties(void *data) { obs_properties_t *ppts = obs_properties_create(); obs_properties_set_flags(ppts, OBS_PROPERTIES_DEFER_UPDATE); obs_properties_add_path(ppts, "path", obs_module_text("VideoFile"), OBS_PATH_FILE, FILE_FILTER, NULL); obs_property_t *list = obs_properties_add_list(ppts, "tp_type", obs_module_text("TransitionPointType"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(list, obs_module_text("TransitionPointTypeTime"), TIMING_TIME); obs_property_list_add_int(list, obs_module_text("TransitionPointTypeFrame"), TIMING_FRAME); obs_property_set_modified_callback(list, transition_point_type_modified); obs_properties_add_int(ppts, "transition_point", obs_module_text("TransitionPoint"), 0, 120000, 1); obs_property_t *monitor_list = obs_properties_add_list(ppts, "audio_monitoring", obs_module_text("AudioMonitoring"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(monitor_list, obs_module_text("AudioMonitoring.None"), OBS_MONITORING_TYPE_NONE); obs_property_list_add_int(monitor_list, obs_module_text("AudioMonitoring.MonitorOnly"), OBS_MONITORING_TYPE_MONITOR_ONLY); obs_property_list_add_int(monitor_list, obs_module_text("AudioMonitoring.Both"), OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT); obs_property_t *audio_fade_style = obs_properties_add_list(ppts, "audio_fade_style", obs_module_text("AudioFadeStyle"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(audio_fade_style, obs_module_text("AudioFadeStyle.FadeOutFadeIn"), FADE_STYLE_FADE_OUT_FADE_IN); obs_property_list_add_int(audio_fade_style, obs_module_text("AudioFadeStyle.CrossFade"), FADE_STYLE_CROSS_FADE); UNUSED_PARAMETER(data); return ppts; }
static BOOL CALLBACK enum_monitor_props(HMONITOR handle, HDC hdc, LPRECT rect, LPARAM param) { UNUSED_PARAMETER(hdc); UNUSED_PARAMETER(rect); obs_property_t *monitor_list = (obs_property_t*)param; MONITORINFO mi; size_t monitor_id = 0; struct dstr monitor_desc = { 0 }; struct dstr resolution = { 0 }; struct dstr format_string = { 0 }; monitor_id = obs_property_list_item_count(monitor_list); mi.cbSize = sizeof(mi); GetMonitorInfo(handle, &mi); dstr_catf(&resolution, "%dx%d @ %d,%d", mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, mi.rcMonitor.left, mi.rcMonitor.top); dstr_copy(&format_string, "%s %d: %s"); if (mi.dwFlags == MONITORINFOF_PRIMARY) { dstr_catf(&format_string, " (%s)", TEXT_PRIMARY_MONITOR); } dstr_catf(&monitor_desc, format_string.array, TEXT_MONITOR, monitor_id, resolution.array); obs_property_list_add_int(monitor_list, monitor_desc.array, (int)monitor_id); dstr_free(&monitor_desc); dstr_free(&resolution); dstr_free(&format_string); return TRUE; }
static bool get_monitor_props(obs_property_t *monitor_list, int monitor_idx) { struct dstr monitor_desc = {0}; struct gs_monitor_info info; if (!gs_get_duplicator_monitor_info(monitor_idx, &info)) return false; dstr_catf(&monitor_desc, "%s %d: %ldx%ld @ %ld,%ld", TEXT_MONITOR, monitor_idx, info.cx, info.cy, info.x, info.y); obs_property_list_add_int(monitor_list, monitor_desc.array, monitor_idx); dstr_free(&monitor_desc); return true; }
static obs_properties_t *vaapi_properties(void *unused) { UNUSED_PARAMETER(unused); obs_properties_t *props = obs_properties_create(); obs_property_t * list; list = obs_properties_add_list(props, "vaapi_device", "VAAPI Device", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); char path[128] = "/dev/dri/renderD1"; for (int i = 28;; i++) { sprintf(path, "/dev/dri/renderD1%d", i); if (access(path, F_OK) == 0) { char card[128] = "Card: "; sprintf(card, "Card%d: %s", i - 28, path); obs_property_list_add_string(list, card, path); } else { break; } } list = obs_properties_add_list(props, "vaapi_codec", "VAAPI Codec", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(list, "H.264 (default)", AV_CODEC_ID_H264); list = obs_properties_add_list(props, "level", "Level", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(list, "480p30 (3.0)", 30); obs_property_list_add_int(list, "720p30/480p60 (3.1)", 31); obs_property_list_add_int( list, "Compatibility mode (4.0 default)", 40); obs_property_list_add_int(list, "720p60/1080p30 (4.1)", 41); obs_property_list_add_int(list, "1080p60 (4.2)", 42); obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), 0, 300000, 50); obs_properties_add_int(props, "keyint_sec", obs_module_text("Keyframe Interval (seconds)"), 0, 20, 1); return props; }
static obs_properties_t *ffmpeg_source_getproperties(void *data) { struct dstr filter = {0}; UNUSED_PARAMETER(data); obs_properties_t *props = obs_properties_create(); obs_properties_set_flags(props, OBS_PROPERTIES_DEFER_UPDATE); obs_property_t *prop; // use this when obs allows non-readonly paths prop = obs_properties_add_bool(props, "is_local_file", obs_module_text("LocalFile")); obs_property_set_modified_callback(prop, is_local_file_modified); dstr_copy(&filter, obs_module_text("MediaFileFilter.AllMediaFiles")); dstr_cat(&filter, media_filter); dstr_cat(&filter, obs_module_text("MediaFileFilter.VideoFiles")); dstr_cat(&filter, video_filter); dstr_cat(&filter, obs_module_text("MediaFileFilter.AudioFiles")); dstr_cat(&filter, audio_filter); dstr_cat(&filter, obs_module_text("MediaFileFilter.AllFiles")); dstr_cat(&filter, " (*.*)"); obs_properties_add_path(props, "local_file", obs_module_text("LocalFile"), OBS_PATH_FILE, filter.array, NULL); dstr_free(&filter); obs_properties_add_bool(props, "looping", obs_module_text("Looping")); obs_properties_add_bool(props, "restart_on_activate", obs_module_text("RestartWhenActivated")); obs_properties_add_text(props, "input", obs_module_text("Input"), OBS_TEXT_DEFAULT); obs_properties_add_text(props, "input_format", obs_module_text("InputFormat"), OBS_TEXT_DEFAULT); obs_properties_add_bool(props, "hw_decode", obs_module_text("HardwareDecode")); obs_properties_add_bool(props, "clear_on_media_end", obs_module_text("ClearOnMediaEnd")); prop = obs_properties_add_bool(props, "advanced", obs_module_text("Advanced")); obs_property_set_modified_callback(prop, is_advanced_modified); obs_properties_add_bool(props, "force_scale", obs_module_text("ForceFormat")); prop = obs_properties_add_int(props, "audio_buffer_size", obs_module_text("AudioBufferSize"), 1, 9999, 1); obs_property_set_visible(prop, false); prop = obs_properties_add_int(props, "video_buffer_size", obs_module_text("VideoBufferSize"), 1, 9999, 1); obs_property_set_visible(prop, false); prop = obs_properties_add_list(props, "frame_drop", obs_module_text("FrameDropping"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(prop, obs_module_text("DiscardNone"), AVDISCARD_NONE); obs_property_list_add_int(prop, obs_module_text("DiscardDefault"), AVDISCARD_DEFAULT); obs_property_list_add_int(prop, obs_module_text("DiscardNonRef"), AVDISCARD_NONREF); obs_property_list_add_int(prop, obs_module_text("DiscardBiDir"), AVDISCARD_BIDIR); #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 67, 100) obs_property_list_add_int(prop, obs_module_text("DiscardNonIntra"), AVDISCARD_NONINTRA); #endif obs_property_list_add_int(prop, obs_module_text("DiscardNonKey"), AVDISCARD_NONKEY); obs_property_list_add_int(prop, obs_module_text("DiscardAll"), AVDISCARD_ALL); obs_property_set_visible(prop, false); return props; }
static obs_properties_t *ffmpeg_source_getproperties(void *data) { struct ffmpeg_source *s = data; struct dstr filter = {0}; struct dstr path = {0}; UNUSED_PARAMETER(data); obs_properties_t *props = obs_properties_create(); obs_properties_set_flags(props, OBS_PROPERTIES_DEFER_UPDATE); obs_property_t *prop; // use this when obs allows non-readonly paths prop = obs_properties_add_bool(props, "is_local_file", obs_module_text("LocalFile")); obs_property_set_modified_callback(prop, is_local_file_modified); dstr_copy(&filter, obs_module_text("MediaFileFilter.AllMediaFiles")); dstr_cat(&filter, media_filter); dstr_cat(&filter, obs_module_text("MediaFileFilter.VideoFiles")); dstr_cat(&filter, video_filter); dstr_cat(&filter, obs_module_text("MediaFileFilter.AudioFiles")); dstr_cat(&filter, audio_filter); dstr_cat(&filter, obs_module_text("MediaFileFilter.AllFiles")); dstr_cat(&filter, " (*.*)"); if (s && s->input && *s->input) { const char *slash; dstr_copy(&path, s->input); dstr_replace(&path, "\\", "/"); slash = strrchr(path.array, '/'); if (slash) dstr_resize(&path, slash - path.array + 1); } obs_properties_add_path(props, "local_file", obs_module_text("LocalFile"), OBS_PATH_FILE, filter.array, path.array); dstr_free(&filter); dstr_free(&path); prop = obs_properties_add_bool(props, "looping", obs_module_text("Looping")); obs_properties_add_bool(props, "restart_on_activate", obs_module_text("RestartWhenActivated")); obs_properties_add_text(props, "input", obs_module_text("Input"), OBS_TEXT_DEFAULT); obs_properties_add_text(props, "input_format", obs_module_text("InputFormat"), OBS_TEXT_DEFAULT); #ifndef __APPLE__ obs_properties_add_bool(props, "hw_decode", obs_module_text("HardwareDecode")); #endif obs_properties_add_bool(props, "clear_on_media_end", obs_module_text("ClearOnMediaEnd")); prop = obs_properties_add_bool(props, "close_when_inactive", obs_module_text("CloseFileWhenInactive")); obs_property_set_long_description(prop, obs_module_text("CloseFileWhenInactive.ToolTip")); prop = obs_properties_add_list(props, "color_range", obs_module_text("ColorRange"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); obs_property_list_add_int(prop, obs_module_text("ColorRange.Auto"), VIDEO_RANGE_DEFAULT); obs_property_list_add_int(prop, obs_module_text("ColorRange.Partial"), VIDEO_RANGE_PARTIAL); obs_property_list_add_int(prop, obs_module_text("ColorRange.Full"), VIDEO_RANGE_FULL); return props; }
static bool decklink_device_changed(obs_properties_t *props, obs_property_t *list, obs_data_t *settings) { const char *name = obs_data_get_string(settings, DEVICE_NAME); const char *hash = obs_data_get_string(settings, DEVICE_HASH); const char *mode = obs_data_get_string(settings, MODE_NAME); long long modeId = obs_data_get_int(settings, MODE_ID); size_t itemCount = obs_property_list_item_count(list); bool itemFound = false; for (size_t i = 0; i < itemCount; i++) { const char *curHash = obs_property_list_item_string(list, i); if (strcmp(hash, curHash) == 0) { itemFound = true; break; } } if (!itemFound) { obs_property_list_insert_string(list, 0, name, hash); obs_property_list_item_disable(list, 0, true); } obs_property_t *modeList = obs_properties_get(props, MODE_ID); obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT); obs_property_list_clear(modeList); obs_property_list_clear(channelList); obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_NONE, SPEAKERS_UNKNOWN); obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_0CH, SPEAKERS_STEREO); ComPtr<DeckLinkDevice> device; device.Set(deviceEnum->FindByHash(hash)); if (!device) { obs_property_list_add_int(modeList, mode, modeId); obs_property_list_item_disable(modeList, 0, true); } else { const std::vector<DeckLinkDeviceMode*> &modes = device->GetModes(); for (DeckLinkDeviceMode *mode : modes) { obs_property_list_add_int(modeList, mode->GetName().c_str(), mode->GetId()); } if (device->GetMaxChannel() >= 8) { obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_5_1CH, SPEAKERS_5POINT1); obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_5_1CH_BACK, SPEAKERS_5POINT1_SURROUND); obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_7_1CH, SPEAKERS_7POINT1); } } return true; }