This document explains how to code a Port-Based Agent with native methods in legacy C code by way of JNI technology.
This is not intended to be a JNI tutorial. If you are not familiar with JNI technology, you can take a look at the JNI Sun Tutorial page.
For our robotics examples we have been using the Pioneer robots. They have a Sony camera and a frame grabber installed. C code is available to control the frame grabber features and for image processing. Since C code can deal with hardware and it is faster than Java code, it does not make sense to rewrite this code in Java. We developed a Java wrapper for the C code using Java Native Interface (JNI) technology.
In this example we develop a PBA driver agent (PBD) that samples the video camera and displays it.
C code is available to initialize the frame grabber and sample the video
images in different formats.
To hide implementation representations from the PBD, we develop a
Java class JNIcamera that contains all the native method definitions and
calls. This class is instantiated in the PBD and becomes an internal
variable of the agent.
The C code is compiled as a dynamic library that gets loaded at runtime,
when the PBD is instantiated.
Java Details
Here is a section of the JNIcamera source:
public class JNIcamera { //Native method declarations native int grabSingleFrame(); native int grabContFrame(); native int initFrameGrabber(); native void closeFrameGrabber(); native int startContCapture(); native int stopCapture(); // last display additions to library native void getByteFrame(byte[] frame); native void getIntFrame(int[] frame); public JNIcamera() { System.loadLibrary("JNIcamera"); System.err.println("Loading JNI library for camera-grabber"); } public synchronized int JNIgrabSingleFrame() { return grabSingleFrame(); } public synchronized int JNIgrabContFrame() { return grabContFrame(); } public synchronized int JNIinitFrameGrabber() { return initFrameGrabber(); } public synchronized void JNIgetByteFrame(byte[] frame) { getByteFrame(frame); } public synchronized void JNIgetIntFrame(int[] frame) { getIntFrame(frame); } }
A JNIcamera.so library file has to be created and setup in the
library path for this class to load correctly.
The library is created using the header file created from
JNIcamera.class by
/* * Class: adaptive_support_vision_JNIcamera * Method: initFrameGrabber * Signature: ()I */ JNIEXPORT jint JNICALL Java_adaptive_support_vision_JNIcamera_initFrameGrabber (JNIEnv, jobject); /* * Class: adaptive_support_vision_JNIcamera * Method: grabSingleFrame * Signature: ()I */ JNIEXPORT jint JNICALL Java_adaptive_support_vision_JNIcamera_grabSingleFrame(JNIEnv, jobject); /* * Class: adaptive_support_vision_JNIcamera * Method: getIntFrame * Signature: ([I)V */ JNIEXPORT void JNICALL Java_adaptive_support_vision_JNIcamera_getIntFrame (JNIEnv, jobject, jintArray);
Therefore, the original C code has to be wrapped around these calls to be understandable by the Java Virtual Machine.
Note: The long names are due to the fact that JNIcamera is included in our package adaptive.support.vision.
Finally, the PBD main steps look like this:
package adaptive.agents.drivers; // Importing JNIcamera import adaptive.support.vision.*; // PBA core classes import adaptive.core.*; // other util classes ommited public class PBACameraDisplay extends PBAgent implements Driver { // some constants static final int rows = 120; static final int cols = 160; static final int resol = 16; // frame storage variables byte[] frame; int[] pixels; // frame displaying variables CamDisplay view; Frame Display; // JNI library clas interface static protected JNIcamera Camera; /********************************************************************** Process our internal state. @param @return @exception **********************************************************************/ protected boolean processInternalState(Object state){ // cut extra stuff here // allocating internals pixels = new int[rows*cols]; frame = new byte[frame_size]; Display = new Frame(); view = new CamDisplay(rows,cols,resol,pixels); Display.add(view,BorderLayout.CENTER); Display.setSize(cols+4,rows+26); Display.show(); // loading JNI and starting services Camera = new JNIcamera(); // initializing camera-grabber if (0>Camera.JNIinitFrameGrabber()) System.err.println("error: initFrameGrabber"); if (0>Camera.JNIstartContCapture()) System.err.println("error: startContCapture"); } /********************************************************************** runLoop method required by Runnable Interface. @param @return @exception **********************************************************************/ public void runLoop() { // refreshing frame Camera.JNIgrabContFrame(); // tranlating frame to variable Camera.JNIgetIntFrame(pixels); // refresh display using new pixels view.refresh(); view.repaint(); } }