Example #1
0
/**
 * 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;
}
Example #2
0
/*
 * 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);
}
Example #3
0
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;
}
Example #4
0
/*
 * 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++;
	}
}
Example #5
0
/*
 * 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);
}
Example #6
0
/*
 * 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);
}
Example #7
0
/*
 * 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);
}
Example #8
0
/*
 * 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++;
	}
}
Example #9
0
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;
}
Example #10
0
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;
}
Example #12
0
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;
}
Example #14
0
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;
}
Example #15
0
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;
}