FFMPEG音视频处理--FFMPEG编码

目录

一:视频编码流程

二:FFmpeg编码流程

三:FFmpeg编码函数 

四:FFmpeg编码的数据结构 

五:FFmpeg数据结构简介 

六:FFmpeg数据结构分析

七:编码视频数据

八:相关函数介绍

九:编码--代码具体实现

编码类定义如下

9.1 编码初始化

9.2 循环编码

9.3 尾部信息写入


一:视频编码流程

1.1 纯净的视频编码流程

像素数据->压缩编码数据。

例如编码YUV,就是“YUV->H.264”。

1.2 一般的视频编码流程

将像素流存储为一定封装格式(例如H264等)中。

例如将像素流编程成MP4格式,就是“YUV->H.264码流->MP4”。

二:FFmpeg编码流程

三:FFmpeg编码函数 

av_register_all():注册所有组件。
av_guess_format():已经注册的最合适的输出格式
avcodec_find_encoder():查找一个已经注册的音视频编码器
avcode_open2():打开编码码器
avformat_write_header():把流头信息写入到媒体文件中
av_read_frame():读取一帧压缩数据。
avcodec_send_frame():发送一帧像素数据
avcodec_receive_packet():接受一帧编码数据
av_packet_rescale_ts():时间基转换
av_write_frame():写一帧数据
flush_encoder():将最后一帧写入文件
av_write_trailer():把流尾信息写入文件
av_code_close():关闭流

四:FFmpeg编码的数据结构 

五:FFmpeg数据结构简介 

六:FFmpeg数据结构分析

七:编码视频数据

从摄像头获取,存储到本地流程:

1、摄像头数据 -> 像素数据->编码数据->.mp4文件

2、avPacket -> avFrame -> avPacket

八:相关函数介绍

九:编码--代码具体实现

编码类定义如下

#ifndef VIDEOCODE_H
#define VIDEOCODE_H

#include <QObject>

//当前C++兼容C语言
extern "C"
{
//avcodec:编解码(最重要的库)
#include <libavcodec/avcodec.h>
//avformat:封装格式处理
#include <libavformat/avformat.h>
//swscale:视频像素数据格式转换
#include <libswscale/swscale.h>
//avdevice:各种设备的输入输出
#include <libavdevice/avdevice.h>
//avutil:工具库(大部分库都需要这个库的支持)
#include <libavutil/avutil.h>
//#include<libavutil/imgutils.h>
//#include<libavutil/opt.h>
}

class videoCode : public QObject
{
    Q_OBJECT
public:
    explicit videoCode(QObject *parent = 0);

    //编码一帧图片的函数
    void codeingOneFrame(AVFrame *frame);
    AVOutputFormat *outputformat;
    //视频文件上下文格式
    AVFormatContext* avformat_context;
    //编解码器上下文格式
    AVCodecContext* avcodec_context;
    AVStream* outputstream;
    AVPacket* av_packet;

    //编码帧计数定义
    int pkt_index;
    //写入尾部信息
    void writeTrailer();


signals:

public slots:
};

#endif // VIDEOCODE_H

编码初始化+循环编码+尾部信息写入

9.1 编码初始化

videoCode::videoCode(QObject *parent) : QObject(parent)
{
    //循环编码前,需要对相关对象进行初始化操作
    qDebug()<<"1.编码--注册所有组件";
    //1.注册初始化
    av_register_all();
    //硬件设备初始化--设备上播放视频内容
    avdevice_register_all();

    //2.定义编码输出文件以及数据包
    QString outputfilename = "outfile.h264";
    av_packet = av_packet_alloc();

    //3.定义视频输入文件上下文
    avformat_context = avformat_alloc_context();
    if(nullptr == avformat_context)
    {
        qDebug()<<"视频封装器开辟失败";
    }

    //4.文件格式上下文猜测--av_guess_format  接收返回AVOutputFormat *outputformat;在头文件中定义
    outputformat = av_guess_format(nullptr,outputfilename.toStdString().c_str(),nullptr);

    //5.猜测的输出文件格式--把猜测得到的数据存入到视频文件上下文格式中
    avformat_context->oformat = outputformat;
    qDebug()<<"视频封装器初始化成功";

    //6.打开视频流--根据流信息找到编码器   int avio_open(AVIOContext **s, const char *url, int flags);
    int ret = avio_open(&avformat_context->pb,outputfilename.toStdString().c_str(),AVIO_FLAG_WRITE);
    if(ret < 0)
    {
        qDebug()<<"编码--打开视频文件失败";
    }

    //7.新建流信息操作--AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);
    //接收返回AVStream* outputstream;在头文件中定义
    outputstream = avformat_new_stream(avformat_context,nullptr);
    if(nullptr == outputstream)
    {
        qDebug()<<"编码--新建流信息失败";
    }

    //8.编码时间基设置--1秒编码25张图片--编码频率设置
    AVRational rate;
    rate.num = 1;
    rate.den = 25;
    outputstream->time_base = rate;

    //9.找到合适的编码器--根据不同文件格式的流信息找到合适编码器
    avcodec_context = outputstream->codec;

    //10.从上下文中获取编码参数--int avcodec_parameters_from_context(AVCodecParameters *par,const AVCodecContext *codec);
    avcodec_parameters_from_context(outputstream->codecpar,avcodec_context);

        qDebug()<<"----------------编码准备工作完成-----------------";
        pkt_index = 0;//编码帧计数的初始化
}

