欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

借助libx265库实现一个简单视频编码器

程序员文章站 2022-06-23 21:50:27
1.VS新建工程win10+VS2013,新建一个工程,x265_self;右键源文件,添加新建项,x265_self.cpp;右键头文件/外部依赖项,添加x265.h, x265_config.h(文末附)。2. 编写main函数只是简单实现一个编码过程,主要包括以下几个过程:x265_param结构体:存储编码命令行参数;x265_encoder结构体:编码器相关参数;x265_picture结构体:存储读取的每一帧,用于编码;x265_param_alloc():定义参数集并为参数集...

1.VS新建工程

win10+VS2013,新建一个工程,x265_self;
右键源文件,添加新建项,x265_self.cpp;
右键头文件/外部依赖项,添加x265.h, x265_config.h(文末附)。

2. 编写main函数

只是简单实现一个编码过程,主要包括以下几个过程:
x265_param结构体:存储编码命令行参数;
x265_encoder结构体:编码器相关参数;
x265_picture结构体:存储读取的每一帧,用于编码;

x265_param_alloc():定义参数集并为参数集分配内存;
x265_param_default(pParam):设置默认参数;
x265_encoder_open(pParam):打开编码器,并依据pParam进行配置初始化;
x265_picture_alloc():为图像结构体x265_picture分配内存
x265_picture_init(pParam, pPic_in):依据命令行参数,进行图像结构体初始化
x265_encoder_encode():编码核心函数,将pPic_in内存的图像转化为二进制码流;

/Flush Decoder/部分,使用的函数和编码模块是一样的。唯一的不同在于不再输入视频像素数据。它的作用是输出编码器中剩余的码流数据。

int main(int argc, char** argv){   //argc代表命令行给main的参数数量,
                                //argv存各参数地址,每一个元素指向一个参数
 int i, j;
 FILE *fp_src = NULL;
 FILE *fp_dst = NULL;
 char* buff = NULL;
 x265_param* pParam = NULL;
 x265_encoder* pHandle = NULL;
 x265_picture* pPic_in = NULL;
 int ret = 0;
 int y_size = 0;
 x265_nal *pNals = NULL;
 uint32_t iNal = 0;

 //encode 50 frames
 int frame_num = 50;
 int width = 640, height = 360;
 int csp = X265_CSP_I420;
 
 fp_src = fopen("../cuc_ieschool_640x360_yuv420p.yuv","rb");
 fp_dst = fopen("../cuc_ieschool.h265","wb");

 if (fp_src == NULL || fp_dst == NULL)
  return -1;

 //各编码参数变量的定义、初始化
 pParam = x265_param_alloc(); //定义、为参数集分配内存
 x265_param_default(pParam);
 //除默认配置外,以下参数自定义
 pParam->sourceWidth = width;
 pParam->sourceHeight = height;
 pParam->fpsNum = 25;
 pParam->fpsDenom = 1;
 pParam->internalCsp = csp;

 //打开编码器,并依据pParam进行配置初始化
 pHandle = x265_encoder_open(pParam);
 if (pHandle == NULL){
  printf("x265_encoder_open() err\n");
  return 0;
 }
 y_size = pParam->sourceHeight * pParam->sourceWidth;

 //为图像结构体x265_picture分配内存 
 pPic_in = x265_picture_alloc(); 
 x265_picture_init(pParam, pPic_in);

 switch (csp){
 case X265_CSP_I420:{
     buff = (char*)malloc(y_size*3/2);  //申请内存够一帧420图像的
  pPic_in->planes[0] = buff;         //记录各通道首地址
  pPic_in->planes[1] = buff+y_size;
  pPic_in->planes[2] = buff+y_size*5/4;
  pPic_in->stride[0] = width;        //记录各通道步长
  pPic_in->stride[1] = width / 2;
  pPic_in->stride[2] = width / 2;
  break;
 }
 default:{
    printf("Colorspace not support.\n");
    return -1;
 }
 }
 //计算总帧数
 if (frame_num == 0){
  fseek(fp_src, 0, SEEK_END);//将读写位置移动到文件尾
  // ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数
  switch (csp){
  case X265_CSP_I420:frame_num = ftell(fp_src) / (y_size * 3 / 2); break;
  default:printf("Colorspace Not Support.\n"); 
   return -1;
  }
  fseek(fp_src, 0, SEEK_SET); //将读写位置移回到文件头
 }

 //循环每帧编码
 for ( i = 0; i < frame_num; i++){
  switch (csp){
  case X265_CSP_I420:{
   //从fp_src中读取,y_size次,每次1个字节,存到pPic_in
   fread(pPic_in->planes[0], 1, y_size, fp_src);       //Y 
   fread(pPic_in->planes[1], 1, y_size / 4, fp_src); //U
   fread(pPic_in->planes[2], 1, y_size / 4, fp_src); //V
   break;
  }
  default:{
     printf("Colorspace Not Support.\n");
     return -1; }
  }
  ret = x265_encoder_encode(pHandle, &pNals, &iNal, pPic_in, NULL);
  printf("Succeed encode %5d frames\n", i);
  
  //fwrite以二进制写入文件,从pNals中,取pNals[j].sizeBytes个元素
  //每次1字节,写入fp_dst
  for ( j = 0; j<iNal; j++){
   fwrite(pNals[j].payload, 1, pNals[j].sizeBytes, fp_dst);
  }
 }

 /*Flush Decoder,使用的函数和编码模块是一样的。
   唯一的不同在于不再输入视频像素数据。
   它的作用是输出编码器中剩余的码流数据。*/
 while (1){
  ret = x265_encoder_encode(pHandle, &pNals, &iNal, NULL, NULL);
  if (ret == 0){
   break;
  }
  printf("Flush 1 frame.\n");
  for ( j = 0; j<iNal; j++){
   fwrite(pNals[j].payload, 1, pNals[j].sizeBytes, fp_dst);
  }
 }
 x265_encoder_close(pHandle);
 x265_picture_free(pPic_in);
 x265_param_free(pParam);
 free(buff);
 fclose(fp_src);
 fclose(fp_dst);

 return 0;
}

3.添加库配置

将libx265.lib、libx265.dll复制到cpp所在文件夹,右键工程->属性,
VC++目录->库目录,添加libx265.lib所在目录;
链接器->附加库目录,添加libx265.lib所在目录;
链接器->附加依赖项,添加libx265.lib;
c/c+±>预处理器定义,添加_CRT_SECURE_NO_WARNINGS;(为了消除VS对fopen等函数的报错)

生成解决方案,即可得到所需exe编码器;
调试/运行,即可完成编码得到压缩后的.h265文件;
PS:我们这里在main函数里直接定义了输入输出文件,所以不需要命令行,直接运行即可完成编码。

本文参考雷神的“最简单的视频编码器:最简单的视频编码器:基于libx265(编码YUV为H.265)”,并增加了一些配置说明,致敬雷神。

工程文件:
CSDN:https://download.csdn.net/download/baozhakaiv/12598720

本文地址:https://blog.csdn.net/baozhakaiv/article/details/107261037