Exemple #1
0
static int obs_init_graphics(struct obs_video_info *ovi)
{
	struct obs_core_video *video = &obs->video;
	struct gs_init_data graphics_data;
	bool success = true;
	int errorcode;

	make_gs_init_data(&graphics_data, ovi);

	errorcode = gs_create(&video->graphics, ovi->graphics_module,
			&graphics_data);
	if (errorcode != GS_SUCCESS) {
		switch (errorcode) {
		case GS_ERROR_MODULE_NOT_FOUND:
			return OBS_VIDEO_MODULE_NOT_FOUND;
		case GS_ERROR_NOT_SUPPORTED:
			return OBS_VIDEO_NOT_SUPPORTED;
		default:
			return OBS_VIDEO_FAIL;
		}
	}

	gs_enter_context(video->graphics);

	char *filename = find_libobs_data_file("default.effect");
	video->default_effect = gs_effect_create_from_file(filename,
			NULL);
	bfree(filename);

	if (gs_get_device_type() == GS_DEVICE_OPENGL) {
		filename = find_libobs_data_file("default_rect.effect");
		video->default_rect_effect = gs_effect_create_from_file(
				filename, NULL);
		bfree(filename);
	}

	filename = find_libobs_data_file("solid.effect");
	video->solid_effect = gs_effect_create_from_file(filename,
			NULL);
	bfree(filename);

	filename = find_libobs_data_file("format_conversion.effect");
	video->conversion_effect = gs_effect_create_from_file(filename,
			NULL);
	bfree(filename);

	if (!video->default_effect)
		success = false;
	if (gs_get_device_type() == GS_DEVICE_OPENGL) {
		if (!video->default_rect_effect)
			success = false;
	}
	if (!video->solid_effect)
		success = false;
	if (!video->conversion_effect)
		success = false;

	gs_leave_context();
	return success ? OBS_VIDEO_SUCCESS : OBS_VIDEO_FAIL;
}
static void *crop_filter_create(obs_data_t *settings, obs_source_t *context)
{
	struct crop_filter_data *filter = bzalloc(sizeof(*filter));
	char *effect_path = obs_module_file("crop_filter.effect");

	filter->context = context;

	obs_enter_graphics();
	filter->effect = gs_effect_create_from_file(effect_path, NULL);
	obs_leave_graphics();

	bfree(effect_path);

	if (!filter->effect) {
		bfree(filter);
		return NULL;
	}

	filter->param_mul = gs_effect_get_param_by_name(filter->effect,
			"mul_val");
	filter->param_add = gs_effect_get_param_by_name(filter->effect,
			"add_val");

	obs_source_update(context, settings);
	return filter;
}
Exemple #3
0
static void *color_filter_create(obs_data_t *settings, obs_source_t *context)
{
	struct color_filter_data *filter =
		bzalloc(sizeof(struct color_filter_data));
	char *effect_path = obs_module_file("color_filter.effect");

	filter->context = context;

	obs_enter_graphics();

	filter->effect = gs_effect_create_from_file(effect_path, NULL);
	if (filter) {
		filter->color_param = gs_effect_get_param_by_name(
				filter->effect, "color");
		filter->contrast_param = gs_effect_get_param_by_name(
				filter->effect, "contrast");
		filter->brightness_param = gs_effect_get_param_by_name(
				filter->effect, "brightness");
		filter->gamma_param = gs_effect_get_param_by_name(
				filter->effect, "gamma");
	}

	obs_leave_graphics();

	bfree(effect_path);

	if (!filter->effect) {
		color_filter_destroy(filter);
		return NULL;
	}

	color_filter_update(filter, settings);
	return filter;
}
Exemple #4
0
void *fade_create(obs_data_t *settings, obs_source_t *source)
{
    struct fade_info *fade;
    char *file = obs_module_file("fade_transition.effect");
    gs_effect_t *effect;

    obs_enter_graphics();
    effect = gs_effect_create_from_file(file, NULL);
    obs_leave_graphics();
    bfree(file);

    if (!effect) {
        blog(LOG_ERROR, "Could not find fade_transition.effect");
        return NULL;
    }

    fade = bmalloc(sizeof(*fade));
    fade->source = source;
    fade->effect = effect;
    fade->a_param = gs_effect_get_param_by_name(effect, "tex_a");
    fade->b_param = gs_effect_get_param_by_name(effect, "tex_b");
    fade->fade_param = gs_effect_get_param_by_name(effect, "fade_val");

    UNUSED_PARAMETER(settings);
    return fade;
}
static void *sharpness_create(obs_data_t *settings, obs_source_t *context)
{
	struct sharpness_data *filter =
		bzalloc(sizeof(struct sharpness_data));
	char *effect_path = obs_module_file("sharpness.effect");

	filter->context = context;

	obs_enter_graphics();

	filter->effect = gs_effect_create_from_file(effect_path, NULL);
	if (filter) {
		filter->sharpness_param = gs_effect_get_param_by_name(
			filter->effect, "sharpness");
		filter->texture_width = gs_effect_get_param_by_name(
			filter->effect, "texture_width");
		filter->texture_height = gs_effect_get_param_by_name(
			filter->effect, "texture_height");
	}

	obs_leave_graphics();

	bfree(effect_path);

	if (!filter->effect) {
		sharpness_destroy(filter);
		return NULL;
	}

	sharpness_update(filter, settings);
	return filter;
}
static void color_grade_filter_update(void *data, obs_data_t *settings)
{
	struct lut_filter_data *filter = data;

	const char *path = obs_data_get_string(settings, SETTING_IMAGE_PATH);
	double clut_amount = obs_data_get_double(settings, SETTING_CLUT_AMOUNT);

	bfree(filter->file);
	if (path)
		filter->file = bstrdup(path);

	obs_enter_graphics();
	gs_image_file_free(&filter->image);
	obs_leave_graphics();

	gs_image_file_init(&filter->image, path);

	obs_enter_graphics();

	gs_image_file_init_texture(&filter->image);

	filter->target = filter->image.texture;
	filter->clut_amount = (float)clut_amount;

	char *effect_path = obs_module_file("color_grade_filter.effect");
	gs_effect_destroy(filter->effect);
	filter->effect = gs_effect_create_from_file(effect_path, NULL);
	bfree(effect_path);

	obs_leave_graphics();
}
static void *chroma_key_create(obs_data_t *settings, obs_source_t *context)
{
	struct chroma_key_filter_data *filter =
		bzalloc(sizeof(struct chroma_key_filter_data));
	char *effect_path = obs_module_file("chroma_key_filter.effect");

	filter->context = context;

	obs_enter_graphics();

	filter->effect = gs_effect_create_from_file(effect_path, NULL);
	if (filter) {
		filter->color_param = gs_effect_get_param_by_name(
				filter->effect, "color");
		filter->contrast_param = gs_effect_get_param_by_name(
				filter->effect, "contrast");
		filter->brightness_param = gs_effect_get_param_by_name(
				filter->effect, "brightness");
		filter->gamma_param = gs_effect_get_param_by_name(
				filter->effect, "gamma");
		filter->chroma_param = gs_effect_get_param_by_name(
				filter->effect, "chroma_key");
		filter->key_rgb_param = gs_effect_get_param_by_name(
				filter->effect, "key_rgb");
		filter->pixel_size_param = gs_effect_get_param_by_name(
				filter->effect, "pixel_size");
		filter->similarity_param = gs_effect_get_param_by_name(
				filter->effect, "similarity");
		filter->smoothness_param = gs_effect_get_param_by_name(
				filter->effect, "smoothness");
		filter->spill_param = gs_effect_get_param_by_name(
				filter->effect, "spill");
	}

	obs_leave_graphics();

	bfree(effect_path);

	if (!filter->effect) {
		chroma_key_destroy(filter);
		return NULL;
	}

	chroma_key_update(filter, settings);
	return filter;
}
Exemple #8
0
static void *filter_create(obs_data_t settings, obs_source_t source)
{
	struct test_filter *tf = bzalloc(sizeof(struct test_filter));
	char *effect_file;

	obs_enter_graphics();

	effect_file = obs_module_file("test.effect");

	tf->source = source;
	tf->whatever = gs_effect_create_from_file(effect_file, NULL);
	bfree(effect_file);
	if (!tf->whatever) {
		filter_destroy(tf);
		return NULL;
	}

	obs_leave_graphics();

	UNUSED_PARAMETER(settings);
	return tf;
}
static void ft2_source_update(void *data, obs_data_t settings)
{
	struct ft2_source *srcdata = data;
	obs_data_t font_obj = obs_data_get_obj(settings, "font");
	bool vbuf_needs_update = false;
	bool word_wrap = false;
	uint32_t color[2];
	uint32_t custom_width = 0;

	const char *font_name  = obs_data_get_string(font_obj, "face");
	const char *font_style = obs_data_get_string(font_obj, "style");
	uint16_t   font_size   = (uint16_t)obs_data_get_int(font_obj, "size");
	uint32_t   font_flags  = (uint32_t)obs_data_get_int(font_obj, "flags");

	if (!font_obj)
		return;

	srcdata->drop_shadow = obs_data_get_bool(settings, "drop_shadow");
	srcdata->outline_text = obs_data_get_bool(settings, "outline");
	word_wrap = obs_data_get_bool(settings, "word_wrap");

	color[0] = (uint32_t)obs_data_get_int(settings, "color1");
	color[1] = (uint32_t)obs_data_get_int(settings, "color2");

	custom_width = (uint32_t)obs_data_get_int(settings, "custom_width");
	if (custom_width >= 100) {
		if (custom_width != srcdata->custom_width) {
			srcdata->custom_width = custom_width;
			vbuf_needs_update = true;
		}
	}
	else {
		if (srcdata->custom_width >= 100)
			vbuf_needs_update = true;
		srcdata->custom_width = 0;
	}

	if (word_wrap != srcdata->word_wrap) {
		srcdata->word_wrap = word_wrap;
		vbuf_needs_update = true;
	}

	if (color[0] != srcdata->color[0] || color[1] != srcdata->color[1]) {
		srcdata->color[0] = color[0];
		srcdata->color[1] = color[1];
		vbuf_needs_update = true;
	}

	bool from_file = obs_data_get_bool(settings, "from_file");
	bool chat_log_mode = obs_data_get_bool(settings, "log_mode");

	srcdata->log_mode = chat_log_mode;

	if (ft2_lib == NULL) goto error;

	if (srcdata->draw_effect == NULL) {
		char *effect_file = NULL;
		char *error_string = NULL;

		effect_file =
			obs_module_file("text_default.effect");

		if (effect_file) {
			obs_enter_graphics();
			srcdata->draw_effect = gs_effect_create_from_file(
				effect_file, &error_string);
			obs_leave_graphics();

			bfree(effect_file);
			if (error_string != NULL)
				bfree(error_string);
		}
	}

	if (srcdata->font_size != font_size ||
	    srcdata->from_file != from_file)
		vbuf_needs_update = true;

	srcdata->file_load_failed = false;
	srcdata->from_file = from_file;

	if (srcdata->font_name != NULL) {
		if (strcmp(font_name,  srcdata->font_name)  == 0 &&
		    strcmp(font_style, srcdata->font_style) == 0 &&
		    font_flags == srcdata->font_flags &&
		    font_size  == srcdata->font_size)
			goto skip_font_load;

		bfree(srcdata->font_name);
		bfree(srcdata->font_style);
		srcdata->font_name = NULL;
		srcdata->font_style = NULL;
		srcdata->max_h = 0;
	}

	srcdata->font_name  = bstrdup(font_name);
	srcdata->font_style = bstrdup(font_style);
	srcdata->font_size  = font_size;
	srcdata->font_flags = font_flags;

	if (!init_font(srcdata) || srcdata->font_face == NULL) {
		blog(LOG_WARNING, "FT2-text: Failed to load font %s",
			srcdata->font_name);
		goto error;
	}
	else {
		FT_Set_Pixel_Sizes(srcdata->font_face, 0, srcdata->font_size); 
		FT_Select_Charmap(srcdata->font_face, FT_ENCODING_UNICODE);
	}

	if (srcdata->texbuf != NULL) {
		bfree(srcdata->texbuf);
		srcdata->texbuf = NULL;
	}
	srcdata->texbuf = bzalloc(texbuf_w * texbuf_h * 4);

	if (srcdata->font_face)
		cache_standard_glyphs(srcdata);
skip_font_load:;
	if (from_file) {
		const char *tmp = obs_data_get_string(settings, "text_file");
		if (!tmp || !*tmp) {
			blog(LOG_WARNING,
				"FT2-text: Failed to open %s for reading", tmp);
			goto error;
		}

		if (srcdata->text_file != NULL &&
		    strcmp(srcdata->text_file, tmp) == 0 &&
		    !vbuf_needs_update)
			goto error;

		bfree(srcdata->text_file);

		srcdata->text_file = bstrdup(tmp);
		if (chat_log_mode)
			read_from_end(srcdata, tmp);
		else
			load_text_from_file(srcdata, tmp);
		srcdata->last_checked = os_gettime_ns();
	}
	else {
		const char *tmp = obs_data_get_string(settings, "text");
		if (!tmp || !*tmp) goto error;

		if (srcdata->text != NULL) {
			bfree(srcdata->text);
			srcdata->text = NULL;
		}

		os_utf8_to_wcs_ptr(tmp, strlen(tmp), &srcdata->text);
	}

	if (srcdata->font_face) {
		cache_glyphs(srcdata, srcdata->text);
		set_up_vertex_buffer(srcdata);
	}

error:
	obs_data_release(font_obj);
}
/*
 * This function is called (see bottom of this file for more details)
 * whenever the OBS filter interface changes. So when the user is messing
 * with a slider this function is called to update the internal settings
 * in OBS, and hence the settings being passed to the CPU/GPU.
 */
static void color_correction_filter_update(void *data, obs_data_t *settings)
{
	struct color_correction_filter_data *filter = data;

	/* Build our Gamma numbers. */
	double gamma = obs_data_get_double(settings, SETTING_GAMMA);
	gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0));
	vec3_set(&filter->gamma, (float)gamma, (float)gamma, (float)gamma);

	/* Build our contrast number. */
	filter->contrast = (float)obs_data_get_double(settings,
			SETTING_CONTRAST) + 1.0f;
	float one_minus_con = (1.0f - filter->contrast) / 2.0f;

	/* Now let's build our Contrast matrix. */
	filter->con_matrix = (struct matrix4)
	{
		filter->contrast, 0.0f, 0.0f, 0.0f,
		0.0f, filter->contrast, 0.0f, 0.0f,
		0.0f, 0.0f, filter->contrast, 0.0f,
		one_minus_con, one_minus_con, one_minus_con, 1.0f
	};

	/* Build our brightness number. */
	filter->brightness = (float)obs_data_get_double(settings,
			SETTING_BRIGHTNESS);

	/*
	 * Now let's build our Brightness matrix.
	 * Earlier (in the function color_correction_filter_create) we set
	 * this matrix to the identity matrix, so now we only need
	 * to set the 3 variables that have changed.
	 */
	filter->bright_matrix.t.x = filter->brightness;
	filter->bright_matrix.t.y = filter->brightness;
	filter->bright_matrix.t.z = filter->brightness;

	/* Build our Saturation number. */
	filter->saturation = (float)obs_data_get_double(settings,
			SETTING_SATURATION) + 1.0f;

	/* Factor in the selected color weights. */
	float one_minus_sat = (1.0f - filter->saturation) / 3.0f;
	float sat_val = one_minus_sat + filter->saturation;

	/* Now we build our Saturation matrix. */
	filter->sat_matrix = (struct matrix4)
	{
		sat_val, one_minus_sat, one_minus_sat, 0.0f,
		one_minus_sat, sat_val, one_minus_sat, 0.0f,
		one_minus_sat, one_minus_sat, sat_val, 0.0f,
		0.0f, 0.0f, 0.0f, 1.0f
	};

	/* Build our Hue number. */
	filter->hue_shift = (float)obs_data_get_double(settings,
			SETTING_HUESHIFT);

	/* Build our Transparency number. */
	filter->opacity = (float)obs_data_get_int(settings,
			SETTING_OPACITY) * 0.01f;

	/* Hue is the radian of 0 to 360 degrees. */
	float half_angle = 0.5f * (float)(filter->hue_shift / (180.0f / M_PI));

	/* Pseudo-Quaternion To Matrix. */
	float rot_quad1 = root3 * (float)sin(half_angle);
	vec3_set(&filter->rot_quaternion, rot_quad1, rot_quad1,
			rot_quad1);
	filter->rot_quaternion_w = (float)cos(half_angle);

	vec3_mul(&filter->cross, &filter->rot_quaternion,
			&filter->rot_quaternion);
	vec3_mul(&filter->square, &filter->rot_quaternion,
			&filter->rot_quaternion);
	vec3_mulf(&filter->wimag, &filter->rot_quaternion,
			filter->rot_quaternion_w);

	vec3_mulf(&filter->square, &filter->square, 2.0f);
	vec3_sub(&filter->diag, &filter->half_unit, &filter->square);
	vec3_add(&filter->a_line, &filter->cross, &filter->wimag);
	vec3_sub(&filter->b_line, &filter->cross, &filter->wimag);

	/* Now we build our Hue and Opacity matrix. */
	filter->hue_op_matrix = (struct matrix4)
	{
		filter->diag.x * 2.0f,
		filter->b_line.z * 2.0f,
		filter->a_line.y * 2.0f,
		0.0f,

		filter->a_line.z * 2.0f,
		filter->diag.y * 2.0f,
		filter->b_line.x * 2.0f,
		0.0f,

		filter->b_line.y * 2.0f,
		filter->a_line.x * 2.0f,
		filter->diag.z * 2.0f,
		0.0f,

		0.0f, 0.0f, 0.0f, filter->opacity
	};

	/* Now get the overlay color data. */
	uint32_t color = (uint32_t)obs_data_get_int(settings,
			SETTING_COLOR);
	vec4_from_rgba(&filter->color, color);

	/*
	* Now let's build our Color 'overlay' matrix.
	* Earlier (in the function color_correction_filter_create) we set
	* this matrix to the identity matrix, so now we only need
	* to set the 6 variables that have changed.
	*/
	filter->color_matrix.x.x = filter->color.x;
	filter->color_matrix.y.y = filter->color.y;
	filter->color_matrix.z.z = filter->color.z;

	filter->color_matrix.t.x = filter->color.w *
			filter->color.x;
	filter->color_matrix.t.y = filter->color.w *
			filter->color.y;
	filter->color_matrix.t.z = filter->color.w *
			filter->color.z;


	/* First we apply the Contrast & Brightness matrix. */
	matrix4_mul(&filter->final_matrix, &filter->bright_matrix,
			&filter->con_matrix);
	/* Now we apply the Saturation matrix. */
	matrix4_mul(&filter->final_matrix, &filter->final_matrix,
			&filter->sat_matrix);
	/* Next we apply the Hue+Opacity matrix. */
	matrix4_mul(&filter->final_matrix, &filter->final_matrix,
			&filter->hue_op_matrix);
	/* Lastly we apply the Color Wash matrix. */
	matrix4_mul(&filter->final_matrix, &filter->final_matrix,
			&filter->color_matrix);
}

/*
 * Since this is C we have to be careful when destroying/removing items from
 * OBS. Jim has added several useful functions to help keep memory leaks to
 * a minimum, and handle the destruction and construction of these filters.
 */
static void color_correction_filter_destroy(void *data)
{
	struct color_correction_filter_data *filter = data;

	if (filter->effect) {
		obs_enter_graphics();
		gs_effect_destroy(filter->effect);
		obs_leave_graphics();
	}

	bfree(data);
}

/*
 * When you apply a filter OBS creates it, and adds it to the source. OBS also
 * starts rendering it immediately. This function doesn't just 'create' the
 * filter, it also calls the render function (farther below) that contains the
 * actual rendering code.
 */
static void *color_correction_filter_create(obs_data_t *settings,
	obs_source_t *context)
{
	/*
	* Because of limitations of pre-c99 compilers, you can't create an
	* array that doesn't have a known size at compile time. The below
	* function calculates the size needed and allocates memory to
	* handle the source.
	*/
	struct color_correction_filter_data *filter =
		bzalloc(sizeof(struct color_correction_filter_data));

	/*
	 * By default the effect file is stored in the ./data directory that
	 * your filter resides in.
	 */
	char *effect_path = obs_module_file("color_correction_filter.effect");

	filter->context = context;

	/* Set/clear/assign for all necessary vectors. */
	vec3_set(&filter->half_unit, 0.5f, 0.5f, 0.5f);
	matrix4_identity(&filter->bright_matrix);
	matrix4_identity(&filter->color_matrix);

	/* Here we enter the GPU drawing/shader portion of our code. */
	obs_enter_graphics();

	/* Load the shader on the GPU. */
	filter->effect = gs_effect_create_from_file(effect_path, NULL);

	/* If the filter is active pass the parameters to the filter. */
	if (filter->effect) {
		filter->gamma_param = gs_effect_get_param_by_name(
				filter->effect, SETTING_GAMMA);
		filter->final_matrix_param = gs_effect_get_param_by_name(
				filter->effect, "color_matrix");
	}

	obs_leave_graphics();

	bfree(effect_path);

	/*
	 * If the filter has been removed/deactivated, destroy the filter
	 * and exit out so we don't crash OBS by telling it to update
	 * values that don't exist anymore.
	 */
	if (!filter->effect) {
		color_correction_filter_destroy(filter);
		return NULL;
	}

	/*
	 * It's important to call the update function here. If we don't
	 * we could end up with the user controlled sliders and values
	 * updating, but the visuals not updating to match.
	 */
	color_correction_filter_update(filter, settings);
	return filter;
}

