【FFmpeg】 音视频编码详细流程

2023-11-05

目录

一:视频编码流程

二:FFmpeg编码流程

三:FFmpeg编码函数 

四:FFmpeg编码的数据结构 

五:FFmpeg数据结构简介 

六:FFmpeg数据结构分析

七:编码视频数据

八:相关函数介绍

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


一:视频编码流程

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

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

编码初始化

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;//编码帧计数的初始化
}

循环编码

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

尾部信息写入

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文件拖至软件中可查看视频播放信息

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【FFmpeg】 音视频编码详细流程 的相关文章

  • 常量类成员、赋值运算符和 QList

    请确认我是否正确并告诉我是否有更好的解决方案 我了解具有常量成员的对象 例如int const width 无法由编译器隐式创建的合成赋值运算符处理 但是 QList 我想 std list 也是如此 需要一个有效的赋值运算符 因此 当我想
  • QFileInfo::isWritable() 在 Windows 7 上具有完全权限时返回 false

    QFile file filePath QFileInfo fileInfo file file open QIODevice ReadWrite or WriteOnly if fileInfo isWritable Log error
  • QWebSocketServer - 不释放内存

    首先 我在安全 websocket 服务器应用程序上运行 valgrind 并发现了一个问题 在 Qt Memcheck 中我必须检查 外部错误 看到它 一些字节是肯定输了 指着我的main就在我的地方QCoreApplication ex
  • 如何从 Qt 应用程序通过 ODBC 连接到 MySQL 数据库?

    我有一个新安装的 MySQL 服务器 它监听 localhost 3306 从 Qt 应用程序连接到它的正确方法是什么 原来我需要将MySQL添加到ODBC数据源 我在遵循这个视频教程后做到了这一点 https youtu be K3GZi
  • 如何在 Qt simple 上解密/加密某些字符串(例如密码)

    这是我得到的 Qt SDK版本4 6 2 视窗XP 问题 我怎样才能简单地加密和简单地加密QString价值 我需要它能够将一些加密的字符串保存到 INI 文件中 并在重新打开应用程序后将字符串加密为正常的密码字符串值 PS 我正在寻找简单
  • 如何在不同的QT线程中创建一个窗口?

    我有一个应用程序 其中每个线程 主线程除外 都需要创建自己的窗口 我尝试创建一个线程然后调用this gt exec in the run功能 然而 在我接到那个电话之前我就收到了一个错误 ASSERT failure in QWidget
  • 在Qt中,如何正确实现委托?

    我遵循模型 视图 控制器范例 我很确定模型和视图是正确的 但我认为我的代表做错了一些事情 一切都 有效 除了第一次单击控件只是 点亮控件 而第二次单击与之交互之外 这是代表通常的实施方式吗 我的实现需要大量的构造和销毁 由scoped pt
  • 在 Windows 上从源代码构建 PhantomJS-2

    我正在尝试基于这些在 Windows 8 1 x64 上从源代码构建 PhantomJS 2 的开发版本指示 https github com ariya phantomjs wiki PhantomJS 2 但是我收到以下错误 mingw
  • QT C++ QRegularExpression 多个匹配

    我想使用正则表达式从 QString html 中提取信息 我明确想使用正则表达式 无解析器解决方案 和类Q正则表达式 http qt project org doc qt 5 0 qtcore qregularexpression htm
  • 运行最新版本时没有“最新”消息?

    我正在尝试使用Sparkle https sparkle project org与 Qt Go 的绑定 https github com therecipe qt app 闪光 m import
  • 如何使用bind将成员函数作为函数指针传递?

    我试图将成员函数作为函数指针传递 这样我就不需要依赖单例或全局函数来处理 Qt 5 中的 Qt 消息 据我所知 我的 std function 是正确的类型 它具有正确的签名 并且绑定应该允许我插入隐式this指针 本质上是将成员函数传递为
  • 构建qt程序时未定义的符号:找不到qt_version_tag

    我正在学习Qt5 6 我正在使用 Ubuntu 14 4 当我链接我的程序时 出现以下错误 undefined reference to qt version tag 在 CMakeLists txt 中 link libraries Qt
  • Qt - QProcess 不工作

    我尝试启动 Internet Explorer 所以我使用下面的代码 QProcess process new QProcess this QString temp C Program Files Internet Explorer iex
  • Qmake 不支持源目录下的构建目录

    我创建了一个可以在 OS X 上编译和运行的应用程序 我现在想开始让它在 Windows 上运行 首先 我将项目复制到 Windows 机器上并尝试编译 但收到此错误 警告 Qmake不支持源目录下的构建目录 有任何想法吗 将影子构建目录设
  • 如何在Qt中更快地读取数据?

    Qt读取数据库比C 慢吗 我想我错过了一些东西 为了比较阅读速度 我在 Qt 中编写了以下内容 QElapsedTimer t t start int count 0 QString cs Driver SQL Server Server
  • 如何使用样式表删除 QWizard 中的水平线?

    我正在研究一个样式表QWizard我想删除按钮上方的水平线 我已经发布了一个最小的例子here https stackoverflow com q 52538669 8570451 这个问题是由scopchanov从最小的例子中解决的 但是
  • 使用信号和槽更新指针

    我对 Qt 很陌生 请帮我解决这个问题 我正在使用线程在后台执行密集操作 同时我想更新 UI 所以我使用 SIGNALS 和 SLOTS 为了更新 UI 我发出一个信号并更新 UI 让我们考虑下面的示例代码 struct sample QS
  • 如何阻止 Qt Creator 将可执行文件放置在“调试”子目录中?

    我正在 Qt Creator 中构建一个项目 虽然我不关心中间 obj 文件去哪里 但重要的是最终的可执行文件应放入 并运行 许多依赖项 DLL 等所在的特定目录中 被发现 因此 在 Qt Creator 中 我选择 Shadow Buil
  • 如何从 ffmpeg 中打开的文件获取流信息?

    我正在尝试使用 ffmpeg 读取视频文件 我有与其旧版本相对应的工作代码 并开始尝试升级到最新的构建版本 将所有这些已弃用的函数替换为其实际的类似函数 但是我遇到了问题 似乎没有检索到任何流 并且视频负载停止在轨道中 这是我正在使用的代码
  • 更改显示的 DPI 缩放大小使 Qt 应用程序的字体大小渲染得更大

    我使用 Qt 创建了一些 GUI 应用程序 我的 GUI 应用程序包含按钮和单选按钮等控件 当我运行应用程序时 按钮内的按钮和字体看起来正常 当我将显示器的 DPI 缩放大小从 100 更改为 150 或 200 时 无论分辨率如何 控件的

