x264在android平台上的编译和使用

x264在android平台上的移植

gedit ~/.bashrc
将 /opt/android-sdk-linux/tools 添加到 "PATH"环境变量中
将 /opt/android-ndk-r8b 添加到 "PATH"环境变量中

将x264源码包last_x264.tar.bz2下载到~/x264-android目录下,并解压到目录~/x264-android/x264-snapshot-20130808-2245/下

cd ~/x264-android/x264-snapshot-20130808-2245

执行以下配置脚本:

./configure --enable-pic --enable-strip --enable-static --cross-prefix=/opt/android-ndk-r8b/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi- --sysroot=/opt/android-ndk-r8b/platforms/android-14/arch-arm --host=arm-linux --prefix=./build --extra-cflags="-march=armv7-a -mtune=cortex-a8 -mfloat-abi=softfp -mfpu=neon -D__ARM_ARCH_7__ -D__ARM_ARCH_7A__"

然后:make

最后:在当前目录下生成libx264.a的静态库

注:最好用android-ndk-r8b之前的ndk版本(包括android-ndk-r8b)编译,android-ndk-r8b之后的版本编译x264,最后会报"cannot scan executable section 1 of libx264.a(dct-a.o) for Cortex-A8 erratum because it has no mapping symbols"的警告


x264在android平台上的使用

1、创建工程x264_android

android list targets
android create project --target 1 --name x264_android --path ~/workspace/x264_android --package com.modukaikai.x264 --activity x264Activity

2、copy x264的库和头文件到jni目录

mkdir ~/workspace/x264_android/jni
mkdir ~/workspace/x264_android/jni/x264
cp ~/x264-android/x264-snapshot-20130808-2245/libx264.a ~/workspace/x264_android/jni/x264
cp ~/x264-android/x264-snapshot-20130808-2245/x264.h ~/workspace/x264_android/jni/x264
cp ~/x264-android/x264-snapshot-20130808-2245/x264_config.h ~/workspace/x264_android/jni/x264

3、创建x264encoder类封装x264的调用接口

以下是x264encoder.h(放在jni目录下)

#ifndef x264encoder_h
#define x264encoder_h
 
extern "C"
{
#include "stdint.h"
#include "x264.h"
 
    enum bitrate_level
    {
        HIGH_LEVEL = 0,
        STANDARD_LEVEL = 1,
        MEDIUM_LEVEL = 2,
        LOW_LEVEL = 3,
    };
    
    class X264Encoder
    {
    public:
        X264Encoder();
        ~X264Encoder();
         
        bool openX264Encoder();
        //    long x264EncoderProcess(uint8_t *pSrcData, int srcDataSize, x264_nal_t **nals, int& nalsCount);
        long x264EncoderProcess(x264_picture_t *pPicture, x264_nal_t **nals, int& nalsCount);
        bool closeX264Encoder();
         
        void setSourceFormat(unsigned int sourcformat);
        void setResolution(unsigned int w, unsigned int h);
        void setBitrate(unsigned int i_bitrate);
        void setFps(unsigned int fps);
        void setI_KeyInt_Max(unsigned int i_frame_max);
        void setQp_Max(unsigned int qp_max);
        void setQp_Min(unsigned int qp_min);
         
        void forceIDRFrame();
         
        void upgradeBitrateLevel();
        void declineBitrateLevel();
        void setLeastBitrateLevel();
         
    private:
         
        x264_param_t *pParameter;
        x264_t *x264EncoderHandle;
        //    x264_picture_t *pPicture;
        x264_picture_t *pOutput;
         
        unsigned int sourceFormat;
        //    unsigned int i_bitrate;
        unsigned int bitratelevel;
        unsigned int i_fps;
        unsigned int i_keyint_max;
        unsigned int width;
        unsigned int height;
        unsigned int qp_max;
        unsigned int qp_min;
         
        unsigned int current_f_rf_constant;
        unsigned int userSetting_f_rf_constant;
         
        int64_t frameNo;
         
        bool isForceIDRFrameEnabled;
    };
}
 
#endif


以下是x264encoder.cpp(放在jni目录下)

#include <stdlib.h>
#include <string.h>
#include "x264encoder.h"
 
// new version for x264 encoder
X264Encoder::X264Encoder()
{
    this->bitratelevel = STANDARD_LEVEL;
    qp_max = 30;
    qp_min = 0;
    i_fps = 20;
    i_keyint_max = 300;
    width = 352;
    height = 288;
     
    frameNo = 0;
    isForceIDRFrameEnabled = false;
     
    pParameter = NULL;
    x264EncoderHandle = NULL;
    //    pPicture = NULL;
    pOutput = NULL;
}
 
