前言
使用x265对uvc camera视频进行265编码
一、x265 x86编译
-
获取x265源码
去libx265官网上下载源码,地址:http://ftp.videolan.org/pub/videolan/x265/x265_3.2.tar.gz
tar xvf x265_3.2.tar.gz -
编译libx265源码
安装cmake,cmake-curses-gui
sudo apt-get install cmake
sudo apt install cmake-curses-gui
cd x265_3.2/build/linux
sh make-Makefiles.bash
修改BIN_INSTALL_DIR、CMAKE_INSTALL_PREFIX
修改BIN_INSTALL_DIR: /project/x265/x265_3.2/build/linux/__install/bin
CMAKE_INSTALL_PREFIX: /project/x265/x265_3.2/build/linux
make
make install
编译将libx265.a和include目录头文件准备好
二、x265 编码YUV(YUYV)
1. V4L2获取YUV
2. YUV格式转换
uvc camera输出yuv视频格式为YUYV(YUV422)格式,需将YUYV格式转换成YUV420格式再使用x265进行编码
YUYV->YUV420代码如下:
int YUV422ToYUV420(unsigned char *yuv422, unsigned char *yuv420, int width, int height)
{
int y_size = width * height;
int i, j, k=0;
//Y
for (i = 0; i < y_size; i++)
{
yuv420[i] = yuv422[i * 2];
}
//U
for (i = 0; i < height; i++)
{
if ((i % 2) != 0)
{
continue;
}
for (j = 0; j < (width / 2); j++)
{
if ((4 * j + 1) > (2 * width))
break;
yuv420[y_size + k * 2 * width / 4 + j] = yuv422[i * 2 * width + 4 * j + 1];
}
k++;
}
//V
k = 0;
for (i = 0; i < height; i++)
{
if ((i % 2) == 0)
continue;
for (j = 0; j < (width / 2); j++)
{
if ((4 * j + 3) > (2 * width))
break;
yuv420[y_size + y_size / 4 + k * 2 * width / 4 + j] = yuv422[i * 2 * width + 4 * j + 3];
}
k++;
}
return 0;
}
3. H265编码
编码流程:
- x265_param编码参数设置
- x265_encoder_open打开编码器
- 分配x265_picture用于编码源YUV数据
- 为x265_picture的planes分配存放Y,U,V数据内存空间
- 使用x265_encoder_headers编码vps, sps, pps(可省略)
- 使用x265_encoder_encode对YUV420数据进行编码
- 保存编码后265数据,验证编码结果
头文件
#include "x265.h"
数据结构定义
typedef struct x265_encoder_param
{
char *buff;
uint32_t i_nal;
x265_nal *nal;
x265_encoder *handler;
x265_picture *picInput;
x265_picture *picOutput;
x265_param *param;
} X265_ENCODER_PARAM;
typedef struct x265_sps_pps_vps
{
int width;
int height;
int bitRate;
int fps;
unsigned char *sps;
int sps_len;
unsigned char *pps;
int pps_len;
unsigned char *vps;
int vps_len;
} X265_SPS_PPS_VPS;
typedef struct x265_encoder
{
pthread_t thdId;/*编码线程ID*/
int thdRunFlag;
uint32_t pixelformat;/*yuv视频格式*/
int rc_mode;/*编码模式*/
X265_ENCODER_PARAM encParam;/*参数*/
X265_SPS_PPS_VPS spsPpsVps;/*vps pps sps*/
int saveFile;
FILE *fp;
} X265_ENCODER;
static X265_ENCODER gstX265Encoder;
int X265EncodeParamInit(X265_ENCODER *encoder)
{
int y_size = 0;
int rc_mode = encoder->rc_mode;
uint32_t pixel_format = encoder->pixelformat;
X265_ENCODER_PARAM *x265 = &encoder->encParam;
X265_SPS_PPS_VPS *sps_pps_vps = &encoder->spsPpsVps;
x265->param = x265_param_alloc();
x265_param_default(x265->param);
x265_param_default_preset(x265->param, "ultrafast", "zerolatency");
x265->param->frameNumThreads = 0;
x265->param->sourceWidth = sps_pps_vps->width;
x265->param->sourceHeight = sps_pps_vps->height;
x265->param->totalFrames = 0;
x265->param->bAnnexB = 1;
x265->param->bRepeatHeaders = 1;
x265->param->keyframeMin = 0;
x265->param->keyframeMax = sps_pps_vps->fps * 2;
x265->param->internalCsp = X265_CSP_I420;
//muxing parameters
x265->param->fpsDenom = 1;
x265->param->fpsNum = sps_pps_vps->fps;
#if 1
//x265->param.rc.lookahead = 0;
x265->param->rc.bitrate = sps_pps_vps->bitRate;
x265->param->rc.rateControlMode = rc_mode;
switch (rc_mode)
{
case X265_RC_ABR:
//x265->param.rc.b_filler = 1;
x265->param->rc.vbvMaxBitrate = sps_pps_vps->bitRate;
x265->param->rc.vbvBufferSize = sps_pps_vps->bitRate;
break;
case X265_RC_CQP:
x265->param->rc.qp = 32;
break;
case X265_RC_CRF:
x265->param->rc.rfConstantMin = 25;
x265->param->rc.rfConstantMax = 45;
break;
default:
break;
}
#endif
//encoder open
x265->handler = x265_encoder_open(x265->param);
if (x265->handler == NULL)
{
ERROR("x265_encoder_open ERROR");
return -1;
}
x265->picInput = x265_picture_alloc();
x265_picture_init(x265->param, x265->picInput);
y_size = sps_pps_vps->width * sps_pps_vps->height;
switch (pixel_format)
{
case V4L2_PIX_FMT_YUV420:
x265->buff =(char *)malloc(y_size * 3 / 2);
x265->picInput->planes[0] = x265->buff;
x265->picInput->planes[1] = x265->buff + y_size;
x265->picInput->planes[2] = x265->buff + y_size * 5 / 4;
x265->picInput->stride[0] = sps_pps_vps->width;
x265->picInput->stride[1] = sps_pps_vps->width / 2;
x265->picInput->stride[2] = sps_pps_vps->width / 2;
break;
default:
break;
}
x265->picInput->pts = 0;
x265->i_nal = 0;
x265_encoder_headers(x265->handler, &x265->nal, &x265->i_nal);
// get sps pps
if (x265->i_nal > 0)
{
for (uint32_t i = 0; i < x265->i_nal; i++)
{
if (x265->nal[i].type == NAL_UNIT_VPS)
{
sps_pps_vps->vps = new unsigned char[x265->nal[i].sizeBytes];;
sps_pps_vps->vps_len = x265->nal[i].sizeBytes;
memcpy(sps_pps_vps->vps, x265->nal[i].payload, sps_pps_vps->vps_len);
DEBUG("vps: %02x %02x %02x %02x %02x",
sps_pps_vps->vps[0], sps_pps_vps->vps[1],
sps_pps_vps->vps[2], sps_pps_vps->vps[3],
sps_pps_vps->vps[4]);
}
else if (x265->nal[i].type == NAL_UNIT_SPS)
{
sps_pps_vps->sps = new unsigned char[x265->nal[i].sizeBytes];
sps_pps_vps->sps_len = x265->nal[i].sizeBytes;
memcpy(sps_pps_vps->sps, x265->nal[i].payload, sps_pps_vps->sps_len);
DEBUG("sps: %02x %02x %02x %02x %02x",
sps_pps_vps->sps[0], sps_pps_vps->sps[1],
sps_pps_vps->sps[2], sps_pps_vps->sps[3],
sps_pps_vps->sps[4]);
}
else if (x265->nal[i].type == NAL_UNIT_PPS)
{
sps_pps_vps->pps = new unsigned char[x265->nal[i].sizeBytes];;
sps_pps_vps->pps_len = x265->nal[i].sizeBytes;
memcpy(sps_pps_vps->pps, x265->nal[i].payload, sps_pps_vps->pps_len);
DEBUG("pps: %02x %02x %02x %02x %02x",
sps_pps_vps->pps[0], sps_pps_vps->pps[1],
sps_pps_vps->pps[2], sps_pps_vps->pps[3],
sps_pps_vps->pps[4]);
}
}
}
return 0;
}
int X265EncodeYUV(X265_ENCODER *coder, unsigned char *yuvData)
{
int num,i;
unsigned char *data = yuvData;
X265_ENCODER_PARAM *enc = &coder->encParam;
X265_SPS_PPS_VPS *spsPpsVps = &coder->spsPpsVps;
int y_size = spsPpsVps->width * spsPpsVps->height;
switch (coder->pixelformat)
{
case V4L2_PIX_FMT_YUV420:
memcpy(enc->picInput->planes[0], data, y_size); // copy y
memcpy(enc->picInput->planes[1], data + y_size, y_size / 4);
memcpy(enc->picInput->planes[2], data + y_size + y_size / 4, y_size / 4);
break;
default:
break;
}
enc->i_nal = 0;
x265_encoder_encode(enc->handler, &enc->nal, &enc->i_nal, enc->picInput, NULL);
enc->picInput->pts++;
for (uint32_t i = 0; i < enc->i_nal; i++)
{
/* H265RtmpAcceptFrame(RING_BUFF_RTMP,
enc->nal[i].type,
enc->nal[i].payload,
enc->nal[i].sizeBytes);
*/
if (coder->fp && coder->saveFile)
{
fwrite(enc->nal[i].payload, enc->nal[i].sizeBytes, 1, coder->fp);
fflush(coder->fp);
}
}
return 0;
}
void *X265EncodeThread(void *args)
{
int ret = -1;
int buffId = 0;
YUV_RING_BUFFER *ringBuff = NULL;
X265_ENCODER *encoder = NULL;
YUV_NODE yuvNode;
int yuv420Size = 0;
int width = 0;
int height = 0;
unsigned char *yuv420 = NULL;
char cName[128] = {0};
snprintf(cName, sizeof(cName) - 1, "%s", "X265EncodeThread");
prctl(PR_SET_NAME, (unsigned long)cName, 0, 0, 0);
encoder = (X265_ENCODER *)args;
if (!encoder)
{
return NULL;
}
width = encoder->spsPpsVps.width;
height = encoder->spsPpsVps.height;
yuv420Size = width * height * 3 / 2;
yuv420 = (unsigned char *)malloc(yuv420Size);
if (yuv420 == NULL)
{
ERROR("malloc yuv420 memory error!");
goto __exit;
}
ringBuff = YUVRingBufferHandle(buffId);
if (ringBuff == NULL)
{
ERROR("YUVRingBufferHandle error");
goto __exit;
}
ret = X265EncodeParamInit(encoder);
if (0 != ret)
{
ERROR("X264_EncodeInit error");
goto __exit;
}
DEBUG("%s have been created, Id(%lu)", __func__, pthread_self());
while (encoder->thdRunFlag)
{
usleep(100);
memset(&yuvNode, 0, sizeof(YUV_NODE));
if (YUVRingBufferGet(ringBuff, &yuvNode) > 0)
{
YUV422ToYUV420(yuvNode.yuvData, yuv420, width, height);
X265EncodeYUV(encoder, yuv420);
free(yuvNode.yuvData);
yuvNode.yuvData = NULL;
}
}
__exit:
if (yuv420)
{
free(yuv420);
yuv420 = NULL;
}
DEBUG("%s will exit, Id(%lu)", __func__, pthread_self());
return NULL;
}
int X265EncodeInit(int width, int height)
{
int ret = -1;
X265_ENCODER *encoder = &gstX265Encoder;
memset(encoder, 0, sizeof(X265_ENCODER));
encoder->spsPpsVps.width = width;
encoder->spsPpsVps.height = height;
encoder->spsPpsVps.fps = 15; //real frame rate
encoder->spsPpsVps.bitRate = 512;
encoder->pixelformat = V4L2_PIX_FMT_YUV420;
encoder->rc_mode = X265_RC_ABR;
encoder->saveFile = 1;
encoder->fp = fopen("test.h265", "wb");
if (!encoder->fp)
{
ERROR("open test.h265 error");
}
encoder->thdRunFlag = 1;
ret = pthread_create(&encoder->thdId, NULL, X265EncodeThread, (void *)encoder);
if (0 != ret)
{
ERROR("Create X265EncodeThread error");
return -1;
}
return 0;
}
void X265EnocdeDestroy()
{
X265_ENCODER *encoder = &gstX265Encoder;
if (encoder->thdId > 0)
{
encoder->thdRunFlag = 0;
pthread_join(encoder->thdId, 0);
encoder->thdId = 0;
}
if (encoder->fp)
{
fclose(encoder->fp);
encoder->fp = NULL;
}
if (encoder->spsPpsVps.sps)
{
free(encoder->spsPpsVps.sps);
encoder->spsPpsVps.sps = NULL;
}
if (encoder->spsPpsVps.pps)
{
free(encoder->spsPpsVps.pps);
encoder->spsPpsVps.pps = NULL;
}
if (encoder->encParam.handler)
{
x265_encoder_close(encoder->encParam.handler);
encoder->encParam.handler = NULL;
}
if (encoder->encParam.param)
{
x265_param_free(encoder->encParam.param);
encoder->encParam.param = NULL;
}
if (encoder->encParam.picInput)
{
x265_picture_free(encoder->encParam.picInput);
encoder->encParam.picInput = NULL;
}
if (encoder->encParam.buff)
{
free(encoder->encParam.buff);
encoder->encParam.buff = NULL;
}
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了x265的使用,而x265提供了大量能使我们快速便捷地处理数据的函数和方法。