热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Ffmpeg框架结构解读.

1、FFMEPG结构说明1.1》介绍ffmpeg(FastForwardMovingPicturesExpertsGroup)是音视频的分离,转换,编码
1、    FFMEPG结构说明

    1.1》介绍

    ffmpeg(Fast Forward Moving Pictures Experts Group)是音视频的分离,转换,编码解码及流媒体的完全解决方案,其中最重要的就是libavcodec库,是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。ffmpeg的开发是基于Linux操作系统,但是可以在大多数操作系统中编译和使用。FFmpeg支持MPEG、DivX、MPEG4、AC3、DV、FLV等40多种编码,AVI、MPEG、OGG、Matroska、ASF等90多种解码. TCPMP, VLC, MPlayer等开源播放器都用到了FFmpeg。

ffmpeg主目录下主要有libavcodec、libavformat和libavutil等子目录。其中

    libavcodec用于存放各个encode/decode模块,CODEC其实是Coder/Decoder的缩写,也就是编码解码器;用于各种类型声音/图像编解码

    libavformat用于存放muxer/demuxer模块,对音频视频格式的解析;用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构和读取音视频帧等功能;

其中库 libavcodec,libavformat用于对媒体文件进行处理,如格式的转换;

    libavutil集项工具,包含一些公共的工具函数;用于存放内存操作等辅助性模块,是一个通用的小型函数库,该库中实现了CRC校验码的产生,128位整数数学,最大公约数,整数开方,整数取对数,内存分配,大端小端格式的转换等功能

    libavdevice:对输出输入设备的支持;

    libpostproc:用于后期效果处理;

    libswscale:用于视频场景比例缩放、色彩映射转换;

    ffmpeg:该项目提供的一个工具,可用于格式转换、解码或电视卡即时编码等;

    fsever:一个 HTTP 多媒体即时广播串流服务器;

    ffplay:是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示;

    ffmpeg软件包经编译过后将生成三个可执行文件,ffmpeg,ffserver,ffplay。其中ffmpeg用于对媒体文件进行处理,ffserver是一个http的流媒体服务器,ffplay是一个基于SDL的简单播放器。

   

说明:

    muxer/demuxer和encoder/decoder的区别:

    最大的差别是muxer 和demuxer分别是不同的结构AVOutputFormat与AVInputFormat;

    而encoder和decoder都是用的AVCodec 结构。

    muxer/demuxer是分别保存在全局变量AVOutputFormat *first_oformat与AVInputFormat *first_iformat中的。encoder/decoder都是保存在全局变量AVCodec *first_avcodec中的。

    muxer/demuxer和encoder/decoder的相同之处:

    都是在main()开始的av_register_all()函数内初始化的

    都是以链表的形式保存在全局变量中的

    都用函数指针的方式作为开放的公共接口



    1.2》下载与编译

    官方下载网址http://ffmpeg.org/download.html

    编译./configure

        #make

        #make install

安装到/usr/local/bin、/usr/local/include(包含各个头文件)、/usr/local/lib(生成.a文件),编译完毕后

    A》执行./ffmpeg,结果如下:

FFmpeg version SVN-r17579, Copyright (c) 2000-2009 Fabrice Bellard, et al.

  configuration:

  libavutil     49.15. 0 / 49.15. 0

  libavcodec    52.19. 0 / 52.19. 0

  libavformat   52.30. 0 / 52.30. 0

  libavdevice   52. 1. 0 / 52. 1. 0

  built on Mar 25 2011 17:30:17, gcc: 4.3.4

At least one output file must be specified

    B》执行./ffplay,结果如下:

FFplay version SVN-r17579, Copyright (c) 2003-2009 Fabrice Bellard, et al.

  configuration:

  libavutil     49.15. 0 / 49.15. 0

  libavcodec    52.19. 0 / 52.19. 0

  libavformat   52.30. 0 / 52.30. 0

  libavdevice   52. 1. 0 / 52. 1. 0

  built on Mar 25 2011 17:30:17, gcc: 4.3.4

An input file must be specified

    C》执行./ffserver,结果如下:

FFserver version SVN-r17579, Copyright (c) 2000-2009 Fabrice Bellard, et al.

  configuration:

  libavutil     49.15. 0 / 49.15. 0

  libavcodec    52.19. 0 / 52.19. 0

  libavformat   52.30. 0 / 52.30. 0

  libavdevice   52. 1. 0 / 52. 1. 0

  built on Mar 25 2011 17:30:17, gcc: 4.3.4

/etc/ffserver.conf: No such file or directory

Incorrect config file - exiting.

说明:如果缺少fserver.conf文件,需在/etc/中增加ffserver.conf文件。