X264Encoder::~X264Encoder()
{
    this->closeX264Encoder();
}
 
void X264Encoder::setSourceFormat(unsigned int sourcformat)
{
    this->sourceFormat = sourcformat;
}
 
void X264Encoder::setResolution(unsigned int w, unsigned int h)
{
    width = w;
    height = h;
}
 
void X264Encoder::setBitrate(unsigned int i_bitrate)
{
    if (i_bitrate > 0 && i_bitrate <= 64) {
        this->bitratelevel = LOW_LEVEL;
    }else if(i_bitrate > 64 && i_bitrate <= 128){
        this->bitratelevel = MEDIUM_LEVEL;
    }else if (i_bitrate > 128 && i_bitrate <= 256) {
        this->bitratelevel = STANDARD_LEVEL;
    }else if (i_bitrate > 256 && i_bitrate <= 384) {
        this->bitratelevel = HIGH_LEVEL;
    }else if (i_bitrate > 384 && i_bitrate <= 512) {
        this->bitratelevel = HIGH_LEVEL;
    }else {
        this->bitratelevel = STANDARD_LEVEL;
    }
}
 
void X264Encoder::setFps(unsigned int fps)
{
    i_fps = fps;
}
 
void X264Encoder::setI_KeyInt_Max(unsigned int i_frame_max)
{
    i_keyint_max = i_frame_max;
}
 
void X264Encoder::setQp_Max(unsigned int qp_max)
{
    this->qp_max = qp_max;
}
 
void X264Encoder::setQp_Min(unsigned int qp_min)
{
    this->qp_min = qp_min;
}
 
bool X264Encoder::openX264Encoder()
{
    //    return false;
     
    this->closeX264Encoder();
     
    if(!pParameter)
    {
        pParameter = (x264_param_t *)malloc(sizeof(x264_param_t));
         
        if (!pParameter) {
            this->closeX264Encoder();
             
            return false;
        }
         
        memset(pParameter, 0, sizeof(x264_param_t));
    }
     
    int ret = x264_param_default_preset(pParameter, "ultrafast", "zerolatency");
    if (ret != 0) {
         
        this->closeX264Encoder();
         
        return false;
    }
     
    pParameter->i_level_idc = 30;
     
    pParameter->i_width = width;
    pParameter->i_height = height;
     
    pParameter->b_deterministic = 1;
//    pParameter->b_sliced_threads = 1;
    pParameter->i_threads = 1;
     
    pParameter->i_csp = X264_CSP_I420;//X264_CSP_NV12;//X264_CSP_I420;
    
    pParameter->i_fps_num = i_fps;
    pParameter->i_fps_den = 1;
    pParameter->i_bframe = 0;
    pParameter->i_keyint_max = i_keyint_max;
     
//    pParameter->b_open_gop = 1;
     
    //    pParameter->rc.i_bitrate = i_bitrate;
     
    pParameter->rc.i_rc_method = X264_RC_CRF;//X264_RC_CQP;
     
    if (this->bitratelevel == LOW_LEVEL) {
        pParameter->rc.f_rf_constant = 32;
    }else if(this->bitratelevel == MEDIUM_LEVEL){
        pParameter->rc.f_rf_constant = 29;
    }else if (this->bitratelevel == STANDARD_LEVEL) {
        pParameter->rc.f_rf_constant = 26;
    }else if (this->bitratelevel == HIGH_LEVEL) {
        pParameter->rc.f_rf_constant = 24;
    }else {
        pParameter->rc.f_rf_constant = 24;
    }
     
    current_f_rf_constant = pParameter->rc.f_rf_constant;
    userSetting_f_rf_constant = pParameter->rc.f_rf_constant;
     
    // from huxiaopeng
    pParameter->analyse.b_transform_8x8 = 1;
    pParameter->rc.f_aq_strength = 1.5;
     
    pParameter->rc.i_aq_mode = 0;
    pParameter->rc.f_qcompress = 0.0;
    pParameter->rc.f_ip_factor = 0.5;
    pParameter->rc.f_rate_tolerance = 0.1;
     
    pParameter->analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO;
    pParameter->analyse.i_me_method = X264_ME_DIA;
    pParameter->analyse.i_me_range = 16;
    pParameter->analyse.i_subpel_refine = 2;
    //    pParameter->analyse.i_noise_reduction = 1;
     
    pParameter->i_slice_max_size = 1200;
     
//    pParameter->i_nal_hrd = X264_NAL_HRD_NONE;
     
    pParameter->b_deblocking_filter = 1;
    pParameter->i_deblocking_filter_alphac0 = 4;
    pParameter->i_deblocking_filter_beta = 4;
     
    pParameter->rc.b_mb_tree = 0;
     
    pParameter->i_log_level = X264_LOG_NONE;
     
    if(x264_param_apply_profile(pParameter, "baseline"))
    {
        this->closeX264Encoder();
         
        return false;
    }
     
    if (!x264EncoderHandle) {
        x264EncoderHandle = x264_encoder_open(pParameter);
         
        if (!x264EncoderHandle) {
            this->closeX264Encoder();
             
            return false;
        }
    }
    /*
     if (!pPicture) {
     pPicture = (x264_picture_t *)malloc(sizeof(x264_picture_t));
      
     if (!pPicture) {
      
     this->closeX264Encoder();
      
     return false;
     }
      
     memset(pPicture, 0, sizeof(x264_picture_t));
     }
      
     if (x264_picture_alloc(pPicture, X264_CSP_I420, width, height)) {
      
     this->closeX264Encoder();
      
     return false;
     }
     */
    if (!pOutput) {
        pOutput = (x264_picture_t *)malloc(sizeof(x264_picture_t));
         
        if (!pOutput) {
            this->closeX264Encoder();
            return false;
        }
         
        memset(pOutput, 0, sizeof(x264_picture_t));
    }
     
    return true;
}
 
