Skip to content

Commit

Permalink
jni plugin partially done
Browse files Browse the repository at this point in the history
  • Loading branch information
olir committed Mar 22, 2018
1 parent 2497ee2 commit f4103f1
Show file tree
Hide file tree
Showing 19 changed files with 391 additions and 166 deletions.
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Frankenstein VR
Free Video Converter for Virtual Reality and 3D based on ffmpeg and OpenCV for Java.
Free Video Converter for Virtual Reality and 3D based on [FFmpeg](https://ffmpeg.org) and [OpenCV](http://www.opencv.org/releases.html) for Java.
It's a small, open-source platform for OpenCV-based video filtering, where custom filters can be simply added into the processing pipeline:

<img src="doc/pipeline.png" width="100%">
Expand Down Expand Up @@ -48,8 +48,7 @@ Install Pre-Requisites first (see below), then you have 3 options to start it:
- [FFmpeg 3.1.1+](https://ffmpeg.org) installed. Select path at first startup (is stored in frankenstein.ini at user-home)
- [Java JRE 1.8+](https://java.com) installed.
- [VLC 2.2.8](https://mirror.de.leaseweb.net/videolan/vlc/2.2.8/) installed for network stream recording support.
*** Provided (do not install) ***
- [OpenCV 3.2.0+](http://www.opencv.org/releases.html)
- Codecs. See section below.

### FFMPEG OpenH264 support ###
FFMPEG build contains H264 encoder based on the OpenH264 library, that should be installed separatelly.
Expand Down
16 changes: 10 additions & 6 deletions app/src/main/java/de/screenflow/frankenstein/fxml/FxMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import de.screenflow.frankenstein.MovieProcessor;
import de.screenflow.frankenstein.vf.SegmentVideoFilter;
import de.screenflow.frankenstein.vf.segment.BWFilter;
import de.screenflow.frankenstein.vf.segment.NativeExampleFilter;
import de.screenflow.frankenstein.vf.segment.StereoDistanceFilter;
import javafx.application.Application;
import javafx.application.Platform;
Expand All @@ -44,7 +45,7 @@ public class FxMain extends Application {
private ConfigurationSceneController cController;

private List<SegmentVideoFilter> segmentFilters;

public static final String APP_NAME = "Frankenstein VR";

private Configuration configuration;
Expand All @@ -55,15 +56,12 @@ public class FxMain extends Application {

private static Locale locale = Locale.getDefault();


public static void fxmain(Configuration c) {
initialConfiguration = c;
String[] args = {};
launch(args);
}



@Override
public void start(Stage primaryStage) {
try {
Expand All @@ -78,7 +76,7 @@ public void start(Stage primaryStage) {
System.out.println("Warning: nu.pattern.OpenCV not found."); // eclipse
// environment
}
System.out.println("Loading from "+System.getProperty("java.library.path"));
System.out.println("Loading from " + System.getProperty("java.library.path"));
System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);

theStage = primaryStage;
Expand All @@ -103,7 +101,7 @@ public void start(Stage primaryStage) {

private void buildUI() {
createSegmentFilters();

BorderPane sceneRoot = null;
FXMLLoader loader;

Expand Down Expand Up @@ -190,6 +188,12 @@ public void createSegmentFilters() {
segmentFilters.add(new BWFilter());
segmentFilters.add(new StereoDistanceFilter());
segmentFilters.add(new SampleFilter());
try {
segmentFilters.add(new NativeExampleFilter()); // try to load from plugin jar
}
catch(Throwable t) {
t.printStackTrace();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package de.screenflow.frankenstein.vf.segment;

public class NativeExampleConfigController {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2017 Oliver Rode, https://github.com/olir/Frankenstein
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.screenflow.frankenstein.vf.segment;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.opencv.core.Mat;

public class NativeExampleFilter extends NativeSegmentFilter<NativeExampleConfigController> {

private final static String JNI_FILTER_CLASS = "cc0.NativeExample";

private final Class jniProxyClass;
private final Object jniProxy;
private final Method jniProxyInitMethod;
private final Method jniProxyProcessMethod;

@SuppressWarnings("unchecked")
public NativeExampleFilter() throws UnsatisfiedLinkError {
super("native");
try {
// use reflection when classing jni proxy class from Jar, so app do not depend on it.
jniProxyClass = Class.forName(JNI_FILTER_CLASS);
jniProxy = jniProxyClass.newInstance();
jniProxyInitMethod = jniProxyClass.getMethod("init");
jniProxyProcessMethod = jniProxyClass.getMethod("process", Object.class, Integer.class);
jniProxyInitMethod.invoke(jniProxy);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException
| SecurityException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("jni wrapper creation failed", e);
}
}

@Override
public Mat process(Mat sourceFrame, int frameId) {
try {
jniProxyProcessMethod.invoke(jniProxy, sourceFrame, frameId);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
return sourceFrame;
}

@Override
protected void initializeController() {
// getConfigController(). ...
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2017 Oliver Rode, https://github.com/olir/Frankenstein
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.screenflow.frankenstein.vf.segment;

import java.io.IOException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

import org.opencv.core.Mat;

import de.screenflow.frankenstein.fxml.FxMain;
import de.screenflow.frankenstein.vf.SegmentVideoFilter;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;

public abstract class NativeSegmentFilter<C> implements SegmentVideoFilter {
private C configController = null;

private final String identifier;
private final PropertyResourceBundle bundleConfiguration;

protected NativeSegmentFilter(String identifier) {
this.identifier = identifier;

bundleConfiguration = (PropertyResourceBundle) ResourceBundle
.getBundle(getClass().getPackage().getName().replace('.', '/') + '/' + identifier, FxMain.getLocale());
}

public final String toString() {
return bundleConfiguration.getString("name");
}

@Override
public final SegmentVideoFilter createInstance() {
try {
return getClass().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}

@Override
public final Scene createConfigurationScene(String stylesheet) {
FXMLLoader loader = new FXMLLoader(getClass().getResource(identifier + ".fxml"), bundleConfiguration);
try {
loader.load();
} catch (IOException e) {
throw new RuntimeException("Failed to create configuration scene for video filter '" + this + "'", e);
}
Scene scene = new Scene(loader.getRoot());
scene.getStylesheets().add(stylesheet);
configController = loader.getController();
initializeController(); // custom initialization possible here
return scene;
}

abstract protected void initializeController();

protected final C getConfigController() {
return configController;
}

@Override
public final Mat configure(Mat firstFrame) {
return firstFrame;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.screenflow.frankenstein.vf.segment.NativeExampleConfigController">
</BorderPane>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name=Native Example
8 changes: 3 additions & 5 deletions doc/SegmentFilters.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ message=Keine Einstellungenoptionen.

Notice: Eclipse JavaFX-Edition has a SceneBuilder.

Start by copying the following example and change the controller class reference to yours from step 1.
Start by copying the following example and change the controller class reference behind fx:controller= to your class from step 1 (use full-qualified java class name).


__samplefilters/sample.fxml:__
Expand All @@ -79,8 +79,6 @@ __samplefilters/sample.fxml:__
<?import javafx.scene.layout.BorderPane?>

<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="samplefilters.SampleConfigController">
<center>
</center>
<center>
<Label text="%message" BorderPane.alignment="CENTER">
<font>
Expand Down Expand Up @@ -153,6 +151,6 @@ mvn clean package

Test JNI loading with:
```
java -Djava.library.path=target -cp target/jniplugin-java-0.1.1-SNAPSHOT.jar cc0.NativeCode
java -Djava.library.path=target -cp target/jniplugin-java-0.1.1-SNAPSHOT.jar cc0.JniExample
```
Some message shoud appear: **Hello from C!**
Some messages should appear: **Hello from C++!** ...
17 changes: 17 additions & 0 deletions jniplugin/java/src/main/java/cc0/JniExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cc0;

public class JniExample extends NativeFilter {
public JniExample() throws UnsatisfiedLinkError {
}

public static void main(String[] args) {
new JniExample().helloNative();
}

public native void helloNative();

public void process(Object mat, int frameId) {
// not used
}

}
28 changes: 0 additions & 28 deletions jniplugin/java/src/main/java/cc0/NativeCode.java

This file was deleted.

9 changes: 9 additions & 0 deletions jniplugin/java/src/main/java/cc0/NativeExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package cc0;

public class NativeExample extends NativeFilter {
public NativeExample() throws UnsatisfiedLinkError {
}

public native void init();
public native void process(Object mat, int frameId);
}
36 changes: 36 additions & 0 deletions jniplugin/java/src/main/java/cc0/NativeFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package cc0;

import java.io.File;

public abstract class NativeFilter {
private static boolean loaderCalled = false;
private static UnsatisfiedLinkError error = null;

protected NativeFilter() throws UnsatisfiedLinkError {
if (!loaderCalled) {
loaderCalled = true;
System.out.println("Working Directory = " + new File(".").getAbsolutePath());
try {
if (System.getProperty("os.arch").contains("64")
&& System.getProperty("sun.arch.data.model").contains("64")) {
// load 64-bit lib
System.loadLibrary("jniplugin-64");
} else {
// load 32-bit lib
System.loadLibrary("jniplugin-32");
}
} catch (UnsatisfiedLinkError t) {
System.out.println("sun.arch.data.model=" + System.getProperty("sun.arch.data.model"));
System.out.println("os.arch=" + System.getProperty("os.arch"));

error = t;
throw t;
}
if (error != null)
throw error; // throw again
}

}

public abstract void process(Object mat, int frameId);
}
16 changes: 16 additions & 0 deletions jniplugin/native/include/JwMat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef JWMAT_H
#define JWMAT_H

#include <jni.h>

class JwMat // begin declaration of the class
{
public: // begin public section
JwMat(JNIEnv* env); // constructor
~JwMat(); // destructor
private: // begin private section
JNIEnv * env; // member variable
};


#endif

0 comments on commit f4103f1

Please sign in to comment.