ffmpeg之h264格式转yuv

打印 上一主题 下一主题

主题 844|帖子 844|积分 2542

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帧写入输出文件。
  • 清理资源:开释所有分配的资源,确保没有内存泄漏。
具体代码如下:
  1. #include "ffmpeg.h"
  2. #include <QFile>
  3. #include <QDebug>
  4. extern "C"
  5. {
  6. #include <libavcodec/avcodec.h>
  7. #include <libavutil/avutil.h>
  8. #include <libavutil/imgutils.h>
  9. }
  10. #define ERROR_BUF(ret) \
  11.     char errbuf[1024]; \
  12.     av_strerror(ret,errbuf,sizeof(errbuf));
  13. //输入缓冲区的大小
  14. #define IN_DATA_SIZE 4096
  15. ffmpegs::ffmpegs()
  16. {
  17. }
  18. static int decode(AVCodecContext *ctx,AVPacket *pkt,AVFrame *frame,QFile &outFile)
  19. {
  20.     //发送压缩数据到解码器
  21.     int ret = avcodec_send_packet(ctx,pkt);
  22.     if(ret < 0)
  23.     {
  24.         ERROR_BUF(ret);
  25.         qDebug() << "avcodec_send_packet error" << errbuf;
  26.         return ret;
  27.     }
  28.     while(true)
  29.     {
  30.         //获取解码后的数据
  31.         ret = avcodec_receive_frame(ctx,frame);
  32.         if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
  33.         {
  34.             return 0;
  35.         } else if (ret < 0)
  36.         {
  37.             ERROR_BUF(ret);
  38.             qDebug() << "avcodec_receive_frame error" << errbuf;
  39.             return ret;
  40.         }
  41.         // 将解码后的数据写入文件
  42.         //写入y平面
  43.         outFile.write((char *) frame->data[0], frame->linesize[0] * ctx->height);
  44.         //写入u平面
  45.         outFile.write((char *) frame->data[1], frame->linesize[1] * ctx->height >> 1);
  46.         //写入v平面
  47.         outFile.write((char *) frame->data[2], frame->linesize[2] * ctx->height >> 2);
  48.     }
  49. }
  50. void ffmpegs::h264Decode(const char *inFileName, videodecodeSpec &out)
  51. {
  52.     //返回结果
  53.     int ret = 0;
  54.     //用来存放读取的文件数据(h264)
  55.     //加上AV_INPUT_BUFFER_PADDING_SIZE是为了防止某些优化过的reader一次性读取过多导致越界
  56.     char inDataArray[IN_DATA_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
  57.     char *inData = inDataArray;
  58.     //每次从输入文件中读取的长度(h264)
  59.     //输入缓冲区中,剩下等待进行解码的有效数据长度
  60.     int inLen = 0;
  61.     int inEnd = 0;
  62.     //文件
  63.     QFile inFile(inFileName);
  64.     QFile outFile(out.filename);
  65.     //解码器
  66.     AVCodec *codec = nullptr;
  67.     //上下文
  68.     AVCodecContext *ctx = nullptr;
  69.     //编解码器上下文
  70.     AVCodecParserContext *parserCtx = nullptr;
  71.     //存放编码前的数据(h264)
  72.     AVPacket *pkt = nullptr;
  73.     //存放编码后的数据(yuv)
  74.     AVFrame *frame = nullptr;
  75.     //获取编码器
  76.     //codec = avcodec_find_decoder_by_name("h264");
  77.     codec = avcodec_find_decoder(AV_CODEC_ID_H264);
  78.     if(!codec)
  79.     {
  80.         qDebug() << "decodec not found";
  81.         return;
  82.     }
  83.     //初始化解析器上下文
  84.     //    parserCtx = av_parser_init(codec->id);//根据编码器ID进行初始化
  85.     parserCtx = av_parser_init(AV_CODEC_ID_H264);//根据编码器ID进行初始化
  86.     if(!parserCtx)
  87.     {
  88.         qDebug() << "av_parser_init error";
  89.         return;
  90.     }
  91.     //创建上下文
  92.     ctx = avcodec_alloc_context3(codec);
  93.     if(!ctx)
  94.     {
  95.         qDebug() << "avcodec_alloc_context3 error";
  96.         goto end;
  97.     }
  98.     //创建AVPacket
  99.     pkt = av_packet_alloc();
  100.     if(!pkt)
  101.     {
  102.         qDebug() << "av_packet_alloc error";
  103.         goto end;
  104.     }
  105.     //创建AVFrame
  106.     frame = av_frame_alloc();
  107.     if(!frame)
  108.     {
  109.         qDebug() << "av_frame_alloc error";
  110.         goto end;
  111.     }
  112.     //打开解码器
  113.     ret = avcodec_open2(ctx,codec,nullptr);
  114.     if(ret < 0)
  115.     {
  116.         ERROR_BUF(ret);
  117.         qDebug() << "avcodec_open2 error" << errbuf;
  118.         goto end;
  119.     }
  120.     //打开文件
  121.     if(!inFile.open(QFile::ReadOnly))
  122.     {
  123.         qDebug() << "file open error:" << inFileName;
  124.         goto end;
  125.     }
  126.     if(!outFile.open(QFile::WriteOnly))
  127.     {
  128.         qDebug() << "file open error:" << out.filename;
  129.         goto end;
  130.     }
  131.     //读取文件数据
  132.     do
  133.     {
  134.         inLen = inFile.read(inDataArray, IN_DATA_SIZE);
  135.         //设置是否到了文件尾部
  136.         inEnd = !inLen;
  137.         //让inData指向数组的首元素
  138.         inData = inDataArray;
  139.         //只要输入缓冲区中还有等待进行解码的数据
  140.         while(inLen > 0 || inEnd)
  141.         {
  142.             //经过解析器解析
  143.             //内部调用的核心是:ff_aac_ac3_parse
  144.             //到了文件尾部(虽然没有读取任何数据,但也要调用av_parser_parse2)
  145.             //经过解析器解析
  146.             ret = av_parser_parse2(parserCtx,ctx,&pkt->data,&pkt->size,
  147.                                    (uint8_t *)inData,inLen,
  148.                                    AV_NOPTS_VALUE,AV_NOPTS_VALUE,0);
  149.             if (ret < 0)
  150.             {
  151.                 ERROR_BUF(ret);
  152.                 qDebug() << "av_parser_parse2 error" << errbuf;
  153.                 goto end;
  154.             }
  155.             //跳过已经解析过的数据
  156.             inData += ret;
  157.             //减去已经解析过的数据大小
  158.             inLen -= ret;
  159.             qDebug() << inEnd << pkt->size << ret;
  160.             //解码
  161.             if(pkt->size > 0)
  162.             {
  163.                 if(decode(ctx,pkt,frame,outFile) < 0)
  164.                 {
  165.                     goto end;
  166.                 }
  167.             }
  168.             //如果到了文件末尾
  169.             if(inEnd) break;
  170.         }
  171.     }while(!inEnd);
  172.     //刷新缓冲区
  173.     //    pkt->data = nullptr;
  174.     //    pkt->size = 0;
  175.     //    decode(ctx,pkt,frame,outFile);
  176.     //刷新缓冲区
  177.     decode(ctx,nullptr,frame,outFile);
  178.     // 设置输出参数
  179.     out.width = ctx->width;
  180.     out.height = ctx->height;
  181.     out.pixFmt = ctx->pix_fmt;
  182.     out.fps = ctx->time_base.num;
  183. end:
  184.     inFile.close();
  185.     outFile.close();
  186.     av_packet_free(&pkt);
  187.     av_frame_free(&frame);
  188.     av_parser_close(parserCtx);
  189.     avcodec_free_context(&ctx);
  190. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

来自云龙湖轮廓分明的月亮

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表