Skip to content

Commit

Permalink
obs-filters: Move NVIDIA filters in their own project
Browse files Browse the repository at this point in the history
This commit does the following:
1. Factor out NVIDIA Audio Effects from Noise Suppression filter.
2. Move NVIDIA Audio Effects to a new filter in a new nv-filters
project.
3. Migrate Noise Suppression filter settings to the new filter when
NVIDIA Audio effects were used.
4. Migrate NVIDIA AI Greenscreen to the new nv-filters project for
easier maintainance of all NVIDIA Maxine effects.

Context:
 Currently, the three NVIDIA Audio Effects (noise suppression, room
echo removal, noise suppression + room echo removal combined) are part
of the noise suppression filter.
Historically, it's because a lot of code was shared between speex,
rnnoise & NVIDIA noise suppression.
But the NVIDIA code has become bulkier & cumbersome due to:
- addition of other effects;
- addition of a deferred loading thread.
The factorisation makes the code very difficult to maintain for
(un)readability reasons.
This will make it easier to add other audio effects, should we wish to.
Developers life will be easier too when debugging.
The code has been reorganized and comments added.
I also added a mutex in the process_fx function to avoid a crash when
swapping effects.

Signed-off-by: pkv <[email protected]>
  • Loading branch information
pkviet authored and RytoEX committed Aug 8, 2024
1 parent 37b4018 commit 00c495b
Show file tree
Hide file tree
Showing 17 changed files with 1,746 additions and 665 deletions.
2 changes: 2 additions & 0 deletions plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0)
add_obs_plugin(mac-syphon PLATFORMS MACOS)
add_obs_plugin(mac-videotoolbox PLATFORMS MACOS)
add_obs_plugin(mac-virtualcam PLATFORMS MACOS)
add_obs_plugin(nv-filters PLATFORMS WINDOWS)

check_obs_browser()

Expand Down Expand Up @@ -114,6 +115,7 @@ if(OS_WINDOWS)
add_subdirectory(obs-text)
add_subdirectory(vlc-video)
add_subdirectory(obs-vst)
add_subdirectory(nv-filters)

check_obs_browser()
elseif(OS_MACOS)
Expand Down
32 changes: 32 additions & 0 deletions plugins/nv-filters/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.22...3.25)
if(OS_WINDOWS)
add_library(nv-filters MODULE)
add_library(OBS::nv-filters ALIAS nv-filters)

option(ENABLE_NVAFX "Enable building with NVIDIA Audio Effects SDK (requires redistributable to be installed)" ON)
option(ENABLE_NVVFX "Enable building with NVIDIA Video Effects SDK (requires redistributable to be installed)" ON)

if(ENABLE_NVAFX)
target_enable_feature(nv-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION)
target_sources(nv-filters PRIVATE nvidia-audiofx-filter.c)
else()
target_disable_feature(nv-filters "NVIDIA Audio FX support")
endif()

if(ENABLE_NVVFX)
target_enable_feature(nv-filters "NVIDIA Video FX support" LIBNVVFX_ENABLED)
target_sources(nv-filters PRIVATE nvidia-greenscreen-filter.c)
else()
target_disable_feature(nv-filters "NVIDIA Video FX support")
endif()

configure_file(cmake/windows/obs-module.rc.in nv-filters.rc)
target_sources(nv-filters PRIVATE nv-filters.rc)
target_sources(nv-filters PRIVATE nv-filters.c)

target_link_libraries(nv-filters PRIVATE OBS::libobs $<$<PLATFORM_ID:Windows>:OBS::w32-pthreads>)

# cmake-format: off
set_target_properties_obs(nv-filters PROPERTIES FOLDER plugins PREFIX "")
# cmake-format: on
endif()
24 changes: 24 additions & 0 deletions plugins/nv-filters/cmake/windows/obs-module.rc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
1 VERSIONINFO
FILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "${OBS_COMPANY_NAME}"
VALUE "FileDescription", "NVIDIA A/V Filters"
VALUE "FileVersion", "${OBS_VERSION_CANONICAL}"
VALUE "ProductName", "${OBS_PRODUCT_NAME}"
VALUE "ProductVersion", "${OBS_VERSION_CANONICAL}"
VALUE "Comments", "${OBS_COMMENTS}"
VALUE "LegalCopyright", "${OBS_LEGAL_COPYRIGHT}"
VALUE "InternalName", "nv-filters"
VALUE "OriginalFilename", "nv-filters"
END
END

BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 0x04B0
END
END
95 changes: 95 additions & 0 deletions plugins/nv-filters/data/color.effect
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
float srgb_linear_to_nonlinear_channel(float u)
{
return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1. / 2.4)) - 0.055);
}

