前言

使用 ffmpeg 库时,最好先理解好ffmpeg的代码结构图。

下面这张图表明了FFmpeg在解码一个视频的时候的函数调用流程,为了保证结构清晰,其中仅列出了最关键的函数,剔除了其它不是特别重要的函数。

多媒体处理基本流程

只有真正了解了多媒体处理的基本流程,研读 ffmpeg 源代码才能事半功倍。
下面分析一下多媒体中最基本最核心的视频解码过程,平常我们从网上下载一部电影或者一首歌曲,那么相应的多 媒体播放器为我们做好了一切工作,我们只用欣赏就 ok 了。目前几乎所有的主流多媒体播放器都是基于开源多媒 体框架 ffmpeg 来做的,可见 ffmpeg 的强大。下面是对一个媒体文件进行解码的主要流程:

graph TD
A(Media file) --> B(Demux, 解复用)
B(解码, Decode) --> C(YUV/RGB, 数据)

解复用(Demux)

当我们打开一个多媒体文件之后,第一步就是解复用,称之为 Demux。为什么需要这一步,这一步究竟是做什么的?
我们知道在一个多媒体文件中,既包括音频也包括视频,而且音频和视频都是分开进行压缩的,因为音频和视频的压缩算法不一样,既然压缩算法不一样,那么肯定解码也不一样,所以需要对音频和视频分别进行解码。虽然音频和视频是分开进行压缩的,但是为了传输过程的方便,将压缩过的音频和视频捆绑在一起进行传输。所以我们解码的第一步就是将这些绑在一起的音频和视频流分开来,也就是传说中的解复用:所以一句话,解复用这一步就是将文件中捆绑在一起的音频流和视频流分开来以方便后面分别对它们进行解码。

解码(Decode)
这一步不用多说,一个多媒体文件肯定是经过某种或几种格式的压缩的,也就是通常所说的视频和音频编码,编码是为了减少数据量,否则的话对我们的存储设备是一个挑战,如果是流媒体的话对网络带宽也是一个几乎不可能完 成的任务。所以我们必须对媒体信息进行尽可能的压缩。

FFmpeg 中解码流程对应的 API 函数
了解了上面的一个媒体文件从打开到解码的流程,就可以很轻松的阅读 ffmpeg 代码,ffmpeg 的框架也基本是按照这个流程来的,但不是每个流程对应一个 API,下面这副图是我分析 ffmpeg 并根据自己的理解得到的 ffmpeg 解码 流程对应的 API,我想这幅图应该对理解 ffmpeg 和编解码有一些帮助。

graph TD
A[Media file 容器] -->|avformat_open_input| B{Video Strems}
A[Media file 容器] --> C{Audio Strems}
A[Media file 容器] --> |demux|D{Subtitle Strems}

B{Video Strems} --> |av_read_frame| E[数据流的数据包]

E[数据流的数据包]-->|avcodec_decode_video2| 解码之后的视频帧

C{Audio Strems} --> F[音频流的数据包]

F[音频流的数据包]-->|avcodec_decode_audio4| 解码之后的音频帧

D{Subtitle Strems} --> G[字幕流的数据包]

G[字幕流的数据包]-->|avcodec_decode_subtitttle2,Decode| 解码之后的字幕

Ffmpeg 中 Demux 这一步是通过 avformat_open_input()这个 api 来做的,这个 api 读出文件的头部信息,并做 demux, 在此之后我们就可以读取媒体文件中的音频和视频流,然后通过 av_read_frame()从音频和视频流中读取出基本数据 流 packet,然后将 packet 送到 avcodec_decode_video2()和相对应的 api 进行解码。

流媒体数据流程讲解

FFMpeg 的 output_example.c 例子分析

该例子讲了如何输出一个 libavformat 库所支持格式的媒体文件。

(1)av_regis ter_all(),初始化 libavcodec 库,并注册所有的编解码器和格式。

(2)guess_form at(),根据文件名来获取输出文件格式,默认为 mpeg。

(3)av_alloc_form at_context()分配输出媒体内容。 ov->oform at = fm t; s nprintf( oc->filename, sizeof(oc->filename), “%s ”, filenam e );

(4)add_video_s tream ()使用默认格式的编解码器来增加一个视频流,并初始化编解码器。 (4.1)av_new_s tream ()增加一个新的流到一个媒体文件。

