FFMpeg分析详细分析

与其说是分析,不如说是学习,只是看在自己第一次写系列文章的份上,给足自己面子,取个有"深度"的题目!如有人被题目所蒙骗进来,还望见谅!

      URLProtocol,URLContext和ByteIOContext是FFMpeg操作文件(即I/O,包括网络数据流)的结构,这几个结构现实的功能类似于C++的多态继承吧,C++的多态是通过子类继承实现,而FFMpeg的“多态”是通过静态对像现实。这部分的代码非常值得C程序借鉴,我是说,如果你要在C里实现类似C++多态性的功能;比如当你要区分你老婆和情人之间的不同功能时。

     好了,先来看一下这三个struct的定义吧

typedef struct URLProtocol {

 

    const char *name;                                                                   //Rotocol名称

    int (*url_open)(URLContext *h, const char *url, int flags);      //open函数指针对象,以下类似

    int (*url_read)(URLContext *h, unsigned char *buf, int size); 

    int (*url_write)(URLContext *h, unsigned char *buf, int size);

    int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);

    int (*url_close)(URLContext *h);

    struct URLProtocol *next;                                                        //指向下一个URLProtocol,具体说明见备注1

    int (*url_read_pause)(URLContext *h, int pause);

    int64_t (*url_read_seek)(URLContext *h, int stream_index,int64_t timestamp, int flags);

    int (*url_get_file_handle)(URLContext *h);

} URLProtocol;

 

备注1:FFMpeg所有的Protol类型都用这个变量串成一个链表,表头为avio.c里的URLProtocol *first_protocol = NULL;

每个文件类似都有自己的一个URLProtocol静态对象,如libavformat/file.c里

 

URLProtocol file_protocol = {

    "file",

    file_open,

    file_read,

    file_write,

    file_seek,

    file_close,

    .url_get_file_handle = file_get_handle,

};

再通过av_register_protocol()将他们链接成链表。在FFMpeg中所有的URLProtocol对像值都在编译时确定。

 

 

typedef struct URLContext {

#if LIBAVFORMAT_VERSION_MAJOR >= 53

    const AVClass *av_class; ///< information for av_log(). Set by url_open().

#endif

    struct URLProtocol *prot;          //指向具体的I/0类型,在运行时通过文件URL确定,如是file类型时就是file_protocol          

    int flags;

    int is_streamed;  /**< true if streamed (no seek possible), default = false */

    int max_packet_size;  /**< if non zero, the stream is packetized with this max packet size */

    void *priv_data;                       //指向具体的I/O句柄

    char *filename; /**< specified URL */

} URLContext;

不同于URLProtocol对象值在编译时确定,URLContext对象值是在运行过程中根据输入的I/O类型动态确定的。这一动一静组合起到了C++的多态继承一样的作用。URLContext像是基类,为大家共同所有,而URLProtocol像是子类部分。

 

typedef struct {

    unsigned char *buffer;

    int buffer_size;

    unsigned char *buf_ptr, *buf_end;

    void *opaque;

    int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);

    int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);

    int64_t (*seek)(void *opaque, int64_t offset, int whence);

    int64_t pos; /**< position in the file of the current buffer */

    int must_flush; /**< true if the next seek should flush */

    int eof_reached; /**< true if eof reached */

    int write_flag;  /**< true if open for writing */

    int is_streamed;

    int max_packet_size;

    unsigned long checksum;

    unsigned char *checksum_ptr;

    unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);

    int error;         ///< contains the error code or 0 if no error happened

    int (*read_pause)(void *opaque, int pause);

    int64_t (*read_seek)(void *opaque, int stream_index,

                         int64_t timestamp, int flags);

} ByteIOContext;

ByteIOContext是URLContext和URLProtocol 一个扩展,也是FFMpeg提供给用户的接口,URLContext和URLProtocol对用户是透明,我们所有的操作是通过ByteIOContext现实。这几个struct的相关的关键函数有:

int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,

                       AVInputFormat *fmt,

                       int buf_size,

                       AVFormatParameters *ap)

{

    int url_fopen(ByteIOContext **s, const char *filename, int flags)

    {

         url_open(URLContext **puc, const char *filename, int flags)

         {

               URLProtocol *up;

               //根据filename确定up

               url_open_protocol (URLContext **puc, struct URLProtocol *up, const char *filename, int flags)

               {

                    //初始化URLContext对像,并通过 up->url_open()将I/O打开将I/O fd赋值给URLContext的priv_data对像

               }

         }

         url_fdopen(ByteIOContext **s, URLContext *h)

         {

               //初始化ByteIOContext 对像

         }

    }

}

