ffmpeg 过程分析

  • 简介

FFmpeg是一个集录制、转换、音/视频编码解码功能为一体的完整的开源解决方案。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模块,libavformat用于存放muxer/demuxer模块,libavutil用于存放内存操作等常用模块。

以flash movie的flv文件格式为例, muxer/demuxer的flvenc.c和flvdec.c文件在libavformat目录下,encode/decode的mpegvideo.c和h263de.c在libavcodec目录下。

muxer/demuxer与encoder/decoder定义与初始化

muxer/demuxer和encoder/decoder在FFmpeg中的实现代码里,有许多相同的地方,而二者最大的差别是muxer和demuxer分别是不同的结构AVOutputFormat与AVInputFormat,而encoder和decoder都是用的AVCodec结构。

muxer/demuxer和encoder/decoder在FFmpeg中相同的地方有:

l        二者都是在main()开始的av_register_all()函数内初始化的。

l        二者都是以链表的形式保存在全局变量中的。

muxer/demuxer是分别保存在全局变量AVOutputFormat *first_oformat与AVInputFormat *first_iformat中的。

encoder/decoder都是保存在全局变量AVCodec *first_avcodec中的。

l        二者都用函数指针的方式作为开放的公共接口。

demuxer开放的接口有:

      int (*read_probe)(AVProbeData *);

      int(*read_header)(struct AVFormatContext *, AVFormatParameters *ap);

int(*read_packet)(struct AVFormatContext *, AVPacket *pkt);

int(*read_close)(struct AVFormatContext *);

int(*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, intflags);

        muxer开放的接口有:

                  int (*write_header)(struct AVFormatContext *);

                  int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);

               int (*write_trailer)(struct AVFormatContext *);

encoder/decoder的接口都是一样的,只不过二者分别只实现encoder和decoder函数:

int(*init)(AVCodecContext *);

              int (*encode)(AVCodecContext*, uint8_t *buf, int buf_size, void *data);

              int (*close)(AVCodecContext*);

                int (*decode)(AVCodecContext*, void *outdata, int *outdata_size, uint8_t *buf, int buf_size);

仍以flv文件为例来说明muxer/demuxer的初始化。

在libavformat\allformats.c文件的av_register_all(void)函数中,通过执行

REGISTER_MUXDEMUX(FLV,flv);

将支持flv 格式的flv_muxerflv_demuxer变量分别注册到全局变量first_oformatfirst_iformat链表的最后位置。

其中flv_muxer在libavformat\flvenc.c中定义如下:

      AVOutputFormat flv_muxer = {

   "flv",

   "flv format",

   "video/x-flv",

   "flv",

   sizeof(FLVContext),

#ifdefCONFIG_LIBMP3LAME

   CODEC_ID_MP3,

#else //CONFIG_LIBMP3LAME

   CODEC_ID_NONE,

   CODEC_ID_FLV1,

   flv_write_header,

   flv_write_packet,

   flv_write_trailer,

   .codec_tag= (const AVCodecTag*[]){flv_video_codec_ids, flv_audio_codec_ids, 0},

}

AVOutputFormat结构的定义如下:

typedefstruct AVOutputFormat {

   const char *name;

   const char *long_name;

   const char *mime_type;

   const char *extensions; /**< comma separated filename extensions */

   /** size of private data so that it can be allocated in the wrapper */

   int priv_data_size;

   /* output support */

   enum CodecID audio_codec; /**< default audio codec */

   enum CodecID video_codec; /**< default video codec */

   int (*write_header)(struct AVFormatContext *);

   int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);

   int (*write_trailer)(struct AVFormatContext *);

   /** can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_GLOBALHEADER */

   int flags;

   /** currently only used to set pixel format if not YUV420P */

   int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *);

   int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in,int flush);

   /**

    * list of supported codec_id-codec_tag pairs, ordered by "better choicefirst"

    * the arrays are all CODEC_ID_NONE terminated

    */

   const struct AVCodecTag **codec_tag;

   /* private fields */

   struct AVOutputFormat *next;

} AVOutputFormat;

AVOutputFormat结构的定义可知,flv_muxer变量初始化的第一、第二个成员分别为该muxer的名称与长名称,第三、第四个成员为所对应MIMIE Type和后缀名,第五个成员是所对应的私有结构的大小,第六、第七个成员为所对应的音频编码和视频编码类型ID,接下来就是三个重要的接口函数,该muxer的功能也就是通过调用这三个接口实现的。

flv_demuxer在libavformat\flvdec.c中定义如下, 与flv_muxer类似,在这儿主要也是设置了5个接口函数,其中flv_probe接口用途是测试传入的数据段是否是符合当前文件格式,这个接口在匹配当前demuxer的时候会用到。

AVInputFormatflv_demuxer = {

   "flv",

   "flv format",

   0,

   flv_probe,

   flv_read_header,

   flv_read_packet,

   flv_read_close,

   flv_read_seek,

   .extensions = "flv",

   .value = CODEC_ID_FLV1,

};

