QT中使用FFmpeg视频编码

视频编码

视频编码流程图

①对编码需要用到的数据进行初始化

②在编码类的构造函数中进行初始化,注册所有组件,开辟内存空间

④封装一个函数用于编码前的系列准备

猜测编码输出的格式是否存在并判断

打开视频文件流并判断是否打开成功

首先要设置输出格式

打开视频文件流

新建视频流

编码器的参数设置:格式、宽、高、码率、帧率等

根据猜测到的编码器的 ID 查找编码器并判断

找到之后,就可以打开编码器

编码器打开成功即可写入编码的头部信息,完成编码前的所有初始化工作

⑤封装一个编码的函数

首先发送一帧需要编码的像素数据给编码器并判断

.编码的一帧像素数据给编码器进行编码的时候,可能一个 AVPacket 放不下,就需要两个

AVPacket,循环处理去接收码流数据

⑥编码完成后,记得写入尾巴帧,结束此次的编码(并将需要释放的资源释放掉)

⑦应用

创建一个编码类对象

编码前准备

编码

写入尾巴帧

编码类package_ecoder代码

package_ecoder.h

#ifndef PACKAGE_ECODER_H
#define PACKAGE_ECODER_H
 
#include <QDebug>
#include <QImage>
#include <QThread>
#include "package_decoder.h"
 
extern "C"
{
    #include <libavcodec/avcodec.h>
    #include <libavdevice/avdevice.h>
    #include <libavformat/avformat.h>
    #include <libswresample/swresample.h>
    #include <libavfilter/avfilter.h>
    #include <libavutil/adler32.h>
    #include <libswscale/swscale.h>
}
class package_ecoder:public QThread
{
    Q_OBJECT
public:
    package_ecoder();
    void condeInit(int width,int height);
    void codecFrame(AVFrame * frame);
    void writeEnd();
 
    void run();
private:
    AVFormatContext * forContext;//保存视频信息结构体
    AVCodecContext * codecContext;//保存编辑器信息的一个上下文结构体
 
    AVOutputFormat * avformat;
    AVStream *newStream;
    AVCodec * codec;
    AVPacket *pkt;
    int pkt_index;
    int flag;
 
    AVFrame * yuv;
 
    package_decoder *dec1;
 
public slots:
    void recviceyuv(AVFrame * yuv);
    void endrecvice();
 
 
 
};
 
#endif // PACKAGE_ECODER_H

package_ecoder.cpp

​​​​​​​

#include "package_ecoder.h"
 
package_ecoder::package_ecoder()
{
    av_register_all();//注册组件
 
    forContext = avformat_alloc_context();//分配内存空间
    pkt = av_packet_alloc();//分配内存空间
    pkt_index=0;
    flag=0;
    dec1 = new package_decoder;
 
 
 
 
}
 
void package_ecoder::condeInit(int width, int height)
{
    char * fileout ="save.h264";
    //猜测编码器
    avformat =av_guess_format(nullptr,fileout,nullptr);
    if(avformat==nullptr)//判断有无数据文件(.h264)
    {
        qDebug()<<"没有数据文件";
        return;
    }
    //设置视频信息最终的输出格式
    forContext->oformat=avformat;
    //打开文件流(打开格式:写入的方式打开)
    int res=avio_open(&forContext->pb,fileout,AVIO_FLAG_WRITE);
    if(res<0)
    {
        qDebug()<<"打开文件流对象失败";
        return;
    }
    //新建视频流
    newStream=avformat_new_stream(forContext,nullptr);
    if(newStream==nullptr)
    {
        qDebug()<<"新建流失败";
        return;
    }
    //目标文件的设置已经完成
 
    //对编码器进行一系列设置
    //根据新建流去获取到编码器上下文对象结构体信息
    codecContext=newStream->codec;
    codecContext->width=width;
    codecContext->height=height;
    //编码格式的设置:使用的是yuv
    codecContext->pix_fmt=AV_PIX_FMT_YUV420P;
    //设置码率,码率不能太大,太大视频也会太大
    codecContext->bit_rate=400000;
    //设置帧率,每秒25张
    codecContext->time_base={1,25};
    //设置显示的帧率
    codecContext->framerate={25,1};
    //每10张图片为一组
    codecContext->gop_size=10;
    //影响视频的清晰度,越小越清晰
    codecContext->qmax=51;
    codecContext->qmin=10;
    //整个视频中没有b帧
    codecContext->max_b_frames=0;
    //设置流为视频流
    codecContext->codec_type=AVMEDIA_TYPE_VIDEO;
    //设置编码器的id
    codecContext->codec_id=avformat->video_codec;
 
 
    //根据编码器的id去查找有无编码器存在
    codec=avcodec_find_encoder(codecContext->codec_id);
    if(codec==nullptr)
    {
        qDebug()<<"没有找到编码器";
        return;
    }
    //打开编码器
    res=avcodec_open2(codecContext,codec,nullptr);
    if(res<0)
    {
        qDebug()<<"打开编码器失败";
        return;
    }
    //将编码相关的流信息写入到媒体文件中
    res=avformat_write_header(forContext,nullptr);
    if(res<0)
    {
        qDebug()<<"写入失败";
        return;
    }
    else
    {
        qDebug()<<"写入成功";
        return;
    }
 
}
 
void package_ecoder::codecFrame(AVFrame *frame)
{
   int res= avcodec_send_frame(codecContext,frame);
   if(res<0)
   {
       qDebug()<<"发送失败";
       qDebug()<<res;
       return;
   }
   else
   {
       qDebug()<<"发送成功";
       qDebug()<<res;
   }
   //进行编码的时候要注意:有可能一帧的像素数据压缩为一个AVpacket可能存不下
   while(res>=0)
   {
        frame->pts=pkt_index++;
        //接收编码之后的压缩码流数据
        res=avcodec_receive_packet(codecContext,pkt);
        if(res==AVERROR(EAGAIN)||res==AVERROR(EOF))
        {
            qDebug()<<"一帧的的像素数据没有存完";
        }
        else if(res<0)
        {
            qDebug()<<"失败了";
            return;
        }
        //设置当前这一帧是视频流
        pkt->stream_index=0;
        //将编码得到的码流数据写入文件中
        av_interleaved_write_frame(forContext,pkt);
   }
   //packet在使用一次之后记得要清空,便于写入第二帧
   av_packet_unref(pkt);
 
}
 
//写入尾巴帧
void package_ecoder::writeEnd()
{
    av_write_trailer(forContext);
}
 
void package_ecoder::run()
{
    while (1)
    {
 
        if(this->flag==0)
        {
                this->codecFrame(this->yuv);
        }
        else if(this->flag==1)
        {
            qDebug()<<"写入尾帧";
            this->writeEnd();
            break;
        }
 
    }
 
}
 
void package_ecoder::recviceyuv(AVFrame * yuv)
{
    qDebug()<<"找到yuv";
    this->yuv=yuv;
}
 
void package_ecoder::endrecvice()
{
    qDebug()<<"结束";
    this->flag=1;
}

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值