I decided to implement JNI for x264 encoder as one of most popular H264 codec implementations. My short guideline covers x264, JNI related compilation steps and sample Java class.
So let start.
The first step that you need to perform is to compile proper library from x264 C code. For this activity I used following Alex's guide. I used several steps from this manual. One difference with my way was 64bit mingw installed on Ubuntu 14 64bit LTS, installed newest JDK 1.8. The JNI code compilation requires C headers copied from the Windows JDK located under C:\Program Files\Java\jdk1.8.0\include\win32. Copy this directory into your Ubuntu JDK folder: /usr/lib/jvm/java-8-oracle/include. Finally I configured x264:
./configure --cross-prefix=x86_64-w64-mingw32- --host=x86_64-pc-mingw32 --disable-avs --enable-shared --disable-cli --enable-win32thread
make
After a time you have to see the libx264-142.dll.
Next step I compile the JNI related code:
x86_64-w64-mingw32-gcc -Wl,--add-stdcall-alias -shared -m64 -O3 -ffast-math -I/home/vadym/win32-cross/src/x264 -I/home/vadym/win32-cross/include -I $JAVA_HOME/include -I $JAVA_HOME/include/win32 -mfpmath=sse -msse -std=gnu99 -fomit-frame-pointer -fno-tree-vectorize -fno-zero-initialized-in-bss -o h264-jni.dll h264-jni.c -L ~/win32-cross/src/x264 -l x264.dll
This command gets h264-jni.dll
These two dll's will be required for your Java encoder:
H264Encoder.java
So let start.
The first step that you need to perform is to compile proper library from x264 C code. For this activity I used following Alex's guide. I used several steps from this manual. One difference with my way was 64bit mingw installed on Ubuntu 14 64bit LTS, installed newest JDK 1.8. The JNI code compilation requires C headers copied from the Windows JDK located under C:\Program Files\Java\jdk1.8.0\include\win32. Copy this directory into your Ubuntu JDK folder: /usr/lib/jvm/java-8-oracle/include. Finally I configured x264:
./configure --cross-prefix=x86_64-w64-mingw32- --host=x86_64-pc-mingw32 --disable-avs --enable-shared --disable-cli --enable-win32thread
make
After a time you have to see the libx264-142.dll.
Next step I compile the JNI related code:
x86_64-w64-mingw32-gcc -Wl,--add-stdcall-alias -shared -m64 -O3 -ffast-math -I/home/vadym/win32-cross/src/x264 -I/home/vadym/win32-cross/include -I $JAVA_HOME/include -I $JAVA_HOME/include/win32 -mfpmath=sse -msse -std=gnu99 -fomit-frame-pointer -fno-tree-vectorize -fno-zero-initialized-in-bss -o h264-jni.dll h264-jni.c -L ~/win32-cross/src/x264 -l x264.dll
This command gets h264-jni.dll
These two dll's will be required for your Java encoder:
H264Encoder.java
package x264;
public class H264Encoder {
static {
System.loadLibrary("libx264-142");
System.loadLibrary("h264-jni");
}
private long encoder_pointer;
public H264Encoder(int width, int height) {
this.encoder_pointer = openEncoder(width, height);
}
public long openEncoder(int width, int height){
encoder_pointer = this.open(width, height);
return encoder_pointer;
}
public int encode(int type, byte[] in, int insize, byte[] out, boolean[] keyframe){
return this.encode(encoder_pointer, type, in, insize, out, keyframe);
}
public int finish(){
return this.finish(encoder_pointer);
}
private final native long open(int width, int height);
private final native int encode(long encoder, int type, byte[] in, int insize, byte[] out, boolean[] keyframe);
private final native int finish(long encoder);
public static void main(String[] args){
H264Encoder encoder = new H264Encoder(300, 200);
encoder.finish();
}
}
h264-jni.c
#include <stdint.h> #include <string.h> #include <jni.h> #include <stdio.h> #include <stdlib.h> #include <x264.h> #define DATA_MAX 3000000 #define H264_MTU 1024 typedef struct { x264_param_t * param; x264_t *handle; x264_picture_t * picture; x264_nal_t *nal; } Encoder; JNIEXPORT jlong JNICALL Java_x264_H264Encoder_open(JNIEnv* env, jobject thiz, jint width, jint height){ Encoder * en = (Encoder *) malloc(sizeof(Encoder)); en->param = (x264_param_t *) malloc(sizeof(x264_param_t)); en->picture = (x264_picture_t *) malloc(sizeof(x264_picture_t)); x264_param_default(en->param); //set default param //ultrafast,superfast,veryfast,faster,fast,medium,slow,slower,veryslow,placebo //film,animation,grain,stillimage,psnr,ssim,fastdecode,zerolatency,touhou x264_param_default_preset(en->param, "fast", "animation"); //en->param->rc.i_rc_method = X264_RC_CQP; //en->param->i_log_level = X264_LOG_NONE; en->param->i_width = width; //set frame width en->param->i_height = height; //set frame height //en->param->rc.i_lookahead = 0; en->param->i_bframe = 1; //en->param->i_fps_num = 5; //en->param->i_fps_den = 1; //baseline,main,high,high10,high422,high444 x264_param_apply_profile(en->param, "main"); if ((en->handle = x264_encoder_open(en->param)) == 0) { return 0; } /* Create a new pic */ x264_picture_alloc(en->picture, X264_CSP_I420, en->param->i_width, en->param->i_height); return (jlong) en; } JNIEXPORT jint JNICALL Java_x264_H264Encoder_finish(JNIEnv* env, jobject thiz, jlong handle){ Encoder * en = (Encoder *) handle; if(en->picture) { x264_picture_clean(en->picture); free(en->picture); en->picture = 0; } if(en->param) { free(en->param); en->param = 0; } if(en->handle) { x264_encoder_close(en->handle); } free(en); return 0; } JNIEXPORT jint JNICALL Java_x264_H264Encoder_encode(JNIEnv* env, jobject thiz, jlong handle, jint type, jbyteArray in, jint insize, jbyteArray out, jbooleanArray keyframe) { Encoder * en = (Encoder *) handle; jboolean key_frame[1]; x264_picture_t pic_out; int i_data=0; int nNal =-1; int result=0; int i=0; unsigned char in_buf[insize]; unsigned char * buf = in_buf; (*env)->GetByteArrayRegion (env, in, 0, insize, buf); jbyte h264Buf[insize]; jbyte * pTmpOut = h264Buf; int nPicSize=en->param->i_width*en->param->i_height; int size = nPicSize*sizeof(unsigned char)/4; memcpy(en->picture->img.plane[0], in_buf, 4*size); memcpy(en->picture->img.plane[1],&buf[nPicSize], size); memcpy(en->picture->img.plane[2],&buf[nPicSize+size],size); /*#define X264_TYPE_AUTO 0x0000 #define X264_TYPE_IDR 0x0001 #define X264_TYPE_I 0x0002 #define X264_TYPE_P 0x0003 #define X264_TYPE_BREF 0x0004 #define X264_TYPE_B 0x0005 #define X264_TYPE_KEYFRAME 0x0006*/ en->picture->i_type = type; if(x264_encoder_encode(en->handle, &(en->nal), &nNal, en->picture, &pic_out) < 0) { return -1; } for (i = 0; i < nNal; i++){ memcpy(pTmpOut, en->nal[i].p_payload, en->nal[i].i_payload); pTmpOut += en->nal[i].i_payload; result += en->nal[i].i_payload; } key_frame[0] = (jboolean) pic_out.b_keyframe; (*env)->SetBooleanArrayRegion(env, keyframe, 0, 1, key_frame); (*env)->SetByteArrayRegion(env, out, 0, result, h264Buf); return result; }