在上述av_register_all(void)函数中通过执行libavcodec\allcodecs.c文件里的avcodec_register_all(void)函数来初始化全部的encoder/decoder。

因为不是每种编码方式都支持encode和decode,所以有以下三种注册方式:

#defineREGISTER_ENCODER(X,x) \

                  if(ENABLE_##X##_ENCODER) register_avcodec(&x##_encoder)

#defineREGISTER_DECODER(X,x) \

                if(ENABLE_##X##_DECODER) register_avcodec(&x##_decoder)

#defineREGISTER_ENCDEC(X,x)

 REGISTER_ENCODER(X,x);REGISTER_DECODER(X,x)

如支持flv的flv_encoderflv_decoder变量就分别是在libavcodec\mpegvideo.c和libavcodec\h263de.c中创建的。

当前muxer/demuxer的匹配

在FFmpeg的文件转换过程中,首先要做的就是根据传入文件和传出文件的后缀名匹配合适的demuxer和muxer。

匹配上的demuxer和muxer都保存在如下所示,定义在ffmpeg.c里的全局变量file_iformatfile_oformat中:

staticAVInputFormat *file_iformat;

staticAVOutputFormat *file_oformat;

1.        demuxer匹配

在libavformat\utils.c中的static AVInputFormat*av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)函数用途是根据传入的probe data数据,依次调用每个demuxer的read_probe接口,来进行该demuxer是否和传入的文件内容匹配的判断。其调用顺序如下:

voidparse_options(int argc, char **argv, const OptionDef *options)   

static voidopt_input_file(const char *filename)

static voidopt_input_file(const char *filename)

                    int av_open_input_file(…… )

                                    AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)

staticAVInputFormat *av_probe_input_format2(……)

                                                

opt_input_file函数是在保存在const OptionDef options[]数组中,用于void parse_options(int argc, char**argv, const OptionDef *options)中解析argv里的“-i” 参数,也就是输入文件名时调用的。

2.        muxer匹配

与demuxer的匹配不同,muxer的匹配是调用guess_format函数,根据main( ) 函数的argv里的输出文件后缀名来进行的。

voidparse_options(int argc, char **argv, const OptionDef *options)

voidparse_arg_file(const char *filename)

static voidopt_output_file(const char *filename)

AVOutputFormat*guess_format(const char *short_name, const char *filename,

                            const char *mime_type)

当前encoder/decoder的匹配

main( )函数中除了解析传入参数并初始化demuxer与muxer的parse_options()函数以外,其他的功能都是在av_encode( )函数里完成的。

在libavcodec\utils.c中有如下二个函数。

             AVCodec *avcodec_find_encoder(enum CodecID id)

                  AVCodec *avcodec_find_decoder(enum CodecID id)

他们的功能就是根据传入的CodecID,找到匹配的encoder和decoder。

av_encode( )函数的开头,首先初始化各个AVInputStreamAVOutputStream,然后分别调用上述二个函数,并将匹配上的encoder与decoder分别保存在AVInputStream->AVStream*st->AVCodecContext *codec->struct AVCodec *codecAVOutputStream->AVStream*st->AVCodecContext *codec->struct AVCodec *codec变量中。

其他主要数据结构

1.       AVFormatContext

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

staticAVFormatContext *output_files[MAX_FILES];

staticAVFormatContext *input_files[MAX_FILES];

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

   struct AVInputFormat *iformat;

   struct AVOutputFormat *oformat;

对一个AVFormatContext来说,二个成员不能同时有值,即一个AVFormatContext不能同时含有demuxer和muxer。

main( )函数开头的parse_options( )函数中找到了匹配的muxer和demuxer之后,根据传入的argv参数,初始化每个输入和输出的AVFormatContext结构,并保存在相应的output_filesinput_files指针数组中。

av_encode( )函数中,output_filesinput_files是作为函数参数传入后,在其他地方就没有用到了。

2.        AVCodecContext

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

         enum CodecType codec_type; /* seeCODEC_TYPE_xxx */

               enumCodecID codec_id; /* see CODEC_ID_xxx */

如上所示,codec_type保存的是CODEC_TYPE_VIDEOCODEC_TYPE_AUDIO等媒体类型,

codec_id保存的是CODEC_ID_FLV1CODEC_ID_VP6F等编码方式。

以支持flv格式为例,在前述的av_open_input_file(……) 函数中,匹配到正确的AVInputFormat demuxer后,通过av_open_input_stream( )函数中调用AVInputFormatread_header接口来执行flvdec.c中的flv_read_header()函数。在flv_read_header()函数内,根据文件头中的数据,创建相应的视频或音频AVStream,并设置AVStreamAVCodecContext的正确的codec_type值。codec_id值是在解码过程中flv_read_packet( )函数执行时根据每一个packet头中的数据来设置的。

3.        AVStream

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

            AVCodecContext*codec; /**< codec context */

void*priv_data;

其中codec指针保存的就是上节所述的encoder或decoder结构。priv_data指针保存的是和具体编解码流相关的数据,如下代码所示,在ASF的解码过程中,priv_data保存的就是ASFStream结构的数据。

   AVStream *st;