float3 srgb_linear_to_nonlinear(float3 v)
{
return float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b));
}

float srgb_nonlinear_to_linear_channel(float u)
{
return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
}

float3 srgb_nonlinear_to_linear(float3 v)
{
return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
}

float3 rec709_to_rec2020(float3 v)
{
float r = dot(v, float3(0.62740389593469903, 0.32928303837788370, 0.043313065687417225));
float g = dot(v, float3(0.069097289358232075, 0.91954039507545871, 0.011362315566309178));
float b = dot(v, float3(0.016391438875150280, 0.088013307877225749, 0.89559525324762401));
return float3(r, g, b);
}

float3 rec2020_to_rec709(float3 v)
{
float r = dot(v, float3(1.6604910021084345, -0.58764113878854951, -0.072849863319884883));
float g = dot(v, float3(-0.12455047452159074, 1.1328998971259603, -0.0083494226043694768));
float b = dot(v, float3(-0.018150763354905303, -0.10057889800800739, 1.1187296613629127));
return float3(r, g, b);
}

float3 reinhard(float3 rgb)
{
rgb /= rgb + float3(1., 1., 1.);
rgb = saturate(rgb);
rgb = pow(rgb, float3(1. / 2.4, 1. / 2.4, 1. / 2.4));
rgb = srgb_nonlinear_to_linear(rgb);
return rgb;
}

float linear_to_st2084_channel(float x)
{
float c = pow(abs(x), 0.1593017578);
return pow((0.8359375 + 18.8515625 * c) / (1. + 18.6875 * c), 78.84375);
}

float st2084_to_linear_channel(float u)
{
float c = pow(abs(u), 1. / 78.84375);
return pow(abs(max(c - 0.8359375, 0.) / (18.8515625 - 18.6875 * c)), 1. / 0.1593017578);
}

float eetf_0_Lmax(float maxRGB1_pq, float Lw, float Lmax)
{
float Lw_pq = linear_to_st2084_channel(Lw / 10000.);
float E1 = saturate(maxRGB1_pq / Lw_pq); // Ensure normalization in case Lw is a lie
float maxLum = linear_to_st2084_channel(Lmax / 10000.) / Lw_pq;
float KS = (1.5 * maxLum) - 0.5;
float E2 = E1;
if (E1 > KS)
{
float T = (E1 - KS) / (1. - KS);
float Tsquared = T * T;
float Tcubed = Tsquared * T;
float P = (2. * Tcubed - 3. * Tsquared + 1.) * KS + (Tcubed - 2. * Tsquared + T) * (1. - KS) + (-2. * Tcubed + 3. * Tsquared) * maxLum;
E2 = P;
}
float E3 = E2;
float E4 = E3 * Lw_pq;
return E4;
}

float3 maxRGB_eetf_internal(float3 rgb_linear, float maxRGB1_linear, float maxRGB1_pq, float Lw, float Lmax)
{
float maxRGB2_pq = eetf_0_Lmax(maxRGB1_pq, Lw, Lmax);
float maxRGB2_linear = st2084_to_linear_channel(maxRGB2_pq);

// avoid divide-by-zero possibility
maxRGB1_linear = max(6.10352e-5, maxRGB1_linear);

rgb_linear *= maxRGB2_linear / maxRGB1_linear;
return rgb_linear;
}

float3 maxRGB_eetf_linear_to_linear(float3 rgb_linear, float Lw, float Lmax)
{
float maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b);
float maxRGB1_pq = linear_to_st2084_channel(maxRGB1_linear);
return maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax);
}
16 changes: 16 additions & 0 deletions plugins/nv-filters/data/locale/en-US.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Nvafx="NVIDIA Audio Effects"
Nvafx.SuppressLevel="Suppression Level"
Nvafx.Intensity="Suppression Intensity"
Nvafx.Method="Method"
Nvafx.Method.Denoiser="NVIDIA Noise Removal"
Nvafx.Method.Dereverb="NVIDIA Room Echo Removal"
Nvafx.Method.DenoiserPlusDereverb="NVIDIA Noise Removal + Room Echo Removal"
Nvafx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Audio SDK is outdated."
Nvvfx.Method.Greenscreen="NVIDIA Background Removal"
Nvvfx.Method.Greenscreen.Mode="Mode"
Nvvfx.Method.Greenscreen.Quality="Quality (higher GPU usage, better quality)"
Nvvfx.Method.Greenscreen.Performance="Performance (lower GPU usage, good quality)"
Nvvfx.Method.Greenscreen.Threshold="Threshold"
Nvvfx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Video SDK is outdated."
Nvvfx.Method.Greenscreen.Processing="Mask refresh frequency in frames"
Nvvfx.Method.Greenscreen.Processing.Hint="This alleviates GPU load by generating a mask every N frames only (2 on default)."
183 changes: 183 additions & 0 deletions plugins/nv-filters/data/rtx_greenscreen.effect
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#include "color.effect"

