yuv转h264格式步骤
- 初始化FFmpeg库:通过
av_register_all()
和avcodec_register_all()
来初始化必要的组件。 - 查找编码器:使用
avcodec_find_encoder
查找H.264编码器。 - 配置编码上下文:使用
avcodec_alloc_context3
分配编码上下文,并设置必要的参数。 - 打开编码器:通过
avcodec_open2
打开编码器。 - 准备输入数据:从文件中读取YUV数据,并将其填充到 AVFrame 中。
- 编码帧:使用
avcodec_send_frame
发送帧到编码器,并通过avcodec_receive_packet
获取编码后的数据包。 - 输出H.264数据:将编码后的数据包写入输出文件。
- 清理资源:释放所有分配的资源,确保没有内存泄漏。
详细代码如下:
cpp
#include "ffmpegs.h"
#include <QFile>
#include <QDebug>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
}
#define ERROR_BUF(ret) \
char errbuf[1024]; \
av_strerror(ret,errbuf,sizeof(errbuf));
ffmpegs::ffmpegs()
{
}
//检查像素格式
static int check_pix_fmt(const AVCodec *codec,enum AVPixelFormat pixFmt)
{
const enum AVPixelFormat *p = codec->pix_fmts;
while(*p != AV_PIX_FMT_NONE)
{
if(*p == pixFmt)
{
return 1;
}
p++;
}
return 0;
}
//音频编码,返回负数:中途出现了错误,返回0:编码操作正常完成
static int encode(AVCodecContext *ctx,AVFrame *frame,AVPacket *pkt,QFile &outFile)
{
int ret = avcodec_send_frame(ctx,frame);
if(ret < 0)
{
ERROR_BUF(ret);
qDebug() << "avcodec_send_frame error" << errbuf;
return 0;
}
//不断从编码器中取出编码后的数据
while(ret >= 0)
{
ret = avcodec_receive_packet(ctx,pkt);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
//继续读取数据到frame,然后送到编码器
return 0;
}
else if(ret < 0)//其他错误
{
return ret;
}
//成功从编码器拿到的数据
outFile.write((char*)pkt->data,pkt->size);
//释放pkt内部资源
av_packet_unref(pkt);
}
}
void ffmpegs::h264Encode(VideoEncodeSpec &in,const char *outFileName,QString &str)
{
//文件
QFile inFile(in.filename);
QFile outFile(outFileName);
//一帧图片的大小
int imgSize = av_image_get_buffer_size(in.pixFmt,in.width,in.height,1);
int ret = 0; //返回结果
const AVCodec *codec = nullptr; //编码器
AVCodecContext *ctx = nullptr; //编码上下文
AVFrame *frame = nullptr; //存放编码前的数据(yuv)
AVPacket *pkt = nullptr; //存放编码后的数据(h264)
//uint8_t *buf = nullptr;
//获取编码器
codec = avcodec_find_encoder_by_name("libx264");
if(!codec)
{
qDebug() << "encoder not found";
return;
}
qDebug() << codec->name;
//libfdk_aac对输入数据的要求:采样格式必须是16位整数
//检查输入数据的采样格式
if(!check_pix_fmt(codec,in.pixFmt))
{
qDebug() << "unsupported pixel format" << av_get_pix_fmt_name(in.pixFmt);
return;
}
//创建编码器上下文
ctx = avcodec_alloc_context3(codec);
if(!ctx)
{
qDebug() << "avcodec_alloc_context3 error";
return;
}
//设置yuv参数
ctx->width = in.width;
ctx->height = in.height;
ctx->pix_fmt = in.pixFmt;
//设置帧率(1秒钟显示多少帧)
ctx->time_base = {1,in.fps};
//打开编码器
ret = avcodec_open2(ctx,codec,nullptr);
if(ret < 0)
{
ERROR_BUF(ret);
qDebug() << "avcodec_open2_error" << errbuf;
goto end;
}
//创建AVFrame -- AVFrame用来存放编码前的数据
frame = av_frame_alloc();
if(!frame)
{
qDebug() << "av_frame_alloc error";
goto end;
}
//创建AVFrame
frame->width = ctx->width;
frame->height = ctx->height;
frame->format = ctx->pix_fmt;
frame->pts = 0;
//利用width、height、format创建缓冲区
ret = av_image_alloc(frame->data,frame->linesize,in.width,in.height,in.pixFmt,1);
if(ret < 0)
{
ERROR_BUF(ret);
qDebug() << "avcodec_open2_error" << errbuf;
goto end;
}
// //创建输入缓冲区(方法2)
// buf = (uint8_t*)av_malloc(imgSize);//实际buf和frame->data[0]地址一样
// ret = av_image_fill_arrays(frame->data,frame->linesize,
// buf,
// in.pixFmt,in.width,in.height,1);
// if(ret < 0)
// {
// ERROR_BUF(ret);
// qDebug() << "av_image_fill_arrays error" << errbuf;
// goto end;
// }
//创建AVPacket
pkt = av_packet_alloc();
if(!pkt)
{
qDebug() << "av_packet_alloc error";
goto end;
}
//打开文件
if(!inFile.open(QFile::ReadOnly))
{
qDebug() << "file open error" << in.filename;
goto end;
}
if(!outFile.open(QFile::WriteOnly))
{
qDebug() << "file open error" << outFileName;
goto end;
}
//读取数据到frame中
while((ret = inFile.read((char*)frame->data[0],imgSize)) > 0)
{
//进行编码
if(encode(ctx,frame,pkt,outFile) < 0)
{
goto end;
}
//设置帧的序号
frame->pts++;
}
//刷新缓冲区
encode(ctx,frame,pkt,outFile);
end:
//关闭文件
inFile.close();
outFile.close();
//av_freep(&buf);
//释放资源
if(frame)
{
av_freep(&frame->data[0]);
av_frame_free(&frame);
}
av_packet_free(&pkt);
avcodec_free_context(&ctx);
}