【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】 音视频编码详细流程 的相关文章

  • 右键单击 QPushButton 上的 contextMenu

    对于我的应用程序 我在 Qt Designer 中创建了一个 GUI 并将其转换为 python 2 6 代码 关于一些QPushButton 与设计器创建 我想添加右键单击上下文菜单 菜单选项取决于应用程序状态 如何实现这样的上下文菜单
  • 另一个宏中的 Q_PROPERTY 宏

    如何放置Q PROPERTY另一个宏里面 辅助宏 define SimpleAllinOne member type public void Set member type arg member m member arg member ty
  • 如何将 QByteArray 转换为十六进制字符串?

    我有打击QByteArray QByteArray ba ba 0 0x01 ba 1 0x10 ba 2 0x00 ba 3 0x07 我真的不知道如何将此 QByteArray 转换为结果字符串 其中包含 01100007 我将使用 Q
  • 打开和关闭附加窗口 (QML)

    目前我有一个通过以下方式打开的窗口 property variant win Button id testButton MouseArea onClicked var component Qt createComponent test qm
  • 仅在内部/外部抚摸路径?

    Given a QPainterPath http qt project org doc qt 4 8 qpainterpath html如何仅在路径的内侧或外侧边缘 或非闭合路径的左侧或右侧 描边路径 QPainter strokePat
  • Qt 人工鼠标点击无法正常工作

    小玩具应用程序可以在这里找到 http gist github com 517445 http gist github com 517445 我正在尝试将人工鼠标事件发送到小部件 我使用QApplication sendEvent为此 接下
  • Qt qDebug() 在 Windows shell 中不起作用

    我正在使用一个qDebug Qt 框架的printf屏幕上有东西 当我从 Qt Creator 运行应用程序时它工作得很好 但是当我尝试从 Windows 执行它时cmd它什么也没显示 为什么会发生这种情况 你必须添加 CONFIG con
  • 用 C++/Qt 编写的程序中的 RTF / doc / docx 文本提取

    我正在写一些程序Qt https en wikipedia org wiki Qt 28software 29 C 我需要从中读取文本微软Word https en wikipedia org wiki Microsoft Word RTF
  • 在 QtCreator 中使用 .pro 和 .pri 正确(树)显示 Qt 项目

    是否可以使用项目包含文件 pri 显示不止一个额外级别 例如如果 pro文件包括 pri文件包含两个 pri files pro pri pri pri 在 QtCreator 项目 中显示具有误导性 pro pri pri pri 它根本
  • new 运算符(以及 malloc)无法分配约 450 MB 的内存 [重复]

    这个问题在这里已经有答案了 我正在开发一个程序 该程序在内存中存储大约 2 2 亿个短值的数组 该数据块的分配方式如下 short arrayName new short SIZE OF ARRAY 然后将文件的内容读入内存 在团队中的另一
  • 如何在 OS X 上的 Qt 应用程序中设置应用程序图标,足以进行分发?

    跟进这个答案 https stackoverflow com a 20918932 368896 to 这个问题 https stackoverflow com questions 20909341 what is the fastest
  • Retina 显示屏中具有 QOpenGLWIdget 的 Qt MainWindow 显示错误大小

    我有一个 Qt 应用程序MainWindow 我嵌入一个QOpenGLWidget在里面 一切正常 直到我开始使用 Apple Retina 显示屏并在高 DPI 模式下运行我的应用程序 我的QOpenGLWidget只是它应该具有的大小的
  • 完全彻底卸载QT Creator

    问题 如何从 Linux 机器上卸载 QT Creator 我的 Debian Jessie 机器上的安装已损坏 我尝试过重新安装 修复等 但没有成功 建议我完全卸载 获取最新版本并重新安装 问题是我不确定如何执行此操作 每次我尝试时 QT
  • 如何在Qt 5中的paintEvent上使用mouseMoveEvent?

    我是 Qt 和 c 的新手 所以我遇到了一些困难 我正在尝试创建一个小部件 它可以获取 mouseMoveEvent 位置并在鼠标位置的像素图上绘制椭圆 下面你可以看到代码 include myimage h include
  • C++ SQL 查询构建库

    我正在寻找一个提供与 c SelectQueryBuilder 库类似功能的 c 库 http www codeproject com Articles 13419 SelectQueryBuilder Building complex a
  • 使用 OpenGL 渲染 QImage

    与我相关的其他问题 https stackoverflow com questions 20126354 render qimage from sooffscreenrenderer in qglwidget 我认为更核心的问题是 如何渲染
  • PyQt4 QPalette 不工作

    btn QtGui QPushButton Button self palettes btn palette palettes setColor btn backgroundRole QtCore Qt green btn setPalet
  • 是否有 Qt 小部件可以浏览应用程序中小部件的层次结构(类似于 Spy++)?

    我们有一个具有复杂的小部件层次结构的应用程序 我希望能够以与 Spy 类似的方式浏览此层次结构 查看和编辑属性 例如大小 如果有一个小部件可以显示此信息 则它不需要在外部应用程序中运行 那么问题来了 这样的神兽存在吗 您可以使用Gammar
  • Qt中用于线程间通信的类设计

    问题陈述 用相机跟踪物体并相应地移动相机的方位角和仰角 Process 相机获取物体的图像 处理相机的每一帧以查找物体 应该被跟踪 并将每帧中生成的信息传递给机械设备 万向节 以平移和倾斜方式移动摄像机 Design 主 Gui 在一个线程
  • Qt 支持 Windows 蓝牙 API 吗?

    谁能告诉我 Qt 是否支持 Windows 蓝牙 API 如果是这样 您能否分享一些有关如何使用它的信息 自上次答复以来 这个问题的答案发生了一些变化 Qt 5 2 版为 Linux BlueZ 和 BlackBerry 设备实现了蓝牙 A

随机推荐

  • 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数据结构分析 七 编码视频数据 八 相关函数介绍 九 编码 代码具体实现 一 视频编码