当前位置: 首页 > article >正文

ffmpeg之yuv格式转h264

yuv转h264格式步骤

  1. 初始化FFmpeg库:通过 av_register_all() avcodec_register_all() 来初始化必要的组件。
  2. 查找编码器:使用 avcodec_find_encoder 查找H.264编码器。
  3. 配置编码上下文:使用avcodec_alloc_context3分配编码上下文,并设置必要的参数。
  4. 打开编码器:通过 avcodec_open2 打开编码器。
  5. 准备输入数据:从文件中读取YUV数据,并将其填充到 AVFrame 中。
  6. 编码帧:使用 avcodec_send_frame 发送帧到编码器,并通过 avcodec_receive_packet 获取编码后的数据包。
  7. 输出H.264数据:将编码后的数据包写入输出文件。
  8. 清理资源:释放所有分配的资源,确保没有内存泄漏。

详细代码如下:

#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);
}

http://www.kler.cn/a/469032.html

相关文章:

  • 面试经典150题——链表(二)
  • C++单例模式跨DLL调用问题梳理
  • 问题:Flask应用中的用户会话(Session)管理失效
  • SUB输入5V升压充电16.8V芯片HU5912
  • HarmonyOS:@Builder装饰器:自定义构建函数
  • Vue3+Element Plus的表格分页实战
  • 人工智能-Python网络编程-TCP
  • 数据库基础:SQL 与 NoSQL 的区别与应用场景
  • BERT的中文问答系统61
  • 桌面开发 的设计模式(Design Patterns)核心知识
  • Qt实现使用TCP与RS485串口设备通信————附带详细实践方法
  • KCP技术原理
  • HTML——77.网页编码及乱码处理
  • 深度学习之父
  • 会员制电商创新:开源 AI 智能名片与 2+1 链动模式的协同赋能
  • HTTP STATUS CODE详情,HTTP状态码大全列表
  • MYsql--------ubantu中安装mysql
  • 【顶刊TPAMI 2025】多头编码(MHE)之极限分类 Part 2:基础知识
  • 模型 九屏幕分析法
  • 麒麟服务器安装kafka--亲测
  • net core介绍
  • 六、Hadoop环境搭建之克隆虚拟机
  • go语言zero框架中教务crm系统的在职继承和离职交接的设计与实践
  • 求职:求职者在现场面试中应该注意哪些问题?
  • 设计模式学习[15]---适配器模式
  • visual studio 安全模式