- 流媒体-H264协议-编码-x264学习-相关概念x264编译及文件解析(一)
- 流媒体-H264协议-编码-x264学习-主要结构体(二)
- 流媒体-H264协议-编码-x264学习-主要函数(三)
- 流媒体-H264协议-编码-x264学习-C++11多线程实现编码(四)
文章目录
一、Encode类实现
/**
* @projectName Testx264_01
* @author wangbaojia
* @date 2020-12-16
* @brief 参考x264源码:example.c文件
* 读取yuv文件进行h264编码
* //生成原生的yuv数据
* ffmpeg -i in.mp4 -pix_fmt yuv420p -vcodec rawvideo -an out.yuv
*/
#ifndef X264ENCODE_H
#define X264ENCODE_H
#include <stdio.h>
#include <string>
#include <atomic>
#include <x264.h>
#include <x264_config.h>
class X264Encode
{
public:
X264Encode();
virtual ~X264Encode();
virtual bool Open(std::string infile,std::string outfile);
virtual void Close();
virtual void Clear();
/**
* @brief Init x264_param_t参数初始化及相关设置
* @return
*/
virtual bool Init();
virtual bool Encode();
virtual bool IsFileEnd();
private:
std::string m_strInFileName = "";
std::string m_strOutFileName = "";
FILE* m_pInFp = nullptr;
FILE* m_pOutFp = nullptr;
std::atomic<bool> m_bIsFileEnd;
int m_iWidth = 768;
int m_iHeight = 320;
/**
* @brief m_pParam 结构体x264_param_t定义在x264.h中,用来初始化编码器
*/
x264_param_t* m_pParam = nullptr;
x264_t* m_pX264Handle = nullptr;
/**
* @brief m_pPicIn 描述一视频帧的特征
*/
x264_picture_t* m_pPicIn = nullptr;
x264_picture_t* m_pPicOut = nullptr;
// x264_nal_t* m_pNals = nullptr;
/* Colorspace type */
// #define X264_CSP_MASK 0x00ff /* */
// #define X264_CSP_NONE 0x0000 /* Invalid mode */
// #define X264_CSP_I400 0x0001 /* monochrome 4:0:0 */
// #define X264_CSP_I420 0x0002 /* yuv 4:2:0 planar */
// #define X264_CSP_YV12 0x0003 /* yvu 4:2:0 planar */
// #define X264_CSP_NV12 0x0004 /* yuv 4:2:0, with one y plane and one packed u+v */
// #define X264_CSP_NV21 0x0005 /* yuv 4:2:0, with one y plane and one packed v+u */
// #define X264_CSP_I422 0x0006 /* yuv 4:2:2 planar */
// #define X264_CSP_YV16 0x0007 /* yvu 4:2:2 planar */
// #define X264_CSP_NV16 0x0008 /* yuv 4:2:2, with one y plane and one packed u+v */
// #define X264_CSP_YUYV 0x0009 /* yuyv 4:2:2 packed */
// #define X264_CSP_UYVY 0x000a /* uyvy 4:2:2 packed */
// #define X264_CSP_V210 0x000b /* 10-bit yuv 4:2:2 packed in 32 */
// #define X264_CSP_I444 0x000c /* yuv 4:4:4 planar */
// #define X264_CSP_YV24 0x000d /* yvu 4:4:4 planar */
// #define X264_CSP_BGR 0x000e /* packed bgr 24bits */
// #define X264_CSP_BGRA 0x000f /* packed bgr 32bits */
// #define X264_CSP_RGB 0x0010 /* packed rgb 24bits */
// #define X264_CSP_MAX 0x0011 /* end of list */
// #define X264_CSP_VFLIP 0x1000 /* the csp is vertically flipped */
// #define X264_CSP_HIGH_DEPTH 0x2000 /* the csp has a depth of 16 bits per pixel component */
int m_icsp = X264_CSP_I420 ;/* 编码比特流的CSP(Colorspace type:颜色空间类型),视频图像色彩空间设置 */
};
#endif // X264ENCODE_H
#include "x264encode.h"
#include <iostream>
#include <chrono>
X264Encode::X264Encode()
{
m_bIsFileEnd = true;
}
X264Encode::~X264Encode()
{
std::cout<<"destory.."<<std::endl;
}
bool X264Encode::Open(std::string infile,std::string outfile)
{
if(infile.length()== 0 || outfile.length() == 0)
{
std::cout<<"file name is empty.."<<std::endl;
return false;
}
m_strInFileName = infile;
m_strOutFileName = outfile;
m_pInFp = fopen(const_cast<char*>(infile.c_str()),"rb");
if(!m_pInFp)
{
std::cout<<"open input file failed.."<<std::endl;
return false;
}
m_bIsFileEnd = false;//初始化文件读取位置
m_pOutFp = fopen(const_cast<char*>(outfile.c_str()),"wb");
if(!m_pInFp)
{
std::cout<<"open output file failed.."<<std::endl;
return false;
}
return true;
}
/**
* @brief X264Encode::Init x264_param_t参数初始化及相关设置
* @return
*/
bool X264Encode::Init()
{
/参数设置start
//分配空间
m_pParam = static_cast<x264_param_t*>(malloc(sizeof (x264_param_t)));
if(!m_pParam)
{
std::cout<<"malloc x264_param_t failed"<<std::endl;
return false;
}
#ifdef PRESET
if( x264_param_default_preset( m_pParam, "medium", nullptr ) < 0 )
{
return false;
}
#else
/**
* fill x264_param_t with default values and do CPU detection
* 函数内部第一行执行 memset( param, 0, sizeof( x264_param_t ) );因此需要先分配空间
*/
x264_param_default(m_pParam);
#endif
// int x264_param_default_preset( x264_param_t *param, const char *preset, const char *tune );将调用以下两个函数
// static int param_apply_preset( x264_param_t *param, const char *preset );
// static int param_apply_tune( x264_param_t *param, const char *tune );
/* Configure non-default params */
m_pParam->i_log_level = X264_LOG_NONE;//X264_LOG_DEBUG; // 打印debug信息
/* 编码比特流的CSP(Colorspace type:颜色空间类型),视频图像色彩空间设置 */
// m_pParam->i_csp = X264_CSP_I420;//可不设置,默认即为此格式
m_pParam->i_width = m_iWidth;
m_pParam->i_height = m_iHeight;
// std::cout<<m_pParam->i_width<<std::endl;
// m_pParam->i_bitdepth = 8; //像素比特位深度,新版增
/* VFR input. If 1, use timebase and timestamps for ratecontrol purposes.
* If 0, use fps only. */
// m_pParam->b_vfr_input = 0;
// m_pParam->b_repeat_headers = 1; /* put SPS/PPS before each keyframe */
/* if set, place start codes (4 bytes) before NAL units,
* otherwise place size (4 bytes) before NAL units. */
// m_pParam->b_annexb = 1;
/**
* @brief x264_param_apply_profile 设置画质级别
* static const char * const x264_profile_names[] = { "baseline", "main", "high", "high10", "high422", "high444", 0 };
*/
x264_param_apply_profile(m_pParam, x264_profile_names[0]);
/* 描述一视频帧的特征
typedef struct
{
int i_type; // 帧的类型,取值有X264_TYPE_KEYFRAME、X264_TYPE_P、
// X264_TYPE_AUTO等。初始化为auto,则在编码过程自行控制。
int i_qpplus1; // 此参数减1代表当前帧的量化参数值
int i_pic_struct; // 帧的结构类型,表示是帧还是场,是逐行还是隔行,
// 取值为枚举值 pic_struct_e,定义在x264.h中
int b_keyframe; // 输出:是否是关键帧
int64_t i_pts; // 一帧的显示时间戳
int64_t i_dts; // 输出:解码时间戳。当一帧的pts非常接近0时,该dts值可能为负。
//编码器参数设置,如果为NULL则表示继续使用前一帧的设置。某些参数
//(例如aspect ratio) 由于收到H264本身的限制,只能每隔一个GOP才能改变。
//这种情况下,如果想让这些改变的参数立即生效,则必须强制生成一个IDR帧。
x264_param_t *param;
x264_image_t img; // 存放一帧图像的真实数据
x264_image_properties_t prop;
x264_hrd_t hrd_timing; // 输出:HRD时间信息,仅当i_nal_hrd设置了才有效
void *opaque; // 私有数据存放区,将输入数据拷贝到输出帧中
} x264_picture_t ;
*/
m_pPicIn = static_cast<x264_picture_t*>(malloc(sizeof (x264_picture_t)));
m_pPicOut = static_cast<x264_picture_t*>(malloc(sizeof (x264_picture_t)));
/* x264_picture_alloc:
* alloc data for a picture. You must call x264_picture_clean on it.
* returns 0 on success, or -1 on malloc failure or invalid colorspace. */
x264_picture_alloc(m_pPicIn,m_icsp,m_pParam->i_width,m_pParam->i_height);
/* x264_picture_init:
* initialize an x264_picture_t. Needs to be done if the calling application
* allocates its own x264_picture_t 而不是使用 using x264_picture_alloc. */
x264_picture_init(m_pPicOut);
参数设置end/
/**
* 用于打开编码器,其中初始化了libx264编码所需要的各种变量
* create a new encoder handler, all parameters from x264_param_t are copied
*
x264_validate_parameters():检查输入参数(例如输入图像的宽高是否为正数)。
x264_predict_16x16_init():初始化Intra16x16帧内预测汇编函数。
x264_predict_4x4_init():初始化Intra4x4帧内预测汇编函数。
x264_pixel_init():初始化像素值计算相关的汇编函数(包括SAD、SATD、SSD等)。
x264_dct_init():初始化DCT变换和DCT反变换相关的汇编函数。
x264_mc_init():初始化运动补偿相关的汇编函数。
x264_quant_init():初始化量化和反量化相关的汇编函数。
x264_deblock_init():初始化去块效应滤波器相关的汇编函数。
x264_lookahead_init():初始化Lookahead相关的变量。
x264_ratecontrol_new():初始化码率控制相关的变量。
*/
m_pX264Handle = x264_encoder_open(m_pParam);
if(!m_pX264Handle)
{
std::cout<<"x264_encoder_open failed"<<std::endl;
return false;
}
return true;
}
bool X264Encode::Encode()
{
static int isPts = 0;
size_t iLumaSize = m_pParam->i_width * m_pParam->i_height;
size_t iChromaSize = iLumaSize / 4;
int iNnal;
x264_nal_t *pNals;
//读取yuv数据填充m_pPicIn
if(fread(m_pPicIn->img.plane[0],1,iLumaSize,m_pInFp) != iLumaSize) //Y
{
return false;
}
if(IsFileEnd()) return false;
if(fread(m_pPicIn->img.plane[1],1,iChromaSize,m_pInFp) != iChromaSize) //U
{
return false;
}
if(IsFileEnd()) return false;
if(fread(m_pPicIn->img.plane[2],1,iChromaSize,m_pInFp) != iChromaSize) //V
{
return false;
}
if(IsFileEnd()) return false;
m_pPicIn->i_pts = isPts;
/* x264_encoder_encode:用于编码一帧YUV为H.264码流
* encode one picture.
* *pi_nal is the number of NAL units outputted in pp_nal.
* returns the number of bytes in the returned NALs.
* returns negative on error and zero if no NAL units returned.
* the payloads of all output NALs are guaranteed to be sequential in memory. */
int iFrameSize = x264_encoder_encode(m_pX264Handle,&pNals,&iNnal,m_pPicIn,m_pPicOut);
if(iFrameSize < 0)
{
std::cout<<"x264_encoder_encode failed"<<std::endl;
return false;
}
// std::cout<<"Encode iNals="<<iNnal<<std::endl;
//生成多个NAL单元,进行处理
for(int i = 0; i < iNnal;i++)
{
if( !fwrite( pNals[i].p_payload, 1,pNals[i].i_payload, m_pOutFp))
{
std::cout<<"fwrite failed"<<std::endl;
return false;
}
}
return true;
}
bool X264Encode::IsFileEnd()
{
if(!m_pInFp) return true;
if(feof(m_pInFp))
{
std::cout<<"read to file end"<<std::endl;
return true;
}
return false;
}
void X264Encode::Close()
{
if(m_pPicIn)
{
free(m_pPicIn);
}
if(m_pPicOut)
{
free(m_pPicOut);
}
if(m_pParam)
{
free(m_pParam);
}
if(m_pInFp)
{
fclose(m_pInFp);
}
if(m_pOutFp)
{
fclose(m_pOutFp);
}
}
void X264Encode::Clear()
{
int iNnal;
x264_nal_t *pNals;
/* Flush delayed frames */
std::cout<<"buffer size="<<x264_encoder_delayed_frames( m_pX264Handle)<<std::endl;
while(x264_encoder_delayed_frames( m_pX264Handle))
{
//输入为nullptr
int iFrameSize = x264_encoder_encode( m_pX264Handle, &pNals, &iNnal, nullptr, m_pPicOut );
// std::cout<<"flush iNals="<<iNnal<<std::endl;
if( iFrameSize < 0 )
return;
for(int i = 0; i < iNnal;i++)
{
if( !fwrite( pNals[i].p_payload, 1,pNals[i].i_payload, m_pOutFp))
{
std::cout<<"fwrite failed"<<std::endl;
return;
}
}
}
if(m_pPicIn)//init分配需手动释放
{
x264_picture_clean(m_pPicIn);
}
if(m_pX264Handle)
{
x264_encoder_close(m_pX264Handle);
}
}