在实现x264编码之前,我们先定义一些常用视频编码接口:
根据上面的定义,可以使用C++来抽象出一个虚基类
namespace toy {
// 编码器基类
class VideoEncoder {
public:
// 参数定义
struct Setting {
Setting() {
fps = 15;
frame_skip = false;
}
uint32_t width; // 视频宽
uint32_t height; // 视频高
uint32_t bitrate_bps; // 目标码率
uint32_t fps; // 帧率
bool frame_skip; // 是否开启跳帧
uint32_t qp; // qp值
};
// 创建编码器的静态接口
static std::shared_ptr<VideoEncoder> Create(VideoCodecType type);
virtual ~VideoEncoder() {}
// 打开编码器
virtual bool Open(const Setting& param) = 0;
// 重新配置编码器
virtual void ReConfigure(const Setting& param) = 0;
// 返回编码器的参数
virtual Setting GetConfig() = 0;
// 编码
virtual bool Encode(
const uint8_t** yuv_data,// uint8_t* yuv_data[3]
uint8_t* pkt,
size_t& pkt_size,
bool& is_keyframe,
bool& got_output
) = 0;
// 编码
virtual bool Encode(
const uint8_t* yuv_data,
uint8_t* pkt,
size_t& pkt_size,
bool& is_keyframe,
bool& got_output
) = 0;
// 请求关键帧
virtual void ForceKeyframe() = 0;
// 关闭编码器
virtual void Close() = 0;
};
}
头文件定义:
#ifndef __VIDEO_X264_ENCODER_H__
#define __VIDEO_X264_ENCODER_H__
#include "video_codec.h"
struct x264_t;
struct x264_param_t;
struct x264_picture_t;
class X264Encoder:public toy::VideoEncoder
{
public:
X264Encoder();
~X264Encoder();
//初始化编解码
virtual bool Open(const Setting& param) override;
//结束释放
virtual void Close() override;
//编码
virtual bool Encode(
const uint8_t** yuv_data,
uint8_t* pkt,
size_t& pkt_size,
bool& is_keyframe,
bool& got_output
) override;
virtual bool Encode(
const uint8_t* yuv_data,
uint8_t* pkt,
size_t& pkt_size,
bool& is_keyframe,
bool& got_output
) override;
//调整码率
virtual void ReConfigure(const Setting& param) override;
//强制i帧
virtual void ForceKeyframe() override;
virtual Setting GetConfig() override;
private:
Setting setting_;
x264_t* encoder_;
x264_param_t* encode_param_;
x264_picture_t* frame_;
x264_picture_t* encoded_frame_;
bool is_open_;
uint64_t encoded_frame_count_;
bool enable_fixed_gop_;
uint32_t gop_size_;
uint64_t timestamp_;
bool force_keyframe_;
};
#endif __VIDEO_X264_ENCODER_H__
源文件实现:
#include "x264_encoder.h"
#include "x264/x264.h"
#include
#include
#define kVideoBitrateMin 80000
#define kVideoBitrateMax 6000000
X264Encoder::X264Encoder()
{
encoder_ = nullptr;
encode_param_ = nullptr;
frame_ = nullptr;
encoded_frame_ = nullptr;
is_open_ = false;
encoded_frame_count_ = 0;
enable_fixed_gop_ = true;
gop_size_ = 400;
timestamp_ = 0;
force_keyframe_ = false;
}
X264Encoder::~X264Encoder()
{
Close();
}
void X264Encoder::Close()
{
if (encoder_) {
x264_encoder_close(encoder_);
encoder_ = NULL;
}
if (frame_)
{
x264_picture_clean(frame_);
delete frame_;
frame_ = nullptr;
}
if (encoded_frame_)
{
delete encoded_frame_;
encoded_frame_ = nullptr;
}
if (encode_param_)
{
delete encode_param_;
encode_param_ = nullptr;
}
is_open_ = false;
encoded_frame_count_ = 0;
}
bool X264Encoder::Open(const Setting& param)
{
Close();
encode_param_ = new x264_param_t;
frame_ = new x264_picture_t;
encoded_frame_ = new x264_picture_t;
setting_ = param;
x264_picture_init(encoded_frame_);
x264_picture_alloc(frame_, X264_CSP_I420, setting_.width, setting_.height);
x264_param_default(encode_param_);
x264_param_default_preset(encode_param_, "veryfast", "zerolatency");//会修改profile
x264_param_apply_profile(encode_param_, "baseline");//baseline+cabac
//x264_param_apply_profile_(encode_param_, "main");//老版本1.4的openh264不支持
encode_param_->i_level_idc = 40;
encode_param_->i_log_level = X264_LOG_NONE;
encode_param_->i_width = setting_.width;
encode_param_->i_height = setting_.height;
encode_param_->b_repeat_headers = 1; // 重复SPS/PPS 放到关键帧前面
encode_param_->b_cabac = 1;
encode_param_->i_threads = 1;//多帧编码
encode_param_->b_sliced_threads = 0;//(webrtc ? 0 : 1);
encode_param_->i_fps_num = setting_.fps;
encode_param_->i_fps_den = 1;
encode_param_->i_keyint_max = setting_.fps;
if (enable_fixed_gop_)
{
encode_param_->i_keyint_max = gop_size_;
encode_param_->i_keyint_min = gop_size_;
}
encode_param_->i_frame_reference = 1;//参考帧
encode_param_->analyse.b_psnr = 1;
//码率控制模式有ABR(平均码率)、CQP(恒定质量)、CRF(恒定码率)
// ABR模式下调整i_bitrate,vbv开了后就是CBR模式
// CQP下调整i_qp_constant调整QP值,范围0~51,值越大图像越模糊,默认23
// 太细致了人眼也分辨不出来,为了增加编码速度降低数据量还是设大些好,
// CRF下调整f_rf_constant和f_rf_constant_max影响编码速度和图像质量(数据量)
encode_param_->rc.i_rc_method = X264_RC_ABR;
//encode_param_->rc.i_qp_constant = 40;
//encode_param_->rc.i_qp_min = 10;
//encode_param_->rc.i_qp_max = 45;
//encode_param_->rc.f_rf_constant = 20;
//encode_param_->rc.f_rf_constant_max = 45;
//encode_param_->rc.f_rate_tolerance = 0.1;
encode_param_->rc.i_bitrate = (int)setting_.bitrate_bps / 1000;
encode_param_->rc.i_vbv_max_bitrate = (int)((setting_.bitrate_bps*1.0) / 1000); // 平均码率模式下,最大瞬时码率,默认0(与-B设置相同)
encode_param_->rc.i_vbv_buffer_size = (int)setting_.bitrate_bps * 2 / 1000;
encode_param_->rc.f_ip_factor = 1.12; //调整I、P帧比例,使得码率控制更满足网络传输需要
encode_param_->rc.f_rate_tolerance = 0.75; //使码率控制更准确
if ((encoder_ = x264_encoder_open(encode_param_)) == NULL)
{
Close();
return false;
}
is_open_ = true;
return true;
}
void X264Encoder::ReConfigure(const Setting& param) {
if (!is_open_) {
return;
}
// 分辨率改变,重启编码器
if (param.width != setting_.width || param.height != setting_.height) {
Close();
Open(param);
return;
}
// 如果帧率码率都相同,那么不用处理,直接返回
if (encode_param_->rc.i_bitrate == param.bitrate_bps / 1000 &&
encode_param_->i_fps_num == param.fps) {
return;
}
// 码率设置
if (encode_param_->rc.i_bitrate != param.bitrate_bps / 1000) {
setting_.bitrate_bps = param.bitrate_bps;
encode_param_->rc.i_vbv_max_bitrate = (int)((param.bitrate_bps*1.0) / 1000); // 平均码率模式下,最大瞬时码率,默认0(与-B设置相同)
encode_param_->rc.i_bitrate = (int)param.bitrate_bps / 1000;
encode_param_->rc.i_vbv_buffer_size = (int)param.bitrate_bps * 2 / 1000;
}
// 帧率设置
if (encode_param_->i_fps_num != param.fps) {
encode_param_->i_fps_num = param.fps;
encode_param_->i_fps_den = 1;
encode_param_->i_keyint_max = param.fps;
if (enable_fixed_gop_)
{
encode_param_->i_keyint_max = gop_size_;
encode_param_->i_keyint_min = gop_size_;
}
}
// TODO:QP
int err = x264_encoder_reconfig(encoder_, encode_param_);
if (err != 0)
{
printf("x264_encoder_reconfig failed! code %d\n", err);
}
setting_ = param;
}
bool X264Encoder::Encode(
const uint8_t** yuv_data,
uint8_t* pkt,
size_t& pkt_size,
bool& is_keyframe,
bool& got_output )
{
is_keyframe = false;
got_output = false;
pkt_size = 0;
if (!is_open_) {
return false;
}
encoded_frame_count_++;
int y_size = setting_.width * setting_.height;
memcpy(frame_->img.plane[0] + 0, yuv_data[0], y_size);
memcpy(frame_->img.plane[0] + y_size, yuv_data[1], y_size / 4);
memcpy(frame_->img.plane[0] + y_size * 5 / 4, yuv_data[2], y_size / 4);
frame_->i_pts = timestamp_++;
if (force_keyframe_)
{
frame_->i_type = X264_TYPE_IDR;
force_keyframe_ = false;
}
else
{
frame_->i_type = X264_TYPE_AUTO;
}
int iFrameSize = 0;
int iNal = 0;
x264_nal_t* pNals = NULL;
//编码
int frame_size = x264_encoder_encode(encoder_, &pNals, &iNal, frame_, encoded_frame_);
is_keyframe = IS_X264_TYPE_I(encoded_frame_->i_type) ? true : false;
if (frame_size > 0 && iNal > 0)
{
for (int i = 0; i < iNal; ++i)
{
int32_t i_num_nal_h = 0;
if (pNals[i].i_payload > 4)
{
while (i_num_nal_h < 5 && pNals[i].p_payload[i_num_nal_h] == 0)
{
i_num_nal_h++;
}
static char nal_head_s[3] = { 0, 0, 0 };
if (i_num_nal_h < 3)
{
memcpy(pkt + iFrameSize, nal_head_s, 3 - i_num_nal_h);
iFrameSize += (3 - i_num_nal_h);
}
}
memcpy(pkt + iFrameSize, pNals[i].p_payload, pNals[i].i_payload);
iFrameSize += pNals[i].i_payload;
}
got_output = true;
pkt_size = iFrameSize;
}
return true;
}
bool X264Encoder::Encode(
const uint8_t* yuv_data,
uint8_t* pkt,
size_t& pkt_size,
bool& is_keyframe,
bool& got_output
)
{
if (!is_open_) {
return false;
}
const uint8_t* yuv[3] = { 0 };
if (yuv_data == NULL) {
return Encode(yuv, pkt, pkt_size, is_keyframe, got_output);
}
else {
int y_size = setting_.width * setting_.height;
yuv[0] = yuv_data;
yuv[1] = yuv_data + y_size;
yuv[2] = yuv_data + y_size * 5 / 4;
return Encode(yuv, pkt, pkt_size, is_keyframe, got_output);
}
}
//强制i帧
void X264Encoder::ForceKeyframe()
{
force_keyframe_ = true;
}
toy::VideoEncoder::Setting X264Encoder::GetConfig() {
return setting_;
}