ASFStream*asf_st;   

    … …

st->priv_data= asf_st;

4.        AVInputStream/AVOutputStream

根据输入和输出流的不同,前述的AVStream结构都是封装在AVInputStreamAVOutputStream结构中,在av_encode( )函数中使用。

AVInputStream中还保存的有与时间有关的信息。

AVOutputStream中还保存有与音视频同步等相关的信息。

5.        AVPacket

AVPacket结构定义如下,其是用于保存读取的packet数据。

typedefstruct AVPacket {

   int64_t pts;           ///< presentation time stamp in time_base units

   int64_t dts;           ///< decompression time stamp in time_base units

   uint8_t *data;

   int   size;

   int   stream_index;

   int   flags;

   int   duration;        ///<presentation duration in time_base units (0 if not available)

   void (*destruct)(struct AVPacket *);

   void *priv;

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

} AVPacket;

av_encode( )函数中,调用AVInputFormat(*read_packet)(structAVFormatContext *, AVPacket *pkt);接口,读取输入文件的一帧数据保存在当前输入AVFormatContextAVPacket成员中。

av_encode函数主要流程

av_encode( )函数是FFMpeg中最重要的函数,编解码和输出等大部分功能都在此函数内完成,因此有必要详细描述一下这个函数的主要流程。

1.        input streamsinitializing

2.        outputstreams initializing

3.        encoders anddecoders initializing

4.        set meta datainformation from input file if required.

5.        write outputfiles header

6.        loop ofhandling each frame

a.      read frame frominput file:

b.      decode framedata

c.      encode newframe data

d.      write newframe to output file

7.        write outputfiles trailer

8.        close eachencoder and decoder

 

·  IV. AVCodecContext

此结构在Ffmpeg SDK中的注释是:main external api structure其重要性可见一斑。而且在avcodec它的定义处,对其每个

成员变量,都给出了十分详细的介绍。应该说AVCodecContext的初始化是Codec使用中最重要的一环。虽然在前面的

AVStream中已经有所提及,但是这里还是要在说一遍。AVCodecContext作为Avstream的一个成员结构,必须要在Avstream初

始化後(30)再对其初始化(AVStream的初始化用到AVFormatContex)。虽然成员变量比较多,但是这里只说一下在

output_example.c中用到了,其他的请查阅avcodec.h文件中介绍。

// static AVStream*add_video_stream(AVFormatContext *oc, int codec_id)

AVCodecContext *c;

st = av_new_stream(oc, 0);

c = st->codec;

c->codec_id = codec_id;

c->codec_type = CODEC_TYPE_VIDEO;

c->bit_rate = 400000; // 400kbits/s

c->width = 352;

c->height = 288; // CIF

// 帧率做分母,秒做分子,那么time_base也就是一帧所用时间。(时间基!)

c->time_base.den =STREAM_FRAME_RATE;

c->time_base.num = 1;

c->gop_size =12;

// here define:

// #define STREAM_PIX_FMT PIX_FMT_YUV420P

// pixel format, see PIX_FMT_xxx

// -encoding: set by user.

// -decoding: set by lavc.

c->pix_fmt = STREAM_PIX_FMT;

除了以上列出了的。还有诸如指定运动估计算法的: me_method。量化参数、最大b帧数:max_b_frames。码率控制的参数、

差错掩盖error_concealment、模式判断模式:mb_decision (这个参数蛮有意思的,可以看看avcodec.h 1566行)、

Lagrange multipler参数:lmin & lmax 和 宏块级Lagrange multipler参数:mb_lmin & mb_lmax、constant

quantization parameter rate controlmethod: cqp等。

值得一提的是在AVCodecContext中有两个成员数据结构:AVCodec、AVFrame。AVCodec记录了所要使用的Codec信息并且含有

5个函数:init、encoder、close、decode、flush来完成编解码工作(参见avcode.h 2072行)。AVFrame中主要是包含了编

码後的帧信息,包括本帧是否是key frame、*data[4]定义的Y、Cb和Cr信息等,随后详细介绍。

初始化後,可以说AVCodecContext在(8)&(10)中大显身手。先在(8)open_video()中初始化AVCodec *codec以及AVFrame*

picture:

// AVCodecContext *c;

codec = avcodec_find_encoder(c->codec_id);

……

picture =alloc_picture(PIX_FMT_YUV420P, c->width, c->height);

後在writer_video_frame(AVFormatContext *oc, AVStream *st)中作为一个编解码器的主要参数被利用:

AVCodecContext *c;

c = st->codec;

……

out_size = avcodec_encode_video(c,video_outbuf, video_outbuf_size, picture);

V.AVCodec

结构AVCodec中成员变量和成员函数比较少,但是很重要。他包含了CodecID,也就是用哪个Codec、

 

像素格式信息。还有前面提到过的5个函数(init、encode、close、decoder、flush)。顺便提一下,虽然在参考代码