uniform float4x4 ViewProj;
uniform texture2d image;
uniform float multiplier;

uniform texture2d mask;
uniform float threshold;

sampler_state texSampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};

struct VertData {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};

struct VertInOut {
float2 uv : TEXCOORD0;
float4 pos : POSITION;
};

struct FragData {
float2 uv : TEXCOORD0;
};

struct FragPos {
float4 pos : POSITION;
};

VertInOut VSDefault(VertData v_in)
{
VertInOut v_out;
v_out.uv = v_in.uv;
v_out.pos = mul(float4(v_in.pos.xyz, 1.), ViewProj);
return v_out;
}

FragPos VSConvertUnorm(uint id : VERTEXID)
{
float idHigh = float(id >> 1);
float idLow = float(id & uint(1));

float x = idHigh * 4.0 - 1.0;
float y = idLow * 4.0 - 1.0;

FragPos vert_out;
vert_out.pos = float4(x, y, 0.0, 1.0);
return vert_out;
}

float4 Mask(FragData f_in)
{
float4 rgba = image.Sample(texSampler, f_in.uv);
rgba *= smoothstep(threshold - 0.1,threshold,mask.Sample(texSampler, f_in.uv).a);
return rgba;
}

float4 PSMask(FragData f_in) : TARGET
{
float4 rgba = Mask(f_in);
return rgba;
}

float4 PSMaskMultiply(FragData f_in) : TARGET
{
float4 rgba = Mask(f_in);
rgba.rgb *= multiplier;
return rgba;
}

float4 PSMaskTonemap(FragData f_in) : TARGET
{
float4 rgba = Mask(f_in);
rgba.rgb = rec709_to_rec2020(rgba.rgb);
rgba.rgb = reinhard(rgba.rgb);
rgba.rgb = rec2020_to_rec709(rgba.rgb);
return rgba;
}

float4 PSMaskMultiplyTonemap(FragData f_in) : TARGET
{
float4 rgba = Mask(f_in);
rgba.rgb *= multiplier;
rgba.rgb = rec709_to_rec2020(rgba.rgb);
rgba.rgb = reinhard(rgba.rgb);
rgba.rgb = rec2020_to_rec709(rgba.rgb);
return rgba;
}

float4 PSConvertUnorm(FragPos f_in) : TARGET
{
float4 rgba = image.Load(int3(f_in.pos.xy, 0));
rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
return rgba;
}

float4 PSConvertUnormTonemap(FragPos f_in) : TARGET
{
float4 rgba = image.Load(int3(f_in.pos.xy, 0));
rgba.rgb = rec709_to_rec2020(rgba.rgb);
rgba.rgb = reinhard(rgba.rgb);
rgba.rgb = rec2020_to_rec709(rgba.rgb);
rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
return rgba;
}

float4 PSConvertUnormMultiplyTonemap(FragPos f_in) : TARGET
{
float4 rgba = image.Load(int3(f_in.pos.xy, 0));
rgba.rgb *= multiplier;
rgba.rgb = rec709_to_rec2020(rgba.rgb);
rgba.rgb = reinhard(rgba.rgb);
rgba.rgb = rec2020_to_rec709(rgba.rgb);
rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
return rgba;
}

technique Draw
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSMask(f_in);
}
}

technique DrawMultiply
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSMaskMultiply(f_in);
}
}

technique DrawTonemap
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSMaskTonemap(f_in);
}
}

technique DrawMultiplyTonemap
{
pass
{
vertex_shader = VSDefault(v_in);
pixel_shader = PSMaskMultiplyTonemap(f_in);
}
}

technique ConvertUnorm
{
pass
{
vertex_shader = VSConvertUnorm(id);
pixel_shader = PSConvertUnorm(f_in);
}
}

technique ConvertUnormTonemap
{
pass
{
vertex_shader = VSConvertUnorm(id);
pixel_shader = PSConvertUnormTonemap(f_in);
}
}

technique ConvertUnormMultiplyTonemap
{
pass
{
vertex_shader = VSConvertUnorm(id);
pixel_shader = PSConvertUnormMultiplyTonemap(f_in);
}
}
Loading

0 comments on commit 00c495b

Please sign in to comment.