使用libx265对YUV进行h265编码

it2024-04-04  57

文章目录

前言一、x265 x86编译二、x265 编码YUV(YUYV)1. V4L2获取YUV2. YUV格式转换3. H265编码 总结参考


前言

使用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

参考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提供了大量能使我们快速便捷地处理数据的函数和方法。

参考

最简单的视频编码器:基于libx265(编码YUV为H.265) 在linux下源码编译x265

最新回复(0)