我们先看一下音视频播放器的大概结构(个人想法,不保证正确):1、数据源输入(Input)->2、文件格式解析器(Demux)->3、音视频解码(Decoder)->4、颜色空间转换(仅视频)->5、渲染输出(Render Output)。前一篇介绍的几个struct是数据源输入模块里的内容,哪么这一帖所讲的就是第二个模块即文件格式解析器里用到的内容。

      AVInputFormat、AVOutputFormat与URLProtocol类似,每一种文件类型都有一个AVInputFormat和AVOutputFormat静态对像并通过av_register_input_format和av_register_output_format函数链成一个表,其表头在utils.c:

 

/** head of registered input format linked list */

AVInputFormat *first_iformat = NULL;

/** head of registered output format linked list */

AVOutputFormat *first_oformat = NULL;

 

AVInputFormat和AVOutputFormat的定义分别在avformat.h,代码很长,不贴出来浪费空间了。

当程序运行时,AVInputFormat对像的

 

int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,

                       AVInputFormat *fmt,

                       int buf_size,

                       AVFormatParameters *ap)

{

 

     fmt = av_probe_input_format(pd, 0);//返回该文件的AVInputFormat类型

}

至于AVOutputFormat嘛,下次再说吧,晚安!

   AVFormatContext在FFMpeg里是一个非常重要的的结构,是其它输入、输出相关信息的一个容器,需要注意的是其中两个成员:

struct AVInputFormat *iformat;//数据输入格式
 struct AVOutputFormat *oformat;//数据输出格式
这两个成员不能同时赋值,即AVFormatContext不能同时做为输入、输出格式的容器。AVFormatContext和AVIContext、FLVContext等XXXContext之间像前面讲的URLContext和URLProtocol的关系一样,是一种"多态"关系,即AVFormatContext对像记录着运行时大家共有的信息,而各个XXXContext记录自己文件格式的信息,如AVIContext、FLVContext等。AVInputFormat->priv_data_size记录相对应的XXXContext的大小,该值大小在编译时静态确定。AVFormatContext的void *priv_data记录XXXContext指针。
AVFormatContext对像的初始化主要在AVInputFormat的read_header函数中进行,read_header是个函数指针,指向
具体的文件类型的read_header,如flv_read_header(),avi_read_header()等,AVFormatContext、AVInputFormat和XXXContext组成一起共同完成数据输入模块,可以出来粗鲁的认为,AVFormatContext是一个类容器,AVInputFormat是这个类的操作函数集合,XXXContext代表该类的私有数据对像。AVFormatContext还有个重要的成员 AVStream *streams[MAX_STREAMS];也是在read_header里初始化,这个等会儿再讲。
 前几篇说的都还是数据源文件格式解析部分,哪么解析完后呢,读出的数据流保存在哪呢?正是现在讲的AVStream对像,在AVInputFormat的read_header中初始化AVFormatContext对像时,他会解析出该输入文件有哪些类型的数据流,并初始化AVFormatContext的AVStream *streams[MAX_STREAMS];一个AVStream代表一个流对像,如音频流、视频流,nb_streams记录流对像个数。主版本号大于53时MAX_STREAMS为100,小于53为20。AVStream也是个容器,其

void *priv_data;//

成员变量指向具体的Stream类型对像,如AVIStream。其

AVCodecContext *actx;//记录具体的编解容器,这个下面会讲

也在这读头文件信息里初始化。

主要相关的函数有

int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,

                       AVInputFormat *fmt,

                       int buf_size,

                       AVFormatParameters *ap)

{

 

    av_open_input_stream(AVFormatContext **ic_ptr,ByteIOContext *pb, const char *filename,AVInputFormat                                                              *fmt, AVFormatParameters *ap)

    {

         fmt.read_header()//调用具体的AVInputFormat的read_header,如avi_read_header

         {

               //根据文件头信息初始化AVStream *streams及AVStream里的

               //void *priv_data和AVCodecContext *actx;成员对像

         }        

    }

}

  他们之间的关系和URLProtocol、URLContext之间是一样的,AVCodecContext动态的记录一个解码器的上下文信息,而AVCodec是每个解码器都会拥有一个自己的静态对像,并通过avcodec_register()函数注册成一个链表,表头在utils.c里定义

static AVCodec *first_avcodec = NULL;

AVCodecContext的enum CodecID codec_id成员记录者当前数据流的Codec,void *priv_data记录具体Codec所对应的上下文信息对像的指针,如MsrleContext。这三个结合起来现实数据解码的作用。我们可以傻逼的认为AVCodecContext是这个解码模块的容器类,Codec是操作函数集合,类似MsrleContext的就是操作数据对像。

他们之间关系的确立:

