libx264 

libx264是一个自由的H.264编码库,是x264项目的一部分,使用广泛,ffmpeg的H.264实现就是用的libx264。

代码

要把一个I420视频文件编码为H264格式。I420是YUV中planar格式的一种,一张I420图片中有三个plane,分别存放整张图片的Y、U、V分量;采样比例为4:2:0,12bpp,Y:U:V的分量长度是4:1:1。

头文件

1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <x264.h>

变量声明和参数

 1 int width = 480;
 2 int height = 360;
 3 int fps = 25;
 4 size_t yuv_size = width * height * 3 / 2;
 5 x264_t *encoder;
 6 x264_picture_t pic_in, pic_out;
 7 int inf, outf;
 8 uint8_t *yuv_buffer;
 9 
10 if (argc != 3) {
11     printf("usage: %s input output\n", argv[0]);
12 }
  • 视频尺寸是480×360,YUV I420格式,每个像素1.5个字节,所以一张YUV图片大小是width * height * 1.5
  • encoder就是编码器,x264_t格式在x264.h文件中只有

    typedef struct x264_t x264_t
    

    编码器类型只需要也只能声明为x264_t的指针类型

  • 每次编码时,YUV图片的信息都保存在pic_in中
  • 输入输出的文件描述符
  • 从文件读入的YUV的缓冲区

初始化encoder

 1 x264_param_t param;
 2 x264_param_default_preset(&param, "veryfast", "zerolatency");
 3 param.i_threads = 1;
 4 param.i_width = width;
 5 param.i_height = height;
 6 param.i_fps_num = fps;
 7 param.i_fps_den = 1;
 8 
 9 param.i_keyint_max = 25;
10 param.b_intra_refresh = 1;
11 
12 param.b_annexb = 1;
13 
14 x264_param_apply_profile(&param, "baseline");
15 encoder = x264_encoder_open(&param);

初始化pic_in

1 x264_picture_alloc(&pic_in, X264_CSP_I420, width, height);
2 
3 yuv_buffer = malloc(yuv_size);
4 
5 pic_in.img.plane[0] = yuv_buffer;
6 pic_in.img.plane[1] = pic_in.img.plane[0] + width * height;
7 pic_in.img.plane[2] = pic_in.img.plane[1] + width * height / 4;
  • pic_in.img中保存YUV图片各分量的信息

    typedef struct {
        int i_csp;
        int i_plane;
        int i_stride[4];
        uint8_t *plane[4];
    } x264_image_t;

    其中icsp, iplane, istride的值在picin初始化的时候已经被赋值,代码中只需要将plane数组指向正确的位置

  • 程序中每一帧的图片都是读取到yuv_buffer中,所以在这里设置一次就行了

初始化文件描述符

1 inf = open(argv[1], O_RDONLY);
2 if (inf < 0) {
3     return -1;
4 }
5 outf = open(argv[2], O_CREAT | O_WRONLY, 444);
6 if (outf < 0) {
7     return -1;
8 }

编码

 1 int64_t i_pts = 0;
 2 
 3 x264_nal_t *nals;
 4 int nnal;
 5 while (read(inf, yuv_buffer, yuv_size) > 0) {
 6     pic_in.i_pts = i_pts++;
 7     x264_encoder_encode(encoder, &nals, &nnal, &pic_in, &pic_out);
 8     x264_nal_t *nal;
 9     for (nal = nals; nal < nals + nnal; nal++) {
10         write(outf, nal->p_payload, nal->i_payload);
11     }
12 }
  • 关于ffmpeg的pts,网上有好多种公式,其实只要步长为1递增就行了
  • H.264的NAL层是为了适应网络传输的需要,将大的编码后的帧分成多个块
  • p_payload就是编码后的H.264的帧数据,写入输出文件

扫尾

1 x264_encoder_close(encoder);
2 close(inf);
3 close(outf);
4 free(yuv_buffer);
5 return 0;

编译

gcc sourcefile -lx264 -Wall -o execfile

 

这里有一段I420视频可供测试。

参考

  1. How does one encode a series of images into H264 using the x264 C API? - Stack Overflow
  2. YUV RGB 常见视频格式解析 - 一指流砂 - 博客园