开源的 H.264/AVC 视频编码器库-x264 的交叉编译 和 程序测试

发布于:2025-07-16 ⋅ 阅读:(14) ⋅ 点赞:(0)

一、环境准备

  1. 安装交叉编译工具链
    根据目标ARM架构选择对应工具链(如arm-linux-gnueabihf-):
    # Ubuntu/Debian系统
    sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
    
    # 验证安装
    arm-linux-gnueabihf-gcc --version
    

或者手动指定交叉工具链

export PATH=$PATH:/home/user/Desktop/output/host/bin

在这里插入图片描述

  1. 创建工作目录
    mkdir x264 && cd x264
    

二、下载x264源码

cd x264
git clone https://code.videolan.org/videolan/x264.git

在这里插入图片描述

三、配置编译参数

通过./configure指定交叉编译参数:

# 禁止生成共享动态库
./configure --cross-prefix=arm-linux-  --host=arm-linux --enable-static --disable-shared --disable-asm --prefix=$(pwd)/output

在这里插入图片描述

./configure \
  --cross-prefix=arm-linux-gnueabihf- \
  --host=arm-linux \
  --enable-static \
  --disable-shared \
  --disable-asm \
  --prefix=$(pwd)/output
# 允许生成共享动态库
./configure \
  --cross-prefix=arm-linux-gnueabihf- \
  --host=arm-linux \
  --enable-static \
  --enable-shared \
  --disable-asm \
  --prefix=$(pwd)/output

参数说明

  • --cross-prefix:交叉编译器前缀
  • --host:目标平台
  • --disable-asm:禁用ARM汇编优化(部分架构可能不兼容)
  • --prefix:指定安装路径

四、编译与安装

make -j$(nproc)  # 并行编译

在这里插入图片描述

make install      # 安装到output目录

在这里插入图片描述

五、验证编译结果

编译成功后,在output目录下生成:

  • bin/x264:可执行文件
  • lib/libx264.a:静态库
  • include/x264.h:头文件

在这里插入图片描述

六、编写测试程序

在这里插入图片描述

使用 x264 API 将 YUV 视频帧编码为 H.264 数据流。
创建test_x264.c测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <x264.h>

#define WIDTH  320//640
#define HEIGHT 240//480
#define FPS    30
#define TOTAL_FRAMES 10//100  // 编码帧数

int main() {
    // 1. 初始化参数结构体
    x264_param_t param;
    if (x264_param_default_preset(&param, "medium", NULL) < 0) {
        fprintf(stderr, "Failed to apply preset\n");
        return -1;
    }

    // 2. 设置编码参数
    param.i_width = WIDTH;
    param.i_height = HEIGHT;
    param.i_fps_num = FPS;
    param.i_fps_den = 1;
    param.i_keyint_max = FPS * 2;  // 关键帧间隔(2秒)
    param.b_vfr_input = 0;
    param.b_repeat_headers = 1;    // 在每个关键帧前添加SPS/PPS
    param.b_annexb = 1;            // 输出annex-b格式
    
    // 3. 应用配置
    if (x264_param_apply_profile(&param, "high") < 0) {
        fprintf(stderr, "Failed to apply profile\n");
        return -1;
    }

    // 4. 打开编码器
    x264_t *encoder = x264_encoder_open(&param);
    if (!encoder) {
        fprintf(stderr, "Failed to open encoder\n");
        return -1;
    }

    // 5. 创建输入图像
    x264_picture_t pic_in, pic_out;
    x264_picture_alloc(&pic_in, param.i_csp, param.i_width, param.i_height);
    x264_picture_init(&pic_out);

    // 6. 准备输出文件
    FILE *outfile = fopen("test.264", "wb");
    if (!outfile) {
        perror("Failed to open output file");
        return -1;
    }

    printf("Start encoding %dx%d video at %d fps...\n", WIDTH, HEIGHT, FPS);

    // 7. 编码循环
    for (int i = 0; i < TOTAL_FRAMES; i++) {
        // 生成测试图像(渐变彩条)
        uint8_t *y = pic_in.img.plane[0];
        uint8_t *u = pic_in.img.plane[1];
        uint8_t *v = pic_in.img.plane[2];
        
        // Y分量(亮度)
        for (int h = 0; h < HEIGHT; h++) {
            for (int w = 0; w < WIDTH; w++) {
                y[h * pic_in.img.i_stride[0] + w] = w * 255 / WIDTH;
            }
        }
        
        // UV分量(色度)
        for (int h = 0; h < HEIGHT/2; h++) {
            for (int w = 0; w < WIDTH/2; w++) {
                u[h * pic_in.img.i_stride[1] + w] = 128 + (h * 128 / (HEIGHT/2));
                v[h * pic_in.img.i_stride[2] + w] = 128 + (w * 128 / (WIDTH/2));
            }
        }

        pic_in.i_pts = i;  // 设置时间戳

        // 执行编码
        x264_nal_t *nals;
        int i_nal;
        int frame_size = x264_encoder_encode(encoder, &nals, &i_nal, &pic_in, &pic_out);
        
        if (frame_size < 0) {
            fprintf(stderr, "Encoding error at frame %d\n", i);
            break;
        }

        // 8. 写入输出文件
        for (int j = 0; j < i_nal; j++) {
            fwrite(nals[j].p_payload, 1, nals[j].i_payload, outfile);
        }

        if (frame_size > 0) {
            printf("Frame %4d: size=%6d bytes, type=%s, qp=%d\n", 
                   i, frame_size,
                   (pic_out.i_type == X264_TYPE_IDR) ? "IDR" :
                   (pic_out.i_type == X264_TYPE_I) ? "I" :
                   (pic_out.i_type == X264_TYPE_P) ? "P" : "B",
                   pic_out.i_qpplus1 - 1);
        }
    }

    // 9. 刷新编码器(处理延迟帧)
    while (1) {
        x264_nal_t *nals;
        int i_nal;
        int frame_size = x264_encoder_encode(encoder, &nals, &i_nal, NULL, &pic_out);
        if (frame_size <= 0) break;
        
        for (int j = 0; j < i_nal; j++) {
            fwrite(nals[j].p_payload, 1, nals[j].i_payload, outfile);
        }
        printf("Flush frame: size=%d bytes\n", frame_size);
    }

    // 10. 清理资源
    x264_encoder_close(encoder);
    x264_picture_clean(&pic_in);
    fclose(outfile);

    printf("\nEncoding completed. Output saved to test.264\n");
    printf("You can verify with:\n");
    printf("  ffplay test.264\n");
    printf("  ffprobe test.264\n");

    return 0;
}
  1. 参数配置

    • x264_param_default_preset():应用预设配置(medium/fast/placebo等)
    • x264_param_apply_profile():设置编码规格(baseline/main/high)
  2. 图像处理

    • x264_picture_alloc():分配YUV420P图像内存
    • 手动生成渐变彩条测试图案(实际应用应从文件/摄像头获取)
  3. 编码核心

    • x264_encoder_encode():执行帧编码
    • 输出NAL单元包含:SPS/PPS/IDR帧/P帧等
  4. 输出处理

    • 写入annex-b格式的原始H.264流
    • 最后刷新编码器获取延迟帧

