Skip to content

Instantly share code, notes, and snippets.

@kepocnhh
Last active October 24, 2020 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kepocnhh/554e688edbcfa150cf8348733e3529b0 to your computer and use it in GitHub Desktop.
Save kepocnhh/554e688edbcfa150cf8348733e3529b0 to your computer and use it in GitHub Desktop.
/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */
#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES)
# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
#endif
/* Intel's compiler complains if a variable which was never initialised is
* cast to void, which is a common idiom which we use to indicate that we
* are aware a variable isn't used. So we just silence that warning.
* See: https://github.com/swig/swig/issues/192 for more discussion.
*/
#ifdef __INTEL_COMPILER
# pragma warning disable 592
#endif
/* Fix for jlong on 64-bit x86 Solaris */
#if defined(__x86_64)
# ifdef _LP64
# undef _LP64
# endif
#endif
#include <jni.h>
#include <stdlib.h>
#include <string.h>
#include <x264.h>
#include "x264.h"
#ifdef __cplusplus
extern "C" {
#endif
static void log(
JNIEnv *env,
jclass cls,
const char* message
) {
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "log", "(Ljava/lang/String;)V");
jstring m = (*env)->NewStringUTF(env, message);
(*env)->CallStaticVoidMethod(env, cls, mid, m);
}
typedef struct {
x264_t* x264_encoder;
x264_param_t x264_param;
x264_picture_t picture_in;
} EncoderContext;
static void releaseEncoderContext(EncoderContext* encoder_context) {
x264_t* x264_encoder = encoder_context->x264_encoder;
x264_nal_t *pp_nal;
int pi_nal;
x264_picture_t pic_out;
while (x264_encoder_delayed_frames(x264_encoder)) {
x264_encoder_encode(x264_encoder, &pp_nal, &pi_nal, NULL, &pic_out);
}
x264_encoder_close(x264_encoder);
encoder_context->x264_encoder = NULL;
free(encoder_context);
}
jlong JNICALL Java_org_videolan_x264_x264JNI_encoderStart(JNIEnv *env, jclass ignored) {
EncoderContext* encoder_context = (EncoderContext*) malloc(sizeof(EncoderContext));
x264_param_t* x264_param = malloc(sizeof(x264_param_t));
//ultrafast,superfast,veryfast,faster,fast,medium,slow,slower,veryslow,placebo
//film,animation,grain,stillimage,psnr,ssim,fastdecode,zerolatency,touhou
x264_param_default_preset(x264_param, "medium", "zerolatency"); // todo
int width = 640;
int height = 360;
x264_param->i_width = width;
x264_param->i_height = height;
x264_param->rc.i_bitrate = 500; // todo Kbps or bps?
x264_param->rc.i_rc_method = X264_RC_ABR;
int fps = 30;
x264_param->i_fps_num = fps;
x264_param->i_fps_den = 1;
x264_param->i_keyint_max = fps * 2; // todo gop?
x264_param->b_repeat_headers = 0;
//baseline,main,high,high10,high422,high444
int apply_profile_result = x264_param_apply_profile(x264_param, "high"); // todo
if (apply_profile_result < 0) {
return 0; // todo
}
encoder_context->x264_param = *x264_param;
x264_t* x264_encoder = x264_encoder_open(x264_param);
if (x264_encoder == NULL) {
return 0; // todo
}
encoder_context->x264_encoder = x264_encoder;
x264_picture_t* picture_in = (x264_picture_t*) malloc(sizeof(x264_picture_t));
int csp = X264_CSP_YV12;
x264_picture_alloc(picture_in, csp, width, height);
picture_in->img.i_csp = csp; // todo
picture_in->i_type = X264_TYPE_AUTO;
picture_in->img.i_stride[0] = width;
encoder_context->picture_in = *picture_in;
return (long) encoder_context;
}
void JNICALL Java_org_videolan_x264_x264JNI_encoderStop(
JNIEnv *env,
jclass ignored,
jlong encoder_context_pointer
) {
EncoderContext* encoder_context = (EncoderContext*) encoder_context_pointer;
releaseEncoderContext(encoder_context);
}
JNIEXPORT jbyteArray JNICALL Java_org_videolan_x264_x264JNI_encode(
JNIEnv *env,
jclass cls,
jlong encoder_context_pointer,
jbyteArray decoded,
jlong time
) {
EncoderContext* encoder_context = (EncoderContext*) encoder_context_pointer;
if (encoder_context == NULL) {
log(env, cls, "encoder_context == NULL");
return NULL;
}
jbyte* input_frame = (*env)->GetByteArrayElements(env, decoded, NULL);
if (input_frame == NULL) {
log(env, cls, "input_frame == NULL");
return NULL;
}
x264_nal_t* pp_nal;
int pi_nal;
x264_picture_t picture_out;
encoder_context->picture_in.i_pts = time; // todo
encoder_context->picture_in.img.plane[0] = (uint8_t *) input_frame; // todo
int y_size = encoder_context->x264_param.i_width * encoder_context->x264_param.i_height;
switch (encoder_context->picture_in.img.i_csp) {
case X264_CSP_YV12:
encoder_context->picture_in.img.i_plane = 3;
encoder_context->picture_in.img.plane[1] = encoder_context->picture_in.img.plane[0] + y_size;
encoder_context->picture_in.img.i_stride[1] = encoder_context->x264_param.i_width / 2;
encoder_context->picture_in.img.plane[2] = encoder_context->picture_in.img.plane[1] + y_size / 4;
encoder_context->picture_in.img.i_stride[2] = encoder_context->x264_param.i_width / 2;
break;
default:
log(env, cls, "csp not supported");
return NULL;
}
int frame_size = x264_encoder_encode(
encoder_context->x264_encoder,
&pp_nal,
&pi_nal,
&encoder_context->picture_in,
&picture_out
);
(*env)->ReleaseByteArrayElements(env, decoded, input_frame, JNI_ABORT);
if (frame_size < 0) {
log(env, cls, "frame_size < 0");
return NULL; // todo
}
if (frame_size == 0) {
log(env, cls, "frame_size == 0");
return NULL;
}
if (pi_nal < 0) {
log(env, cls, "pi_nal < 0");
return NULL; // todo
}
if (pi_nal == 0) {
log(env, cls, "pi_nal == 0");
return NULL;
}
jbyteArray output_frame = (*env)->NewByteArray(env, frame_size);
if (output_frame == NULL) {
log(env, cls, "output_frame == NULL");
return NULL;
}
int offset = 4;
jbyte* source = (jbyte*) pp_nal[0].p_payload;
source += offset;
(*env)->SetByteArrayRegion(
env,
output_frame,
0,
frame_size - offset,
source
);
return output_frame;
}
#ifdef __cplusplus
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment