添加x264依赖
在QT工程pro中添加
LIBS +=-lx264
下载yuv测试文件
trace.eas.asu.edu/yuv/waterfa…
QT源码
x264manager.h
#define X264MANAGER_H
#include <QString>
#include <QDebug>
#include <unistd.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <x264.h>
#include <x264_config.h>
}
typedef void*(*handler_t)(void*);
class X264Manager
{
public:
X264Manager();
bool testYuv2H264(QString& yuvFile,QString& destFile);
void start();
static void* encode(void *);
handler_t callback;
};
#endif // X264MANAGER_H
x264manager.cpp
#include "x264manager.h"
X264Manager::X264Manager()
{
}
void* X264Manager::encode(void* arg){
QString yuv("/home/***/qt/waterfall_cif.yuv");
QString desth264("/home/***/qt/waterfall_cif_e.h264");
X264Manager* px264 = static_cast<X264Manager*>(arg);
px264->testYuv2H264(yuv,desth264);
}
void X264Manager::start(){
pthread_t tid;
callback = encode;
pthread_create(&tid,NULL,callback,this);
}
bool X264Manager::testYuv2H264(QString& yuvFile, QString& destFile)
{
bool ret = false;
// 检测输入文件
if(!yuvFile.endsWith(".yuv"))
{
qDebug() << __FILE__ << __LINE__ << "Failed to recgnize ext:" << yuvFile;
return ret;
}
// 检测输出文件
if(destFile.isEmpty())
{
destFile = yuvFile;
destFile.truncate(destFile.lastIndexOf(".yuv"));
destFile += ".h264";
}else if(!destFile.endsWith(".h264"))
{
qDebug() << __FILE__ << __LINE__ << "Failed to recgnize ext:" << destFile;
return ret;
}
qDebug() << __FILE__ << __LINE__ << yuvFile << "to" << destFile;
// 打开输入文件
FILE *fpSrc = fopen(yuvFile.toUtf8().data(), "rb");
if(!fpSrc)
{
qDebug() << __FILE__ << __LINE__ << "Failed to open file:" << yuvFile;
}
// 打开输出文件
FILE *fpDst = fopen(destFile.toUtf8().data(), "wb");
if(!fpDst)
{
qDebug() << __FILE__ << __LINE__ << "Failed to open file:" << destFile;
}
// 编码参数
int width = 352; // 宽度
int height = 288; // 高度
x264_param_t x264ParamT; // 编码参数
x264_picture_t x264PictureTIn; // 帧缓存
x264_picture_t x264PictureTOut; // 帧缓存
int inFrames = 0;
x264_nal_t * pX264NalT;
int x264NalT;
// 给结构体赋默认值
x264_param_default(&x264ParamT);
// 初始化编码参数
// 日志登记
x264ParamT.i_log_level = X264_LOG_DEBUG;
// 自动选择最佳前瞻线程缓冲区大小
x264ParamT.i_threads = X264_SYNC_LOOKAHEAD_AUTO;
// 颜色深度
x264ParamT.i_bitdepth = 8;
// 输入为420
x264ParamT.i_csp = X264_CSP_I420;
// 宽度
x264ParamT.i_width = width;
// 高度
x264ParamT.i_height = height;
// 设置帧率(分子)
x264ParamT.i_fps_num = 25;
// 设置帧率时间1s(分母)
x264ParamT.i_fps_den = 1;
//在此间隔设置IDR关键帧的数量
x264ParamT.i_keyint_max = 10;
// 码率控制方法,CQP(恒定质量),CRF(恒定码率,缺省值23),ABR(平均码率)
// x264ParamT.rc.i_rc_method = X264_RC_CQP; // 设置后,会导致参数应用失败
x264ParamT.rc.i_rc_method = X264_RC_CRF; // 设置后,会导致参数应用失败 或者 编码器打开失败
// x264ParamT.rc.i_rc_method = X264_RC_CQP; // 设置后,会导致参数应用失败
// 应用参数
if(x264_param_apply_profile(&x264ParamT, "baseline") < 0)
{
qDebug() << __FILE__ << __LINE__ << "Failed to x264_param_apply_profile";
return ret;
}
// 初始化删除
x264_picture_init(&x264PictureTOut);
// 分配帧缓存
if(x264_picture_alloc(&x264PictureTIn, x264ParamT.i_csp, x264ParamT.i_width, x264ParamT.i_height) < 0)
{
qDebug() << __FILE__ << __LINE__ << "Failed to x264_picture_alloc";
return ret;
}
// 获取编码器
x264_t *pX264T = 0;
pX264T = x264_encoder_open(&x264ParamT);
if(!pX264T)
{
qDebug() << __FILE__ << __LINE__ << "Failed to x264_encoder_open";
x264_picture_clean(&x264PictureTIn);
return ret;
}
// 总点大小
int framesPixelCount = x264ParamT.i_width * x264ParamT.i_height;
// 判断帧的数量(文件大小/每帧大小)
if(inFrames == 0)
{
fseek(fpSrc, 0, SEEK_END);
switch (x264ParamT.i_csp)
{
case X264_CSP_I444:
inFrames = ftell(fpSrc) / (framesPixelCount * 3);
break;
case X264_CSP_I422:
inFrames = ftell(fpSrc) / (framesPixelCount * 2);
break;
case X264_CSP_I420:
inFrames = ftell(fpSrc) / (framesPixelCount * 3 / 2);
break;
default:
break;
}
fseek(fpSrc, 0, SEEK_SET);
}
// 循环编码
for(int index = 0; index < inFrames; index++)
{
switch (x264ParamT.i_csp)
{
case X264_CSP_I444:
fread(x264PictureTIn.img.plane[0], framesPixelCount, 1, fpSrc); // Y
fread(x264PictureTIn.img.plane[1], framesPixelCount, 1, fpSrc); // U
fread(x264PictureTIn.img.plane[2], framesPixelCount, 1, fpSrc); // V
break;
case X264_CSP_I422:
{
int i = 0;
int y_i=0,u_i=0,v_i=0;
for(i = 0 ; i < framesPixelCount * 2 ;)
{
fread(&x264PictureTIn.img.plane[0][y_i++], 1, 1, fpSrc); //Y
index++;
fread(&x264PictureTIn.img.plane[1][u_i++], 1, 1, fpSrc); //U
index++;
fread(&x264PictureTIn.img.plane[0][y_i++], 1, 1, fpSrc); //Y
index++;
fread(&x264PictureTIn.img.plane[2][v_i++], 1, 1, fpSrc); //V
index++;
}break;
}
break;
case X264_CSP_I420:
fread(x264PictureTIn.img.plane[0], framesPixelCount, 1, fpSrc); // Y
fread(x264PictureTIn.img.plane[1], framesPixelCount/4, 1, fpSrc); // U
fread(x264PictureTIn.img.plane[2], framesPixelCount/4, 1, fpSrc); // V
break;
default:
break;
}
// 当前帧数
x264PictureTIn.i_pts = index;
qDebug() << __FILE__ << __LINE__ << "Succeed to get frame:" << index << "/" << inFrames;
int iRet = x264_encoder_encode(pX264T, &pX264NalT, &x264NalT, &x264PictureTIn, &x264PictureTOut);
if(iRet < 0)
{
qDebug() << __FILE__ << __LINE__ << "Failed to x264_encoder_encode";
return ret;
}
qDebug() << __FILE__ << __LINE__ << "Succeed to encode frame:" << index;
#if 1
// 此处是一边拿到编码器一边编码写入到文件中
int i = 0;
qDebug() << __FILE__ << __LINE__ << i << inFrames << iRet;
// 编码器编码存入文件
for(int j = 0; j < x264NalT; j++)
{
fwrite(pX264NalT[j].p_payload, 1, pX264NalT[j].i_payload, fpDst);
}
#endif
}
#if 0
// 此处是全部拿到编码器里面后,再循环拿取所有,然后写入
int i = 0;
while(true)
{
int iRet = x264_encoder_encode(pX264T, &pX264NalT, &x264NalT, NULL, &x264PictureTOut);
if(iRet == 0)
{
break;
}
qDebug() << __FILE__ << __LINE__ << i << inFrames << iRet;
// 编码器编码存入文件
for(int j = 0; j < x264NalT; j++)
{
fwrite(pX264NalT[j].p_payload, 1, pX264NalT[j].i_payload, fpDst);
}
i++;
}
#endif
qDebug() << __FILE__ << __LINE__ << "It's finished!!!";
x264_picture_clean(&x264PictureTIn);
x264_encoder_close(pX264T);
pX264T = 0;
fclose(fpSrc);
fclose(fpDst);
}
开始编码
#include "x264manager.h"
X264Manager x264Manager;
x264Manager.start();
运行结果
编码前后对比
使用VLC播放编码后的h264文件
文中关键代码抄袭了