其它功能

  1. 从文件读取YUV
FILE *yuv_file = fopen("input.yuv", "rb");
fread(pic_in.img.plane[0], 1, WIDTH*HEIGHT, yuv_file);        // Y
fread(pic_in.img.plane[1], 1, WIDTH*HEIGHT/4, yuv_file);     // U
fread(pic_in.img.plane[2], 1, WIDTH*HEIGHT/4, yuv_file);     // V
  1. 调整编码参数
// 提高编码质量
param.rc.i_rc_method = X264_RC_CRF;
param.rc.f_rf_constant = 22.0;  // 0-51, 值越小质量越好

// 启用B帧
param.i_bframe = 3;

// 开启心理视觉优化
param.analyse.b_psy = 1;
  1. 实时编码统计
x264_stats_t stats;
param.b_stats = 1;
x264_encoder_encode(encoder, &nals, &i_nal, &pic_in, &pic_out);
x264_encoder_get_stats(encoder, &stats, 0);
printf("PSNR: Y=%.2f dB, U=%.2f dB, V=%.2f dB\n", 
       stats.f_psnr_avg[0], stats.f_psnr_avg[1], stats.f_psnr_avg[2]);

七、交叉编译测试程序

arm-linux-gnueabihf-gcc test_x264.c -o test_x264 \
  -I$(pwd)/output/include \
  -L$(pwd)/output/lib \
  -lx264 -lpthread -lm

在这里插入图片描述
在这里插入图片描述

八、部署与测试

  1. 复制文件到ARM设备
    scp test_x264 root@192.168.1.100:/tmp/  # 替换为实际IP
    scp output/lib/libx264.so.* root@192.168.1.100:/lib/
    

将输出的/lib目录下的库文件拷贝到开发板的/lib/目录下
将输出的/bin目录下的可执行文件拷贝到开发板的/bin/目录下
文件需要赋权限
在这里插入图片描述
测试指令
在这里插入图片描述

  1. 在ARM设备上运行

    # 设置库路径
    export LD_LIBRARY_PATH=/lib:$LD_LIBRARY_PATH
    
    # 执行测试
    /tmp/test_x264
    
  2. 输出

在这里插入图片描述
在这里插入图片描述

# 使用FFmpeg播放生成的视频
ffplay test.264

在这里插入图片描述


# 查看视频信息
ffprobe test.264

在这里插入图片描述

# 检查编码统计
ffmpeg -i test.264 -f null -

在这里插入图片描述

常见问题及解决方案

  1. 编译报错:undefined reference to 'sqrt'
    解决:在链接命令中添加-lm(数学库)。

  2. 运行报错:libx264.so.x: cannot open shared object file
    解决:确保库文件已复制到ARM设备的/lib目录,并执行ldconfig刷新缓存。

  3. 性能优化

    • 若ARM设备支持NEON指令集,可启用汇编优化:

      ./configure --enable-neon  # 根据架构选择--enable-*选项
      
      ./configure --enable-shared --prefix=/home/ubuntu/x264_install/ --host=arm-linux-gnueabihf --disable-asm
      
         --prefix=/home/ubuntu//x264_install/ :指定编译后存放路径
         --host=arm-linux-gnueabihf :指定交叉编译链,用户需要修改为自己的交叉编译器
         --enable-shared :允许共享
         --disable-asm:跳过汇编
      

网站公告

今日签到

点亮在社区的每一天
去签到