output_example.c中的编码函数用的是avcodec_encode_video(),我怀疑在其中就是调用了AVCodec的encode函数,他们

传递的参数和返回值都是一致的,当然还没有得到确认,有兴趣可以看看ffmpeg源代码。在参考代码中,AVCodec的初始化

後的使用都是依附于AVCodecContex,前者是后者的成员。在AVCodecContext初始化後(add_video_stream()),AVCodec也

就能很好的初始化了:

//初始化

codec =avcodec_find_encoder(c->codec_id); (33)

//打开Codec

avcodec_open(c, codec) (34)

VI. AVFrame

AVFrame是个很有意思的结构,它本身是这样定义的:

typedef struct AVFrame {

FF_COMMON_FRAME

}AVFrame;

其中,FF_COMMON_FRAME是以一个宏出现的。由于在编解码过程中AVFrame中的数据是要经常存取的。为了加速,要采取这样

的代码手段。

AVFrame是作为一个描述“原始图像”(也就是YUV或是RGB…还有其他的吗?)的结构,他的头两个成员数据,uint8_t

*data[4],int linesize[4],第一个存放的是Y、Cb、Cr(yuv格式),linesize是啥?由这两个数据还能提取处另外一个

数据结构:

typedef struct AVPicture {

uint8_t *data[4];

int linesize[4]; // number of bytesper line

}AVPicture ;

此外,AVFrame还含有其他一些成员数据,比如。是否key_frame、已编码图像书coded_picture_number、是否作为参考帧

reference、宏块类型 *mb_type等等(avcodec.h 446行)。

AVFrame的初始化并没有他结构上看上去的那么简单。由于AVFrame还有一个承载图像数据的任务(data[4])因此,对他分

配内存应该要小心完成。output_example.c中提供了alloc_picute()来完成这项工作。参考代码中定义了两个全局变量:

AVFrame *picture,*tmp_picture。(如果使用yuv420格式的那么只用到前一个数据picture就行了,将图像信息放入

picture中。如果是其他格式,那么先要将yuv420格式初始化后放到tmp_picture中在转到需求格式放入picture中。)在

open_video()打开编解码器后初始化AVFrame:

picture =alloc_picture(c->pix_fmt, c->width, c->height);

tmp_picture =alloc_picture(PIX_FMT_YUV420P, c->width, c->height);

static AVFrame *alloc_picture(intpix_fmt, int width, int height){

AVFrame *picture;

uint8_t *picture_buf; // think aboutwhy use uint8_t? a byte!

picture = avcodec_alloc_frame();(35)

if(!picture)

return NULL;

size = avpicture_get_size(pix_fmt,width, height); (36)

picture_buf = av_malloc(size); (37)

if(!picture_buf){

av_free(picture); (38)

return NULL;

}

avpicture_fill ( (AVPicture*)picture, picture_buf, pix_fmt, width, height); (39)

return picture;

}

从以上代码可以看出,完成对一个AVFrame的初始化(其实也就是内存分配),基本上是有这样一个固定模式的。至于(35)

(39)分别完成了那些工作,以及为什么有这样两步,还没有搞清楚,需要看原代码。我的猜测是(35)对AVFrame做了基本的

内存分配,保留了对可以提取出AVPicture的前两个数据的内存分配到(39)来完成。

说到这里,我们观察到在(39)中有一个(AVPicture *)picture,AVPicture这个结构也很有用。基本上他的大小也就是要在

网络上传输的包大小,我们在后面可以看到AVPacket跟AVPicture有密切的关系。 

 

VII.AVPicture

AVPicture在参考代码中没有自己本身的申明和初始化过程。出现了的两次都是作为强制类型转换由AVFrame中提取出来的:

// open_video() 中

avpicture_fill((AVPicture *)picture,picture_buf, pix_fmt, width, height); (40)

//write_video_frame 中

// AVPacket pkt;

if(oc->oformat->flags &AVFMT_RAWPICTURE){

……

pkt.size = sizeof(AVPicture); (41)

}

在(40)中,实际上是对AVFrame的data[4]、linesize[4]分配内存。由于这两个数据大小如何分配确实需要有pix_fmt、

width、height来确定。如果输出文件格式就是RAW 图片(如YUV和RGB),AVPacket作为将编码后数据写入文件的基本数据

单元,他的单元大小、数据都是由AVPacket来的。

总结起来就是,AVPicture的存在有以下原因,AVPicture将Picture的概念从Frame中提取出来,就只由Picture(图片)本

身的信息,亮度、色度和行大小。而Frame还有如是否是key frame之类的信息。这样的类似“分级”是整个概念更加清晰。

VIII.AVPacket

AVPacket的存在是作为写入文件的基本单元而存在的。我们可能会认为直接把编码后的比特流写入文件不就可以了,为什么

还要麻烦设置一个AVPacket结构。在我看来这样的编码设置是十分有必要的,特别是在做视频实时传输,同步、边界问题可