2、    Ffmpeg编码、解码

    2.1》主要流程如下:

    输入流初始化input streams initializing

    输出流初始化output streams initializing

    编码器和解码器初始化encoders and decoders initializing

    如有需要的情况下,设置来自输入文件的Meta数据信息set meta data information from input file if required.

    写输出文件头文件write output files header

    循环处理每个数据单元loop of handling each frame(frame是指Stream中的一个数据单元)

    从输入文件中读取数据单元read frame from input file:

    解码数据单元内数据decode frame data

    编码数据单元内数据encode new frame data

    写新的数据单元到输出文件中write new frame to output file

    写输出文件的尾文件write output files trailer

    关闭每个编码器和解码器close each encoder and decoder

说明:

    av_encode函数是FFMpeg中最重要的函数,编码/解码和输出等大部分功能都在此函数完成。ffmpeg.c中av_encode(AVFormatContext **output_files,

                     int nb_output_files,

                     AVFormatContext **input_files,

                     int nb_input_files,

                     AVStreamMap *stream_maps, int nb_stream_maps)



    AVFormatContext是FFMpeg格式转换过程中实现输入和输出功能、保存相关数据的主要结构。每一个输入和输出文件,都在如下定义的指针数组全局变量中有对应的实体。

    static AVFormatContext *output_files[MAX_FILES];

    static AVFormatContext *input_files[MAX_FILES];

    对于输入和输出,因为共用的是同一个结构体,所以需要分别对该结构中如下定义的iformat或oformat成员赋值。

    struct AVInputFormat *iformat;

    struct AVOutputFormat *oformat;

    对一个AVFormatContext来说,这二个成员不能同时有值,即一个AVFormatContext不能同时含有demuxer和muxer。在main( )函数开头的parse_options( )函数中找到了匹配的muxer和demuxer之后,根据传入的argv参数,初始化每个输入和输出的AVFormatContext结构,并保存在相应的output_files和input_files指针数组中。在av_encode( )函数中,output_files和input_files是作为函数参数传入后,在其他地方就没有用到了。

 

    AVCodecContext保存AVCodec指针和与codec相关数据,如video的width、height,audio的sample rate等。AVCodecContext中的codec_type,codec_id二个变量对于encoder/decoder的匹配来说,最为重要。

    enum CodecType codec_type;     /* see CODEC_TYPE_xxx */

    enum CodecID codec_id;         /* see CODEC_ID_xxx */

    codec_type保存的是CODEC_TYPE_VIDEO,CODEC_TYPE_AUDIO等媒体类型,codec_id保存的是CODEC_ID_FLV1,CODEC_ID_VP6F等编码方式。

 

    AVStream结构保存与数据流相关的编解码器,数据段等信息。比较重要的有如下二个成员:

    AVCodecContext *codec; /**
    void *priv_data;

    其中codec指针保存的就是encoder或decoder结构。priv_data指针保存的是和具

体编解码流相关的数据。



    AVInputStream/ AVOutputStream根据输入和输出流的不同,前述的AVStream结构都是封装在AVInputStream和AVOutputStream结构中,在av_encode( )函数中使用。AVInputStream中还保存的有与时间有关的信息。AVOutputStream中还保存有与音视频同步等相关的信息。   



    2.2》视频文件解码流程

    A》初始化 libavcodec库,并注册所有容器格式(format)、编解码器CODEC、,解析器(parsers)以及码流过滤器(bitstream filters),打开一个文件时,自动选择相应的文件格式和编码器:

    avcodec_register_all();

    avdevice_register_all();

    av_register_all();

    avformat_alloc_context();分配播放avformat的上下文,分配输出媒体内容。



    B》打开文件: av_open_input_file()

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

                       AVInputFormat *fmt,

                       int buf_size,

                       AVFormatParameters *ap)

    {

           ......

        if (!fmt) {

            /* guess format if no file can be opened */

            fmt = av_probe_input_format(pd, 0);

        }

           ......

        err = av_open_input_stream(ic_ptr, pb, filename, fmt, ap);

           ......

    }

    主要是两件事情:

    侦测容器文件格式(是在AVFormatContext定义中);

    从容器文件获取Stream的信息,就是调用特定文件的demuxer以分离Stream的过程,描述如下:

av_open_input_file

    av_probe_input_format2()从first_iformat中遍历注册的所有demuxer以调用相应的probe函数

    av_open_input_stream()调用指定demuxer的read_header函数以获取相关流的信息ic->iformat->read_header



    C》从文件中提取流信息: av_find_stream_info()用有效的信息把 AVFormatContext 的流域(streams field)填满。对于音频/视频每个Packet包含完整的或多个复合的frame。从文件中读取packet,从Packet中解码相应的frame。

    av_find_stream_info(AVFormatContext *ic)主要是两部分:

    一部分是使用av_open_input_file()解复用(demuxer)

    然后是使用av_read_frame(AVFormatContext *s, AVPacket *pkt)和 avcodec_decode_video() 解码(decode)



    D》遍历所有的流,查找其中种类为CODEC_TYPE_VIDEO,描叙如下:

