-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathDevolay.java
More file actions
259 lines (228 loc) · 10.8 KB
/
Devolay.java
File metadata and controls
259 lines (228 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
package me.walkerknapp.devolay;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Used to control loading of Devolay and NDI libraries.
*
* Loading of the Devolay native libraries requires either:
* <ul>
* <li>Using the devolay-integrated maven package</li>
* <li>Using the {@link #loadLibraries(Path, Path)} method to tell Devolay where the devolay native is</li>
* </ul>
*
* Loading of the NDI native libraries requires either:
* <ul>
* <li>Using the devolay-integrated maven package</li>
* <li>Android - libraries are loaded from an aar</li>
* <li>Using the {@link #loadLibraries(Path, Path)} method to tell Devolay where the NDI native is</li>
* </ul>
*
* Recommended usage:
* <ul>
* <li>Use the devolay-integrated maven package until you run into a problem.</li>
* <li>Windows / Windows Store deployment - use devolay-integrated</li>
* <li>Ubuntu / Snap Store deployment - use devolay-integrated</li>
* <li>MacOS - use devolay-integrated</li>
* <li>MacOS Store - manually load both Devolay and NDI libraries using {@link #loadLibraries(Path, Path)} and packaging the libraries per MacOS Store requirements</li>
* <li>Android - use devolay-integrated</li>
* </ul>
*
* Example usage - In your app initialization:
* <pre>
* // to load the native libraries from the devolay-integrated maven package
* Devolay.loadLibraries();
* // or to manually load the native libraries
* Devolay.loadLibraries(pathToDevolayLibrary, pathToNDILibrary);
* </pre>
*/
public class Devolay {
private static final AtomicBoolean librariesLoaded = new AtomicBoolean(false);
private static Path extractedDevolayNativesPath = null;
private static Path extractedNDINativesPath = null;
/**
* Only extract natives from the integrated build during static initialization
* so that the Devolay class can still be loaded and then used to load the native
* libraries from anywhere.
*/
static {
String devolayLibraryName = System.mapLibraryName("devolay-natives");
String ndiLibraryName = System.mapLibraryName("ndi");
String libraryExtension = devolayLibraryName.substring(devolayLibraryName.indexOf('.'));
String osDirectory = getOsDirectory();
String archDirectory = getArchDirectory();
if (!osDirectory.equals("android")) {
extractedDevolayNativesPath = extractNative("devolay-natives", libraryExtension,
"/natives/" + osDirectory + "/" + archDirectory + "/" + devolayLibraryName);
extractedNDINativesPath = extractNative("ndi", libraryExtension,
"/natives/" + osDirectory + "/" + archDirectory + "/" + ndiLibraryName);
} else {
String path = findLibrary("ndi");
extractedNDINativesPath = Paths.get(path);
}
}
private static String getOsDirectory() {
final String osNameProperty = System.getProperty("os.name").toLowerCase();
final String javaRuntimeProperty = System.getProperty("java.runtime.name");
if (javaRuntimeProperty != null && javaRuntimeProperty.toLowerCase().contains("android")) {
return "android";
} else if (osNameProperty.contains("nix") || osNameProperty.contains("nux")) {
return "linux";
} else if (osNameProperty.contains("win")) {
return "windows";
} else if (osNameProperty.contains("mac")) {
return "macos";
} else {
throw new IllegalStateException("Unsupported OS: " + osNameProperty + ". Please open an issue at https://github.com/WalkerKnapp/devolay/issues");
}
}
private static String getArchDirectory() {
final String osArchProperty = System.getProperty("os.arch").toLowerCase();
if (osArchProperty.contains("aarch64") || (osArchProperty.contains("arm") && (osArchProperty.contains("64") || osArchProperty.contains("v8")))) {
return "arm64-v8a";
} else if (osArchProperty.contains("aarch32") || (osArchProperty.contains("arm") && (osArchProperty.contains("32") || osArchProperty.contains("v7")))) {
return "armv7a";
} else if (osArchProperty.contains("64")) {
return "x86-64";
} else if (osArchProperty.contains("86")) {
return "x86";
} else {
throw new IllegalStateException("Unsupported Arch: " + osArchProperty + ". Please open an issue at https://github.com/WalkerKnapp/devolay/issues");
}
}
private static Path extractNative(String prefix, String suffix, String pathInJar) {
try(InputStream is = Devolay.class.getResourceAsStream(pathInJar)) {
if(is == null) {
return null;
}
// Get a temporary directory to place natives
Path tempPath = Files.createTempFile(prefix, suffix);
// Create a lock file for this dll
Path tempLock = tempPath.resolveSibling(tempPath.getFileName().toString() + ".lock");
Files.createFile(tempLock);
tempLock.toFile().deleteOnExit();
// Copy the natives to be loaded
Files.copy(is, tempPath, StandardCopyOption.REPLACE_EXISTING);
// Clean up any natives from previous runs that do not have a corresponding lock file
Files.list(tempPath.getParent())
.filter(path -> path.getFileName().toString().startsWith(prefix) && path.getFileName().toString().endsWith(suffix))
.filter(path -> !Files.exists(path.resolveSibling(path.getFileName().toString() + ".lock")))
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
// ignored, the file is in use without a lock
}
});
return tempPath;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static String findLibrary(String libraryName) {
try {
Method findLibraryHandle = ClassLoader.class.getDeclaredMethod("findLibrary", String.class);
findLibraryHandle.setAccessible(true);
return (String) findLibraryHandle.invoke(Devolay.class.getClassLoader(), libraryName);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
return null;
}
}
/**
* Loads the NDI run-time from the NDI Redist Environment Variable (NDI_RUNTIME_DIR_V3)
*
* @return An int representing the success of the library loading.
* 0 - Success
* -1 - The libraries are not installed (the environment variable doesn't exist)
* -2 - The library load failed. (The end user should reinstall the libraries, and should be provided with the redist URL)
*/
public static int loadLibraries() {
return loadLibraries(null, null);
}
/**
* Loads the native libraries for both Devolay and NDI. The given paths can be null, in which case
* the extracted library locations will be used.
* @param overrideDevolayNativesPath can be null
* @param overrideNDINativesPath can be null
* @return int
* @throws IllegalStateException If the extracted paths are null
* @throws UnsatisfiedLinkError If the native library could not be found or loaded
* @see #loadLibraries()
*/
public static int loadLibraries(Path overrideDevolayNativesPath, Path overrideNDINativesPath) {
if(!librariesLoaded.get()) {
// load devolay natives first
loadDevolayLibrary(overrideDevolayNativesPath != null ? overrideDevolayNativesPath : extractedDevolayNativesPath);
// load ndi natives next
int ret = loadNDILibrary(overrideNDINativesPath != null ? overrideNDINativesPath : extractedNDINativesPath);
if (ret == 0) {
// mark that we've loaded successfully
librariesLoaded.set(true);
}
return ret;
}
return 0;
}
/**
* Loads the Devolay library at the given path.
* @param path the path; cannot be null
* @throws UnsatisfiedLinkError if the path cannot be loaded
* @throws NullPointerException if the path is null
*/
private static void loadDevolayLibrary(Path path) {
String osDirectory = getOsDirectory();
if (!osDirectory.equals("android")) {
if (path == null) {
throw new NullPointerException("This build of Devolay is not compiled for your OS. Please use a different build or follow the compilation instructions on https://github.com/WalkerKnapp/devolay.");
}
System.load(path.toAbsolutePath().toString());
} else {
// Devolay on Android should be loaded as an aar, so natives don't have to be extracted.
System.loadLibrary("devolay-natives");
}
}
/**
* Loads the NDI library at the given path.
* @param path; when null, the library is loaded from an install directory
* @return int
*/
private static int loadNDILibrary(Path path) {
if (path != null) {
return nLoadLibraries(path.toAbsolutePath().toString());
} else {
return nLoadLibraries(null);
}
}
/**
* Returns the current version of the underlying NDI(tm) library runtime.
*
* @return A string containing the version of the NDI(tm) runtimes.
* @throws IllegalStateException if loadLibraries has not been called or failed
*/
public static String getNDIVersion() {
if (!librariesLoaded.get())
throw new IllegalStateException("The getNDIVersion method cannot be called until native libraries have been loaded using one of the loadLibraries methods.");
return nGetVersion();
}
/**
* Returns whether the current CPU in the system is capable of running NDI(tm), and by extension, Devolay.
*
* @return true if the system's CPU is capable of running NDI(tm), false if it is not capable.
* @throws IllegalStateException if loadLibraries has not been called or failed
*/
public static boolean isSupportedCpu() {
if (!librariesLoaded.get())
throw new IllegalStateException("The isSupportedCpu method cannot be called until native libraries have been loaded using one of the loadLibraries methods.");
return nIsSupportedCpu();
}
// Native Methods
private static native int nLoadLibraries(String extractedNdiPath);
private static native String nGetVersion();
private static native boolean nIsSupportedCpu();
}