Skip to content
Snippets Groups Projects
Commit 0966bd94 authored by Leo Ma's avatar Leo Ma
Browse files

Use volatile for communication


Signed-off-by: default avatarLeo Ma <begeekmyfriend@gmail.com>
parent 92235cd5
No related branches found
No related tags found
No related merge requests found
#include <jni.h>
#include <string.h>
#include <libyuv.h>
#include <x264.h>
#include <android/log.h>
#define LIBENC_LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "libenc", __VA_ARGS__))
#define LIBENC_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO , "libenc", __VA_ARGS__))
#define LIBENC_LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN , "libenc", __VA_ARGS__))
#define LIBENC_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "libenc", __VA_ARGS__))
#define LIBENC_ARRAY_ELEMS(a) (sizeof(a) / sizeof(a[0]))
using namespace libyuv;
struct YuvFrame {
int width;
int height;
uint8_t *data;
uint8_t *y;
uint8_t *u;
uint8_t *v;
};
typedef struct x264_context {
// encode parameter
x264_param_t params;
x264_t *encoder;
x264_picture_t picture;
bool global_nal_header;
// input
int width;
int height;
int bitrate;
int fps;
int gop;
char preset[16];
// output
int64_t pts;
int dts;
bool is_key_frame;
} x264_context;
static JavaVM* jvm;
static JNIEnv *jenv;
static struct x264_context x264_ctx;
static uint8_t h264_es[1024 * 1024];
static struct YuvFrame i420_rotated_frame;
static struct YuvFrame i420_scaled_frame;
static struct YuvFrame nv12_frame;
static bool nv21_to_i420(jbyte *nv21_frame, jint src_width, jint src_height,
jboolean need_flip, jint rotate_degree) {
int y_size = src_width * src_height;
if (rotate_degree % 180 == 0) {
if (i420_rotated_frame.width != src_width || i420_rotated_frame.height != src_height) {
free(i420_rotated_frame.data);
i420_rotated_frame.width = src_width;
i420_rotated_frame.height = src_height;
i420_rotated_frame.data = (uint8_t *) malloc(y_size * 3 / 2);
i420_rotated_frame.y = i420_rotated_frame.data;
i420_rotated_frame.u = i420_rotated_frame.y + y_size;
i420_rotated_frame.v = i420_rotated_frame.u + y_size / 4;
}
} else {
if (i420_rotated_frame.width != src_height || i420_rotated_frame.height != src_width) {
free(i420_rotated_frame.data);
i420_rotated_frame.width = src_height;
i420_rotated_frame.height = src_width;
i420_rotated_frame.data = (uint8_t *) malloc(y_size * 3 / 2);
i420_rotated_frame.y = i420_rotated_frame.data;
i420_rotated_frame.u = i420_rotated_frame.y + y_size;
i420_rotated_frame.v = i420_rotated_frame.u + y_size / 4;
}
}
jint ret = ConvertToI420((uint8_t *) nv21_frame, y_size,
i420_rotated_frame.y, i420_rotated_frame.width,
i420_rotated_frame.u, i420_rotated_frame.width / 2,
i420_rotated_frame.v, i420_rotated_frame.width / 2,
0, 0,
src_width, src_height,
src_width, src_height,
(RotationMode) rotate_degree, FOURCC_NV21);
if (ret < 0) {
LIBENC_LOGE("ConvertToI420 failure");
return false;
}
ret = I420Scale(i420_rotated_frame.y, i420_rotated_frame.width,
i420_rotated_frame.u, i420_rotated_frame.width / 2,
i420_rotated_frame.v, i420_rotated_frame.width / 2,
need_flip ? -i420_rotated_frame.width : i420_rotated_frame.width, i420_rotated_frame.height,
i420_scaled_frame.y, i420_scaled_frame.width,
i420_scaled_frame.u, i420_scaled_frame.width / 2,
i420_scaled_frame.v, i420_scaled_frame.width / 2,
i420_scaled_frame.width, i420_scaled_frame.height,
kFilterNone);
if (ret < 0) {
LIBENC_LOGE("I420Scale failure");
return false;
}
return true;
}
static void libenc_setEncoderBitrate(JNIEnv* env, jobject thiz, jint bitrate) {
x264_ctx.bitrate = bitrate / 1000; // kbps
}
static void libenc_setEncoderFps(JNIEnv* env, jobject thiz, jint fps) {
x264_ctx.fps = fps;
}
static void libenc_setEncoderGop(JNIEnv* env, jobject thiz, jint gop_size) {
x264_ctx.gop = gop_size;
}
static void libenc_setEncoderPreset(JNIEnv* env, jobject thiz, jstring preset) {
const char *enc_preset = env->GetStringUTFChars(preset, NULL);
strcpy(x264_ctx.preset, enc_preset);
env->ReleaseStringUTFChars(preset, enc_preset);
}
static void libenc_setEncoderResolution(JNIEnv* env, jobject thiz, jint out_width, jint out_height) {
int y_size = out_width * out_height;
if (i420_scaled_frame.width != out_width || i420_scaled_frame.height != out_height) {
free(i420_scaled_frame.data);
i420_scaled_frame.width = out_width;
i420_scaled_frame.height = out_height;
i420_scaled_frame.data = (uint8_t *) malloc(y_size * 3 / 2);
i420_scaled_frame.y = i420_scaled_frame.data;
i420_scaled_frame.u = i420_scaled_frame.y + y_size;
i420_scaled_frame.v = i420_scaled_frame.u + y_size / 4;
}
if (nv12_frame.width != out_width || nv12_frame.height != out_height) {
free(nv12_frame.data);
nv12_frame.width = out_width;
nv12_frame.height = out_height;
nv12_frame.data = (uint8_t *) malloc(y_size * 3 / 2);
nv12_frame.y = nv12_frame.data;
nv12_frame.u = nv12_frame.y + y_size;
nv12_frame.v = nv12_frame.u + y_size / 4;
}
x264_ctx.width = out_width;
x264_ctx.height = out_height;
}
// For COLOR_FormatYUV420Planar
static jbyteArray libenc_NV21ToI420(JNIEnv* env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree) {
jbyte* nv21_frame = env->GetByteArrayElements(frame, NULL);
if (!nv21_to_i420(nv21_frame, src_width, src_height, need_flip, rotate_degree)) {
return NULL;
}
int y_size = i420_scaled_frame.width * i420_scaled_frame.height;
jbyteArray i420Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(i420Frame, 0, y_size * 3 / 2, (jbyte *) i420_scaled_frame.data);
env->ReleaseByteArrayElements(frame, nv21_frame, JNI_ABORT);
return i420Frame;
}
// For COLOR_FormatYUV420SemiPlanar
static jbyteArray libenc_NV21ToNV12(JNIEnv* env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree) {
jbyte* nv21_frame = env->GetByteArrayElements(frame, NULL);
if (!nv21_to_i420(nv21_frame, src_width, src_height, need_flip, rotate_degree)) {
return NULL;
}
int ret = ConvertFromI420(i420_scaled_frame.y, i420_scaled_frame.width,
i420_scaled_frame.u, i420_scaled_frame.width / 2,
i420_scaled_frame.v, i420_scaled_frame.width / 2,
nv12_frame.data, nv12_frame.width,
nv12_frame.width, nv12_frame.height,
FOURCC_NV12);
if (ret < 0) {
LIBENC_LOGE("ConvertFromI420 failure");
return NULL;
}
int y_size = nv12_frame.width * nv12_frame.height;
jbyteArray nv12Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(nv12Frame, 0, y_size * 3 / 2, (jbyte *) nv12_frame.data);
env->ReleaseByteArrayElements(frame, nv21_frame, JNI_ABORT);
return nv12Frame;
}
static int encode_nals(const x264_nal_t *nals, int nnal) {
int i;
uint8_t *p = h264_es;
for (i = 0; i < nnal; i++) {
memcpy(p, nals[i].p_payload, nals[i].i_payload);
p += nals[i].i_payload;
}
return p - h264_es;
}
static int encode_global_nal_header() {
int nnal;
x264_nal_t *nals;
x264_ctx.global_nal_header = false;
x264_encoder_headers(x264_ctx.encoder, &nals, &nnal);
return encode_nals(nals, nnal);
}
static int x264_encode(struct YuvFrame *i420_frame, int64_t pts) {
int out_len, nnal;
x264_nal_t *nal;
x264_picture_t pic_out;
int y_size = i420_frame->width * i420_frame->height;
x264_ctx.picture.img.i_csp = X264_CSP_I420;
x264_ctx.picture.img.i_plane = 3;
x264_ctx.picture.img.plane[0] = i420_frame->y;
x264_ctx.picture.img.i_stride[0] = i420_frame->width;
x264_ctx.picture.img.plane[1] = i420_frame->u;
x264_ctx.picture.img.i_stride[1] = i420_frame->width / 2;
x264_ctx.picture.img.plane[2] = i420_frame->v;
x264_ctx.picture.img.i_stride[2] = i420_frame->width / 2;
x264_ctx.picture.i_pts = pts;
x264_ctx.picture.i_type = X264_TYPE_AUTO;
if (x264_encoder_encode(x264_ctx.encoder, &nal, &nnal, &x264_ctx.picture, &pic_out) < 0) {
LIBENC_LOGE("Fail to encode in x264");
return -1;
}
x264_ctx.pts = pic_out.i_pts;
x264_ctx.dts = pic_out.i_dts;
x264_ctx.is_key_frame = pic_out.i_type == X264_TYPE_IDR;
return encode_nals(nal, nnal);
}
static jint libenc_NV21SoftEncode(JNIEnv* env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree, jlong pts) {
jbyte* nv21_frame = env->GetByteArrayElements(frame, NULL);
if (!nv21_to_i420(nv21_frame, src_width, src_height, need_flip, rotate_degree)) {
return JNI_ERR;
}
int es_len = x264_ctx.global_nal_header ? encode_global_nal_header() : x264_encode(&i420_scaled_frame, pts);
if (es_len <= 0) {
LIBENC_LOGE("Fail to encode nalu");
return JNI_ERR;
}
jbyteArray outputFrame = env->NewByteArray(es_len);
env->SetByteArrayRegion(outputFrame, 0, es_len, (jbyte *) h264_es);
jclass clz = env->GetObjectClass(thiz);
jmethodID mid = env->GetMethodID(clz, "onSoftEncodedData", "([BJZ)V");
env->CallVoidMethod(thiz, mid, outputFrame, x264_ctx.pts, x264_ctx.is_key_frame);
env->ReleaseByteArrayElements(frame, nv21_frame, JNI_ABORT);
return JNI_OK;
}
static void libenc_closeSoftEncoder(JNIEnv* env, jobject thiz) {
int nnal;
x264_nal_t *nal;
x264_picture_t pic_out;
if (x264_ctx.encoder != NULL) {
while(x264_encoder_delayed_frames(x264_ctx.encoder)) {
x264_encoder_encode(x264_ctx.encoder, &nal, &nnal, NULL, &pic_out);
}
x264_encoder_close(x264_ctx.encoder);
x264_ctx.encoder = NULL;
}
}
static jboolean libenc_openSoftEncoder(JNIEnv* env, jobject thiz) {
// presetting
x264_param_default_preset(&x264_ctx.params, x264_ctx.preset, "zerolatency");
x264_ctx.params.b_repeat_headers = 0;
// for iOS HW decoding
x264_ctx.params.b_sliced_threads = 0;
x264_ctx.global_nal_header = true;
// resolution
x264_ctx.params.i_width = x264_ctx.width;
x264_ctx.params.i_height = x264_ctx.height;
// bitrate
x264_ctx.params.rc.i_bitrate = x264_ctx.bitrate; // kbps
x264_ctx.params.rc.i_rc_method = X264_RC_ABR;
// fps
x264_ctx.params.i_fps_num = x264_ctx.fps;
x264_ctx.params.i_fps_den = 1;
// gop
x264_ctx.params.i_keyint_max = x264_ctx.gop;
if (x264_param_apply_profile(&x264_ctx.params, "baseline" ) < 0) {
LIBENC_LOGE("Fail to apply profile");
return JNI_FALSE;
}
x264_ctx.encoder = x264_encoder_open(&x264_ctx.params);
if (x264_ctx.encoder == NULL) {
LIBENC_LOGE("Fail to open x264 encoder!");
return JNI_FALSE;
}
return JNI_TRUE;
}
static JNINativeMethod libenc_methods[] = {
{ "setEncoderResolution", "(II)V", (void *)libenc_setEncoderResolution },
{ "setEncoderFps", "(I)V", (void *)libenc_setEncoderFps },
{ "setEncoderGop", "(I)V", (void *)libenc_setEncoderGop },
{ "setEncoderBitrate", "(I)V", (void *)libenc_setEncoderBitrate },
{ "setEncoderPreset", "(Ljava/lang/String;)V", (void *)libenc_setEncoderPreset },
{ "NV21ToI420", "([BIIZI)[B", (void *)libenc_NV21ToI420 },
{ "NV21ToNV12", "([BIIZI)[B", (void *)libenc_NV21ToNV12 },
{ "openSoftEncoder", "()Z", (void *)libenc_openSoftEncoder },
{ "closeSoftEncoder", "()V", (void *)libenc_closeSoftEncoder },
{ "NV21SoftEncode", "([BIIZIJ)I", (void *)libenc_NV21SoftEncode },
};
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
jvm = vm;
if (jvm->GetEnv((void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
LIBENC_LOGE("Env not got");
return JNI_ERR;
}
jclass clz = jenv->FindClass("net/ossrs/yasea/SrsEncoder");
if (clz == NULL) {
LIBENC_LOGE("Class \"net/ossrs/yasea/SrsEncoder\" not found");
return JNI_ERR;
}
if (jenv->RegisterNatives(clz, libenc_methods, LIBENC_ARRAY_ELEMS(libenc_methods))) {
LIBENC_LOGE("methods not registered");
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
#include <jni.h>
#include <string.h>
#include <libyuv.h>
#include <x264.h>
#include <android/log.h>
#define LIBENC_LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "libenc", __VA_ARGS__))
#define LIBENC_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO , "libenc", __VA_ARGS__))
#define LIBENC_LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN , "libenc", __VA_ARGS__))
#define LIBENC_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "libenc", __VA_ARGS__))
#define LIBENC_ARRAY_ELEMS(a) (sizeof(a) / sizeof(a[0]))
using namespace libyuv;
struct YuvFrame {
int width;
int height;
uint8_t *data;
uint8_t *y;
uint8_t *u;
uint8_t *v;
};
typedef struct x264_context {
// encode parameter
x264_param_t params;
x264_t *encoder;
x264_picture_t picture;
bool global_nal_header;
// input
int width;
int height;
int bitrate;
int fps;
int gop;
char preset[16];
// output
int64_t pts;
int dts;
bool is_key_frame;
} x264_context;
static JavaVM *jvm;
static JNIEnv *jenv;
static struct x264_context x264_ctx;
static uint8_t h264_es[1024 * 1024];
static const int SRC_COLOR_FMT = FOURCC_RGBA;
static const int DST_COLOR_FMT = FOURCC_NV12;
static struct YuvFrame i420_rotated_frame;
static struct YuvFrame i420_scaled_frame;
static struct YuvFrame nv12_frame;
static bool convert_to_i420(uint8_t *src_frame, jint src_width, jint src_height,
jboolean need_flip, jint rotate_degree, int format) {
int y_size = src_width * src_height;
if (rotate_degree % 180 == 0) {
if (i420_rotated_frame.width != src_width || i420_rotated_frame.height != src_height) {
free(i420_rotated_frame.data);
i420_rotated_frame.width = src_width;
i420_rotated_frame.height = src_height;
i420_rotated_frame.data = (uint8_t *) malloc(y_size * 4 / 2);
i420_rotated_frame.y = i420_rotated_frame.data;
i420_rotated_frame.u = i420_rotated_frame.y + y_size;
i420_rotated_frame.v = i420_rotated_frame.u + y_size / 4;
}
} else {
if (i420_rotated_frame.width != src_height || i420_rotated_frame.height != src_width) {
free(i420_rotated_frame.data);
i420_rotated_frame.width = src_height;
i420_rotated_frame.height = src_width;
i420_rotated_frame.data = (uint8_t *) malloc(y_size * 4 / 2);
i420_rotated_frame.y = i420_rotated_frame.data;
i420_rotated_frame.u = i420_rotated_frame.y + y_size;
i420_rotated_frame.v = i420_rotated_frame.u + y_size / 4;
}
}
jint ret = ConvertToI420(src_frame, y_size,
i420_rotated_frame.y, i420_rotated_frame.width,
i420_rotated_frame.u, i420_rotated_frame.width / 2,
i420_rotated_frame.v, i420_rotated_frame.width / 2,
0, 0,
src_width, src_height,
src_width, src_height,
(RotationMode) rotate_degree, format);
if (ret < 0) {
LIBENC_LOGE("ConvertToI420 failure");
return false;
}
ret = I420Scale(i420_rotated_frame.y, i420_rotated_frame.width,
i420_rotated_frame.u, i420_rotated_frame.width / 2,
i420_rotated_frame.v, i420_rotated_frame.width / 2,
need_flip ? -i420_rotated_frame.width : i420_rotated_frame.width,
i420_rotated_frame.height,
i420_scaled_frame.y, i420_scaled_frame.width,
i420_scaled_frame.u, i420_scaled_frame.width / 2,
i420_scaled_frame.v, i420_scaled_frame.width / 2,
i420_scaled_frame.width, i420_scaled_frame.height,
kFilterNone);
if (ret < 0) {
LIBENC_LOGE("I420Scale failure");
return false;
}
return true;
}
static bool convert_to_i420_with_crop_scale(uint8_t *src_frame, jint src_width, jint src_height,
jint crop_x, jint crop_y, jint crop_width,
jint crop_height,
jboolean need_flip, jint rotate_degree, int format) {
int y_size = src_width * src_height;
if (rotate_degree % 180 == 0) {
if (i420_rotated_frame.width != src_width || i420_rotated_frame.height != src_height) {
free(i420_rotated_frame.data);
i420_rotated_frame.data = (uint8_t *) malloc(y_size * 3 / 2);
i420_rotated_frame.y = i420_rotated_frame.data;
i420_rotated_frame.u = i420_rotated_frame.y + y_size;
i420_rotated_frame.v = i420_rotated_frame.u + y_size / 4;
}
i420_rotated_frame.width = crop_width;
i420_rotated_frame.height = crop_height;
} else {
if (i420_rotated_frame.width != src_height || i420_rotated_frame.height != src_width) {
free(i420_rotated_frame.data);
i420_rotated_frame.data = (uint8_t *) malloc(y_size * 3 / 2);
i420_rotated_frame.y = i420_rotated_frame.data;
i420_rotated_frame.u = i420_rotated_frame.y + y_size;
i420_rotated_frame.v = i420_rotated_frame.u + y_size / 4;
}
i420_rotated_frame.width = crop_height;
i420_rotated_frame.height = crop_width;
}
jint ret = ConvertToI420(src_frame, y_size,
i420_rotated_frame.y, i420_rotated_frame.width,
i420_rotated_frame.u, i420_rotated_frame.width / 2,
i420_rotated_frame.v, i420_rotated_frame.width / 2,
crop_x, crop_y,
src_width, need_flip ? -src_height : src_height,
crop_width, crop_height,
(RotationMode) rotate_degree, format);
if (ret < 0) {
LIBENC_LOGE("ConvertToI420 failure");
return false;
}
ret = I420Scale(i420_rotated_frame.y, i420_rotated_frame.width,
i420_rotated_frame.u, i420_rotated_frame.width / 2,
i420_rotated_frame.v, i420_rotated_frame.width / 2,
i420_rotated_frame.width, i420_rotated_frame.height,
i420_scaled_frame.y, i420_scaled_frame.width,
i420_scaled_frame.u, i420_scaled_frame.width / 2,
i420_scaled_frame.v, i420_scaled_frame.width / 2,
i420_scaled_frame.width, i420_scaled_frame.height,
kFilterNone);
if (ret < 0) {
LIBENC_LOGE("I420Scale failure");
return false;
}
return true;
}
static void libenc_setEncoderBitrate(JNIEnv *env, jobject thiz, jint bitrate) {
x264_ctx.bitrate = bitrate / 1024; // kbps
}
static void libenc_setEncoderFps(JNIEnv *env, jobject thiz, jint fps) {
x264_ctx.fps = fps;
}
static void libenc_setEncoderGop(JNIEnv *env, jobject thiz, jint gop_size) {
x264_ctx.gop = gop_size;
}
static void libenc_setEncoderPreset(JNIEnv *env, jobject thiz, jstring preset) {
const char *enc_preset = env->GetStringUTFChars(preset, NULL);
strcpy(x264_ctx.preset, enc_preset);
env->ReleaseStringUTFChars(preset, enc_preset);
}
static void
libenc_setEncoderResolution(JNIEnv *env, jobject thiz, jint out_width, jint out_height) {
int y_size = out_width * out_height;
if (i420_scaled_frame.width != out_width || i420_scaled_frame.height != out_height) {
free(i420_scaled_frame.data);
i420_scaled_frame.width = out_width;
i420_scaled_frame.height = out_height;
i420_scaled_frame.data = (uint8_t *) malloc(y_size * 3 / 2);
i420_scaled_frame.y = i420_scaled_frame.data;
i420_scaled_frame.u = i420_scaled_frame.y + y_size;
i420_scaled_frame.v = i420_scaled_frame.u + y_size / 4;
}
if (nv12_frame.width != out_width || nv12_frame.height != out_height) {
free(nv12_frame.data);
nv12_frame.width = out_width;
nv12_frame.height = out_height;
nv12_frame.data = (uint8_t *) malloc(y_size * 3 / 2);
nv12_frame.y = nv12_frame.data;
nv12_frame.u = nv12_frame.y + y_size;
nv12_frame.v = nv12_frame.u + y_size / 4;
}
x264_ctx.width = out_width;
x264_ctx.height = out_height;
}
// For COLOR_FormatYUV420Planar
static jbyteArray libenc_RGBAToI420(JNIEnv *env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree) {
jbyte *rgba_frame = env->GetByteArrayElements(frame, NULL);
if (!convert_to_i420((uint8_t *) rgba_frame, src_width, src_height, need_flip, rotate_degree,
FOURCC_RGBA)) {
return NULL;
}
int y_size = i420_scaled_frame.width * i420_scaled_frame.height;
jbyteArray i420Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(i420Frame, 0, y_size * 3 / 2, (jbyte *) i420_scaled_frame.data);
env->ReleaseByteArrayElements(frame, rgba_frame, JNI_ABORT);
return i420Frame;
}
static jbyteArray
libenc_NV21ToNV12Scaled(JNIEnv *env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree,
jint crop_x, jint crop_y, jint crop_width, jint crop_height) {
jbyte *rgba_frame = env->GetByteArrayElements(frame, NULL);
if (!convert_to_i420_with_crop_scale((uint8_t *) rgba_frame, src_width, src_height,
crop_x, crop_y, crop_width, crop_height,
need_flip, rotate_degree, FOURCC_NV21)) {
return NULL;
}
int ret = ConvertFromI420(i420_scaled_frame.y, i420_scaled_frame.width,
i420_scaled_frame.u, i420_scaled_frame.width / 2,
i420_scaled_frame.v, i420_scaled_frame.width / 2,
nv12_frame.data, nv12_frame.width,
nv12_frame.width, nv12_frame.height,
FOURCC_NV12);
if (ret < 0) {
LIBENC_LOGE("ConvertFromI420 failure");
return NULL;
}
int y_size = nv12_frame.width * nv12_frame.height;
jbyteArray nv12Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(nv12Frame, 0, y_size * 3 / 2, (jbyte *) nv12_frame.data);
env->ReleaseByteArrayElements(frame, rgba_frame, JNI_ABORT);
return nv12Frame;
}
static jbyteArray
libenc_NV21ToI420Scaled(JNIEnv *env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree,
jint crop_x, jint crop_y, jint crop_width, jint crop_height) {
jbyte *argb_frame = env->GetByteArrayElements(frame, NULL);
if (!convert_to_i420_with_crop_scale((uint8_t *) argb_frame, src_width, src_height,
crop_x, crop_y, crop_width, crop_height,
need_flip, rotate_degree, FOURCC_NV21)) {
return NULL;
}
int y_size = i420_scaled_frame.width * i420_scaled_frame.height;
jbyteArray i420Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(i420Frame, 0, y_size * 3 / 2, (jbyte *) i420_scaled_frame.data);
env->ReleaseByteArrayElements(frame, argb_frame, JNI_ABORT);
return i420Frame;
}
// For Bitmap.getPixels() ARGB_8888
static jbyteArray libenc_ARGBToI420(JNIEnv *env, jobject thiz, jintArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree) {
jint *argb_frame = env->GetIntArrayElements(frame, NULL);
if (!convert_to_i420((uint8_t *) argb_frame, src_width, src_height, need_flip, rotate_degree,
FOURCC_ARGB)) {
return NULL;
}
int y_size = i420_scaled_frame.width * i420_scaled_frame.height;
jbyteArray i420Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(i420Frame, 0, y_size * 3 / 2, (jbyte *) i420_scaled_frame.data);
env->ReleaseIntArrayElements(frame, argb_frame, JNI_ABORT);
return i420Frame;
}
// For Bitmap.getPixels() ARGB_8888
static jbyteArray
libenc_ARGBToI420Scaled(JNIEnv *env, jobject thiz, jintArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree,
jint crop_x, jint crop_y, jint crop_width, jint crop_height) {
jint *argb_frame = env->GetIntArrayElements(frame, NULL);
if (!convert_to_i420_with_crop_scale((uint8_t *) argb_frame, src_width, src_height,
crop_x, crop_y, crop_width, crop_height,
need_flip, rotate_degree, FOURCC_ARGB)) {
return NULL;
}
int y_size = i420_scaled_frame.width * i420_scaled_frame.height;
jbyteArray i420Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(i420Frame, 0, y_size * 3 / 2, (jbyte *) i420_scaled_frame.data);
env->ReleaseIntArrayElements(frame, argb_frame, JNI_ABORT);
return i420Frame;
}
// For COLOR_FormatYUV420SemiPlanar
static jbyteArray libenc_RGBAToNV12(JNIEnv *env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree) {
jbyte *rgba_frame = env->GetByteArrayElements(frame, NULL);
if (!convert_to_i420((uint8_t *) rgba_frame, src_width, src_height, need_flip, rotate_degree,
FOURCC_RGBA)) {
return NULL;
}
int ret = ConvertFromI420(i420_scaled_frame.y, i420_scaled_frame.width,
i420_scaled_frame.u, i420_scaled_frame.width / 2,
i420_scaled_frame.v, i420_scaled_frame.width / 2,
nv12_frame.data, nv12_frame.width,
nv12_frame.width, nv12_frame.height,
DST_COLOR_FMT);
if (ret < 0) {
LIBENC_LOGE("ConvertFromI420 failure");
return NULL;
}
int y_size = nv12_frame.width * nv12_frame.height;
jbyteArray nv12Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(nv12Frame, 0, y_size * 3 / 2, (jbyte *) nv12_frame.data);
env->ReleaseByteArrayElements(frame, rgba_frame, JNI_ABORT);
return nv12Frame;
}
// For Bitmap.getPixels() ARGB_8888
static jbyteArray libenc_ARGBToNV12(JNIEnv *env, jobject thiz, jintArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree) {
jint *argb_frame = env->GetIntArrayElements(frame, NULL);
if (!convert_to_i420((uint8_t *) argb_frame, src_width, src_height, need_flip, rotate_degree,
FOURCC_ARGB)) {
return NULL;
}
int ret = ConvertFromI420(i420_scaled_frame.y, i420_scaled_frame.width,
i420_scaled_frame.u, i420_scaled_frame.width / 2,
i420_scaled_frame.v, i420_scaled_frame.width / 2,
nv12_frame.data, nv12_frame.width,
nv12_frame.width, nv12_frame.height,
DST_COLOR_FMT);
if (ret < 0) {
LIBENC_LOGE("ConvertFromI420 failure");
return NULL;
}
int y_size = nv12_frame.width * nv12_frame.height;
jbyteArray nv12Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(nv12Frame, 0, y_size * 3 / 2, (jbyte *) nv12_frame.data);
env->ReleaseIntArrayElements(frame, argb_frame, JNI_ABORT);
return nv12Frame;
}
// For Bitmap.getPixels() ARGB_8888
static jbyteArray
libenc_ARGBToNV12Scaled(JNIEnv *env, jobject thiz, jintArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree,
jint crop_x, jint crop_y, jint crop_width, jint crop_height) {
jint *argb_frame = env->GetIntArrayElements(frame, NULL);
if (!convert_to_i420_with_crop_scale((uint8_t *) argb_frame, src_width, src_height,
crop_x, crop_y, crop_width, crop_height,
need_flip, rotate_degree, FOURCC_ARGB)) {
return NULL;
}
int ret = ConvertFromI420(i420_scaled_frame.y, i420_scaled_frame.width,
i420_scaled_frame.u, i420_scaled_frame.width / 2,
i420_scaled_frame.v, i420_scaled_frame.width / 2,
nv12_frame.data, nv12_frame.width,
nv12_frame.width, nv12_frame.height,
DST_COLOR_FMT);
if (ret < 0) {
LIBENC_LOGE("ConvertFromI420 failure");
return NULL;
}
int y_size = nv12_frame.width * nv12_frame.height;
jbyteArray nv12Frame = env->NewByteArray(y_size * 3 / 2);
env->SetByteArrayRegion(nv12Frame, 0, y_size * 3 / 2, (jbyte *) nv12_frame.data);
env->ReleaseIntArrayElements(frame, argb_frame, JNI_ABORT);
return nv12Frame;
}
static int encode_nals(const x264_nal_t *nals, int nnal) {
int i;
uint8_t *p = h264_es;
for (i = 0; i < nnal; i++) {
memcpy(p, nals[i].p_payload, nals[i].i_payload);
p += nals[i].i_payload;
}
return p - h264_es;
}
static int encode_global_nal_header() {
int nnal;
x264_nal_t *nals;
x264_ctx.global_nal_header = false;
x264_encoder_headers(x264_ctx.encoder, &nals, &nnal);
return encode_nals(nals, nnal);
}
static int x264_encode(struct YuvFrame *i420_frame, int64_t pts) {
int out_len, nnal;
x264_nal_t *nal;
x264_picture_t pic_out;
int y_size = i420_frame->width * i420_frame->height;
x264_ctx.picture.img.i_csp = X264_CSP_I420;
x264_ctx.picture.img.i_plane = 3;
x264_ctx.picture.img.plane[0] = i420_frame->y;
x264_ctx.picture.img.i_stride[0] = i420_frame->width;
x264_ctx.picture.img.plane[1] = i420_frame->u;
x264_ctx.picture.img.i_stride[1] = i420_frame->width / 2;
x264_ctx.picture.img.plane[2] = i420_frame->v;
x264_ctx.picture.img.i_stride[2] = i420_frame->width / 2;
x264_ctx.picture.i_pts = pts;
x264_ctx.picture.i_type = X264_TYPE_AUTO;
if (x264_encoder_encode(x264_ctx.encoder, &nal, &nnal, &x264_ctx.picture, &pic_out) < 0) {
LIBENC_LOGE("Fail to encode in x264");
return -1;
}
x264_ctx.pts = pic_out.i_pts;
x264_ctx.dts = pic_out.i_dts;
x264_ctx.is_key_frame = pic_out.i_type == X264_TYPE_IDR;
return encode_nals(nal, nnal);
}
static jint libenc_RGBASoftEncode(JNIEnv *env, jobject thiz, jbyteArray frame, jint src_width,
jint src_height, jboolean need_flip, jint rotate_degree,
jlong pts) {
jbyte *rgba_frame = env->GetByteArrayElements(frame, NULL);
if (!convert_to_i420((uint8_t *) rgba_frame, src_width, src_height, need_flip, rotate_degree,
FOURCC_RGBA)) {
return JNI_ERR;
}
int es_len = x264_ctx.global_nal_header ? encode_global_nal_header() : x264_encode(
&i420_scaled_frame, pts);
if (es_len <= 0) {
LIBENC_LOGE("Fail to encode nalu");
return JNI_ERR;
}
jbyteArray outputFrame = env->NewByteArray(es_len);
env->SetByteArrayRegion(outputFrame, 0, es_len, (jbyte *) h264_es);
jclass clz = env->GetObjectClass(thiz);
jmethodID mid = env->GetMethodID(clz, "onSoftEncodedData", "([BJZ)V");
env->CallVoidMethod(thiz, mid, outputFrame, x264_ctx.pts, x264_ctx.is_key_frame);
env->ReleaseByteArrayElements(frame, rgba_frame, JNI_ABORT);
return JNI_OK;
}
static void libenc_closeSoftEncoder(JNIEnv *env, jobject thiz) {
int nnal;
x264_nal_t *nal;
x264_picture_t pic_out;
if (x264_ctx.encoder != NULL) {
while (x264_encoder_delayed_frames(x264_ctx.encoder)) {
x264_encoder_encode(x264_ctx.encoder, &nal, &nnal, NULL, &pic_out);
}
x264_encoder_close(x264_ctx.encoder);
x264_ctx.encoder = NULL;
}
}
static jboolean libenc_openSoftEncoder(JNIEnv *env, jobject thiz) {
// presetting
x264_param_default_preset(&x264_ctx.params, x264_ctx.preset, "zerolatency");
x264_ctx.params.b_repeat_headers = 0;
// for iOS HW decoding
x264_ctx.params.b_sliced_threads = 0;
x264_ctx.global_nal_header = true;
// resolution
x264_ctx.params.i_width = x264_ctx.width;
x264_ctx.params.i_height = x264_ctx.height;
// bitrate
x264_ctx.params.rc.i_bitrate = x264_ctx.bitrate; // kbps
x264_ctx.params.rc.i_rc_method = X264_RC_ABR;
// fps
x264_ctx.params.i_fps_num = x264_ctx.fps;
x264_ctx.params.i_fps_den = 1;
// gop
x264_ctx.params.i_keyint_max = x264_ctx.gop;
if (x264_param_apply_profile(&x264_ctx.params, "baseline") < 0) {
LIBENC_LOGE("Fail to apply profile");
return JNI_FALSE;
}
x264_ctx.encoder = x264_encoder_open(&x264_ctx.params);
if (x264_ctx.encoder == NULL) {
LIBENC_LOGE("Fail to open x264 encoder!");
return JNI_FALSE;
}
return JNI_TRUE;
}
static JNINativeMethod libenc_methods[] = {
{"setEncoderResolution", "(II)V", (void *) libenc_setEncoderResolution},
{"setEncoderFps", "(I)V", (void *) libenc_setEncoderFps},
{"setEncoderGop", "(I)V", (void *) libenc_setEncoderGop},
{"setEncoderBitrate", "(I)V", (void *) libenc_setEncoderBitrate},
{"setEncoderPreset", "(Ljava/lang/String;)V", (void *) libenc_setEncoderPreset},
{"RGBAToI420", "([BIIZI)[B", (void *) libenc_RGBAToI420},
{"RGBAToNV12", "([BIIZI)[B", (void *) libenc_RGBAToNV12},
{"ARGBToI420Scaled", "([IIIZIIIII)[B", (void *) libenc_ARGBToI420Scaled},
{"ARGBToNV12Scaled", "([IIIZIIIII)[B", (void *) libenc_ARGBToNV12Scaled},
{"ARGBToI420", "([IIIZI)[B", (void *) libenc_ARGBToI420},
{"ARGBToNV12", "([IIIZI)[B", (void *) libenc_ARGBToNV12},
{"NV21ToNV12Scaled", "([BIIZIIIII)[B", (void *) libenc_NV21ToNV12Scaled},
{"NV21ToI420Scaled", "([BIIZIIIII)[B", (void *) libenc_NV21ToI420Scaled},
{"openSoftEncoder", "()Z", (void *) libenc_openSoftEncoder},
{"closeSoftEncoder", "()V", (void *) libenc_closeSoftEncoder},
{"RGBASoftEncode", "([BIIZIJ)I", (void *) libenc_RGBASoftEncode},
};
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
jvm = vm;
if (jvm->GetEnv((void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
LIBENC_LOGE("Env not got");
return JNI_ERR;
}
jclass clz = jenv->FindClass("net/ossrs/yasea/SrsEncoder");
if (clz == NULL) {
LIBENC_LOGE("Class \"net/ossrs/yasea/SrsEncoder\" not found");
return JNI_ERR;
}
if (jenv->RegisterNatives(clz, libenc_methods, LIBENC_ARRAY_ELEMS(libenc_methods))) {
LIBENC_LOGE("methods not registered");
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
This diff is collapsed.
Loading
Loading
@@ -26,7 +26,6 @@ public class SrsFlvMuxer {
 
private volatile boolean connected = false;
private DefaultRtmpPublisher publisher;
private RtmpHandler mHandler;
 
private Thread worker;
private final Object txFrameLock = new Object();
Loading
Loading
@@ -48,7 +47,6 @@ public class SrsFlvMuxer {
* @param handler the rtmp event handler.
*/
public SrsFlvMuxer(RtmpHandler handler) {
mHandler = handler;
publisher = new DefaultRtmpPublisher(handler);
}
 
Loading
Loading
@@ -91,7 +89,6 @@ public class SrsFlvMuxer {
} catch (IllegalStateException e) {
// Ignore illegal state.
}
connected = false;
mVideoSequenceHeader = null;
mAudioSequenceHeader = null;
Log.i(TAG, "worker: disconnect ok.");
Loading
Loading
@@ -110,7 +107,7 @@ public class SrsFlvMuxer {
}
 
private void sendFlvTag(SrsFlvFrame frame) {
if (!connected || frame == null) {
if (frame == null) {
return;
}
 
Loading
Loading
@@ -176,6 +173,7 @@ public class SrsFlvMuxer {
* stop the muxer, disconnect RTMP connection.
*/
public void stop() {
connected = false;
mFlvTagCache.clear();
if (worker != null) {
worker.interrupt();
Loading
Loading
@@ -190,7 +188,7 @@ public class SrsFlvMuxer {
flv.reset();
needToFindKeyFrame = true;
Log.i(TAG, "SrsFlvMuxer closed");
// We should not block the main thread
new Thread(new Runnable() {
@Override
public void run() {
Loading
Loading
@@ -982,9 +980,11 @@ public class SrsFlvMuxer {
}
 
private void flvTagCacheAdd(SrsFlvFrame frame) {
mFlvTagCache.add(frame);
if (frame.isVideo()) {
getVideoFrameCacheNumber().incrementAndGet();
if (connected) {
mFlvTagCache.add(frame);
if (frame.isVideo()) {
getVideoFrameCacheNumber().incrementAndGet();
}
}
synchronized (txFrameLock) {
txFrameLock.notifyAll();
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment