HEVC(H.265) encode
demo: https://github.com/wangzuxing/MyFFmpegH264H265YUVOpenGL
编码库: X265 — 编码符合高效率视频编码(HEVC/H.265)标准的视频的开源库
H.265/HEVC和H.264/AVC的编码架构相似:
帧内预测(intra prediction)、
帧间预测(inter prediction)、
转换(transform)、
量化(quantization)、
去区块滤波器(deblocking filter)、
熵编码(entropy coding)等模块
HEVC编码架构中,整体被分为了三个基本单位:编码单位(coding unit,CU)、预测单位(predict unit,PU) 和转换单位(transform unit,TU )。
编码单位:
H.265可以选择从最小的8x8到最大的64x64。
H.264宏块(macroblock/MB)大小都是固定的16x16像素
帧内预测模式:
H.265的支持33种方向,提供了更好的运动补偿处理和矢量预测方法
H.264只支持8种
压缩比率更高,编码视频质量更好:
相同的图象质量下,H.265编码的视频大小将减少大约39-44%。
在码率减少51-74%的情况下,H.265编码视频的质量能与H.264编码视频近似甚至更好,其本质上说是比预期的信噪比(PSNR)要好
Java端:
MainActivity0:
static {
...
System.loadLibrary("myx265"); // x265以libx265.a文件形式集成到myx265库中
}
...
//x265
public native boolean X265Start(String file);
public native void X265Enc(byte[] array, int length); // camera preview data 经swapYV12toI420()函数转换成H.265编码器支持的I420视频格式
public native void X265End();
...
/*
packed formats:将Y、U、V值储存成Macro Pixels阵列,和RGB的存放方式类似。
planar formats:将Y、U、V的三个份量分别存放在不同的矩阵中。
COLOR_FormatYUV420Planar: YUV420P I420
COLOR_FormatYUV420SemiPlanar: YUV420SP NV12
YUV420P,Y,U,V三个分量都是平面格式,分为I420和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。
YUV420SP, Y分量平面格式,UV打包格式, 即NV12。 NV12与NV21类似,U 和 V 交错排列,不同在于UV顺序。
I420: YYYYYYYY UU VV =>YUV420P
YV12: YYYYYYYY VV UU =>YUV420P
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP
*/
//yv12 =》 yuv420p : yvu -> yuv
private void swapYV12toI420(byte[] yv12bytes, byte[] i420bytes, int width, int height)
{
System.arraycopy(yv12bytes, 0, i420bytes, 0,width*height);
System.arraycopy(yv12bytes, width*height+width*height/4, i420bytes, width*height,width*height/4);
System.arraycopy(yv12bytes, width*height, i420bytes, width*height+width*height/4,width*height/4);
}
//camera preview data
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
// TODO Auto-generated method stub
swapYV12toI420(data, h264, width, height); //yvu -> yuv(H.265编码器只支持YUV视频格式输入)
X265Enc(h264, 0);
camera.addCallbackBuffer(buf);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Button btn = (Button)v;
if(btn_id==0)
{
Log.i("Encoder", " onClick ");
...
}else if(btnClick2 == btn){//x265(Encode)
btn_id = 3;
btn_id0= 3;
File f = new File(Environment.getExternalStorageDirectory(), "H265_03f.h265");
try {
if(!f.exists()){
f.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
Log.i("Encoder", "X265Start = f.getPath() = "+f.getPath()+", f.getAbsolutePath() = "+f.getAbsolutePath());
X265Start(f.getPath());
myshow.setText("X265 Encode --> .h265");
myshow.setBackgroundColor(Color.RED);
btnClick2.setBackgroundColor(Color.MAGENTA);
Log.i("Encoder", "--------------X265Start btnClick2--------------");
}
...
}else{
Log.i("Encoder", " onClick release ");
...
}else if(btnClick2 == btn && btn_id0==3){
...
X265End();
...
Log.i("Encoder", "--------------X265End btnClick2--------------");
}
...
}
}
JNI端:
mp_x.cpp
#include <stdio.h>
#include <jni.h>
#include <malloc.h>
#include <string.h>
#include <android/log.h>
#include <string.h>
#include "x265.h"
#define LOG_TAG "libmp4"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
#ifdef __cplusplus
extern "C" {
#endif
FILE *h265_file;
int ret;
x265_nal *pNals=NULL;
uint32_t iNal=0;
x265_param* pParam=NULL;
x265_encoder* pHandle=NULL;
x265_picture *pPic_in=NULL;
int y_size;
int y_size0;
int frame_num=0;
int csp=X265_CSP_I420;
unsigned char *yuv_buff;
JNIEXPORT bool JNICALL Java_com_example_mymp4v2h264_MainActivity0_X265Start
(JNIEnv *env, jclass clz, jstring file_name)
{
LOGI(" X265Start ");
int width=640,height=480;
const char* h265_title = env->GetStringUTFChars(file_name, NULL);
h265_file=fopen(h265_title,"w");
if(!h265_file){
LOGI(" Open File Error ");
return false;
}
pParam=x265_param_alloc();
x265_param_default(pParam);
pParam->bRepeatHeaders=1;//write sps,pps before keyframe
pParam->internalCsp=csp;
pParam->sourceWidth=width;
pParam->sourceHeight=height;
pParam->fpsNum=25;
pParam->fpsDenom=1;
//Init
pHandle=x265_encoder_open(pParam);
if(pHandle==NULL){
LOGI("x265_encoder_open err\n");
return false;
}
y_size0= pParam->sourceWidth * pParam->sourceHeight;
y_size = (y_size0*3)/2;
pPic_in = x265_picture_alloc();
x265_picture_init(pParam,pPic_in);
yuv_buff=(unsigned char *)malloc(y_size);
//pPic_in->planes[0] = yuv_buff;
//pPic_in->planes[1] = pPic_in->planes[0] + width * height;
//pPic_in->planes[2] = pPic_in->planes[1] + width * height / 4;
pPic_in->planes[0]=yuv_buff;
pPic_in->planes[1]=yuv_buff+y_size0;
pPic_in->planes[2]=yuv_buff+y_size0*5/4;
pPic_in->stride[0]=width;
pPic_in->stride[1]=width/2;
pPic_in->stride[2]=width/2;
LOGI(" X265Start end ");
env->ReleaseStringUTFChars(file_name, h265_title);
return true;
}
JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_X265Enc
(JNIEnv *env, jclass clz, jbyteArray data, jint size)
{
int j;
uint8_t *buf = (uint8_t *)env->GetByteArrayElements(data, JNI_FALSE);
memcpy(yuv_buff, buf, y_size);
frame_num++;
ret=x265_encoder_encode(pHandle,&pNals,&iNal,pPic_in,NULL);
LOGI("Succeed encode %d frames, Nals = %d\n", frame_num, iNal);
for(j=0;j<iNal;j++){
fwrite(pNals[j].payload,1,pNals[j].sizeBytes,h265_file);
}
env->ReleaseByteArrayElements(data, (jbyte *)buf, 0);
}
JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_X265End
(JNIEnv *env, jclass clz)
{
LOGI(" X265End ");
//Flush Decoder
while(1){
int j;
static int kk = 0;
ret=x265_encoder_encode(pHandle,&pNals,&iNal,NULL,NULL);
if(ret<0){
break;
}
LOGI("Flush 1 frame.\n");
kk++;
if(kk>=2){
kk = 0;
LOGI("Flush end.\n");
break;
}
for(j=0;j<iNal;j++){
fwrite(pNals[j].payload,1,pNals[j].sizeBytes,h265_file);
}
}
x265_encoder_close(pHandle);
x265_picture_free(pPic_in);
x265_param_free(pParam);
free(yuv_buff);
fclose(h265_file);
LOGI(" X265End ");
}
#ifdef __cplusplus
}
#endif
Android.mk:
...
include $(CLEAR_VARS)
LOCAL_MODULE := x265
LOCAL_SRC_FILES := libx265.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += \
$(LOCAL_PATH) \
...
$(LOCAL_PATH)/x265 \
$(LOCAL_PATH)/x265/common \
$(LOCAL_PATH)/x265/encoder \
$(LOCAL_PATH)/x265/compat \
...
LOCAL_SHARED_LIBRARIES := mp4v2 faac lame x264 rtmp ffmpeg live555 sdl
LOCAL_STATIC_LIBRARIES += x265 de265
LOCAL_MODULE := myx265
LOCAL_SRC_FILES += mp.c mp_x.cpp streamer.cpp yuv.c dec265.cpp ./SDL/src/main/android/SDL_android_main.c
LOCAL_LDLIBS += -lGLESv1_CM -llog
include $(BUILD_SHARED_LIBRARY)