/* This is where the actual rendering of the filter takes place. */
static void color_correction_filter_render(void *data, gs_effect_t *effect)
{
	struct color_correction_filter_data *filter = data;

	if (!obs_source_process_filter_begin(filter->context, GS_RGBA,
			OBS_ALLOW_DIRECT_RENDERING))
		return;

	/* Now pass the interface variables to the .effect file. */
	gs_effect_set_vec3(filter->gamma_param, &filter->gamma);
	gs_effect_set_matrix4(filter->final_matrix_param, &filter->final_matrix);

	obs_source_process_filter_end(filter->context, filter->effect, 0, 0);

	UNUSED_PARAMETER(effect);
}

/*
 * This function sets the interface. the types (add_*_Slider), the type of
 * data collected (int), the internal name, user-facing name, minimum,
 * maximum and step values. While a custom interface can be built, for a
 * simple filter like this it's better to use the supplied functions.
 */
static obs_properties_t *color_correction_filter_properties(void *data)
{
	obs_properties_t *props = obs_properties_create();

	obs_properties_add_float_slider(props, SETTING_GAMMA,
			TEXT_GAMMA, -3.0f, 3.0f, 0.01f);

	obs_properties_add_float_slider(props, SETTING_CONTRAST,
			TEXT_CONTRAST, -2.0f, 2.0f, 0.01f);
	obs_properties_add_float_slider(props, SETTING_BRIGHTNESS,
			TEXT_BRIGHTNESS, -1.0f, 1.0f, 0.01f);
	obs_properties_add_float_slider(props, SETTING_SATURATION,
			TEXT_SATURATION, -1.0f, 5.0f, 0.01f);
	obs_properties_add_float_slider(props, SETTING_HUESHIFT,
			TEXT_HUESHIFT, -180.0f, 180.0f, 0.01f);
	obs_properties_add_int_slider(props, SETTING_OPACITY,
			TEXT_OPACITY, 0, 100, 1);

	obs_properties_add_color(props, SETTING_COLOR, TEXT_COLOR);

	UNUSED_PARAMETER(data);
	return props;
}

