ffmpeg 学习:002-代码架构
前言
使用 ffmpeg 库时,最好先理解好ffmpeg的代码结构图。
下面这张图表明了FFmpeg在解码一个视频的时候的函数调用流程,为了保证结构清晰,其中仅列出了最关键的函数,剔除了其它不是特别重要的函数。
多媒体处理基本流程
只有真正了解了多媒体处理的基本流程,研读 ffmpeg 源代码才能事半功倍。
下面分析一下多媒体中最基本最核心的视频解码过程,平常我们从网上下载一部电影或者一首歌曲,那么相应的多 媒体播放器为我们做好了一切工作,我们只用欣赏就 ok 了。目前几乎所有的主流多媒体播放器都是基于开源多媒 体框架 ffmpeg 来做的,可见 ffmpeg 的强大。下面是对一个媒体文件进行解码的主要流程:
A(Media file) --> B(Demux, 解复用)
B(解码, Decode) --> C(YUV/RGB, 数据)
解复用(Demux)
当我们打开一个多媒体文件之后,第一步就是解复用,称之为 Demux。为什么需要这一步,这一步究竟是做什么的?
我们知道在一个多媒体文件中,既包括音频也包括视频,而且音频和视频都是分开进行压缩的,因为音频和视频的压缩算法不一样,既然压缩算法不一样,那么肯定解码也不一样,所以需要对音频和视频分别进行解码。虽然音频和视频是分开进行压缩的,但是为了传输过程的方便,将压缩过的音频和视频捆绑在一起进行传输。所以我们解码的第一步就是将这些绑在一起的音频和视频流分开来,也就是传说中的解复用:所以一句话,解复用这一步就是将文件中捆绑在一起的音频流和视频流分开来以方便后面分别对它们进行解码。
解码(Decode)
这一步不用多说,一个多媒体文件肯定是经过某种或几种格式的压缩的,也就是通常所说的视频和音频编码,编码是为了减少数据量,否则的话对我们的存储设备是一个挑战,如果是流媒体的话对网络带宽也是一个几乎不可能完 成的任务。所以我们必须对媒体信息进行尽可能的压缩。
FFmpeg 中解码流程对应的 API 函数
了解了上面的一个媒体文件从打开到解码的流程,就可以很轻松的阅读 ffmpeg 代码,ffmpeg 的框架也基本是按照这个流程来的,但不是每个流程对应一个 API,下面这副图是我分析 ffmpeg 并根据自己的理解得到的 ffmpeg 解码 流程对应的 API,我想这幅图应该对理解 ffmpeg 和编解码有一些帮助。
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-代码架构的更多相关文章
- FFmpeg学习5:多线程播放视音频
在前面的学习中,视频和音频的播放是分开进行的.这主要是为了学习的方便,经过一段时间的学习,对FFmpeg的也有了一定的了解,本文就介绍了 如何使用多线程同时播放音频和视频(未实现同步),并对前面的学习 ...
- 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程
原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html 关键词:蓝牙blueZ UART HCI_UART H4 HCI L2CAP ...
- JavaWeb学习之三层架构实例(三)
引言 通过上一篇博客JavaWeb学习之三层架构实例(二)我们基本上已经实现了对学生信息列表的增删改查操作(UI除外),但是不难看出,代码冗余度太高了,尤其是StudentDao这个类,其中的增删改查 ...
- ML平台_小米深度学习平台的架构与实践
(转载:http://www.36dsj.com/archives/85383)机器学习与人工智能,相信大家已经耳熟能详,随着大规模标记数据的积累.神经网络算法的成熟以及高性能通用GPU的推广,深度学 ...
- Libvirt代码架构
Libvirt介绍 参考资料 Libvirt学习 通过virsh了解libvirt api的调用方式 通过virHypervisorDriver了解libvirt api的实现 virsh代码阅读 通 ...
- (转)MyBatis框架的学习(二)——MyBatis架构与入门
http://blog.csdn.net/yerenyuan_pku/article/details/71699515 MyBatis框架的架构 MyBatis框架的架构如下图: 下面作简要概述: S ...
- FFmpeg 学习(五):FFmpeg 编解码 API 分析
在上一篇文章 FFmpeg学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编 ...
- FFmpeg 学习(七):FFmpeg 学习整理总结
一.FFmpeg 播放视频的基本流程整理 播放流程: video.avi(Container) -> 打开得到 Video_Stream -> 读取Packet -> 解析到 Fra ...
- 20145314郑凯杰 《Java程序设计》第2周学习总结 代码开始!
---恢复内容开始--- 20145314郑凯杰 <Java程序设计>第2周学习总结 代码开始! 教材学习内容总结 跟着教材的顺序开始总结我学过的内容: 1编辑.编译.运行教材上代码 这部 ...
- FFmpeg学习起步 —— 环境搭建
下面是我搭建FFmpeg学习环境的步骤. 一.在Ubuntu下 从http://www.ffmpeg.org/download.html下载最新的FFmpeg版本,我的版本是ffmpeg-2.7.2. ...
随机推荐
- CentOS7 安装 OpenCV 的一些问题解决办法
由于强迫症,实在受不了root权限的旧gcc才能使用boost而普通权限却是最新版gcc,经过一番折腾后,终于把配置全部弄好了,实际上就只需要把新版gcc的各个文件放到系统找到旧gcc的地方,并建立新 ...
- how to analyze jmeter results
https://octoperf.com/blog/2017/10/19/how-to-analyze-jmeter-results/
- Win Tomcat8 占用内存过高
1.解压版 找到tomcat/bin/catalina.bat 文件,修改对应参数 2.安装版 windows服务执行的是bin/tomcat.exe.他读取注册表中的值,而不是catalina.ba ...
- 小白笔记:Git入门之常见命令
安装 这里就不介绍安装了,度娘一大堆,找不到可以去找谷爹(前提是你能找到).安装好就跟着笔记进行下一步 准备工作 首先我们需要一个可以 git 的东西,所以我们需要一个文件夹和一个文件 创建文件夹 t ...
- c++中的运算符重载operator2(翁恺c++公开课[31-33]学习笔记)
上一篇operator1中,大概说了下重载的基本用法,接下来对c++中常见的可重载运算符归一下类,说一下它们的返回值,讨论下较为复杂的运算符重载上的坑
- 【PAT甲级】1034 Head of a Gang (30 分)
题意: 输入两个正整数N和K(<=1000),接下来输入N行数据,每行包括两个人由三个大写字母组成的ID,以及两人通话的时间.输出团伙的个数(相互间通过电话的人数>=3),以及按照字典序输 ...
- get方法和load方法的区别
get方法的特点 get方法采用的是立即检索策略(查询):执行到这行的时候,马上发送SQL查询 get方法查询后返回的是真实对象的本身 load方法的特点 load方法采用的是延 ...
- 使用taglib指令在jsp页面导入要使用的jstl标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 高版本的Hibernate
我的查询语句是“from TA pojo where pojo.tbs.name='tb1'”,可结果报错. 高版本的Hibernate不能这样查Set了, 要改成这样: from TA pojo i ...
- 安装pymongo
cd /usr/local/src wget --no-check-certificate https://pypi.python.org/packages/source/p/pymongo/pymo ...