ffmpeg数据结构解释

来源:互联网 发布:背包推荐 知乎 编辑:程序博客网 时间:2024/06/07 03:11

ffmpeg数据结构解释

创建时间:2013-5-13 14:09|修改时间:2013-5-20 11:20|作者:|来源:http://www.5ishare.com/snews/353990.shtml

 

 

FFMPEG组成组件

  • ffmpeg 是一个命令行工具,用来对视频文件转换格式,也支持对电视卡实时编码
  • ffserver 是一个 HTTP 多媒体实时广播流服务器,支持时光平移
  • ffplay 是一个简单的播放器,基于 SDL 与 FFmpeg 库
  • libavcodec 包含了全部 FFmpeg 音频/视频 编解码库
  • libavdevice  对输出输入设备的支持; 包含多媒体输入/输出软件框架中抓取和呈现的输入输出设备,包video4linuxvideo4linux2,vfwALSA的库。
  • libavfilter   提供了通用的视频音频滤镜框架
  • libavformat  包含 demuxers 和 muxer 库;提供对音频、视频、字幕流的分解和合并;
  • libavutil 包含一些工具库
  • libpostproc 对于视频做前处理的库
  • libswscale 对于图像作缩放的库
  • libavresampler  为libswresample库中的音频重采样功能提供更高层次的接口。
  • libswresample 对音频重采样、音频格式转换、 Rematrixing;
  • libswscale : 视频场景比例缩放、色彩映射转换;

注:包含ffmpeg头文件时必须加上extern "C" 修饰,否则编译时会报外部符号未找到错误:
Error 1 error LNK2001: unresolved external symbol "void __cdecl avcodec_register_all(void)" (?avcodec_register_all@@YAXXZ)ffmpeg_test.obj ffmpeg_test

正确的头文件包含方式:
extern "C" {
    #include "libavcodec/avcodec.h"
}

FFMPEG解码流程:

  1. 注册所有容器格式和CODEC: av_register_all()

  2. 打开文件:                   av_open_input_file()

  3. 从文件中提取流信息:         av_find_stream_info()

  4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO

  5. 查找对应的解码器:           avcodec_find_decoder()

  6. 打开编解码器:               avcodec_open()

  7. 为解码帧分配内存:           avcodec_alloc_frame()

  8. 不停地从码流中提取出帧数据av_read_frame()

  9. 判断帧的类型,对于视频帧调用:avcodec_decode_video()

  10.解码完后,释放解码器:      avcodec_close()

  11.关闭输入文件:               avformat_close_input_file()


基本概念:

编解码器数据帧媒体流容器是数字媒体处理系统的四个基本概念。

首先需要统一术语:

  • 容器/文件(Conainer/File):即特定格式的多媒体文件。
  • 媒体流(Stream):指时间轴上的一段连续数据,如一段声音数据,一段视频数据或一段字幕数据,可以是压缩的,也可以是非压缩的,压缩的数据需要关联特定的编解码器。
  •  数据帧/数据包(Frame/Packet):通常,一个媒体流由大量的数据帧组成,对于压缩数据,帧对应着编解码器的最小处理单元。通常,分属于不同媒体流的数据帧交错复用于容器之中,参见交错。
  •  编解码器:编解码器以帧为单位实现压缩数据和原始数据之间的相互转换。

FFMPEG中,使用AVFormatContextAVStreamAVCodecContextAVCodecAVPacket等结构来抽象这些基本要素,


主要数据结构

AVCodecContext

这是一个描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息;

如果是单纯使用libavcodec,这部分信息需要调用者进行初始化;如果是使用整个FFMPEG库,这部分信息在调用 av_open_input_file和av_find_stream_info的过程中根据文件的头信息及媒体流内的头部信息完成初始化。其中几个主要域的释义如下:

  • extradata/extradata_size: 这个buffer中存放了解码器可能会用到的额外信息,在av_read_frame中填充。一般来说,首先,某种具体格式的demuxer在读取格式头 信息的时候会填充extradata,其次,如果demuxer没有做这个事情,比如可能在头部压根儿就没有相关的编解码信息,则相应的parser会继 续从已经解复用出来的媒体流中继续寻找。在没有找到任何额外信息的情况下,这个buffer指针为空。
  • time_base:
  • width/height:视频的宽和高。
  • sample_rate/channels:音频的采样率和信道数目。
  • sample_fmt: 音频的原始采样格式。
  • codec_name/codec_type/codec_id/codec_tag:编解码器的信息。
AVStream
该结构体描述一个媒体流
主要域的释义如下,其中大部分域的值可以由av_open_input_file根据文件头的信息确定,缺少的信息需要通过调用av_find_stream_info读帧及软解码进一步获取:
  • index/id:index对应流的索引,这个数字是自动生成的,根据index可以从AVFormatContext::streams表中索引到该流;而id则是流的标识,依赖于具体的容器格式。比如对于MPEG TS格式,id就是pid。
  • time_base:流的时间基准,是一个实数,该流中媒体数据的pts和dts都将以这个时间基准为粒度。通常,使用av_rescale/av_rescale_q可以实现不同时间基准的转换。
  • start_time:流的起始时间,以流的时间基准为单位,通常是该流中第一个帧的pts。
  • duration:流的总时间,以流的时间基准为单位。
  • need_parsing:对该流parsing过程的控制域。
  • nb_frames:流内的帧数目。
  • r_frame_rate/framerate/avg_frame_rate:帧率相关。
  • codec:指向该流对应的AVCodecContext结构,调用av_open_input_file时生成。
  • parser:指向该流对应的AVCodecParserContext结构,调用av_find_stream_info时生成。