随机推荐

  • AIGC的1000+篇文章总结

    AIGC的1000 篇文章总结 本文收集和总结了有关AIGC的1000 篇文章 由于篇幅有限只能总结近期的内容 想了解更多内容可以访问 http www ai2news com 其分享了有关AI的论文 文章 图书 query AIGC AI
  • vue中的函数式组件

    用过react的同学都知道 函数式组件在react中的应用是很流行的 那如何在vue中使用函数式组件呢 什么是函数式组件 熟悉react的同学应该都知道 react中的函数式组件其实就是一个接收一些prop的函数 然后返回HTML vue的
  • Active Directory配置与应用

    Active Directory 配置与应用
  • 菜鸟学习nodejs--回调函数

    什么是回调函数 如果大家使用过JQuery 那么会掉函数就像家常便饭一样 例如我们经常会给一个事件传一个function的参数 其实这就是回调函数 回调函数就是我们所说的异步 如果还是有点蒙 那么我们来举个例子 我们新建一个index1 h
  • 基于混沌系统和DNA算法的RGB图像加密(Matlab代码实现)

    目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 1 概述 本文介绍了基于混沌系统和DNA编码的彩色数字图像加密 解密 抗噪声性能分析以及抗裁剪性能分析 2 运行结果 3 参考文献 1 李红凯 基于混沌理论和DNA序列编码的
  • Qt之点击QLineEdit显示软键盘

    点击QLineEdit显示软键盘 在嵌入式开发时 不能通过鼠标键盘输入是很常见的 这时候就需要通过软件层 实现软键盘和输入法来满足用户需求 一般来说 软键盘的显示通常和QLineEdit的点击事件挂钩 而QLineEdit与QPushBut
  • opencv之透视变换

    透视变换 Perspective Transformation 是将图片投影到一个新的视平面 Viewing Plane 也称作投影映射 Projective Mapping 原理 通用的变换公式为 u v是原始图片左边 对应得到变换后的图
  • VUE嵌套路由导致父组件重复渲染BUG(虚惊一场)

    哲神最近在做VUE开发 开发一个模块需要用到嵌套路由 路由如下 path dashboard component gt import views dashboard index name dashboard meta title 数据看板
  • 详解域名和DNS

    目录 一 概念名词 1 域名 1 1域名 1 2二级域名和多级域名 1 3域名对于我们有什么用呢 2 DNS Domain Name System 域名系统 2 1 什么是域名系统 3 CDN Content Delivery Networ
  • golang操作MySQL的具体案例

    golang操作MySQL的具体案例 代码篇的基础操作 package main import fmt github com go sql driver mysql github com jmoiron sqlx type Profile
  • 云服务器只能显示控制台吗,云服务器控制台使用方法

    云服务器控制台使用方法 内容精选 换一换 用户在购买弹性云服务器时会选择弹性云服务器的规格及登录方式 如果选择密钥对登录方式 需要选择已有密钥对或创建新的密钥对 如果没有可用的密钥对 请在控制台创建新的密钥对进行使用 在云服务器控制台左侧导
  • Pytorch(GPU)配环境原理:cuda+cudnn+pytorch配环境的每一步到底干了些什么?

    作者 18届cyl 时间 2022 5 11 参考文章 https blog csdn net qq 42406643 article details 109545766 最近帮舍友配pytorch cuda cudnn环境的时候 回想起来
  • 《剑指offer》 java版本 牛客网全AC

    1 二维数组中的查找 public class Solution public boolean Find int target int array if array length 0 array 0 length 0 return fals
  • C# 利用反射实现消息转发处理

    C 利用反射实现消息转发处理 一 C 反射 二 代码 三 总结 一 C 反射 利用C 反射的特点 我们可以实现函数名来动态调用函数 通过拼接字符串达到消息处理的效果 配合xml可以实现流程可配置化 二 代码 以下是一个简单的示例 using
  • 我正在建造一座大教堂

    有个人经过一个建筑工地 问那里的石匠们在干什么 三个石匠有三个不同的回答 第一个石匠回答 我在做养家糊口的事 混口饭吃 第二个石匠回答 我在做整个国家最出色的石匠工作 第三个石匠回答 我正在建造一座大教堂 赵日磊点评 三个石匠的回答给出了三
  • axios的get、post请求params、data传参总结

    文章原文https www jianshu com p 7a24b5eed364 分类 get请求 params 基础类型接受 名字对应即可 method const params id 123456789 name 张三 test par
  • QMessageBox 设置中文按钮(返回值判断)

    QMessageBox 设置中文按钮 返回值判断 一 解决乱码 pragma execution character set UTF 8 二 按钮修改为中文 方法一 创建一个question弹出对话框 添加两个按钮 Yes和No QMess
  • C语言:指针和数组(看完拿捏指针和数组)

    目录 数组名的理解 一维数组 解析 字符数组 解析 解析 字符串数组 解析 解析 一级指针 解析 解析 二维数组 解析 指针笔试题 题一 一维数组 题二 结构体指针 题三 一维数组 题四 二维数组 题五 二维数组 题六 二维数组 题七 指针
  • python3中argparse模块详解

    python标准库sys模块 sys模块 用于提供对Python解释器相关的操作 sys argv 命令行参数List 第一个元素是程序本身路径 sys exit n 退出程序 正常退出时exit 0 sys version 获取Pytho
  • 【FFmpeg】 音视频编码详细流程

    目录 一 视频编码流程 二 FFmpeg编码流程 三 FFmpeg编码函数 四 FFmpeg编码的数据结构 五 FFmpeg数据结构简介 六 FFmpeg数据结构分析 七 编码视频数据 八 相关函数介绍 九 编码 代码具体实现 一 视频编码