浅显易懂 FFmpeg学习(05)— QT+FFmpeg编码(YUV编H.264)

  • 📢欢迎各位读者:点赞 👍 收藏 ⭐留言 📝
  • 📢博客主页:https://blog.csdn.net/qq_59134387😀
  • 📢原创不易,转载请标明出处;如有错误,敬请指正批评!💦
  • 📢我不去想是否能够成功,既然选择了远方,便只顾风雨兼程!✨


前言

   本篇,作者将分享如何使用FFmpeg对解码后的YUV数据编码成H.246格式视频。


一、编码流程

  • 编码流程可分为两大步骤:一是编码初始化,二是进行循环编码,如下图所示。
    在这里插入图片描述

二、具体实现

1.mp4视频解码为YUV数据

  • 在编码之前,我们需要将mp4视频解码为YUV数据,如何解码可查看以下博客。

  (1)浅显易懂 FFmpeg学习(02)— QT+FFmpeg解码本地视频(上篇)

  (2)浅显易懂 FFmpeg学习(03)— QT+FFmpeg解码本地视频(中篇)

  (3)浅显易懂 FFmpeg学习(04)— QT+FFmpeg解码本地视频(下篇)

  • 本次将采集本地摄像头数据进行编码,需要修改解封装函数的参数,如下图所示。
	//获取摄像头信息
	QList<QCameraInfo> cameras = QCameraInfo::availableCameras();   
	//数据缓冲区扩容
    AVDictionary* dict = NULL;
    av_dict_set(&dict, "rtbufsize", "655350000", 0);    
    //设置视频的推流格式
    AVInputFormat *ifmt=av_find_input_format("dshow");  
    //对视频流进行解封装
    avformat_open_input(&this->formatContext,"video="+cameras[0].description().toLatin1(),ifmt,&dict);

  获取本地摄像头方法可参考:【QT学习】如何获取本地摄像头信息?

  • 从mp4中读取数据帧,并转换为YUV帧
    //给缓冲区动态分配内存
    this->pOutbufferYUV = (uint8_t*)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P,this->codecContext->width,this->codecContext->height));

    //初始化缓冲区
    avpicture_fill((AVPicture *)this->frameYUV, this->pOutbufferYUV,AV_PIX_FMT_YUV420P, this->codecContext->width,this->codecContext->height);

    //设置转换格式
    this->swsYUV = sws_getContext(this->codecContext->width,this->codecContext->height,this->codecContext->pix_fmt,
                               this->codecContext->width,this->codecContext->height,AV_PIX_FMT_YUV420P,
                               SWS_BICUBIC,NULL,NULL,NULL);
    //帧数据转换
    sws_scale(this->swsYUV, (const unsigned char* const*)this->frame->data, this->frame->linesize, 0,
                              this->codecContext->height,this->frameYUV->data,this->frameYUV->linesize);
  • 将mp4视频读取到的帧转化为YUV数据帧,并发送至编码器进行编码
	//格式转换
	sws_scale(this->swsYUV, (const unsigned char* const*)this->frame->data, this->frame->linesize, 0,
                              this->codecContext->height,this->frameYUV->data,this->frameYUV->linesize);
    //编码一帧                
    this->videoEncode->codecFrame(this->frameYUV);		

2.编码类定义

class videoencode : public QObject
{
    Q_OBJECT
public:
    explicit videoencode(QObject *parent = 0);
    void codecInit();
    void codecFrame(AVFrame *frame);
    void writeTailer();
private:
    AVFormatContext *formatContext;
    AVOutputFormat *outputFormat;
    AVStream *newStream;
    AVCodecContext *codecContext;
    AVCodec *codec;
    AVPacket *packet;
    int pktIndex;
signals:

public slots:
};

3.编码初始化

(1)环境注册

    av_register_all();		//针对复用器、编码器等进行注册

(2)猜测并设置输出格式

	//封装格式上下文初始化
    this->formatContext = avformat_alloc_context();				

    //根据输出文件名,猜测并设置输出格式
    const char* out_file="./codeH264.h264";
    this->outputFormat = av_guess_format(NULL,out_file,NULL);
    if(this->outputFormat == NULL)
    {
        qDebug() << "猜测格式失败!";
    }
    
    //设置输出格式
    this->formatContext->oformat = this->outputFormat;			

