热门标签 | 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

推荐阅读
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • CMake跨平台开发实践
    本文介绍如何使用CMake支持不同平台的代码编译。通过一个简单的示例,我们将展示如何编写CMakeLists.txt以适应Linux和Windows平台,并实现跨平台的函数调用。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 使用Vultr云服务器和Namesilo域名搭建个人网站
    本文详细介绍了如何通过Vultr云服务器和Namesilo域名搭建一个功能齐全的个人网站,包括购买、配置服务器以及绑定域名的具体步骤。文章还提供了详细的命令行操作指南,帮助读者顺利完成建站过程。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • dotnet 通过 Elmish.WPF 使用 F# 编写 WPF 应用
    本文来安利大家一个有趣而且强大的库,通过F#和C#混合编程编写WPF应用,可以在WPF中使用到F#强大的数据处理能力在GitHub上完全开源Elmis ... [详细]
  • 本文介绍了如何使用Workman框架构建一个功能全面的即时通讯系统,该系统不仅支持一对一聊天、群组聊天,还集成了视频会议和实时音视频通话功能,同时提供了红包发送等附加功能。 ... [详细]
  • 在Ubuntu 18.04上使用Nginx搭建RTMP流媒体服务器
    本文详细介绍了如何在Ubuntu 18.04上使用Nginx和nginx-rtmp-module模块搭建RTMP流媒体服务器,包括环境搭建、配置文件修改和推流拉流操作。适用于需要搭建流媒体服务器的技术人员。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
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社区 版权所有