int i;

AVCodecContext *pCodecCtx;



// Find the first video stream

videoStream=-1;

for(i=0; inb_streams; i++)

  if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {

    videoStream=i;

    break;

  }

if(videoStream==-1)

  return -1; // Didn't find a video stream



// Get a pointer to the codec context for the video stream

pCodecCtx=pFormatCtx->streams[videoStream]->codec;



    E》查找对应的解码器: avcodec_find_decoder();若成功后,打开解码器 avcodec_open()用给定的 AVCodec来初始化AVCodecContext,描叙如下:

AVCodec *pCodec;



// Find the decoder for the video stream

pCodec=avcodec_find_decoder(pCodecCtx->codec_id);

if(pCodec==NULL) {

  return -1; // Codec not found

}

// Open codec

if(avcodec_open(pCodecCtx, pCodec)<0)

  return -1; // Could not open codec



    F》为解码帧分配内存: avcodec_alloc_frame()&#xff0c;用于存在帧数据



    G》不停地从解码流中提取中帧数据: av_read_frame()

int frameFinished;

AVPacket packet;



i&#61;0;

while(av_read_frame(pFormatCtx, &packet)>&#61;0) {

  // Is this a packet from the video stream?

  if(packet.stream_index&#61;&#61;videoStream) {

    // Decode video frame

    avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,

                         packet.data, packet.size);

   

    // Did we get a video frame?

    if(frameFinished) {

    // Convert the image from its native format to RGB32

        img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB32,

            (AVPicture*)pFrame, pCodecCtx->pix_fmt,

            pCodecCtx->width, pCodecCtx->height);

   

        // Save the frame to disk

           ......

    }

  }

   

  // Free the packet that was allocated by av_read_frame

  av_free_packet(&packet);

}

   

    H》判断帧的类型&#xff0c;对于视频帧调用指定Codec的解码函数: avcodec_decode_video()

    I》解码完后&#xff0c;释放解码器: avcodec_close()

    J》关闭输入文件:av_close_input_file()



3、    代码标记Log

    根据2.2》项中所描述的视频解码流程&#xff0c;作Log标记&#xff08;用printf()方法输出&#xff09;、跟踪视频解码过程。从ffmpeg自带的ffplay播放器着手&#xff0c;跟踪ffplay.c的主函数main()中涉及的调用函数。

/* Called from the main */

int main(int argc, char **argv)

{

    /* register all codecs, demux and protocols */

    avcodec_register_all();

    avdevice_register_all();

    av_register_all();

    ......

    avformat_opts &#61; avformat_alloc_context();

    sws_opts &#61; sws_getContext(16,16,0, 16,16,0, sws_flags, NULL,NULL,NULL);

    show_banner();

    parse_options(argc, argv, options, opt_input_file);

    ......

    cur_stream &#61; stream_open(input_filename, file_iformat);

event_loop();



    /* never returns */

    return 0;

}

    跟踪结果如下&#xff1a;

root&#64;localhost /work/ffmpeg>ffplay /work/test/avi/output.avi

beginning avcodec_register_all... _by jay remarked

beginning avdevice_register_all... _by jay remarked

beginning av_register_all... _by jay remarked

registering MuxDemux MP3... _by jay remarked

returning av_register_all&#39;s initialized



avctx_opts[0]

avctx_opts[1]

avctx_opts[2]

avctx_opts[3]

avctx_opts[4]

returning avformat_alloc_context value..._by jay remarked

returning sws_getContex value..._by jay remarked



showing version banner..._by jay remarked

FFplay version SVN-r17579 _by Jay remarked, Copyright (c) 2003-2009 Fabrice Bellard, et al.

  configuration:

  libavutil     49.15. 0 / 49.15. 0

  libavcodec    52.19. 0 / 52.19. 0

  libavformat   52.30. 0 / 52.30. 0

  libavdevice   52. 1. 0 / 52. 1. 0

  built on Apr  1 2011 09:29:06, gcc: 4.3.4



beginning parse_options... _by jay remarked

returning optindex&#61;[2]

beginning av_init_packet... _by jay remarked

beginning cur_stream... _by jay remarked

returning av_open_input_file&#39;s pd->filename&#61;[T]

[mp3 &#64; 0x9b26d20]mdb:432, lastbuf:0 skipping granule 0

    Last message repeated 1 times

[mp3 &#64; 0x9b26d20]mdb:432, lastbuf:0 skipping granule 1

    Last message repeated 1 times

[mp3 &#64; 0x9b26d20]mdb:460, lastbuf:216 skipping granule 0

    Last message repeated 1 times

[mp3 &#64; 0x9b26d20]mdb:460, lastbuf:216 skipping granule 1

returning av_close_input_file successful

推荐阅读
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
author-avatar
陶磊2922_502
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有