9.2 循环编码

void videoCode::codeingOneFrame(AVFrame *frame)
{
    //1.对一帧图片进行处理--包括有图片大小 帧率
    frame->pts = pkt_index++;
    //2.默认的参数配置
    /*除了可以得到视频编解码的相关信息,编码的话还需要设置很大的参数
     首先是它的宽度和高度*/
    avcodec_context->width = frame->width;
    avcodec_context->height = frame->height;
    //设置码率,每一秒存的比特,这个值的设置也不要随意,码率太大,视频也会变大
    avcodec_context->bit_rate = 400000;
    //设置帧率,每一秒多少张图片 -- 25张 把1秒分成了25等分
    avcodec_context->time_base = {1,25};
    //设置显示的率,也叫码率
    avcodec_context->framerate = {25,1};
    /*设置每一组的图片数量,IPB帧 I帧存一帧的所有数据,p帧根据I解码,B帧根据前后的两帧解码 10帧为1组
      后面的10帧解码不会与前10帧关联*/
    avcodec_context->gop_size = 10;//官方建议10帧为1单位
    //还有两个量化值需要设置,会影响视频的清晰度,越小越清晰,建议默认就可以了
    avcodec_context->qmax = 51;
    avcodec_context->qmin = 10;
    //设置一下b帧为0,这样的话就只有I帧和p帧
    avcodec_context->max_b_frames = 0;
    //设置编码格式--YUV420P像素数据
    avcodec_context->pix_fmt = AV_PIX_FMT_YUV420P;
    //设置流的格式:视频流还是音频流--视频流
    avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO;
    //设置编码器的id,根据匹配到的AVOutputFormat对应信息来设置
    avcodec_context->codec_id = outputformat->video_codec;

    //3.寻找合适的编码器--AVCodec *avcodec_find_encoder(enum AVCodecID id);
    AVCodec *avcodec = avcodec_find_encoder(avcodec_context->codec_id);
    if(nullptr == avcodec_context)
    {
        qDebug()<<"编码器查找失败";
    }
    qDebug()<<"编码器查找成功";

    //4.打开编码器--int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
    if(avcodec_open2(avcodec_context,avcodec,nullptr) < 0)
    {
        qDebug()<<"编码器打开失败";
    }

    //5.写封装格式头部信息--int avformat_write_header(AVFormatContext *s, AVDictionary **options);
    if(avformat_write_header(avformat_context,nullptr) < 0)
    {
        qDebug()<<"写入编码头失败";
    }

    //6.循环写入编码图片数据--发送一帧数据,编码一帧数据
    //int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);
    int ret = avcodec_send_frame(avcodec_context,frame);
    if(ret != 0)
    {
        qDebug()<<"发送一帧数据进行编码";
    }
    if(ret >= 0)
    {
        //接收一帧数据进行编码 从帧收到包--int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
        ret = avcodec_receive_packet(avcodec_context,av_packet);
        if(ret != 0)
        {
            qDebug()<<"编码异常";
        }
        //写入包数据--int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
        av_interleaved_write_frame(avformat_context,av_packet);
    }
}

9.3 尾部信息写入

void videoCode::writeTrailer()
{
    //写入尾部信息
    av_write_trailer(avformat_context);
    //关闭文件指针
    avio_close(avformat_context->pb);
    avformat_free_context(avformat_context);
}
    pvideoCode = new videoCode(); //编码环境初始化
 //解码得到的--分两部分走
                    //一部分做显示  发送信号(图片)--emit
                    emit sigGetOneFrame(image);
                    //一部分做编码
                    //循环 编码每一帧数据  传入pFramein
                    pvideoCode->codeingOneFrame(pFramein);
void videoDecodeThread::videoStop()
{
    //停止线程--改变标志位
    m_stop = true;

    //写编码尾部信息 手动关闭xxx.h264这个文件指针
    pvideoCode->writeTrailer();
}

结果测试:

播放视频,解码的同时,一部分进行显示,一部分进行编码

在bin文件下可查看到编码所得到的.h264文件

将.h264文件拖至软件中可查看视频播放信息

 

 

猜你喜欢

转载自blog.csdn.net/m0_56051805/article/details/125139436