(4.2)初始化编解码器: c = s t->codec; c->codec_id = codec_id; c->codec_type = CODEC_TYPE_ VIDEO; c->bit_rate = 400000; c->width = 352; c->height = 288; c->tim e_base.den = STREAM_FR AME_RATE ; //每秒 25 副图像 c->tim e_base.num = 1; c->gop_size = 12; c->pix_fm t = STREAM_PIX_FMT; //默认格式为 P IX_FMT_ YUV420P …… ……

(5)av_s et_parameters ()设置输出参数,即使没有参数,该函数也必须被调用。

(6)dum p_form at()输出格式信息,用于调试。

(7)open_video()打开视频编解码器并分配必要的编码缓存。

(7.1)avcodec_find_encoder()寻找 c->codec_id 指定的视频编码器。

(7.2)avcodec_open()打开编码器。

(7.3)分配视频输出缓存: video_outbuf_s ize = 200000; video_outbuf = av_m alloc( video_outbuf_s ize );

(7.4)picture = alloc_picture()分配原始图像。

(7.4.1)avcodec_alloc_frame()分配一个 AVFram e 并设置默认值。

(7.4.2)s ize = avpicture_get_s ize()计算对于给定的图片格式以及宽和高,所需占用多少 内存。

(7.4.3)picture_buf = av_malloc( s ize )分配所需内存。

(7.4.4)avpicture_fill()填充 AVPicture 的域。

(7.5)可选。如果输出格式不是 YUV420P,那么临时的 YU V420P 格式的图像也是需要的,由 此再转换为 我们所需的格式,因此需要为临时的 YU V420P 图像分配缓存: tm p_picture = alloc_picture() 说明:tm p_picture,picture,video_outbuf。如果输出格式为 YUV420P,则直接通过 avcodec_ encode_video()函数将 picture 缓存中的原始图像编码保存到 video_outbuf 缓存中;如果输出格式不 是 YU V420P,则需要先通过 sws _s cale()函数,将 YUV420P 格式转换为目标格式,此时 tm p_picture 缓 存存放的是 YU V420P 格式的图像,而 picture 缓存为转换为目标格式后保存的图像,进而再将 picture 缓 存中的图像编码保存到 video_outbuf 缓存中。

(8)url_fopen()打开输出文件,如果需要的话。

(9)av_write_header()写流动头部。

(10)LOOP 循环{ 计算当前视频时间 video_pts 是否超时退出循环? write_video_fram e()视频编码 }

(10.1)write_video_frame() 如果图片不是 YU V420P,则需要用 sws _s cale()函数先进行格式转换。 若需要原始图像: av_init_packet()初始化一个包的选项域。 av_write_fram e()向输出媒体文件写一个包,该包会包含一个视频帧。 若需要编码图像: avcodec_encode_video()编码一视频帧。 av_init_packet() av_write_fram e()

(11)close_video()关闭每个编解码器。

(12)av_write_trailer()写流的尾部。

(13)释放资源 av_freep()释放 AVForm atContext 下的 AVS tream ->AVCodecContext 和 AVStream : for( i = 0; i < oc->nb_s treams ; i++ ){ av_freep( &oc->s treams [i]->codec ); av_freep( &oc->s treams [i] ); } url_fclose()关闭输出文件。 av_free()释放 AVForm atContext

