最简单的视频编码器(一)---libx264

目录

概述

源代码介绍

下载源码


概述

本文记录一个最简单的基于libx264的H.264视频编码器;直接调用libx264完成编码。因此项目的体积非常小巧。该编码器可以将输入的YUV数据编码为H.264码流文件。

流程中主要的函数如下所示:
x264_param_default():设置参数集结构体x264_param_t的缺省值。
x264_picture_alloc():为图像结构体x264_picture_t分配内存。
x264_encoder_open():打开编码器。
x264_encoder_encode():编码一帧图像。
x264_encoder_close():关闭编码器。
x264_picture_clean():释放x264_picture_alloc()申请的资源。
 
其中存储数据的结构体如下所示:
x264_picture_t:存储压缩编码前的像素数据。
x264_nal_t:存储压缩编码后的码流数据。

此外流程图中还包括一个“flush_encoder”模块,该模块使用的函数和编码模块是一样的。唯一的不同在于不再输入视频像素数据。它的作用在于输出编码器中剩余的码流数据。

源代码介绍

   基于libx264的H.264视频编码器封装成simplest_encoder_x264类;里面包含对外暴露接口如下:

    1、long Init(encoder_x264_cfg* cfg);

         这个是用于编码初始化的,具体暴露参数参照结构体:encoder_x264_cfg;返回:0代表成功。
    2、long Encoder(char* inbuff, long long& pts, long long& dts, char* outbuff, int& out_buff_len);

    这个函数用于编码处理的;参数解释如下:

          inbuff:输入buff,目前代码中只实现I420,如需要可自行扩展;

          pts:输入的时间戳;

         outbuff:编程成功后返回的数据buff,应由调用的人负责申请和释放;

         out_buff_len:传输进行的最大buff,编程成功返回outbuff里的数据长度;

   函数返回值:0代表成功;否则失败。
    3、long Flush(long long pts, char* outbuff, int& out_buff_len);

    这个函数用于编码处理的;参数解释如下:          

         pts:输入的时间戳;

         outbuff:编程成功后返回的数据buff,应由调用的人负责申请和释放;

         out_buff_len :传输进行的最大buff,编程成功返回outbuff里的数据长度;

    函数返回值:0代表成功;否则失败。当返回成功应该继续调用Flush函数
    4、long AdjustBitrate(int bitrate);

    这个函数用于编码处理的;参数解释如下:

    bitrate:更换的比特率,单位应该对应比特率*1000;

    函数返回值:0代表成功;否则失败。
   5、 long MandatoryKeyFrame();

   函数解释:这个代表强制输出关键帧,即IDR帧;

   函数返回值:0代表成功;否则失败。

    6、long UnInit();

    函数解释:代表释放libx264相关资源;

   函数返回值:0代表成功;否则失败。

对应源码头文件:simplest_encoder_x264.h

/**
 * 简单的x264视频编码器
 *Simple x264 video encoder
 *
 * 梁启东 qidong.liang
 * 18088708700@163.com
 * https://blog.youkuaiyun.com/u011645307
 *
 *
 * 本程序实现了简单的x264视频编码功能;
 * 支持强制输出关键帧
 * 支持动态修改码率
 * 目前只开放了I420数据格式编码
 *This program realizes a simple x264 video coding function;
 *Support forced output of key frames
 *Support dynamic modification of bit rate
 *At present, only i420 data format coding is open
 *
 */

#ifndef SIMPLEST_ENCODER_X264
#define SIMPLEST_ENCODER_X264

#if defined ( __cplusplus)
#include <stdint.h>
#include <map>
#include <functional>
extern "C"
{
#endif

#include "x264/include/x264.h"

#if defined ( __cplusplus)
};
#endif

typedef enum x264_rc_mode
{
	x264_rc_mode_cqp, //恒定质量,
	x264_rc_mode_crf, //恒定码率,
	x264_rc_mode_abr, //平均码率

}x264_rc_mode;

typedef enum x264_raw_format
{
	x264_raw_format_I420,

}x264_raw_format;