void X264Encoder::forceIDRFrame()
{
    isForceIDRFrameEnabled = true;
}
 
void X264Encoder::upgradeBitrateLevel()
{
    /*
    if (this->bitratelevel == HIGH_LEVEL) {
        return;
    }
     
    this->bitratelevel++;
     
    if (this->bitratelevel == LOW_LEVEL) {
        pParameter->rc.f_rf_constant = 30;
    }else if(this->bitratelevel == MEDIUM_LEVEL){
        pParameter->rc.f_rf_constant = 27;
    }else if (this->bitratelevel == STANDARD_LEVEL) {
        pParameter->rc.f_rf_constant = 24;
    }else if (this->bitratelevel == HIGH_LEVEL) {
        pParameter->rc.f_rf_constant = 22;
    }else {
        pParameter->rc.f_rf_constant = 23;
    }
    */
     
    if (userSetting_f_rf_constant >= current_f_rf_constant) {
        return;
    }
     
    pParameter->rc.f_rf_constant--;
    current_f_rf_constant = pParameter->rc.f_rf_constant;
     
    x264_encoder_reconfig(x264EncoderHandle, pParameter);
}
 
void X264Encoder::setLeastBitrateLevel()
{
    pParameter->rc.f_rf_constant = 32;
    current_f_rf_constant = pParameter->rc.f_rf_constant;
     
    x264_encoder_reconfig(x264EncoderHandle, pParameter);
}
 
void X264Encoder::declineBitrateLevel()
{
    /*
    if (this->bitratelevel == LOW_LEVEL) {
        return;
    }
     
    this->bitratelevel--;
     
    if (this->bitratelevel == LOW_LEVEL) {
        pParameter->rc.f_rf_constant = 30;
    }else if(this->bitratelevel == MEDIUM_LEVEL){
        pParameter->rc.f_rf_constant = 27;
    }else if (this->bitratelevel == STANDARD_LEVEL) {
        pParameter->rc.f_rf_constant = 24;
    }else if (this->bitratelevel == HIGH_LEVEL) {
        pParameter->rc.f_rf_constant = 22;
    }else {
        pParameter->rc.f_rf_constant = 23;
    }
    */
     
    if (32 <= current_f_rf_constant) {
        return;
    }
     
    pParameter->rc.f_rf_constant++;
    current_f_rf_constant = pParameter->rc.f_rf_constant;
     
    x264_encoder_reconfig(x264EncoderHandle, pParameter);
}
 
long X264Encoder::x264EncoderProcess(x264_picture_t *pPicture, x264_nal_t **nals, int& nalsCount)
{
    pPicture->i_pts = (int64_t)(frameNo * pParameter->i_fps_den);
    pPicture->i_type = X264_TYPE_AUTO;
    pPicture->i_qpplus1 = 0;//X264_QP_AUTO;
     
    if (isForceIDRFrameEnabled) {
        pPicture->i_type = X264_TYPE_IDR;
        isForceIDRFrameEnabled = false;
    }
     
    int32_t framesize = -1;
     
     
    framesize = x264_encoder_encode(x264EncoderHandle, nals, &nalsCount, pPicture, pOutput);
     
    if (framesize>0) {
        frameNo++;
    }
     
    return framesize;
}
 
