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数据:将编码后的数据包写入输出文件。
- 清理资源:释放所有分配的资源,确保没有内存泄漏。
详细代码如下:
#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;}//创建AVFrameframe->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;
// }//创建AVPacketpkt = 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);
}