以通过AVPacket来解决。AVPacket的成员数据有两个时间戳、数据data(通常是编码后数据)、大小size等等(参见

avformat.h 48行)。讲AVPacket的用法就不得不提到编解码函数,因为AVPacket的好些信息只有在编解码后才能的知。在

参考代码中(ouput_example.c 从362到394行),做的一个判断分支。如果输出文件格式是RAW图像(即YUV或RGB)那么就

没有编码函数,直接写入文件(因为程序本身生成一个YUV文件),这里的代码虽然在此看来没什么价值,但是如果是解码

函数解出yuv文件(或rgb)那么基本的写文件操作就是这样的:

if(oc->oformat->flags &AVFMT_RAWPICTURE) {

AVPacket pkt; // 这里没有用指针!

av_init_packet(&pkt);

pkt.flags |= PKT_FLAG_KEY // rawpicture 中,每帧都是key frame?

pkt.stream_index = st->index;

pkt.data = (uint8_t *)picture;

pkt.size = sizeof(AVPicture);

ret = av_write_frame(oc, &pkt);

}

输出非raw picture,编码后:

else{

// video_outbuf &video_outbuf_size在open_video() 中初始化

out_size = avcodec_encode_video(c,video_outbuf, video_outbuf_size, picture); (42)

if(out_size > 0){

AVPacket pkt;

av_init_packet(&pkt); (43)

pkt.pts=av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base);(44)

if(c->coded_frame->key_frame)

pkt.flags |= PKT_FLAG_KEY;

pkt.stream_index= st->index;

pkt.data= video_outbuf;

pkt.size= out_size; 

 

/* write the compressed frame in themedia file */

ret = av_write_frame(oc, &pkt);(45)

} else {

ret = 0;

}

if (ret != 0) {

fprintf(stderr, "Error whilewriting video frame\n");

exit(1);

}

其中video_outbuf和video_outbuf_size在open_video()里的初始化是这样的:

video_outbuf = NULL;

// 输出不是raw picture,而确实用到编码codec

if( !(oc->oformat->flags &AVFMT_RAWPICTURE)){

video_outbuf_size = 200000;

video_outbuf =av_malloc(video_outbuf_size);

}

(43)是AVPacket结构的初始化函数。(44)比较难理解,而且为什么会有这样的一些时间戳我也没有搞明白。其他的AVPacket

成员数据的赋值比较容易理解,要注意的是video_outbuf和video_outbuf_size的初始化问题,由于在参考代码中初始化和

使用不在同一函数中,所以比较容易忽视。(45)是写文件函数,AVFormatContext* oc中含有文件名等信息,返回值ret因该

是一共写了多少数据信息,如果返回0则说明写失败。(42)和(45)作为比较重要的SDK函数,后面还会介绍的。.

IX. Conclusion

以上分析了FFMpeg中比较重要的数据结构。下面的这个生成关系理一下思路:(->表示 派生出)

AVFormatContext->AVStream->AVCodecContext->AVCodec

|

AVOutputFormat or AVInputFormat

AVFrame->AVPicture….>AVPacket

二.FFMpeg 中的函数:

在前一部分的分析中我们已经看到FFMpeg SDK提供了许多初始化函数和编码函数。我们要做的就是对主要数据结构正确的初

始化,以及正确使用相应的编解码函数以及读写(I/O)操作函数。作为一个整体化的代码SDK,FFMpeg有一些他自己的标准

化使用过程。比如函数av_register_all(); 就是一个最开始就该调用的“注册函数”,他初始化了libavcodec,“注册”

了所有的的codec和视频文件格式(format)。下面,我沿着参考代码(ouput_example.c)的脉络,介绍一下相关函数。

 

/******************************************************************

main()

******************************************************************/

1. av_register_all ();

usage: initialize ibavcoded, andregister all codecs and formats

每个使用FFMpeg SDK的工程都必须调用的函数。进行codec和format的注册,然后才能使用。声明在allformats.c中,都是

宏有兴趣看看。

2. AVOutputFormat guess_format(constchar *short_name, const char *filename, const char *mime_type)

usage: 通过文件后缀名,猜测文件格式,其实也就是要判断使用什么编码器(or解码器)。

AVOutputFormat *fmt;

fmt = guess_format(NULL, filename,NULL);

3. AVFormatContext*av_alloc_format_context(void) 

 

usage: allocate the output mediacontext.实际是初始化AVFormatContext的成员数据AVClass:

AVFormatContext *ic;

ic->av_class =&av_format_context_class;

//where

// format_to_name, options arepointer to function

static const AVClassav_format_context_class = {“AVFormatContext”, format_to_name, options};

4. static AVStream*add_video_stream(AVFormatContext *ox, int codec_id);

AVStream *video_st;

video_st = add_video_stream(oc,fmt->video_codec);

5. intav_set_parameters(AVFormatContext *s, AVFormatParameters *ap)

usage: set the output parameters(must be done even if no parameters).

AVFormatContext *oc;

// if failed, return integer smallerthan zero

av_set_parameters(oc, NULL);

6. void dump_format(AVFormatContext*ic, int index, const char *url, int is_output);

usage: 这一步会用有效的信息把 AVFormatContext 的流域(streams field)填满。作为一个可调试的诊断,我们会将这

些信息全盘输出到标准错误输出中,不过你在一个应用程序的产品中并不用这么做:

dump_format(oc, 0, filename, 1); // 也就是指明AVFormatContext中的事AVOutputFormat,还是 //AVInputFormat

7. static voidopen_video(AVFormatContext *oc, AVStream *st)

open_video(oc, video_st);

8. intav_write_header(AVFormatContext *s)

usage: allocate the stream privatedata and writer the stream header to an output media file. param s media

file

handle, return 0 if OK, AVERROR_xxxif error.

write the stream header, if any

av_write_header(oc);

9. static voidwrite_video_frame(AVFormatContext *oc, AVStream *st)

write_video_frame(oc, video_st);

10. static voidclose_video(AVFormatContext *oc, AVStream *st)

// close each codec

close_video(oc, video_st);

11. intav_write_trailer(AVFormatContext *s)

usage: write the trailer, if any.Write the stream trailer to an output media file and free the file private

data.

av_write_trailer(oc);

12. void av_freep(void *arg)

usage: free the streams. Freesmemory and sets the pointer to NULL. arg pointer to the pointer which should

be freed .

av_freep(&oc->streams[i]->codec);

av_freeep(&oc->streams[s]); 

 

13. int url_fclose(ByteIOContext*s);

usage: close the output file

url_fclose(&oc->pb);

·  14. void av_free(void *ptr)

usage: free the stream. Free memorywhich has been allocated with av_malloc(z)() or av_realloc().

av_free(oc);

/******************************************************************

******************************************************************

add_video_stream()

AVCodecContext *c

AVStream *st

******************************************************************/

******************************************************************

1.AVStream*av_new_stream(AVFormatContext *s, int id)

usage: add a new stream to a mediafile. s: media file handle, id: file format dependent stream id

st = av_new_stream(oc, 0);

/******************************************************************

******************************************************************

open_video()

AVCodecContext *c

AVCodec *codec

AVFrame *picture, *tmp_picture

uint8_t *video_output

int frame_count, video_outbuf_size;

******************************************************************

******************************************************************/

1 AVCodec *avcodec_find_encoder(enumCodecID id)

usage: find the codec of encoder byCodecID. 在前面main中的guess_format()就已经开始为此准备了。

codec =avcodec_find_encoder(c->codec_id);

2 int avcodec_open(AVCodecContext*avctx, AVCodec *codec);

usage: opens / inits the AVCodecContext.打开失败的话,返回值小于零。

avcodec_open(c, codec);

3 void *av_malloc(unsigned in size);

usage: you can redefine av_mallocand av_free in your project to use your memory allocator. You do not need

to suppress this file because thelinker will do it automatically

Memory allocation of size byte withalignment suitable for all memory accesses (including vectors if

available on the CPU). av_malloc(0)must return a non NULL pointer.

video_outbuf_size = 200000;

video_outbuf =avmalloc(video_outbuf_size);

4 static AVFrame *alloc_picture(intpix_fmt, int width, int height)

picture =alloc_picture(c->pix_fmt, c->width, c->height); 

 

/******************************************************************

******************************************************************

******************************************************************

alloc_picture()

AVFrame *picture

uint8_t *picture_buf

int size

******************************************************************

******************************************************************/

******************************************************************

1. avcodec_alloc_frame()

 

usage: initialize AVFrame* picture

picture = avcodec_alloc_frame()

2. int avpicture_get_size(intpix_fmt, int width, int height)

 

usage: 根据像素格式和视频分辨率获得picture存储大小。

size = avpicture_get_size(pix_fmt,width, height);

picture_buf = av_malloc(size)

3. int avpicture_fill(AVPicture*picture, uint8_t *ptr, int pix_fmt, int width, int height)

 

usage: Picture field are filled with‘ptr’ addresses, also return size。用ptr中的内容根据文件格式(YUV…)

和分辨率填充picture。这里由于是在初始化阶段,所以填充的可能全是零。

avpicture_fill((AVPicture*)picture,picture_buf, pix_fmt, width, height);

/******************************************************************

******************************************************************

write_video_frame()

int out_size, ret;

AVCodecContext *c;

static struct SwsContext*img_convert_ctx

******************************************************************

******************************************************************/

1 struct SwsContext*sws_getContext(int srcW, ……)

usage: 转变raw picture格式的获取context函数,比如下面的代码就是将其他格式的(如yuv422)转为yuv420,就要将

context 保存在img_convert_ctx中,然后再利用后面的介绍函数做转化。

img_convert_ctx =sws_getContext(c->width, c->height,

PIX_FMT_YUV420P,

c->width, c->height,

c->pix_fmt,

sws_flags, NULL, NULL, NULL);

2 int sws_scale(struct SwsContext*ctx, uing8_t* src[], int srcStride[], int srcSliceY, int srcSliceH,

uint8_t* dst[], int dstStride[]);