typedef struct encoder_x264_cfg
{
	int						sourceWidth;		//Width
	int						sourceHeight;		//Height
	x264_raw_format			internalCsp;		//源颜色空间
	int						bRepeatHeaders;	    //关键帧前是否有sps pps
	int                     threads;			//编码线程数
	int                     bframe;             //设置B帧数量;当为0的时候不含B帧
	int                     bitrate;           // 设置码率, 单位是 kbps*1000
	int						gop;               //关键帧间隔
	x264_rc_mode            rcmode;            // rc_mode

	encoder_x264_cfg()
	{
		sourceWidth = 0;
		sourceHeight = 0;
		bframe = 0;
		gop = 15;
		threads = 1;
		bRepeatHeaders = 1;
		bitrate = 1500000;
		internalCsp = x264_raw_format_I420;
		rcmode = x264_rc_mode_cqp;
	}

}encoder_x264_cfg;

class simplest_encoder_x264
{
public:
	simplest_encoder_x264();
	~simplest_encoder_x264();

	long Init(encoder_x264_cfg* cfg);
	long Encoder(char* inbuff, long long& pts, long long& dts, char* outbuff, int& out_buff_len);
	long Flush(long long pts, char* outbuff, int& out_buff_len);
	long AdjustBitrate(int bitrate);
	long MandatoryKeyFrame();
	long UnInit();

private:
	long I420ToX264PictureT(char* inbuff, int width, int height, x264_picture_t* pPic_in);

private:
	bool is_key_frame_;
	int iNal_ ;
	x264_nal_t* pNals_ ;
	x264_t* pHandle_ ;
	x264_picture_t* pPic_in_;
	x264_picture_t* pPic_out_;
	x264_param_t* pParam_;
	std::map<x264_raw_format, int>  internal_csp_map_;
	std::map<x264_rc_mode, int>		x264_rc_mode_map_;
	std::map<int, std::function<long(char* inbuff, int width,int height,x264_picture_t* pPic_in)>>  csp_fun_map_;
};

#endif // SIMPLEST_ENCODER_X264


对应源码源文件:simplest_encoder_x264.cpp


#include "simplest_encoder_x264.h"
#include <iostream>
simplest_encoder_x264::simplest_encoder_x264()
{
	pHandle_ = nullptr;
	is_key_frame_ = false;

	pPic_in_ = (x264_picture_t*)malloc(sizeof(x264_picture_t));
	pPic_out_ = (x264_picture_t*)malloc(sizeof(x264_picture_t));
	pParam_ = (x264_param_t*)malloc(sizeof(x264_param_t));

	internal_csp_map_[x264_raw_format_I420] = X264_CSP_I420;

	csp_fun_map_[X264_CSP_I420] = std::bind(&simplest_encoder_x264::I420ToX264PictureT,this,std::placeholders::_1,std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);

	x264_rc_mode_map_[x264_rc_mode_cqp] = X264_RC_CQP;
	x264_rc_mode_map_[x264_rc_mode_crf] = X264_RC_CRF;
	x264_rc_mode_map_[x264_rc_mode_abr] = X264_RC_ABR;
}

simplest_encoder_x264::~simplest_encoder_x264()
{
	if (pPic_in_)
	{
		free(pPic_in_);
	}
	pPic_in_ = nullptr;

	if (pPic_out_)
	{
		free(pPic_out_);
	}
	pPic_out_ = nullptr;

	if (pParam_)
	{
		free(pParam_);
	}
	pParam_ = nullptr;

}