/*
 * As the functions' namesake, this provides the default settings for any
 * options you wish to provide a default for. Try to select defaults that
 * make sense to the end user, or that don't effect the data.
 * *NOTE* this function is completely optional, as is providing a default
 * for any particular setting.
 */
static void color_correction_filter_defaults(obs_data_t *settings)
{
	obs_data_set_default_double(settings, SETTING_GAMMA, 0.0);
	obs_data_set_default_double(settings, SETTING_CONTRAST, 0.0);
	obs_data_set_default_double(settings, SETTING_BRIGHTNESS, 0.0);
	obs_data_set_default_double(settings,
			SETTING_SATURATION, 0.0);
	obs_data_set_default_double(settings, SETTING_HUESHIFT, 0.0);
	obs_data_set_default_double(settings, SETTING_OPACITY, 100.0);
	obs_data_set_default_int(settings, SETTING_COLOR, 0xFFFFFF);
}

/*
 * So how does OBS keep track of all these plug-ins/filters? How does OBS know
 * which function to call when it needs to update a setting? Or a source? Or
 * what type of source this is?
 *
 * OBS does it through the obs_source_info_struct. Notice how variables are
 * assigned the name of a function? Notice how the function name has the
 * variable name in it? While not mandatory, it helps a ton for you (and those
 * reading your code) to follow this convention.
 */
struct obs_source_info color_filter = {
	.id = "color_filter",
	.type = OBS_SOURCE_TYPE_FILTER,
	.output_flags = OBS_SOURCE_VIDEO,
	.get_name = color_correction_filter_name,
	.create = color_correction_filter_create,
	.destroy = color_correction_filter_destroy,
	.video_render = color_correction_filter_render,
	.update = color_correction_filter_update,
	.get_properties = color_correction_filter_properties,
	.get_defaults = color_correction_filter_defaults
};