ffmpeg 学习:002-代码架构的更多相关文章

  1. FFmpeg学习5:多线程播放视音频

    在前面的学习中,视频和音频的播放是分开进行的.这主要是为了学习的方便,经过一段时间的学习,对FFmpeg的也有了一定的了解,本文就介绍了 如何使用多线程同时播放音频和视频(未实现同步),并对前面的学习 ...

  2. 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程

    原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html 关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP ...

  3. JavaWeb学习之三层架构实例(三)

    引言 通过上一篇博客JavaWeb学习之三层架构实例(二)我们基本上已经实现了对学生信息列表的增删改查操作(UI除外),但是不难看出,代码冗余度太高了,尤其是StudentDao这个类,其中的增删改查 ...

  4. ML平台_小米深度学习平台的架构与实践

    (转载:http://www.36dsj.com/archives/85383)机器学习与人工智能,相信大家已经耳熟能详,随着大规模标记数据的积累.神经网络算法的成熟以及高性能通用GPU的推广,深度学 ...

  5. Libvirt代码架构

    Libvirt介绍 参考资料 Libvirt学习 通过virsh了解libvirt api的调用方式 通过virHypervisorDriver了解libvirt api的实现 virsh代码阅读 通 ...

  6. (转)MyBatis框架的学习(二)——MyBatis架构与入门

    http://blog.csdn.net/yerenyuan_pku/article/details/71699515 MyBatis框架的架构 MyBatis框架的架构如下图: 下面作简要概述: S ...

  7. FFmpeg 学习(五):FFmpeg 编解码 API 分析

    在上一篇文章 FFmpeg学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编 ...

  8. FFmpeg 学习(七):FFmpeg 学习整理总结

    一.FFmpeg 播放视频的基本流程整理 播放流程: video.avi(Container) -> 打开得到 Video_Stream -> 读取Packet -> 解析到 Fra ...

  9. 20145314郑凯杰 《Java程序设计》第2周学习总结 代码开始!

    ---恢复内容开始--- 20145314郑凯杰 <Java程序设计>第2周学习总结 代码开始! 教材学习内容总结 跟着教材的顺序开始总结我学过的内容: 1编辑.编译.运行教材上代码 这部 ...

  10. FFmpeg学习起步 —— 环境搭建

    下面是我搭建FFmpeg学习环境的步骤. 一.在Ubuntu下 从http://www.ffmpeg.org/download.html下载最新的FFmpeg版本,我的版本是ffmpeg-2.7.2. ...

随机推荐

  1. 后端——框架——持久层框架——Mybatis——《Mybatis从入门到精通》读书笔记——初篇

    1.Mybatis知识点 框架的知识点大致可以分为三个部分 基础: 介绍编写增,删,改,查: 动态标签: config配置文件 Mapper配置文件 插件:常见的插件有三个 pageHelper:分页 ...

  2. iOS 开发之 RunLoop 详解

    1)什么是 Runloop ? 1.字面上是运行循环,内部就是 do-while 循环,在这个循环内不断地处理各种任务. 2.一个线程对应一个 Runloop ,主线程的 RunLoop 默认是开启的 ...

  3. 第二节: Vuejs常用特性1

    一. 常用特性 1. 表单元素 通过 v-model指令绑定 输入框.单选/多选框.下拉框.文本框 2. 表单域修饰符 (1) .number:转换成数值,如果输入的是非数字字符串时,无法进行转换 ( ...

  4. Nginx禁止使用ip访问,只允许使用域名访问

    Nginx虚拟主机配置,vhosts下面有很多域名的配置: [root@external-lb01 vhosts]# pwd/data/nginx/conf/vhosts [root@external ...

  5. layer 当前页获取iframe页的DOM元素

    layer.layui  开启iframe 之后,获取iframe 内容做自定义处理. parent.layer.open({ type: , title: '任務執行狀況.', shadeClose ...

  6. 最全Pycharm教程(39)——Pycharm版本控制之本地Git用法

    1.主题 介绍如果通过Pycharm使用本地Git集. 2.准备工作 (1)PyCharm版本为2.7或更高 (2)已经创建一个工程 (3)Git插件可用,对应可执行文件在 Git page页面正确配 ...

  7. Legal High

    不让任何人承担责任,不想看的东西就回避, 但是,如果想夺回值得夸耀的生存方式,就必须看那些不愿意看的现实,必须带着身负重伤的觉悟前进,这才叫做战斗. 有怨言的话去坟墓里说,钱不是全部,钱就是你们向对手 ...

  8. 十二 Spring的AOP开发入门,整合Junit单元测试(AspectJ的XML方式)

    创建web项目,引入jar包 引入Spring配置文件

  9. Win10子系统Ubuntu安装nginx (win10 安装 nginx)

    更新仓库,下载nginx: sudo apt update sudo apt install nginx 检查版本: nginx –v 启动服务: sudo  nginx sudo  service ...

  10. 帆软FineReport报表由于使用HTML显示后无法控制行高

    问题:帆软FineReport报表由于使用HTML显示后无法控制行高. 原因:首先每行的第一个单元格是以HTML显示的,然后,数据库查询的数据集中,sql语句中包含这个代码:'<pre>' ...