每一个解码类型都会有自己的Codec静态对像,Codec的int priv_data_size记录该解码器上下文的结构大小,如MsrleContext。这些都是编译时确定的,程序运行时通过avcodec_register_all()将所有的解码器注册成一个链表。在av_open_input_stream()函数中调用AVInputFormat的read_header()中读文件头信息时,会读出数据流的CodecID,即确定了他的解码器Codec。

typedef struct AVPicture {

    uint8_t *data[4];

    int linesize[4];       ///< number of bytes per line

} AVPicture;

typedef struct AVFrame

{

   uint8_t *data[4]; // 有多重意义,其一用NULL 来判断是否被占用

   int linesize[4];

   uint8_t *base[4]; // 有多重意义,其一用NULL 来判断是否分配内存

   //......其他的数据

} AVFrame;

从定义上可知,AVPicture是AVFrame的一个子集,他们都是数据流在编解过程中用来保存数据缓存的对像,从int av_read_frame(AVFormatContext *s, AVPacket *pkt)函数看,从数据流读出的数据首先是保存在AVPacket里,也可以理解为一个AVPacket最多只包含一个AVFrame,而一个AVFrame可能包含好几个AVPacket,AVPacket是种数据流分包的概念。记录一些音视频相关的属性值,如pts,dts等,定义如下:

typedef struct AVPacket {   

    int64_t pts;   

    int64_t dts;

    uint8_t *data;

    int   size;

    int   stream_index;

    int   flags;   

    int   duration;

    void  (*destruct)(struct AVPacket *);

    void  *priv;

    int64_t pos;                            ///< byte position in stream, -1 if unknown   

    int64_t convergence_duration;

} AVPacket;


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/454995.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《jQuery基础》总结

目前&#xff0c;互联网上最好的jQuery入门教材&#xff0c;是Rebecca Murphey写的《jQuery基础》&#xff08;jQuery Fundamentals&#xff09;。这本书虽然是入门教材&#xff0c;但也足足有100多页。我对它做了一个详细的笔记&#xff0c;试图理清jQuery的设计思想&#xff…

逻辑综合工具DesignCompiler使用教程

逻辑综合工具Design Compiler使用教程 图形界面design vision操作示例 逻辑综合主要是将HDL语言描述的电路转换为工艺库器件构成的网表的过程。综合工具目前比较主流的是synopsys公司Design Compiler&#xff0c;我们在设计实践过程中采用这一工具。Design compiler有两种工作…

遍历结构体_三菱ST语言编程(3)——结构体变量

上篇文章介绍了数组&#xff0c;是一组相同类型数据的列表&#xff0c;那么不同类型的数据能否组合到一起用一个标签表示呢&#xff1f;答案当然是可以的&#xff0c;而实现这个功能的就是结构体(struct)。建立结构体在三菱结构化编程的界面中左侧程序部件里可以找到结构体标签…

关于微信小程序swiper的问题

关于小程序swiper的问题 代码 在官方示例上给swiper添加了currentbindchangecircular添加了一个buttonbindtap用于切换下一张 index.wxml <swiper indicator-dots"{{indicatorDots}}"bindchange"swiperChange"current"{{index}}"circular&quo…

PyQt5案例汇总(完整版)

个人博客点这里 PyQt5案例汇总(完整版) 起步 PyQt5是一套绑定Qt5的应用程序框架。他在Python 2.x和3.x中都是可用的。该教程使用的是Python3.x。 Qt库是一套最有用的GUI库。 PyQt5是作为一套Python模块实现的。他已经超过620个类和6000个函数与方法。他是一个运行在所有主…

中的 隐藏鼠标菜单_Mac移动隐藏删除顶部菜单栏图标教程

苹果菜单栏贯穿 Mac 的屏幕顶部。左侧是苹果菜单和应用菜单&#xff0c;应用菜单一般显示你当前使用的Mac软件的所有功能菜单。右侧通常是以图标显示的状态菜单&#xff0c;帮助你快速查看Mac的状态以及快速访问某些Mac软件。移动图标位置若想要重新排列状态菜单栏的图标&#…

可以用什么代替平面镜

答案是镜面 潜望镜是利用平面镜来改变光路转载于:https://www.cnblogs.com/lidepeng/p/7280593.html

[hadoop] kettle spoon 基础使用 (txt 内容抽取到excel中)

spoon.bat 启动kettle。 测试数据 1. 新建转换 输入中选择文本文件输入 双击设置文本输入 字符集、分隔符设置 获取对应的字段&#xff0c;预览记录。 拖入 excel输出&#xff0c;设置转换关系 设置输出路径 获取字段 启动转换 导入的excel数据&#xff08;设置好格式,图中ID,A…

ffmpeg提取音频播放器总结

ffmpeg提取音频播放器总结&#xff1b; 一&#xff1a;简介 从编写音频播放器代码到完成播放器编写&#xff0c;测试&#xff0c;整整5天的时间&#xff0c;这时间还不算之前对 ffmpeg熟悉的时间&#xff0c;可以说是历经千辛万苦&#xff0c;终于搞出来了&#xff0c;虽然最…