/*
 long X264Encoder::x264EncoderProcess(uint8_t *pSrcData, int srcDataSize, x264_nal_t **nals, int& nalsCount)
 {
 
 pPicture->img.plane[0] = pSrcData;
 pPicture->img.plane[1] = pSrcData + srcDataSize*2/3;
 pPicture->img.plane[2] = pSrcData + srcDataSize*2/3 + srcDataSize/6;
 pPicture->i_pts = (int64_t)(frameNo * pParameter->i_fps_den);
 pPicture->i_type = X264_TYPE_AUTO;
 pPicture->i_qpplus1 = X264_QP_AUTO;
 
 if (isForceIDRFrameEnabled) {
 pPicture->i_type = X264_TYPE_IDR;
 isForceIDRFrameEnabled = false;
 }
 
 int32_t framesize = -1;
 
 
 framesize = x264_encoder_encode(x264EncoderHandle, nals, &nalsCount, pPicture, pOutput);
 
 if (framesize>0) {
 frameNo++;
 }
 
 return framesize;
 }
 */
bool X264Encoder::closeX264Encoder()
{
    if (pOutput) {
        free(pOutput);
        pOutput = NULL;
    }
    /*     
     if (pPicture) {
     free(pPicture);
     pPicture = NULL;
     }
     */     
    if (pParameter) {
        free(pParameter);
        pParameter = NULL;
    }
     
    if (x264EncoderHandle) {
        x264_encoder_close(x264EncoderHandle);
        x264EncoderHandle = NULL;
    }
     
    return true;
}


4、在jni目录下创建测试编码的源文件x264_main.cpp

#include <stdio.h>
#include "x264encoder.h"
 
int main( int argc, char **argv )
{
    X264Encoder x264Encoder;
 
    x264Encoder.setBitrate(512);
    x264Encoder.setResolution(640,480);
    x264Encoder.setFps(20);
 
    FILE *inputFile = NULL;
    FILE *outputFile = NULL;
    inputFile = fopen("yuv_640x480.yuv","rb");
    outputFile = fopen("h264_640x480.h264","wb");
 
    x264_picture_t inputPicture;
    x264_picture_alloc(&inputPicture, X264_CSP_I420, 640, 480);
 
    x264_nal_t *p_nals = NULL;
    int nalsCount = 0;
 
    if(x264Encoder.openX264Encoder())
    {
        for(int j=0; j<20; j++)
        {
            fread(inputPicture.img.plane[0],1,640*480, inputFile);
            fread(inputPicture.img.plane[1],1,640*480/4, inputFile);
            fread(inputPicture.img.plane[2],1,640*480/4, inputFile);
 
            x264Encoder.x264EncoderProcess(&inputPicture,&p_nals,nalsCount);
 
            if(p_nals)
            {
                for(int i=0; i<nalsCount; i++)
                {
                    fwrite(p_nals[i].p_payload, p_nals[i].i_payload, 1, outputFile);
                }
            }
        }
 
    }
 
    fclose(inputFile);
    fclose(outputFile);
 
    x264_picture_clean(&inputPicture);
 
    x264Encoder.closeX264Encoder();
 
    return 0;
}


5、在jni目录下创建编译文件android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := $(LOCAL_PATH)/x264 \
    $(LOCAL_PATH)

LOCAL_SRC_FILES := x264encoder.cpp \
    x264_main.cpp

LOCAL_LDLIBS := -L$(LOCAL_PATH)/x264 -lx264

LOCAL_MODULE := x264_android

include $(BUILD_EXECUTABLE)


6、ndk-build

cd ~/workspace/x264_android
ndk-build

出现错误:/opt/android-ndk-r8e/build/gmsl/__gmsl:512: *** non-numeric second argument

解决:将 <uses-sdk android:minSdkVersion="9" /> 添加进文件 AndroidManifest.xml

最后编译通过,在目录~/workspace/x264_android/libs/armeabi-v7a/下面会生成android可执行文件x264_android (实现H264编码的demo程序)

7、将x264_android测试程序拷贝到真机,运行测试

adb shell
su
mkdir /data/x264
exit
exit

adb push ~/workspace/x264_android/libs/armeabi-v7a/x264_android /data/x264
adb push ~/Desktop/yuv_640x480.yuv /data/x264

adb shell
su
cd /data/x264
./x264_android

最后在设备的/data/x264目录下生成编码后的h264文件h264_640x480.h264

8、将编码后的h264_640x480.h264拷贝到PC桌面

adb pull /data/x264/h264_640x480.h264 ~/Desktop/


9、测试编码后的H264数据h264_640x480.h264

用ubuntu系统下面的Movie Player打开h264_640x480.h264

你可能感兴趣的:(android,x264)