(3)打开视频流文件

    ret = avio_open(&this->formatContext->pb,out_file,AVIO_FLAG_WRITE);
    if(ret < 0)
    {
        char *result = new char[64];
        av_strerror(ret,result,64);
        qDebug() << QString("打开视频流文件失败:%1").arg(result);
    }

(4)创建新视频流

    this->newStream = avformat_new_stream(this->formatContext,NULL);

(5)设置编码器参数

	this->codecContext = this->newStream->codec;                            //获取编码器上下文
    this->codecContext->width = 1280;                                       //设置编码器宽度
    this->codecContext->height = 720;                                       //设置编码器高度
    this->codecContext->bit_rate=400000;                                    //设置每一秒存的bit
    this->codecContext->time_base={1,25};                                   //设置帧率,25帧/秒
    this->codecContext->framerate={25,1};                                   //设置码率,25码/帧
    this->codecContext->gop_size=10;                                        //设置每组帧数,IPB帧(官方建议10帧一组)
    this->codecContext->qmax=51;                                            //设置最大量化值(影响视频清晰度)
    this->codecContext->qmin=10;                                            //设置最小量化值(影响视频清晰度)
    this->codecContext->max_b_frames=0;                                     //设置B帧为0,仅存在I帧和P帧
    this->codecContext->pix_fmt=AV_PIX_FMT_YUV420P;                         //设置编码格式YUV420P 像素数据
    this->codecContext->codec_type=AVMEDIA_TYPE_VIDEO;                      //设置流格式为视频流
    this->codecContext->codec_id=this->formatContext->oformat->video_codec; //设置编码器id(根据AVFormatContext)

(6)寻找合适的编码器

	this->codec = avcodec_find_encoder(this->codecContext->codec_id);
    if(this->codec == NULL)
    {
        qDebug() << "找不到合适的编码器!";
    }
    qDebug() << "寻找编码器成功!";

(7)打开编码器

	ret = avcodec_open2(this->codecContext,this->codec,NULL);
    if(ret < 0)
    {
        char *result = new char[64];
        av_strerror(ret,result,64);
        qDebug() << QString("打开编码器失败:%1").arg(result);
    }

(8)编写编码头部信息

    ret = avformat_write_header(this->formatContext,NULL);
    if(ret < 0)
    {
        char *result = new char[64];
        av_strerror(ret,result,64);
        qDebug() << QString("编写编码头部信息失败:%1").arg(result);
    }

(9)初始化数据包

	this->pktIndex = 0;
    this->packet = av_packet_alloc();

4.循环编码流程

(1)发送数据帧至编码器上下文

    int ret =  avcodec_send_frame(this->codecContext,frame);
    if(ret < 0)
    {
        char *result = new char[64];
        av_strerror(ret,result,64);
        qDebug() << QString("发送像素数据失败:%1").arg(result);
    }

(2)将编码器上下文循环打包

	while(ret >= 0)
    {
        //设置事件基,确保视频播放顺序
        frame->pts = this->pktIndex++;

        //从编码器上下文中接收数据包
        ret = avcodec_receive_packet(this->codecContext,this->packet);
        if(ret < 0)
        {
            qDebug()<<"打包完成!";
            return;
        }

        //设置数据包信息为视频流数据
        this->packet->stream_index = 0;

        //将数据包写入封装格式上下文(码流数据写入文件)
        av_interleaved_write_frame(this->formatContext,this->packet);

        //清空数据包
        av_packet_unref(this->packet);
    }

(3)编写编码尾部信息

    //写入编码的尾部信息
    av_write_trailer(this->formatContext);

    //关闭输入流
    avio_close(this->formatContext->pb);

    //释放视频信息
    avformat_free_context(this->formatContext);

三、实现效果

  • 在工程文件目录下,生成了后缀.h264文件,并且该文件有数据。
    在这里插入图片描述
  • 通过特定的播放器打开.h264文件进行播放,可以发现已经将摄像头推流数据编码为.h264文件。
    在这里插入图片描述

总结

   以上就是浅显易懂 FFmpeg学习(05)— QT+FFmpeg编码(YUV编H.264)的所有内容,希望大家阅读后都能有所收获!原创不易,转载请标明出处,若文章出现有误之处,欢迎读者留言指正批评!

在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤生i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值