【BZOJ 4103】 [Thu Summer Camp 2015]异或运算 可持久化01Trie

我们观察数据&#xff1a;树套树 PASS 主席树 PASS 一层一个Trie PASS 再看&#xff0c;异或&#xff01;我们就把目光暂时定在01Tire然后我们发现&#xff0c;我们可以带着一堆点在01Trie上行走&#xff0c;因为O(n*q*30m*30)是一个可选复杂度。 我们想一下我们正常的时候…

Docker学习笔记——Java及Tomcat Dockerfile

1、Java Dockerfile创建项目目录java&#xff0c;目录下上传所需java版本压缩包&#xff0c;并创建Dockerfile文件&#xff0c;项目结构如下&#xff1a;java-Dockerfile-jdk-8u111-linux-x64.gzDockerfile内容&#xff1a;# JAVA # Version 1.8.0_111 # SOURCE_IMAGE FROM cen…

rabbitmq接口异常函数方法_RabbitMQ监控(三):监控队列状态

#RabbitMQ 监控(三)验证RabbitMQ健康运行只是确保消息通信架构可靠性的一部分&#xff0c;同时&#xff0c;你也需要确保消息通信结构配置没有遭受意外修改&#xff0c;从而避免应用消息丢失。RabbitMQ Management HTTP API提供了一个方法允许你查看任何vhost上的任何队列&…

FFMpeg语法参数中文参考手册

要查看你的ff mpeg支持哪些 格式&#xff0c;可以用如下命令&#xff1a;$ ffmpeg -formats | less还可以把 视频文件导出成jpg序列帧&#xff1a;$ ffmpeg -i bc-cinematic-en.avi example.%d.jpgdebian下安装ffmpeg很简单&#xff1a;&#xff03;apt-get install ffmpegffmp…

Java类集框架 —— LinkedHashMap源码分析

前言 我们知道HashMap底层是采用数组单向线性链表/红黑树来实现的&#xff0c;HashMap在扩容或者链表与红黑树转换过程时可能会改变元素的位置和顺序。如果需要保存元素存入或访问的先后顺序&#xff0c;那就需要采用LinkedHashMap了。 LinkedHashMap结构 LinkedHashMap继承自H…

apache 支持.htaccess重写url

1. httpd.conf 添加&#xff1a; <Directory />Options Indexes FollowSymLinks MultiviewsAllowOverride allRequire all grantedRewriteEngine On</Directory> 开启&#xff1a; 在phpinfo里找到&#xff1a; 说明开启成功。 2.httpd-vhosts.conf &#xff08;开…

oom 如何避免 高并发_【高并发】高并发环境下如何防止Tomcat内存溢出?看完我懂了!!...

【高并发】高并发环境下如何防止Tomcat内存溢出&#xff1f;看完我懂了&#xff01;&#xff01;发布时间&#xff1a;2020-04-19 00:47,浏览次数&#xff1a;126, 标签&#xff1a;Tomcat写在前面随着系统并发量越来越高&#xff0c;Tomcat所占用的内存就会越来越大&#xff0…

[JSOI2008]最小生成树计数

OJ题号&#xff1a;  BZOJ1016 题目大意&#xff1a;   给定一个无向带权图&#xff0c;求最小生成树的个数。 思路&#xff1a;   先跑一遍最小生成树&#xff0c;统计相同权值的边出现的个数。   易证不同的最小生成树&#xff0c;它们不同的那一部分边的权值实际上是…

vuex webpack 配置_vue+webpack切换环境和打包之后服务器配置

import axios from ‘axios‘import store from ‘../store/index‘const rootUrl process.env.API_ROOT//创建axios实例const service axios.create({timeout:30000 //超时时间})//添加request拦截器service.interceptors.request.use(config >{if (Object.keys(config.hea…

redis基本用法学习(C#调用FreeRedis操作redis)

FreeRedis属于常用的基于.net的redis客户端&#xff0c;EasyCaching中也提供适配FreeRedis的包。根据参考文献4中的说法&#xff0c;FreeRedis和CsRedis算是近亲&#xff08;都是GitHub中账号为2881099下的开源项目&#xff09;&#xff0c;因此其用法特别相似。FreeRedis的主要…

opencv:图像的基本变换

0.概述 图像变换的基本原理都是找到原图和目标图的像素位置的映射关系&#xff0c;这个可以用坐标系来思考&#xff0c;在opencv中&#xff0c; 图像的坐标系是从左上角开始(0,0)&#xff0c;向右是x增加方向(cols)&#xff0c;向下时y增加方向(rows)。 普通坐标关系&#xff1…