long simplest_encoder_x264::Init(encoder_x264_cfg* cfg)
{
	if (nullptr == cfg)
	{
		return -1;
	}
	auto iter = internal_csp_map_.find(cfg->internalCsp);
	if (internal_csp_map_.end() == iter)
	{
		return -1;
	}
	auto mode_iter = x264_rc_mode_map_.find(cfg->rcmode);
	if (x264_rc_mode_map_.end() == mode_iter)
	{
		return -1;
	}
	x264_param_default(pParam_);
	pParam_->b_repeat_headers = cfg->bRepeatHeaders;
	pParam_->i_width = cfg->sourceWidth;
	pParam_->i_height = cfg->sourceHeight;
	pParam_->i_csp = iter->second;
	pParam_->rc.i_rc_method = mode_iter->second;
	pParam_->i_bframe = cfg->bframe;
	
	// 设置码率, 单位是 kbps
	pParam_->rc.i_bitrate = cfg->bitrate / 1000;
	// 设置最大码率, 单位 kbps, 该配置与 i_vbv_buffer_size 配套使用
	pParam_->rc.i_vbv_max_bitrate = cfg->bitrate / 1000 * 1.2;
	// 该配置与 i_vbv_max_bitrate 配置配套使用, 码率控制缓冲区大小
	pParam_->rc.i_vbv_buffer_size = cfg->bitrate / 1000;

	pParam_->i_keyint_max = cfg->gop; //关键帧间隔

	x264_param_apply_profile(pParam_, x264_profile_names[5]);

	pHandle_ = x264_encoder_open(pParam_);

	x264_picture_init(pPic_out_);
	x264_picture_alloc(pPic_in_, pParam_->i_csp, pParam_->i_width, pParam_->i_height);

	return 0;
}

long simplest_encoder_x264::Encoder(char* inbuff, long long& pts, long long& dts, char* outbuff,int& out_buff_len)
{
	if (!pParam_)
	{
		return -1;
	}

	auto fun_iter = csp_fun_map_.find(pParam_->i_csp);
	if (csp_fun_map_.end() == fun_iter)
	{
		return -2;
	}
	
	fun_iter->second(inbuff, pParam_->i_width, pParam_->i_height, pPic_in_);
	pPic_in_->i_pts = pts;

	int iNal = 0;
	pPic_in_->i_type = X264_TYPE_AUTO;
	if (is_key_frame_)
	{
		pPic_in_->i_type = X264_TYPE_KEYFRAME;
		is_key_frame_ = false;
	}
	int ret = x264_encoder_encode(pHandle_, &pNals_, &iNal, pPic_in_, pPic_out_);
	if (ret < 0) {
		printf("x264_encoder_encode Error.\n");
		return -3;
	}

	out_buff_len = 0;
	for (int j = 0; j < iNal; ++j)
	{
		memcpy(outbuff+ out_buff_len, pNals_[j].p_payload, pNals_[j].i_payload);
		out_buff_len += pNals_[j].i_payload;
	}

	return 0;
}

long simplest_encoder_x264::Flush(long long pts, char* outbuff, int& out_buff_len)
{
	int iNal = 0;
	out_buff_len = 0;
	int ret = x264_encoder_encode(pHandle_, &pNals_, &iNal, NULL, pPic_out_);
	if (ret == 0) {
		return 0;
	}
	printf("Flush 1 frame.\n");
	for (int j = 0; j < iNal; ++j)
	{
		memcpy(outbuff + out_buff_len, pNals_[j].p_payload, pNals_[j].i_payload);
		out_buff_len += pNals_[j].i_payload;
	}
	return 0;
}

long simplest_encoder_x264::AdjustBitrate(int bitrate)
{
	if (nullptr == pParam_ ||
		nullptr == pHandle_)
	{
		return -1;
	}
	pParam_->rc.i_rc_method = X264_RC_ABR;
	pParam_->rc.i_vbv_max_bitrate = bitrate/1000;
	pParam_->rc.i_bitrate = bitrate / 1000;
	pParam_->rc.i_vbv_buffer_size = bitrate / 1000;


	int nRes = x264_encoder_reconfig(pHandle_, pParam_);
	if (0 != nRes )
	{
		printf("x264_encoder_reconfig:%d\n", nRes);
		return -1;
	}
	return 0;
}

long simplest_encoder_x264::MandatoryKeyFrame()
{
	is_key_frame_ = true;
	return 0;
}

long simplest_encoder_x264::UnInit()
{
	if (pPic_in_)
	{
		x264_picture_clean(pPic_in_);
	}

	if (pHandle_)
	{
		x264_encoder_close(pHandle_);
	}
	pHandle_ = nullptr;
	return 0;
}

