h264格式转yuv具体步骤
- 初始化FFmpeg库:通过av_register_all()来初始化须要的组件。
- 打开输入文件并查找解码器:使用avformat_open_input和 avcodec_find_decoder 打开H.264文件,并查找视频流。
- 分配并配置解码上下文:使用 avcodec_alloc_context3 分配解码上下文,并设置须要的参数。
- 使用av_parser_init初始化分析器上下文
- 打开解码器:通过 avcodec_open2 打开解码器。
- 读取和解码帧:使用 av_parser_parse2 数据经过分析器分析,并使用 avcodec_send_packet 和 avcodec_receive_frame 解码帧。
- 生存YUV帧到文件:将解码后的YUV帧写入输出文件。
- 清理资源:开释所有分配的资源,确保没有内存泄漏。
具体代码如下:
- #include "ffmpeg.h"
- #include <QFile>
- #include <QDebug>
- extern "C"
- {
- #include <libavcodec/avcodec.h>
- #include <libavutil/avutil.h>
- #include <libavutil/imgutils.h>
- }
- #define ERROR_BUF(ret) \
- char errbuf[1024]; \
- av_strerror(ret,errbuf,sizeof(errbuf));
- //输入缓冲区的大小
- #define IN_DATA_SIZE 4096
- ffmpegs::ffmpegs()
- {
- }
- static int decode(AVCodecContext *ctx,AVPacket *pkt,AVFrame *frame,QFile &outFile)
- {
- //发送压缩数据到解码器
- int ret = avcodec_send_packet(ctx,pkt);
- if(ret < 0)
- {
- ERROR_BUF(ret);
- qDebug() << "avcodec_send_packet error" << errbuf;
- return ret;
- }
- while(true)
- {
- //获取解码后的数据
- ret = avcodec_receive_frame(ctx,frame);
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
- {
- return 0;
- } else if (ret < 0)
- {
- ERROR_BUF(ret);
- qDebug() << "avcodec_receive_frame error" << errbuf;
- return ret;
- }
- // 将解码后的数据写入文件
- //写入y平面
- outFile.write((char *) frame->data[0], frame->linesize[0] * ctx->height);
- //写入u平面
- outFile.write((char *) frame->data[1], frame->linesize[1] * ctx->height >> 1);
- //写入v平面
- outFile.write((char *) frame->data[2], frame->linesize[2] * ctx->height >> 2);
- }
- }
- void ffmpegs::h264Decode(const char *inFileName, videodecodeSpec &out)
- {
- //返回结果
- int ret = 0;
- //用来存放读取的文件数据(h264)
- //加上AV_INPUT_BUFFER_PADDING_SIZE是为了防止某些优化过的reader一次性读取过多导致越界
- char inDataArray[IN_DATA_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
- char *inData = inDataArray;
- //每次从输入文件中读取的长度(h264)
- //输入缓冲区中,剩下等待进行解码的有效数据长度
- int inLen = 0;
- int inEnd = 0;
- //文件
- QFile inFile(inFileName);
- QFile outFile(out.filename);
- //解码器
- AVCodec *codec = nullptr;
- //上下文
- AVCodecContext *ctx = nullptr;
- //编解码器上下文
- AVCodecParserContext *parserCtx = nullptr;
- //存放编码前的数据(h264)
- AVPacket *pkt = nullptr;
- //存放编码后的数据(yuv)
- AVFrame *frame = nullptr;
- //获取编码器
- //codec = avcodec_find_decoder_by_name("h264");
- codec = avcodec_find_decoder(AV_CODEC_ID_H264);
- if(!codec)
- {
- qDebug() << "decodec not found";
- return;
- }
- //初始化解析器上下文
- // parserCtx = av_parser_init(codec->id);//根据编码器ID进行初始化
- parserCtx = av_parser_init(AV_CODEC_ID_H264);//根据编码器ID进行初始化
- if(!parserCtx)
- {
- qDebug() << "av_parser_init error";
- return;
- }
- //创建上下文
- ctx = avcodec_alloc_context3(codec);
- if(!ctx)
- {
- qDebug() << "avcodec_alloc_context3 error";
- goto end;
- }
- //创建AVPacket
- pkt = av_packet_alloc();
- if(!pkt)
- {
- qDebug() << "av_packet_alloc error";
- goto end;
- }
- //创建AVFrame
- frame = av_frame_alloc();
- if(!frame)
- {
- qDebug() << "av_frame_alloc error";
- goto end;
- }
- //打开解码器
- ret = avcodec_open2(ctx,codec,nullptr);
- if(ret < 0)
- {
- ERROR_BUF(ret);
- qDebug() << "avcodec_open2 error" << errbuf;
- goto end;
- }
- //打开文件
- if(!inFile.open(QFile::ReadOnly))
- {
- qDebug() << "file open error:" << inFileName;
- goto end;
- }
- if(!outFile.open(QFile::WriteOnly))
- {
- qDebug() << "file open error:" << out.filename;
- goto end;
- }
- //读取文件数据
- do
- {
- inLen = inFile.read(inDataArray, IN_DATA_SIZE);
- //设置是否到了文件尾部
- inEnd = !inLen;
- //让inData指向数组的首元素
- inData = inDataArray;
- //只要输入缓冲区中还有等待进行解码的数据
- while(inLen > 0 || inEnd)
- {
- //经过解析器解析
- //内部调用的核心是:ff_aac_ac3_parse
- //到了文件尾部(虽然没有读取任何数据,但也要调用av_parser_parse2)
- //经过解析器解析
- ret = av_parser_parse2(parserCtx,ctx,&pkt->data,&pkt->size,
- (uint8_t *)inData,inLen,
- AV_NOPTS_VALUE,AV_NOPTS_VALUE,0);
- if (ret < 0)
- {
- ERROR_BUF(ret);
- qDebug() << "av_parser_parse2 error" << errbuf;
- goto end;
- }
- //跳过已经解析过的数据
- inData += ret;
- //减去已经解析过的数据大小
- inLen -= ret;
- qDebug() << inEnd << pkt->size << ret;
- //解码
- if(pkt->size > 0)
- {
- if(decode(ctx,pkt,frame,outFile) < 0)
- {
- goto end;
- }
- }
- //如果到了文件末尾
- if(inEnd) break;
- }
- }while(!inEnd);
- //刷新缓冲区
- // pkt->data = nullptr;
- // pkt->size = 0;
- // decode(ctx,pkt,frame,outFile);
- //刷新缓冲区
- decode(ctx,nullptr,frame,outFile);
- // 设置输出参数
- out.width = ctx->width;
- out.height = ctx->height;
- out.pixFmt = ctx->pix_fmt;
- out.fps = ctx->time_base.num;
- end:
- inFile.close();
- outFile.close();
- av_packet_free(&pkt);
- av_frame_free(&frame);
- av_parser_close(parserCtx);
- avcodec_free_context(&ctx);
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |