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 *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; }
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->effect) { 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; }
bool load_graphics_offsets(bool is32bit) { char *offset_exe_path = NULL; struct dstr offset_exe = {0}; char *config_ini = NULL; struct dstr str = {0}; os_process_pipe_t *pp; bool success = false; char data[128]; dstr_copy(&offset_exe, "get-graphics-offsets"); dstr_cat(&offset_exe, is32bit ? "32.exe" : "64.exe"); offset_exe_path = obs_module_file(offset_exe.array); pp = os_process_pipe_create(offset_exe_path, "r"); if (!pp) { blog(LOG_INFO, "load_graphics_offsets: Failed to start '%s'", offset_exe.array); goto error; } for (;;) { size_t len = os_process_pipe_read(pp, (uint8_t*)data, 128); if (!len) break; dstr_ncat(&str, data, len); } config_ini = obs_module_config_path(is32bit ? "32.ini" : "64.ini"); os_quick_write_utf8_file_safe(config_ini, str.array, str.len, false, "tmp", NULL); bfree(config_ini); success = load_offsets_from_string(is32bit ? &offsets32 : &offsets64, str.array); if (!success) { blog(LOG_INFO, "load_graphics_offsets: Failed to load string"); } os_process_pipe_destroy(pp); error: bfree(offset_exe_path); dstr_free(&offset_exe); dstr_free(&str); return success; }
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; }
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; }
bool obs_module_load(void) { char *local_dir = obs_module_file(""); char *cache_dir = obs_module_config_path(""); if (cache_dir) { update_info = update_info_create( RTMP_SERVICES_LOG_STR, RTMP_SERVICES_VER_STR, RTMP_SERVICES_URL, local_dir, cache_dir, confirm_service_file, NULL); } bfree(local_dir); bfree(cache_dir); obs_register_service(&rtmp_common_service); obs_register_service(&rtmp_custom_service); return true; }
bool obs_module_load(void) { obs_so = NULL; bool success; char *obs_browser_path = obs_module_file("obs-browser"); if (obs_browser_path) { obs_so = os_dlopen(obs_browser_path); if (obs_so) { obs_actual_load = os_dlsym(obs_so, "obs_module_load"); obs_actual_unload = os_dlsym(obs_so, "obs_module_unload"); obs_actual_module_set_pointer = os_dlsym(obs_so, "obs_actual_module_so"); if (obs_actual_load && obs_actual_unload && obs_actual_module_set_pointer) { obs_actual_module_set_pointer( obs_module_pointer); if (obs_actual_load()) { goto success; } } } } error: success = false; if (obs_so) os_dlclose(obs_so); success: if (obs_browser_path) bfree(obs_browser_path); return success; }
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 };
void ScriptsTool::on_addScripts_clicked() { const char **formats = obs_scripting_supported_formats(); const char **cur_format = formats; QString extensions; QString filter; while (*cur_format) { if (!extensions.isEmpty()) extensions += QStringLiteral(" "); extensions += QStringLiteral("*."); extensions += *cur_format; cur_format++; } if (!extensions.isEmpty()) { filter += obs_module_text("FileFilter.ScriptFiles"); filter += QStringLiteral(" ("); filter += extensions; filter += QStringLiteral(")"); } if (filter.isEmpty()) return; static std::string lastBrowsedDir; if (lastBrowsedDir.empty()) { BPtr<char> baseScriptPath = obs_module_file("scripts"); lastBrowsedDir = baseScriptPath; } QFileDialog dlg(this, obs_module_text("AddScripts")); dlg.setFileMode(QFileDialog::ExistingFiles); dlg.setDirectory(QDir(lastBrowsedDir.c_str())); dlg.setNameFilter(filter); dlg.exec(); QStringList files = dlg.selectedFiles(); if (!files.count()) return; lastBrowsedDir = dlg.directory().path().toUtf8().constData(); for (const QString &file : files) { QByteArray pathBytes = file.toUtf8(); const char *path = pathBytes.constData(); if (scriptData->ScriptOpened(path)) { continue; } obs_script_t *script = obs_script_create(path, NULL); if (script) { const char *script_file = obs_script_get_file(script); scriptData->scripts.emplace_back(script); QListWidgetItem *item = new QListWidgetItem(script_file); item->setData(Qt::UserRole, QString(file)); ui->scripts->addItem(item); OBSData settings = obs_data_create(); obs_data_release(settings); obs_properties_t *prop = obs_script_get_properties(script); obs_properties_apply_settings(prop, settings); } } }