AVFormatContext
这个结构体描述了一个媒体文件或媒体流的构成和基本信息;
这是FFMpeg中最为基本的一个结构,是其他所有结构的根,是一个多媒体文件或流的根本抽象。其中:
  • nb_streams和streams所表示的AVStream结构指针数组包含了所有内嵌媒体流的描述;
  • iformat和oformat指向对应的demuxer和muxer指针;
  • pb则指向一个控制底层数据读写的ByteIOContext结构。
  • start_time和duration是从streams数组的各个AVStream中推断出的多媒体文件的起始时间和长度,以微妙为单位。
通常,这个结构由av_open_input_file在内部创建并以缺省值初始化部分成员。但是,如果调用者希望自己创建该结构,则需要显式为该结构的一些成员置缺省值——如果没有缺省值的话,会导致之后的动作产生异常。以下成员需要被关注:
  • probesize
  • mux_rate
  • packet_size
  • flags
  • max_analyze_duration
  • key
  • max_index_size
  • max_picture_buffer
  • max_delay
 
AVPacket
AVPacket定义在avcodec.h中,FFMPEG使用AVPacket来暂存解复用之后、解码之前的媒体数据(一个音/视频帧、一个字幕包等)及附加信息(解码时间戳、显示时间戳、时长等)。其中:
  • dts表示解码时间戳,pts表示显示时间戳,它们的单位是所属媒体流的时间基准。
  • stream_index给出所属媒体流的索引;
  • data为数据缓冲区指针,size为长度;
  • duration为数据的时长,也是以所属媒体流的时间基准为单位;
  • pos表示该数据在媒体流中的字节偏移量;
  • destruct为用于释放数据缓冲区的函数指针;
  • flags为标志域,其中,最低为置1表示该数据是一个关键帧。
    AVPacket 结构本身只是个容器,它使用data成员指向实际的数据缓冲区,这个缓冲区可以通过av_new_packet创建,可以通过av_dup_packet 拷贝,也可以由FFMPEG的API产生(如av_read_frame),使用之后需要通过调用av_free_packet释放。 av_free_packet调用的是结构体本身的destruct函数,它的值有两种情况:
  1. av_destruct_packet_nofree或 0;
  2. av_destruct_packet,
    其中,前者仅仅是将data和size的值清0而已,后者才会真正地释放缓冲区。FFMPEG内部使用 AVPacket结构建立缓冲区装载数据,同时提供destruct函数,如果FFMPEG打算自己维护缓冲区,则将destruct设为 av_destruct_packet_nofree,用户调用av_free_packet清理缓冲区时并不能够将其释放;如果FFMPEG不会再使用 该缓冲区,则将destruct设为av_destruct_packet,表示它能够被释放。对于缓冲区不能够被释放的AVPackt,用户在使用之前 最好调用av_dup_packet进行缓冲区的克隆,将其转化为缓冲区能够被释放的AVPacket,以免对缓冲区的不当占用造成异常错误。而 av_dup_packet会为destruct指针为av_destruct_packet_nofree的AVPacket新建一个缓冲区,然后将原 缓冲区的数据拷贝至新缓冲区,置data的值为新缓冲区的地址,同时设destruct指针为av_destruct_packet。
  • 时间信息
    时间信息用于实现多媒体同步。
    同步的目的在于展示多媒体信息时,能够保持媒体对象之间固有的时间关系。同步有两类,一类是流内同步,其主要任务是保证单个媒体流内的时间关系,以满足感知要求,如按照规定的帧率播放一段视频;另一类是流间同步,主要任务是保证不同媒体流之间的时间关系,如音频和视频之间的关系(lipsync)。
    对于固定速率的媒体,如固定帧率的视频或固定比特率的音频,可以将时间信息(帧率或比特率)置于文件首部(header),如AVI的hdrl List、MP4的moov box,还有一种相对复杂的方案是将时间信息嵌入媒体流的内部,如MPEG TS和Real video,这种方案可以处理变速率的媒体,亦可有效避免同步过程中的时间漂移。
    FFMPEG会为每一个数据包打上时间标 签,以更有效地支持上层应用的同步机制。时间标签有两种,一种是DTS,称为解码时间标签,另一种是PTS,称为显示时间标签。对于声音来说 ,这两个时间标签是相同的,但对于某些视频编码格式,由于采用了双向预测技术,会造成DTS和PTS的不一致。
  • 时间信息的获取:
    通过调用av_find_stream_info,多媒体应用可以从AVFormatContext对象中拿到媒体文件的时间信息:主要是总时间长度和开始时间,此外还有与时间信息相关的比特率和文件大小。其中时间信息的单位是AV_TIME_BASE:微秒。


AVCodecParser

用来对不同视频编码标准,分析出一个完整的一帧,如

AVCodecParser ff_h264_parser = {

{ CODEC_ID_H264 },

sizeof(H264Context),

init,

h264_parse,

close,

h264_split,

};

h.264的parser

4)h.264 decoder

AVCodec ff_h264_decoder = {
    "h264",
    AVMEDIA_TYPE_VIDEO,
    CODEC_ID_H264,
    sizeof(H264Context),
    ff_h264_decode_init,
    NULL,
    ff_h264_decode_end,
    decode_frame,
    /*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 | CODEC_CAP_DELAY |
        CODEC_CAP_FRAME_THREADS |
        CODEC_CAP_SLICE_THREADS,
    .flush= flush_dpb,
    .long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
    .init_thread_copy      = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),
    .update_thread_context = ONLY_IF_THREADS_ENABLED(decode_update_thread_context),
    .profiles = NULL_IF_CONFIG_SMALL(profiles),
};




原创粉丝点击