usage: 根据SwsContext保存的目标文件context将src(source)转为dst(destination)。

sws_scale(img_convert_ctx,tmp_picture->data, tmp_picture->linesize, 0, c->height,picture->data, picture-

>linesize);

4. intavcodec_encode_video(AVCodecContext *avctx, uint8_t *buf, int buf_size, constAVFrame *pict);

 

usage: 根据AVCodecContext将pict编码到buf中,buf_size是buf的大小。 

 

out_size = avcodec_encode_video(c,video_outbuf, video_outbuf_size, picture);

5 static inline voidav_init_packet(AVPacket *pkt)

usage: initialize optional fields ofa packet.初始化AVPacket。

AVPacket pkt;

av_init_packet(&pkt)

6 int64_t av_rescale_q(int64_t a,AVRational bq, AVRational cq)

 

usage: 校准时间基(maybe)

pkt.pts =av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base);

7 int av_write_frame(AVFormatContext*s, AVPacket *pkt)

 

usage: write a packet to an outputmedia file . pkt: the packet, which contains the stream_index,

buf/buf_size, dts/pts, …if errorreturn<0, if OK =0, if end of stream wanted =1

ret = av_write_frame(oc, &pke);

/******************************************************************

******************************************************************

static voidclose_video(AVFormatContext *oc, AVStream *st)

{

avcodec_close(st->codec);

av_free(picture->data[0]);

av_free(picture);

if (tmp_picture) {

av_free(tmp_picture->data[0]);

av_free(tmp_picture);

}

av_free(video_outbuf);

}

/******************************************************************

******************************************************************

讲初始化过的,特别是分配过内存的,都要释放。

注:标定红色的函数为需要编程人员自己按照实际应用情况编写的,蓝色的是FFMpegSDK中定义的函数。我们会分析函数的

作用和调用方法。

由于时间关系,写得很乱。希望没有给你带来太大困扰,这份不成熟的文档能对你有一点点地帮助。chinavideo版上有很多

大牛,我想做抛砖引玉者,希望以后能有更好的文档出现。我也是ffmpeg的初学者,如果大家有什么问题也希望能在版上一

起讨论。我的email:yunfhu@gmail.com

 

 

 


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

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

相关文章

面试之 Python 基础

1&#xff1a;为什么学习Python 家里有在这个IT圈子里面&#xff0c;也想让我接触这个圈子&#xff0c;然后给我建议学的Python&#xff0c;然后自己通过百度和向有学过Python的同学了解了Python&#xff0c;Python这门语言&#xff0c;入门比较简单&#xff0c;它简单易学&…

学习笔记(11月08日)--异常

四周三次课&#xff08;11月8日&#xff09;异常即是一个事件&#xff0c;该事件会在程序执行过程中发生&#xff0c;影响了程序的正常执行。一般情况下&#xff0c;在Python无法正常处理程序时就会发生一个异常。异常是Python对象&#xff0c;表示一个错误。当Python脚本发生异…

html浏览器的区别是什么意思,不同浏览器对css的识别有区别吗?

不同浏览器对css的识别是有区别&#xff0c;因此针对不同的浏览器去写不同的CSS。下面本篇文章给大家介绍一些常用CSS书写技巧(不同浏览器之间的差异)。有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对大家有所帮助。不同的浏览器&#xff0c;比如Int…

关于python

你是如何自学 Python 的&#xff1f; https://www.zhihu.com/question/20702054 Python 的练手项目有哪些值得推荐&#xff1f;https://www.zhihu.com/question/29372574 Python编码规范 -- Python Style Guide http://www.cnblogs.com/lxw0109/p/Python-Style-Guide.htm…

python读写文件的文本模式_Python中文件的读写、写读和追加写读三种模式的特点...

本文主要讨论一下文件的三种可读可写模式的特点及互相之间的区别,以及能否实现修改文件的操作 由于前文已经讨论过编码的事情了&#xff0c;所以这里不再研究编码&#xff0c;所有打开操作默认都是utf-8编码&#xff08;Linux系统下&#xff09; 首先我们看r&#xff08;读写&a…

html颜色叠加代码,html代码大全(基础使用代码)(颜色代码完整版)

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼您在使用以下基础使用代码时请把{}换成<>&#xff01;&#xff01;&#xff01;)(这样这个命令才成立&#xff01;&#xff01;&#xff01;)基础使用代码&#xff1a;1)贴图&#xff1a;{img src"图片地址"}2)加入…

如何看懂源代码

如何看懂源代码--(分析源代码方法) 【转载】 由于今日计划着要看Struts 开源框架的源代码 昨天看了一个小时稍微有点头绪,可是这个速度本人表示非常不满意,先去找了下资料, 觉得不错... 摘自(繁体中文Traditional Chinese):http://www.ithome.com.tw/itadm/article.php?c477…

面试之 Python 进阶

前端相关 1.谈谈你对http协议的认识。 浏览器本质&#xff0c;socket客户端遵循Http协议   HTTP协议本质&#xff1a;通过\r\n分割的规范 请求响应之后断开链接 > 无状态、 短连接 具体&#xff1a;   Http协议是建立在tcp之上的&#xff0c;是一种规范&#xff0c;它…

Lync Server外部访问系列PART5:模拟公网DNS

因为要实现外部用户访问&#xff0c;所以我们必然需要在公网DNS中添加我们所需要的一些A记录&#xff0c;而这样的测试需要我们拥有一个公网的域名以及一个可发布、可访问的IP。如果没有的话就没办法这样测试&#xff0c;所以我们今天在物理局域网中搭建一台DNS服务器&#xff…

C语言运算符和表达式

参考链接&#xff1a;http://blog.csdn.net/qq_31059475/article/details/51195091 概述 C语言一共有34种运算符&#xff0c;10种运算类型&#xff1a;算术运算符&#xff08;、-、*、/、%&#xff09;、关系运算符&#xff08;>、>、、&#xff01;、<、<&#xf…

oracle对查询结果求和_Oracle 闪回技术详解

概述闪回技术是Oracle强大数据库备份恢复机制的一部分&#xff0c;在数据库发生逻辑错误的时候&#xff0c;闪回技术能提供快速且最小损失的恢复(多数闪回功能都能在数据库联机状态下完成)。需要注意的是&#xff0c;闪回技术旨在快速恢复逻辑错误&#xff0c;对于物理损坏或是…

html整人js代码大全,Fool.js恶搞整人网页特效jQuery插件

Fool.js是一个 jQuery 插件&#xff0c;包含了几种页面特效&#xff0c;可以用来在愚人节的时候整人&#xff0c;来实现更多的更变态的愚人功能&#xff0c;当然你也可以使用这个插件完成更多好看的效果。支持的特效消失的滚动条莫名其妙播放的音乐随机消失的页面元素不间断的弹…

HDU - 5934

tarjan 视频讲解 /*** 题目链接&#xff1a;https://vjudge.net/problem/HDU-5934* 题意&#xff1a;给你n个炸弹&#xff0c;引爆每个炸弹会有一定的花费。每个炸弹给出坐标x&#xff0c;y&#xff0c;半径r&#xff0c;引爆花费&#xff1b;* 引爆一个炸弹会把范围内的炸…

Centos7-Lvs+Keepalived架构实验详解

Centos7-LvsKeepalived架构 LVSKeepalived 介绍 1 、 LVS LVS 是一个开源的软件&#xff0c;可以实现 LINUX 平台下的简单负载均衡。 LVS 是 Linux Virtual Server 的缩写&#xff0c;意思是 Linux 虚拟服务器。目前有三种 IP 负载均衡技术&#xff08; VS/NAT 、 VS/TUN 和 …

python调用matlab环境配置、非常详细!!!_[python][matlab]使用python调用matlab程序

问题引入 在做实验的时候&#xff0c;需要用到python和matlab工具来进行不同的处理&#xff0c;比如在run神经网络的时候&#xff0c;需要使用pytorch框架得到网络的各个参数&#xff0c;在得到参数后需要使用matlab进行聚类规划。之前的做法是用python脚本耦合其联系&#xff…

html里写js ajax吗,js、ajax、jquery的区别是什么?

js、ajax、jquery的区别1、JS是一门前端语言。2、Ajax是一门技术&#xff0c;它提供了异步更新的机制&#xff0c;使用客户端与服务器间交换数据而非整个页面文档&#xff0c;实现页面的局部更新。3、jQuery是一个框架&#xff0c;它对JS进行了封装&#xff0c;使其更方便使用。…

Flask 基础

Flask是一个基于Python开发并且依赖 jinja2 模板和 Werkzeug WSGI 服务的一个微型框架&#xff0c;对于Werkzeug本质是Socket服务端&#xff0c;其用于接收http请求并对请求进行预处理&#xff0c;然后触发Flask框架&#xff0c;开发人员基于Flask框架提供的功能对请求进行相应…

IIS 部署asp.net Core程序注意事项

Install the .NET Core Windows Server Hosting bundleInstall the.NET Core Runtime修改应用程序池的.net framework版本为无托管代码转载于:https://www.cnblogs.com/Qos8/p/7616036.html

泰安第一中学2021年高考成绩查询,等级考第一天结束 泰安部分考生已完成2021年高考...

6 月 9 日&#xff0c;山东新高考进入第三天&#xff0c;也是学业水平等级考试的第一天&#xff0c;物理、思想政治、化学三门选考科目的考试已全部完成。由于选考科目不同&#xff0c;考生结束高考的进程也不同&#xff0c;9 日下午&#xff0c;选考物理、思想政治、化学的考生…

基于FFMPEG 的跨平台视频编解码研究

第33卷 第11期2011年11月武 汉 理 工 大 学 学 报JOURNALOF WUHANUNIVERSITYOFTECHNOLOGY Vol.33 No.11췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍췍Nov.2011DOI:10.3963/j.issn.1671-4431.2011.11.029基于FFMPEG 的…