Skip to content

Commit a11b3b5

Browse files
authored
Merge pull request #8 from VRCFaceTracking/feat/better-errors
Better Error Handling
2 parents 6f2c298 + 1b1122b commit a11b3b5

File tree

2 files changed

+104
-104
lines changed

2 files changed

+104
-104
lines changed

SRanipalExtTrackingModule/SRanipalTrackingInterface.cs

Lines changed: 80 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,44 @@ namespace SRanipalExtTrackingInterface
1616
{
1717
public class SRanipalExtTrackingInterface : ExtTrackingModule
1818
{
19-
LipData_v2 lipData;
20-
EyeData_v2 eyeData;
21-
private static bool eyeEnabled, lipEnabled, isViveProEye;
19+
LipData_v2 lipData = default;
20+
EyeData_v2 eyeData = default;
21+
private static bool eyeEnabled = false,
22+
lipEnabled = false,
23+
isViveProEye = false,
24+
isWireless = false;
25+
private static Error eyeError = Error.UNDEFINED;
26+
private static Error lipError = Error.UNDEFINED;
27+
28+
internal static Process? _process;
29+
internal static IntPtr _processHandle;
30+
internal static IntPtr _offset;
31+
2232
private static byte[] eyeImageCache, lipImageCache;
2333

2434
// Kernel32 SetDllDirectory
2535
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
2636
private static extern bool SetDllDirectory(string lpPathName);
37+
38+
private static bool Attach()
39+
{
40+
var processes = Process.GetProcessesByName("sr_runtime");
41+
if (processes.Length <= 0) return false;
42+
_process = processes[0];
43+
_processHandle =
44+
Utils.OpenProcess(Utils.PROCESS_VM_READ,
45+
false, _process.Id);
46+
return true;
47+
}
48+
49+
private static byte[] ReadMemory(IntPtr offset, ref byte[] buf) {
50+
var bytesRead = 0;
51+
var size = buf.Length;
52+
53+
Utils.ReadProcessMemory((int) _processHandle, offset, buf, size, ref bytesRead);
54+
55+
return bytesRead != size ? null : buf;
56+
}
2757

2858
public override (bool SupportsEye, bool SupportsExpression) Supported => (true, true);
2959

@@ -83,15 +113,10 @@ public override (bool eyeSuccess, bool expressionSuccess) Initialize(bool eyeAva
83113

84114
SetDllDirectory(currentDllDirectory + "\\ModuleLibs\\" + (srRuntimeVer.StartsWith("1.3.6") ? "New" : "Old"));
85115

86-
Error eyeError = Error.UNDEFINED, lipError = Error.UNDEFINED;
87-
88-
if (eyeAvailable)
89-
eyeError = SRanipal_API.Initial(SRanipal_Eye_v2.ANIPAL_TYPE_EYE_V2, IntPtr.Zero);
90-
91-
if (expressionAvailable)
92-
lipError = SRanipal_API.Initial(SRanipal_Lip_v2.ANIPAL_TYPE_LIP_V2, IntPtr.Zero);
116+
SRanipal_API.InitialRuntime(); // hack to unblock sranipal!!!
93117

94-
HandleSrErrors(eyeError, lipError);
118+
eyeEnabled = InitTracker(SRanipal_Eye_v2.ANIPAL_TYPE_EYE_V2, "Eye");
119+
lipEnabled = InitTracker(SRanipal_Lip_v2.ANIPAL_TYPE_LIP_V2, "Lip");
95120

96121
if (eyeEnabled && Utils.HasAdmin)
97122
{
@@ -112,7 +137,7 @@ public override (bool eyeSuccess, bool expressionSuccess) Initialize(bool eyeAva
112137
{
113138
_offset = module.BaseAddress;
114139

115-
switch (_process.MainModule.FileVersionInfo.FileVersion)
140+
switch (_process.MainModule?.FileVersionInfo.FileVersion)
116141
{
117142
case "1.3.2.0":
118143
_offset += 0x19190;
@@ -126,8 +151,6 @@ public override (bool eyeSuccess, bool expressionSuccess) Initialize(bool eyeAva
126151
UnifiedTracking.EyeImageData.SupportsImage = false;
127152
break;
128153
}
129-
130-
//(_process.MainModule.FileVersionInfo.FileVersion == "1.3.2.0" ? 0x19190 : 0x19100);
131154
}
132155

133156
UnifiedTracking.EyeImageData.ImageSize = (200, 100);
@@ -140,12 +163,10 @@ public override (bool eyeSuccess, bool expressionSuccess) Initialize(bool eyeAva
140163
{
141164
UnifiedTracking.LipImageData.SupportsImage = true;
142165
UnifiedTracking.LipImageData.ImageSize = (SRanipal_Lip_v2.ImageWidth, SRanipal_Lip_v2.ImageHeight);
143-
UnifiedTracking.LipImageData.ImageData = new byte[SRanipal_Lip_v2.ImageWidth *
144-
SRanipal_Lip_v2.ImageHeight * 4];
145-
146166
lipData.image = Marshal.AllocCoTaskMem(UnifiedTracking.LipImageData.ImageSize.x *
147167
UnifiedTracking.LipImageData.ImageSize.x);
148168

169+
UnifiedTracking.LipImageData.ImageData = new byte[SRanipal_Lip_v2.ImageWidth * SRanipal_Lip_v2.ImageHeight * 4];
149170
lipImageCache = new byte[SRanipal_Lip_v2.ImageWidth * SRanipal_Lip_v2.ImageHeight];
150171
}
151172

@@ -162,89 +183,67 @@ public override (bool eyeSuccess, bool expressionSuccess) Initialize(bool eyeAva
162183

163184
isViveProEye = SRanipal_Eye_API.IsViveProEye();
164185

165-
return (eyeEnabled, lipEnabled);
186+
return (eyeAvailable && eyeEnabled, expressionAvailable && lipEnabled);
166187
}
167188

168-
private static void HandleSrErrors(Error eyeError, Error lipError)
189+
private bool InitTracker(int anipalType, string name)
169190
{
170-
if (eyeError == Error.WORK)
171-
eyeEnabled = true;
191+
Logger.LogInformation($"Initializing {name}...");
192+
var error = SRanipal_API.Initial(anipalType, IntPtr.Zero);
172193

173-
if (lipError == Error.FOXIP_SO)
174-
while (lipError == Error.FOXIP_SO)
175-
lipError = SRanipal_API.Initial(SRanipal_Lip_v2.ANIPAL_TYPE_LIP_V2, IntPtr.Zero);
176-
177-
if (lipError == Error.WORK)
178-
lipEnabled = true;
194+
handler:
195+
switch (error)
196+
{
197+
case Error.FOXIP_SO: // wireless issue
198+
Logger.LogInformation("Vive wireless detected. Forcing initialization...");
199+
while (error == Error.FOXIP_SO)
200+
error = SRanipal_API.Initial(anipalType, IntPtr.Zero);
201+
goto handler;
202+
case Error.WORK:
203+
Logger.LogInformation($"{name} successfully started!");
204+
return true;
205+
default:
206+
break;
207+
}
208+
Logger.LogInformation($"{name} failed to initialize: {error}");
209+
return false;
179210
}
180211

181212
public override void Teardown()
182213
{
183-
// We won't bother terminating sranipal modules here as if this is a shutdown, they'll automatically be released.
214+
SRanipal_API.ReleaseRuntime();
184215
}
185216

186-
#region Update
187-
188-
189217
public override void Update()
190218
{
191219
Thread.Sleep(10);
192220

193221
if (Status != ModuleState.Active)
194222
return;
195-
196-
if (lipEnabled && UpdateMouth() != Error.WORK)
223+
if (lipEnabled && !UpdateMouth())
197224
{
198-
Logger.LogError("An error occured while getting lip data. This might be a wireless crash.");
199-
Logger.LogWarning("Waiting 30 seconds before reinitializing to account for wireless users.");
200-
Thread.Sleep(30000);
201-
// UnifiedLibManager.Initialize();
202-
return;
225+
Logger.LogError("An error has occured when updating tracking. Reinitializing needed runtimes.");
226+
SRanipal_API.InitialRuntime();
227+
InitTracker(SRanipal_Lip_v2.ANIPAL_TYPE_LIP_V2, "Lip");
203228
}
204-
205-
if (eyeEnabled && UpdateEye() != Error.WORK)
229+
if (eyeEnabled && !UpdateEye())
206230
{
207-
Logger.LogError("An error occured while getting eye data. This might be a wireless crash.");
208-
Logger.LogWarning("Waiting 30 seconds before reinitializing to account for wireless users.");
209-
Thread.Sleep(30000);
210-
//UnifiedLibManager.Initialize();
211-
return;
231+
Logger.LogError("An error has occured when updating tracking. Reinitializing needed runtimes.");
232+
SRanipal_API.InitialRuntime();
233+
InitTracker(SRanipal_Eye_v2.ANIPAL_TYPE_EYE_V2, "Eye");
212234
}
213235
}
214236

215-
#endregion
216-
217-
private static Process _process;
218-
private static IntPtr _processHandle;
219-
private IntPtr _offset;
220-
221-
private static bool Attach()
222-
{
223-
if (Process.GetProcessesByName("sr_runtime").Length <= 0) return false;
224-
_process = Process.GetProcessesByName("sr_runtime")[0];
225-
_processHandle =
226-
Utils.OpenProcess(Utils.PROCESS_VM_READ,
227-
false, _process.Id);
228-
return true;
229-
}
230-
231-
private static byte[] ReadMemory(IntPtr offset, ref byte[] buf) {
232-
var bytesRead = 0;
233-
var size = buf.Length;
234-
235-
Utils.ReadProcessMemory((int) _processHandle, offset, buf, size, ref bytesRead);
236-
237-
return bytesRead != size ? null : buf;
238-
}
239-
240-
private Error UpdateEye()
237+
private bool UpdateEye()
241238
{
242-
var updateResult = SRanipal_Eye_API.GetEyeData_v2(ref eyeData);
239+
eyeError = SRanipal_Eye_API.GetEyeData_v2(ref eyeData);
240+
if (eyeError != Error.WORK) return false;
243241

244242
UpdateEyeParameters(ref UnifiedTracking.Data.Eye, eyeData.verbose_data);
245243
UpdateEyeExpressions(ref UnifiedTracking.Data.Shapes, eyeData.expression_data);
246244

247-
if (_processHandle == IntPtr.Zero || !UnifiedTracking.EyeImageData.SupportsImage) return updateResult;
245+
if (_processHandle == IntPtr.Zero || !UnifiedTracking.EyeImageData.SupportsImage)
246+
return true;
248247

249248
// Read 20000 image bytes from the predefined offset. 10000 bytes per eye.
250249
var imageBytes = ReadMemory(_offset, ref eyeImageCache);
@@ -280,7 +279,7 @@ private Error UpdateEye()
280279
}
281280
}
282281

283-
return updateResult;
282+
return true;
284283
}
285284

286285
private static Vector3 GetConvergenceAngleOffset(VerboseData external)
@@ -357,13 +356,15 @@ private void UpdateEyeExpressions(ref UnifiedExpressionShape[] data, EyeExpressi
357356
data[(int)UnifiedExpressions.BrowLowererRight].Weight = external.right.eye_squeeze;
358357
}
359358

360-
private Error UpdateMouth()
359+
private bool UpdateMouth()
361360
{
362-
var updateResult = SRanipal_Lip_API.GetLipData_v2(ref lipData);
363-
361+
lipError = SRanipal_Lip_API.GetLipData_v2(ref lipData);
362+
if (lipError != Error.WORK)
363+
return false;
364364
UpdateMouthExpressions(ref UnifiedTracking.Data, lipData.prediction_data);
365365

366-
if (lipData.image == IntPtr.Zero || !UnifiedTracking.LipImageData.SupportsImage) return updateResult;
366+
if (lipData.image == IntPtr.Zero || !UnifiedTracking.LipImageData.SupportsImage)
367+
return true;
367368

368369
Marshal.Copy(lipData.image, lipImageCache, 0, UnifiedTracking.LipImageData.ImageSize.x *
369370
UnifiedTracking.LipImageData.ImageSize.y);
@@ -383,7 +384,7 @@ private Error UpdateMouth()
383384
}
384385
}
385386

386-
return updateResult;
387+
return true;
387388
}
388389

389390
private void UpdateMouthExpressions(ref UnifiedTrackingData data, PredictionData_v2 external)

SRanipalExtTrackingModule/SRanipal_API.cs

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,33 @@ namespace ViveSR
66
{
77
namespace anipal
88
{
9-
public class SRanipal_API
9+
public static class SRanipal_API
1010
{
11-
/// <summary>
12-
/// Invokes an anipal module.
13-
/// </summary>
14-
/// <param name="anipalType">The index of an anipal module.</param>
15-
/// <param name="config">Indicates the resulting ViveSR.Error status of this method.</returns>
16-
/// <returns>Indicates the resulting ViveSR.Error status of this method.</returns>
17-
[DllImport("SRanipal")]
18-
public static extern Error Initial(int anipalType, IntPtr config);
11+
[DllImport("kernel32.dll", SetLastError = true)]
12+
private static extern IntPtr LoadLibrary(string dllToLoad);
1913

20-
/// <summary>
21-
/// Terminates an anipal module.
22-
/// </summary>
23-
/// <param name="anipalType">The index of an anipal module.</param>
24-
/// <returns>Indicates the resulting ViveSR.Error status of this method.</returns>
25-
[DllImport("SRanipal")]
26-
public static extern Error Release(int anipalType);
14+
[DllImport("kernel32.dll", SetLastError = true)]
15+
[return: MarshalAs(UnmanagedType.Bool)]
16+
private static extern bool FreeLibrary(IntPtr hModule);
2717

28-
/// <summary>
29-
/// Gets the status of an anipal module.
30-
/// </summary>
31-
/// <param name="anipalType">The index of an anipal module.</param>
32-
/// <param name="status">The status of an anipal module.</param>
33-
/// <returns>Indicates the resulting ViveSR.Error status of this method.</returns>
34-
[DllImport("SRanipal")]
35-
public static extern Error GetStatus(int anipalType, out AnipalStatus status);
18+
private static IntPtr module = IntPtr.Zero;
3619

20+
public static void InitialRuntime()
21+
{
22+
if (module != IntPtr.Zero)
23+
ReleaseRuntime();
24+
module = LoadLibrary("SRanipal.dll");
25+
}
26+
27+
public static void ReleaseRuntime()
28+
{
29+
if (!FreeLibrary(module)) // ideally should never happen.
30+
throw new Exception($"Failed to release Lip module DLL.");
31+
module = IntPtr.Zero;
32+
}
33+
34+
[DllImport("SRanipal.dll", CallingConvention = CallingConvention.Cdecl)]
35+
internal static extern Error Initial(int anipalType, IntPtr config);
3736
}
3837
}
39-
}
38+
}

0 commit comments

Comments
 (0)