借助libx265库实现一个简单视频编码器
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
上一篇: JS实现的合并两个有序链表算法示例
下一篇: XML卷之实战锦囊(2):动态查询