使用libx265对YUV进行h265编码


前言

使用x265对uvc camera视频进行265编码


一、x265 x86编译

  1. 获取x265源码
    去libx265官网上下载源码,地址:http://ftp.videolan.org/pub/videolan/x265/x265_3.2.tar.gz
    tar xvf x265_3.2.tar.gz

  2. 编译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编码

编码流程:

  1. x265_param编码参数设置
  2. x265_encoder_open打开编码器
  3. 分配x265_picture用于编码源YUV数据
  4. 为x265_picture的planes分配存放Y,U,V数据内存空间
  5. 使用x265_encoder_headers编码vps, sps, pps(可省略)
  6. 使用x265_encoder_encode对YUV420数据进行编码
  7. 保存编码后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

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: FFmpeg是一款流媒体处理的工具,支持多种视频编码格式,其中也包括YUV编码。H.265是一种高效的视频编码格式,可以将视频文件的大小压缩至原来的一半。 要将YUV编码的视频文件转换为H.265编码,需要进行以下步骤: 1. 通过FFmpeg获取原始视频的YUV数据。 2. 将YUV数据进行处理,将其转换为H.265编码格式。此处需要使用x265编码器,以实现高质量的视频编码。 3. 将处理后的H.265编码数据转化为保存为视频文件。 在使用FFmpeg进行YUV编码为H.265的操作时,需要注意以下几点: 1. YUV数据的格式应该符合编码器的要求,否则将无法进行编码。 2. 编码参数的选择对于编码质量和压缩率都有很大影响,需要注意对应的参数设置。 3. H.265编码是比较耗时的操作,需要足够的计算机性能支持。 通过上述步骤,我们可以将YUV格式的视频文件转换为高效的H.265格式,实现更好的视频质量和更小的视频文件大小。 ### 回答2: FFmpeg是一个跨平台的音视频处理库,其中也包括了对YUV数据的处理和编码功能。而H.265是一种高效的视频编码标准,能够提供更好的视频质量和更小的文件大小。因此,将YUV数据编码为H.265对于提高视频编码的效率和质量非常重要。 在FFmpeg中,可以使用x265编码器来将YUV数据编码为H.265。首先,需要将YUV数据加载到FFmpeg中,并设置相应的编码器参数。然后,使用x265编码器对YUV数据进行压缩编码,并输出为H.265视频文件格式。 具体步骤如下: 1. 使用FFmpeg加载YUV数据,可以通过命令行输入以下命令: ffmpeg -s:v widthxheight -pix_fmt yuv420p -i input.yuv 其中,width和height分别表示YUV数据的宽度和高度,input.yuvYUV数据的文件名。 2. 设置编码器参数,可以通过命令行指定编码器的参数,例如: ffmpeg -c:v libx265 -preset medium -x265-params keyint=60 -b:v 2M output.mp4 其中,-c:v表示指定使用x265编码器,-preset medium表示设置为中等压缩质量,-x265-params keyint=60表示设置关键帧间隔为60,-b:v 2M表示设置输出视频的比特率为2M,output.mp4表示输出为H.265视频文件。 3. 进行YUV编码,可以使用以下命令实现: ffmpeg -i input.yuv -c:v libx265 -preset medium -x265-params keyint=60 -b:v 2M output.mp4 其中,-i input.yuv表示输入YUV数据文件,-c:v libx265表示指定使用x265编码进行编码,后续参数同上。 通过以上步骤,就可以将YUV数据编码为H.265格式的视频文件,从而利用H.265标准的高效性能优势来提高视频编码的效率和质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值