long simplest_encoder_x264::I420ToX264PictureT(char* inbuff, int width, int height, x264_picture_t* pPic_in)
{
	int y_size = width * height;
	memcpy(pPic_in->img.plane[0], inbuff, y_size);	//Y
	memcpy(pPic_in->img.plane[1], inbuff + y_size, y_size / 4);	//U
	memcpy(pPic_in->img.plane[2], inbuff + y_size + y_size / 4, y_size / 4);	//V

	return 0;
}

对应的测试函数 main.cpp

/**
 * 简单的x264视频编码器测试程序
 *Simple x264 video encoder
 *
 * 梁启东 qidong.liang
 * 18088708700@163.com
 * https://blog.youkuaiyun.com/u011645307
 *
 *
 * 本程序测试simplest_encoder_x264视频编码功能;
 * 测试simplest_encoder_x264强制输出关键帧的功能
 * 测试simplest_encoder_x264动态修改码率的功能
 * 测试simplest_encoder_x264I420数据格式编码的功能
 * This program tests simplest_ encoder_ X264 video coding function;
 * Test simplest_ encoder_ X264 forced output keyframe function
 * Test simplest_ encoder_ X264 function of dynamically modifying code rate
 * Test simplest_ encoder_ X264i420 data format coding function
 *
 */

#include "simplest_encoder_x264.h"
#include <iostream>
int main()
{
    FILE* out_file = nullptr;
    FILE* in_file = nullptr;
    int w = 0;
    int h = 0;
    int buffLen = 0;

    fopen_s(&in_file, "../inData/i420_480x272.yuv", "rb+");
    fopen_s(&out_file, "../outData/i420_480x272.h264", "wb+");
    w = 480;
    h = 272;
    buffLen = w * h * 3 / 2;

    if (!in_file)
    {
		printf("in_file is nullptr\n");
		exit(-1);
    }

    encoder_x264_cfg cfg;
    simplest_encoder_x264* x264 = new (std::nothrow)simplest_encoder_x264;
    if (!x264)
    {
        printf("x264 is nullptr\n");
		delete x264;
		x264 = nullptr;

		if (in_file)
		{
			fclose(in_file);
		}
		in_file = nullptr;

		if (out_file)
		{
			fclose(out_file);
		}
		out_file = nullptr;
        exit(-1);
    }

    cfg.bRepeatHeaders = 1;
    cfg.internalCsp = x264_raw_format_I420;
    cfg.sourceHeight = h;
    cfg.sourceWidth = w;
    cfg.bframe = 0;
    if (0 != x264->Init(&cfg))
    {
        printf("x264.Init error\n");
        exit(-1);
    }
   
    char* inbuff = new char[buffLen];
    char* outbuff = new char[buffLen];
    long long i = 0;
    long long d = 0;
    int outLen = buffLen;
	while (true)
	{
        i++;
        outLen = buffLen;
		if (fread(inbuff, 1, buffLen, in_file) != buffLen)
		{
			break;
		}
        if (100 == i )
        {
            x264->AdjustBitrate(80000);
        }
		if (i==10)
		{
			x264->MandatoryKeyFrame();
		}
		
        x264->Encoder(inbuff, i, d, outbuff, outLen);

		fwrite(outbuff, 1, outLen, out_file);
	}

	while (true)
	{
        x264->Flush(i, outbuff, outLen);
        fwrite(outbuff, 1, outLen, out_file);
        if (!outLen)
        {
            break;
        }
    }

	if (0 != x264->UnInit())
	{
		printf("x264.UnInit error\n");
	}

    delete x264;
    x264 = nullptr;

    if (in_file)
    {
        fclose(in_file);
    }
    in_file = nullptr;

	if (out_file)
	{
        fclose(out_file);
	}
    out_file = nullptr;
   
    std::cout << "Hello World!\n";
}

下载源码

csdn:simplest_encoder_x264-编解码文档类资源-优快云下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值