/
Video.java
executable file
·424 lines (371 loc) · 16.2 KB
/
Video.java
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-22 The Processing Foundation
Copyright (c) 2011-12 Ben Fry and Casey Reas
GStreamer implementation ported from GSVideo library by Andres Colubri
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.video;
import org.freedesktop.gstreamer.*;
import processing.core.PApplet;
import processing.core.PConstants;
import java.io.File;
import java.nio.ByteOrder;
import java.nio.file.Paths;
import java.util.List;
/**
* This class contains some basic functions used by the rest of the classes in
* this library.
*/
public class Video implements PConstants {
// Allows to set the amount of desired debug output from GStreamer, according to the following table:
// https://gstreamer.freedesktop.org/documentation/tutorials/basic/debugging-tools.html?gi-language=c#printing-debug-information
public static int DEBUG_LEVEL = 1;
// Path that the video library will use to load the GStreamer base libraries
// and plugins from. They can be passed from the application using the
// gstreamer.library.path and gstreamer.plugin.path system variables (see
// comments in initImpl() below).
public static String gstreamerLibPath = "";
public static String gstreamerPluginPath = "";
protected static boolean usingGStreamerSystemInstall = false;
// OpenGL texture used as buffer sink by default, when the renderer is
// GL-based. This can improve performance significantly, since the video
// frames are automatically copied into the texture without passing through
// the pixels arrays, as well as having the color conversion into RGBA handled
// natively by GStreamer.
protected static boolean useGLBufferSink = true;
protected static boolean defaultGLibContext = false;
protected static long INSTANCES_COUNT = 0;
protected static int bitsJVM;
static {
bitsJVM = PApplet.parseInt(System.getProperty("sun.arch.data.model"));
}
static protected void init() {
if (INSTANCES_COUNT == 0) {
initImpl();
}
INSTANCES_COUNT++;
}
static protected void restart() {
removePlugins();
Gst.deinit();
initImpl();
}
static protected void initImpl() {
// The video library loads the GStreamer libraries according to the following
// priority:
// 1) If the VM argument "gstreamer.library.path" exists, it will use it as the
// root location of the libraries. This is typically the case when running
// the library from Eclipse.
// 2) If the environmental variable is GSTREAMER_1_0_ROOT_(MINGW/MSVC)_64 is defined then
// will try to use its contents as the root path of the system install of GStreamer.
// 3) The bundled version of GStreamer will be used, if present.
// 4) If none of the above works, then will try to use default install locations of GStreamer
// on Windows and Mac, if they exist.
// In this way, priority is given to the system installation of GStreamer only if set in the
// environmental variables, otherwise will try to load the bundled GStreamer, and if it does not
// exist it will look for GStreamer in the system-wide locations. This gives the user the option
// to remove the bundled GStreamer libs to default to the system-wide installation.
String libPath = System.getProperty("gstreamer.library.path");
int winBuildType = 0; // 0: default build, 1: mingw, 2: msvc
if (libPath != null) {
gstreamerLibPath = libPath;
// If the GStreamer installation referred by gstreamer.library.path is not
// a system installation, then the path containing the plugins needs to be
// specified separately, otherwise the plugins will be automatically
// loaded from the default location. The system property for the plugin
// path is "gstreamer.plugin.path"
String pluginPath = System.getProperty("gstreamer.plugin.path");
if (pluginPath != null) {
gstreamerPluginPath = pluginPath;
}
usingGStreamerSystemInstall = false;
} else {
String rootPath = "";
if (bitsJVM == 64) {
// Get 64-bit root of GStreamer install
if (System.getenv("GSTREAMER_1_0_ROOT_X86_64") != null) {
winBuildType = 0;
rootPath = System.getenv("GSTREAMER_1_0_ROOT_X86_64");
} else if (System.getenv("GSTREAMER_1_0_ROOT_MINGW_X86_64") != null) {
winBuildType = 1;
rootPath = System.getenv("GSTREAMER_1_0_ROOT_MINGW_X86_64");
} else if (System.getenv("GSTREAMER_1_0_ROOT_MSVC_X86_64") != null) {
winBuildType = 2;
rootPath = System.getenv("GSTREAMER_1_0_ROOT_MSVC_X86_64");
}
}
if (!rootPath.equals("")) {
if (PApplet.platform == MACOS) {
gstreamerLibPath = Paths.get(rootPath, "lib").toString();
} else {
gstreamerLibPath = Paths.get(rootPath, "bin").toString();
}
File path = new File(gstreamerLibPath);
if (path.exists()) {
// We have a system install of GStreamer
usingGStreamerSystemInstall = true;
buildSystemPaths(rootPath);
} else {
// The environmental variables contain invalid paths...
gstreamerLibPath = "";
}
}
}
if (libPath == null && !usingGStreamerSystemInstall) {
// No GStreamer path in the VM arguments, and not system-wide install in environmental variables,
// will try searching for the bundled GStreamer libs.
if (buildBundldedPaths()) {
// Found bundled GStreamer libs, which in version 2.2 of the library are MSVC-built:
winBuildType = 2;
}
}
if (gstreamerLibPath.equals("")) {
// Finally, no environmental variables defined and did not find bundled gstreamer,
// will try some default system-wide locations.
String rootPath = "";
if (PApplet.platform == MACOS) {
rootPath = "/Library/Frameworks/GStreamer.framework/Versions/1.0";
gstreamerLibPath = Paths.get(rootPath, "lib").toString();
} else if (PApplet.platform == WINDOWS) {
if (bitsJVM == 64) {
if (new File("C:\\gstreamer\\1.0\\x86_64").exists()) {
winBuildType = 0;
rootPath = "C:\\gstreamer\\1.0\\x86_64";
} else if (new File("C:\\gstreamer\\1.0\\mingw_x86_64").exists()) {
winBuildType = 1;
rootPath = "C:\\gstreamer\\1.0\\mingw_x86_64";
} else if (new File("C:\\gstreamer\\1.0\\msvc_x86_64").exists()) {
winBuildType = 2;
rootPath = "C:\\gstreamer\\1.0\\msvc_x86_64";
}
gstreamerLibPath = Paths.get(rootPath, "bin").toString();
}
} else if (PApplet.platform == LINUX) {
if (bitsJVM == 64) {
rootPath = "/lib/x86_64-linux-gnu";
} else {
rootPath = "/lib/x86-linux-gnu";
}
File gstlib = new File(rootPath, "libgstreamer-1.0.so.0");
if (gstlib.exists()) {
gstreamerLibPath = Paths.get(rootPath).toString();
}
}
File path = new File(gstreamerLibPath);
if (path.exists()) {
// We have a system install of GStreamer
if (bitsJVM == 64) {
if (winBuildType == 0) {
Environment.libc.setenv("GSTREAMER_1_0_ROOT_X86_64", gstreamerLibPath, true);
} else if (winBuildType == 1) {
Environment.libc.setenv("GSTREAMER_1_0_ROOT_MINGW_X86_64", gstreamerLibPath, true);
} else if (winBuildType == 2) {
Environment.libc.setenv("GSTREAMER_1_0_ROOT_MSVC_X86_64", gstreamerLibPath, true);
}
}
buildSystemPaths(rootPath);
} else {
System.err.println("We could not find a system-wide or bundled installation of GStreamer, but video might still work if GStreamer was placed somewhere else");
}
usingGStreamerSystemInstall = true;
}
if (!gstreamerLibPath.equals("")) {
// Should be safe because this is setting the jna.library.path,
// not java.library.path, and JNA is being provided by the video library.
// This will need to change if JNA is ever moved into more of a shared
// location (i.e. part of core) because this would overwrite the prop.
System.setProperty("jna.library.path", gstreamerLibPath);
}
Environment.libc.setenv("GST_DEBUG", String.valueOf(DEBUG_LEVEL), true);
if (!usingGStreamerSystemInstall) {
// Disable the use of gst-plugin-scanner on environments where we're
// not using the host system's installation of GStreamer
// the problem with gst-plugin-scanner is that the library expects it
// to exist at a specific location determined at build time
Environment.libc.setenv("GST_REGISTRY_FORK", "no", true);
// Prevent globally installed libraries from being used on platforms
// where we ship GStreamer
if (!gstreamerPluginPath.equals("")) {
Environment.libc.setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", true);
}
}
if (PApplet.platform == WINDOWS || (!usingGStreamerSystemInstall && PApplet.platform == LINUX)) {
// Pre-loading base GStreamer libraries on Windows and Linux,
// otherwise dynamic dependencies cannot be resolved.
LibraryLoader loader = LibraryLoader.getInstance(winBuildType);
if (loader == null) {
System.err.println("Cannot load GStreamer libraries.");
}
}
String[] args = { "" };
Gst.setUseDefaultContext(defaultGLibContext);
Gst.init("Processing core video", args);
// Output GStreamer version, lib path, plugin path
// and whether a system install is being used
printGStreamerInfo();
if (!usingGStreamerSystemInstall) {
// Plugins are scanned explicitly from the bindings if using the
// local GStreamer
scanPlugins();
}
}
static protected void printGStreamerInfo() {
String locInfo = "";
if (usingGStreamerSystemInstall) locInfo = "system-wide";
else locInfo = "bundled";
System.out.println("Processing video library using " + locInfo + " GStreamer " + Gst.getVersion());
}
static protected void scanPlugins() {
if (!gstreamerPluginPath.equals("")) {
Registry reg = Registry.get();
boolean res;
System.out.print("Scanning GStreamer plugins...");
res = reg.scanPath(gstreamerPluginPath);
if (res) {
System.out.println(" Done.");
} else {
System.err.println("Cannot load GStreamer plugins from " + gstreamerPluginPath);
}
}
}
static protected void removePlugins() {
Registry reg = Registry.get();
List<Plugin> list = reg.getPluginList();
for (Plugin plg : list) {
reg.removePlugin(plg);
}
}
/**
* Search for an item by checking folders listed in java.library.path
* for a specific name.
*/
@SuppressWarnings("SameParameterValue")
static private String searchLibraryPath(String what) {
String libraryPath = System.getProperty("java.library.path");
// Should not be null, but cannot assume
if (libraryPath != null) {
String[] folders = PApplet.split(libraryPath, File.pathSeparatorChar);
// Usually, the most relevant paths will be at the front of the list,
// so hopefully this will not walk several entries.
for (String folder : folders) {
// Skip /lib and /usr/lib folders because they contain the system-wide GStreamer on Linux
// and they are on the Java library path.
if (folder.startsWith("/lib/") || folder.startsWith("/usr/lib/")) continue;
File file = new File(folder, what);
if (file.exists()) {
return file.getAbsolutePath();
}
}
}
return null;
}
/**
* Search for an item by checking folders listed in java.class.path
* for a specific name.
*/
@SuppressWarnings("SameParameterValue")
static private String searchClassPath(String what) {
String classPath = System.getProperty("java.class.path");
// Should not be null, but cannot assume
if (classPath != null) {
String[] entries = PApplet.split(classPath, File.pathSeparatorChar);
// Usually, the most relevant paths will be at the front of the list,
// so hopefully this will not walk several entries.
for (String entry : entries) {
File dir = new File(entry);
// If it's a .jar file, get its parent folder. This will lead to some
// double-checking of the same folder, but probably almost as expensive
// to keep track of folders we've already seen.
if (dir.isFile()) {
dir = dir.getParentFile();
}
File file = new File(dir, what);
if (file.exists()) {
return file.getAbsolutePath();
}
}
}
return null;
}
static protected void buildSystemPaths(String rootPath) {
if (System.getenv("GST_PLUGIN_SYSTEM_PATH") != null) {
gstreamerPluginPath = System.getenv("GST_PLUGIN_SYSTEM_PATH");
} else {
if (PApplet.platform == WINDOWS) {
gstreamerPluginPath = Paths.get(rootPath, "lib", "gstreamer-1.0").toString();
} else {
gstreamerPluginPath = Paths.get(gstreamerLibPath, "gstreamer-1.0").toString(); }
}
File path = new File(gstreamerPluginPath);
if (!path.exists()) {
gstreamerPluginPath = "";
}
}
static protected boolean buildBundldedPaths() {
// look for the gstreamer-1.0 folder in the native library path
// (there are natives adjacent to it, so this will work)
gstreamerPluginPath = searchLibraryPath("gstreamer-1.0");
if (gstreamerPluginPath == null) {
gstreamerPluginPath = searchClassPath("gstreamer-1.0");
}
if (gstreamerPluginPath == null) {
gstreamerPluginPath = "";
gstreamerLibPath = "";
usingGStreamerSystemInstall = true;
return false;
} else {
File gstreamerLibDir = new File(gstreamerPluginPath).getParentFile();
gstreamerLibPath = gstreamerLibDir.getAbsolutePath();
return true;
}
}
static protected float nanoSecToSecFrac(long nanosec) {
return (float)(nanosec / 1E9);
}
static protected long secToNanoLong(float sec) {
Double f = Double.valueOf(sec * 1E9);
return f.longValue();
}
/**
* Reorders an OpenGL pixel array (RGBA) into ARGB. The array must be
* of size width * height.
* @param pixels int[]
*/
static protected void convertToARGB(int[] pixels, int width, int height) {
int t = 0;
int p = 0;
if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
// RGBA to ARGB conversion: shifting RGB 8 bits to the right,
// and placing A 24 bits to the left.
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = pixels[p++];
pixels[t++] = (pixel >>> 8) | ((pixel << 24) & 0xFF000000);
}
}
} else {
// We have to convert ABGR into ARGB, so R and B must be swapped,
// A and G just brought back in.
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = pixels[p++];
pixels[t++] = ((pixel & 0xFF) << 16) | ((pixel & 0xFF0000) >> 16) |
(pixel & 0xFF00FF00);
}
}
}
}
}