Skip to content

Commit 00c495b

Browse files
pkvietRytoEX
authored andcommitted
obs-filters: Move NVIDIA filters in their own project
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 <pkv@obsproject.com>
1 parent 37b4018 commit 00c495b

17 files changed

Lines changed: 1746 additions & 665 deletions

plugins/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ if(OBS_CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.0)
5555
add_obs_plugin(mac-syphon PLATFORMS MACOS)
5656
add_obs_plugin(mac-videotoolbox PLATFORMS MACOS)
5757
add_obs_plugin(mac-virtualcam PLATFORMS MACOS)
58+
add_obs_plugin(nv-filters PLATFORMS WINDOWS)
5859

5960
check_obs_browser()
6061

@@ -114,6 +115,7 @@ if(OS_WINDOWS)
114115
add_subdirectory(obs-text)
115116
add_subdirectory(vlc-video)
116117
add_subdirectory(obs-vst)
118+
add_subdirectory(nv-filters)
117119

118120
check_obs_browser()
119121
elseif(OS_MACOS)

plugins/nv-filters/CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
cmake_minimum_required(VERSION 3.22...3.25)
2+
if(OS_WINDOWS)
3+
add_library(nv-filters MODULE)
4+
add_library(OBS::nv-filters ALIAS nv-filters)
5+
6+
option(ENABLE_NVAFX "Enable building with NVIDIA Audio Effects SDK (requires redistributable to be installed)" ON)
7+
option(ENABLE_NVVFX "Enable building with NVIDIA Video Effects SDK (requires redistributable to be installed)" ON)
8+
9+
if(ENABLE_NVAFX)
10+
target_enable_feature(nv-filters "NVIDIA Audio FX support" LIBNVAFX_ENABLED HAS_NOISEREDUCTION)
11+
target_sources(nv-filters PRIVATE nvidia-audiofx-filter.c)
12+
else()
13+
target_disable_feature(nv-filters "NVIDIA Audio FX support")
14+
endif()
15+
16+
if(ENABLE_NVVFX)
17+
target_enable_feature(nv-filters "NVIDIA Video FX support" LIBNVVFX_ENABLED)
18+
target_sources(nv-filters PRIVATE nvidia-greenscreen-filter.c)
19+
else()
20+
target_disable_feature(nv-filters "NVIDIA Video FX support")
21+
endif()
22+
23+
configure_file(cmake/windows/obs-module.rc.in nv-filters.rc)
24+
target_sources(nv-filters PRIVATE nv-filters.rc)
25+
target_sources(nv-filters PRIVATE nv-filters.c)
26+
27+
target_link_libraries(nv-filters PRIVATE OBS::libobs $<$<PLATFORM_ID:Windows>:OBS::w32-pthreads>)
28+
29+
# cmake-format: off
30+
set_target_properties_obs(nv-filters PROPERTIES FOLDER plugins PREFIX "")
31+
# cmake-format: on
32+
endif()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
1 VERSIONINFO
2+
FILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0
3+
BEGIN
4+
BLOCK "StringFileInfo"
5+
BEGIN
6+
BLOCK "040904B0"
7+
BEGIN
8+
VALUE "CompanyName", "${OBS_COMPANY_NAME}"
9+
VALUE "FileDescription", "NVIDIA A/V Filters"
10+
VALUE "FileVersion", "${OBS_VERSION_CANONICAL}"
11+
VALUE "ProductName", "${OBS_PRODUCT_NAME}"
12+
VALUE "ProductVersion", "${OBS_VERSION_CANONICAL}"
13+
VALUE "Comments", "${OBS_COMMENTS}"
14+
VALUE "LegalCopyright", "${OBS_LEGAL_COPYRIGHT}"
15+
VALUE "InternalName", "nv-filters"
16+
VALUE "OriginalFilename", "nv-filters"
17+
END
18+
END
19+
20+
BLOCK "VarFileInfo"
21+
BEGIN
22+
VALUE "Translation", 0x0409, 0x04B0
23+
END
24+
END
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
float srgb_linear_to_nonlinear_channel(float u)
2+
{
3+
return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1. / 2.4)) - 0.055);
4+
}
5+
6+
float3 srgb_linear_to_nonlinear(float3 v)
7+
{
8+
return float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b));
9+
}
10+
11+
float srgb_nonlinear_to_linear_channel(float u)
12+
{
13+
return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4);
14+
}
15+
16+
float3 srgb_nonlinear_to_linear(float3 v)
17+
{
18+
return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));
19+
}
20+
21+
float3 rec709_to_rec2020(float3 v)
22+
{
23+
float r = dot(v, float3(0.62740389593469903, 0.32928303837788370, 0.043313065687417225));
24+
float g = dot(v, float3(0.069097289358232075, 0.91954039507545871, 0.011362315566309178));
25+
float b = dot(v, float3(0.016391438875150280, 0.088013307877225749, 0.89559525324762401));
26+
return float3(r, g, b);
27+
}
28+
29+
float3 rec2020_to_rec709(float3 v)
30+
{
31+
float r = dot(v, float3(1.6604910021084345, -0.58764113878854951, -0.072849863319884883));
32+
float g = dot(v, float3(-0.12455047452159074, 1.1328998971259603, -0.0083494226043694768));
33+
float b = dot(v, float3(-0.018150763354905303, -0.10057889800800739, 1.1187296613629127));
34+
return float3(r, g, b);
35+
}
36+
37+
float3 reinhard(float3 rgb)
38+
{
39+
rgb /= rgb + float3(1., 1., 1.);
40+
rgb = saturate(rgb);
41+
rgb = pow(rgb, float3(1. / 2.4, 1. / 2.4, 1. / 2.4));
42+
rgb = srgb_nonlinear_to_linear(rgb);
43+
return rgb;
44+
}
45+
46+
float linear_to_st2084_channel(float x)
47+
{
48+
float c = pow(abs(x), 0.1593017578);
49+
return pow((0.8359375 + 18.8515625 * c) / (1. + 18.6875 * c), 78.84375);
50+
}
51+
52+
float st2084_to_linear_channel(float u)
53+
{
54+
float c = pow(abs(u), 1. / 78.84375);
55+
return pow(abs(max(c - 0.8359375, 0.) / (18.8515625 - 18.6875 * c)), 1. / 0.1593017578);
56+
}
57+
58+
float eetf_0_Lmax(float maxRGB1_pq, float Lw, float Lmax)
59+
{
60+
float Lw_pq = linear_to_st2084_channel(Lw / 10000.);
61+
float E1 = saturate(maxRGB1_pq / Lw_pq); // Ensure normalization in case Lw is a lie
62+
float maxLum = linear_to_st2084_channel(Lmax / 10000.) / Lw_pq;
63+
float KS = (1.5 * maxLum) - 0.5;
64+
float E2 = E1;
65+
if (E1 > KS)
66+
{
67+
float T = (E1 - KS) / (1. - KS);
68+
float Tsquared = T * T;
69+
float Tcubed = Tsquared * T;
70+
float P = (2. * Tcubed - 3. * Tsquared + 1.) * KS + (Tcubed - 2. * Tsquared + T) * (1. - KS) + (-2. * Tcubed + 3. * Tsquared) * maxLum;
71+
E2 = P;
72+
}
73+
float E3 = E2;
74+
float E4 = E3 * Lw_pq;
75+
return E4;
76+
}
77+
78+
float3 maxRGB_eetf_internal(float3 rgb_linear, float maxRGB1_linear, float maxRGB1_pq, float Lw, float Lmax)
79+
{
80+
float maxRGB2_pq = eetf_0_Lmax(maxRGB1_pq, Lw, Lmax);
81+
float maxRGB2_linear = st2084_to_linear_channel(maxRGB2_pq);
82+
83+
// avoid divide-by-zero possibility
84+
maxRGB1_linear = max(6.10352e-5, maxRGB1_linear);
85+
86+
rgb_linear *= maxRGB2_linear / maxRGB1_linear;
87+
return rgb_linear;
88+
}
89+
90+
float3 maxRGB_eetf_linear_to_linear(float3 rgb_linear, float Lw, float Lmax)
91+
{
92+
float maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b);
93+
float maxRGB1_pq = linear_to_st2084_channel(maxRGB1_linear);
94+
return maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax);
95+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Nvafx="NVIDIA Audio Effects"
2+
Nvafx.SuppressLevel="Suppression Level"
3+
Nvafx.Intensity="Suppression Intensity"
4+
Nvafx.Method="Method"
5+
Nvafx.Method.Denoiser="NVIDIA Noise Removal"
6+
Nvafx.Method.Dereverb="NVIDIA Room Echo Removal"
7+
Nvafx.Method.DenoiserPlusDereverb="NVIDIA Noise Removal + Room Echo Removal"
8+
Nvafx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Audio SDK is outdated."
9+
Nvvfx.Method.Greenscreen="NVIDIA Background Removal"
10+
Nvvfx.Method.Greenscreen.Mode="Mode"
11+
Nvvfx.Method.Greenscreen.Quality="Quality (higher GPU usage, better quality)"
12+
Nvvfx.Method.Greenscreen.Performance="Performance (lower GPU usage, good quality)"
13+
Nvvfx.Method.Greenscreen.Threshold="Threshold"
14+
Nvvfx.OutdatedSDK="WARNING: Please upgrade both NVIDIA Video & Audio SDK. Your current version of Video SDK is outdated."
15+
Nvvfx.Method.Greenscreen.Processing="Mask refresh frequency in frames"
16+
Nvvfx.Method.Greenscreen.Processing.Hint="This alleviates GPU load by generating a mask every N frames only (2 on default)."
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#include "color.effect"
2+
3+
uniform float4x4 ViewProj;
4+
uniform texture2d image;
5+
uniform float multiplier;
6+
7+
uniform texture2d mask;
8+
uniform float threshold;
9+
10+
sampler_state texSampler {
11+
Filter = Linear;
12+
AddressU = Clamp;
13+
AddressV = Clamp;
14+
};
15+
16+
struct VertData {
17+
float4 pos : POSITION;
18+
float2 uv : TEXCOORD0;
19+
};
20+
21+
struct VertInOut {
22+
float2 uv : TEXCOORD0;
23+
float4 pos : POSITION;
24+
};
25+
26+
struct FragData {
27+
float2 uv : TEXCOORD0;
28+
};
29+
30+
struct FragPos {
31+
float4 pos : POSITION;
32+
};
33+
34+
VertInOut VSDefault(VertData v_in)
35+
{
36+
VertInOut v_out;
37+
v_out.uv = v_in.uv;
38+
v_out.pos = mul(float4(v_in.pos.xyz, 1.), ViewProj);
39+
return v_out;
40+
}
41+
42+
FragPos VSConvertUnorm(uint id : VERTEXID)
43+
{
44+
float idHigh = float(id >> 1);
45+
float idLow = float(id & uint(1));
46+
47+
float x = idHigh * 4.0 - 1.0;
48+
float y = idLow * 4.0 - 1.0;
49+
50+
FragPos vert_out;
51+
vert_out.pos = float4(x, y, 0.0, 1.0);
52+
return vert_out;
53+
}
54+
55+
float4 Mask(FragData f_in)
56+
{
57+
float4 rgba = image.Sample(texSampler, f_in.uv);
58+
rgba *= smoothstep(threshold - 0.1,threshold,mask.Sample(texSampler, f_in.uv).a);
59+
return rgba;
60+
}
61+
62+
float4 PSMask(FragData f_in) : TARGET
63+
{
64+
float4 rgba = Mask(f_in);
65+
return rgba;
66+
}
67+
68+
float4 PSMaskMultiply(FragData f_in) : TARGET
69+
{
70+
float4 rgba = Mask(f_in);
71+
rgba.rgb *= multiplier;
72+
return rgba;
73+
}
74+
75+
float4 PSMaskTonemap(FragData f_in) : TARGET
76+
{
77+
float4 rgba = Mask(f_in);
78+
rgba.rgb = rec709_to_rec2020(rgba.rgb);
79+
rgba.rgb = reinhard(rgba.rgb);
80+
rgba.rgb = rec2020_to_rec709(rgba.rgb);
81+
return rgba;
82+
}
83+
84+
float4 PSMaskMultiplyTonemap(FragData f_in) : TARGET
85+
{
86+
float4 rgba = Mask(f_in);
87+
rgba.rgb *= multiplier;
88+
rgba.rgb = rec709_to_rec2020(rgba.rgb);
89+
rgba.rgb = reinhard(rgba.rgb);
90+
rgba.rgb = rec2020_to_rec709(rgba.rgb);
91+
return rgba;
92+
}
93+
94+
float4 PSConvertUnorm(FragPos f_in) : TARGET
95+
{
96+
float4 rgba = image.Load(int3(f_in.pos.xy, 0));
97+
rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
98+
return rgba;
99+
}
100+
101+
float4 PSConvertUnormTonemap(FragPos f_in) : TARGET
102+
{
103+
float4 rgba = image.Load(int3(f_in.pos.xy, 0));
104+
rgba.rgb = rec709_to_rec2020(rgba.rgb);
105+
rgba.rgb = reinhard(rgba.rgb);
106+
rgba.rgb = rec2020_to_rec709(rgba.rgb);
107+
rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
108+
return rgba;
109+
}
110+
111+
float4 PSConvertUnormMultiplyTonemap(FragPos f_in) : TARGET
112+
{
113+
float4 rgba = image.Load(int3(f_in.pos.xy, 0));
114+
rgba.rgb *= multiplier;
115+
rgba.rgb = rec709_to_rec2020(rgba.rgb);
116+
rgba.rgb = reinhard(rgba.rgb);
117+
rgba.rgb = rec2020_to_rec709(rgba.rgb);
118+
rgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);
119+
return rgba;
120+
}
121+
122+
technique Draw
123+
{
124+
pass
125+
{
126+
vertex_shader = VSDefault(v_in);
127+
pixel_shader = PSMask(f_in);
128+
}
129+
}
130+
131+
technique DrawMultiply
132+
{
133+
pass
134+
{
135+
vertex_shader = VSDefault(v_in);
136+
pixel_shader = PSMaskMultiply(f_in);
137+
}
138+
}
139+
140+
technique DrawTonemap
141+
{
142+
pass
143+
{
144+
vertex_shader = VSDefault(v_in);
145+
pixel_shader = PSMaskTonemap(f_in);
146+
}
147+
}
148+
149+
technique DrawMultiplyTonemap
150+
{
151+
pass
152+
{
153+
vertex_shader = VSDefault(v_in);
154+
pixel_shader = PSMaskMultiplyTonemap(f_in);
155+
}
156+
}
157+
158+
technique ConvertUnorm
159+
{
160+
pass
161+
{
162+
vertex_shader = VSConvertUnorm(id);
163+
pixel_shader = PSConvertUnorm(f_in);
164+
}
165+
}
166+
167+
technique ConvertUnormTonemap
168+
{
169+
pass
170+
{
171+
vertex_shader = VSConvertUnorm(id);
172+
pixel_shader = PSConvertUnormTonemap(f_in);
173+
}
174+
}
175+
176+
technique ConvertUnormMultiplyTonemap
177+
{
178+
pass
179+
{
180+
vertex_shader = VSConvertUnorm(id);
181+
pixel_shader = PSConvertUnormMultiplyTonemap(f_in);
182+
}
183+
}

0 commit comments

Comments
 (0)