FFMpeg SDK 开发手册 1

FFMpeg SDK 开发手册(1)[转载] - [3DTV]

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://leezen.blogbus.com/logs/16084170.html

FFMpeg SDK 开发手册

FFMpeg 中比较重要的函数以及数据结构如下:

1. 数据结构:

(1) AVFormatContext

(2) AVOutputFormat

(3) AVInputFormat

(4) AVCodecContext

(5) AVCodec

(6) AVFrame

(7) AVPacket

(8) AVPicture

(9) AVStream

2. 初始化函数:

(1) av_register_all()

(2) avcodec_open()

(3) avcodec_close()

(4) av_open_input_file()

(5) av_find_input_format()

(6) av_find_stream_info()

(7) av_close_input_file()

3. 音视频编解码函数:

(1) avcodec_find_decoder()

(2) avcodec_alloc_frame()

(3) avpicture_get_size()

(4) avpicture_fill()

(5) img_convert()

(6) avcodec_alloc_context()

(7) avcodec_decode_video()

(8) av_free_packet()

(9) av_free()

4. 文件操作:

(1) avnew_steam()

(2) av_read_frame()

(3) av_write_frame()

(4) dump_format()

5. 其他函数:

(1) avpicture_deinterlace()

(2) ImgReSampleContext()

以下就根据,以上数据结构及函数在ffmpeg测试代码output_example.c中出现的前后顺进行分析。在此之前还是先谈一下

ffmpeg的编译问题。在linux下的编译比较简单,这里不多说了。在windows下的编译可以参考以下网页:

http://bbs.chinavideo.org/viewthread.php?tid=1897&extra=page%3D1

值得一提的是,在使用编译后的sdk进行测试时(用到ffmpeg目录下的output_example.c)编译过程中可能会有以下两个问

题:

1 Output_example.c用到了snprintf.h这个头文件。然而这个头文件在win下和linux下有所不同。具体在win

可以用以下方法解决:

http://www.ijs.si/software/snprintf/

2 如果使用vc6,或是vc6的命令行进行编译,inline可能不认。错误会出现在common.h文件中,可以在common.h中加入

#ifdef _MSC_VAR

#define inline __inline

#endif

交待完毕进入正题。

一.FFMpeg 中的数据结构:

I. AVFormatContext

一般在使用ffmpeg sdk的代码中AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。FFmpeg代码

中对这个数据结构的注释是:format I/O context

此结构包含了一个视频流的格式内容。其中存有了AVInputFormator AVOutputFormat同一时间AVFormatContext内只能存

在其中一个),和AVStreamAVPacket这几个重要的数据结构以及一些其他的相关信息,比如title,author,copyright等。

还有一些可能在编解码中会用到的信息,诸如:duration, file_size, bit_rate等。参考avformat.h头文件。

Useage:

声明:

AVFormatContext *oc; (1)

初始化: 由于AVFormatConext结构包含许多信息因此初始化过程是分步完成,而且有些变量如果没有值可用,也可不初始

化。但是由于一般声明都是用指针因此一个分配内存过程不可少:

oc = av_alloc_format_context(); (2)

结构中的AVInputFormat*(或AVOutputFormat*)是一定要初始化的,基本上这是编译码要使用什么codec的依据所在:

oc->oformat = fmt; or oc->iformat = fmt; (3)

其中AVOutputFormat* fmtAVInputFormat* fmt。(AVInputFormat and AVOutputFormat的初始化在后面介绍。随后在参

考代码output_example.c中有一行:

snprintf(oc-filename, sizeof(oc->filename), “%s”, filename); (4)

还不是十分清楚有什么作用,估计是先要在输出文件中写一些头信息。

在完成以上步骤後,(初始化完毕AVInputFormat*(或AVOutputFormat*)以及AVFormatContext)接下来就是要利用oc初始

化本节开始讲到的AVFormatContext中的第二个重要结构。AVStream(假设已经有了声明AVStream *video_st。参考代码中

用了一个函数来完成初始化,当然也可以在主函数中做,传递进函数的参数是oc fmt->video_codec(这个在下一节介绍

(29)):

vdeo_st = add_video_stream(oc, fmt->video_codec); (5)

此函数会在后面讲到AVStream结构时分析。

AVFormatContext最后的一个设置工作是:

if( av_set_paramters(oc,NULL) < 0){ (6)

//handle error;

}

dump_format(oc, 0, filename, 1); (7)

作用就是看看先前的初始化过程中设置的参数是否符合规范,否则将报错。

上面讲的都是初始化的过程,包括AVFormatContext本身的和利用AVFormatContext初始化其他数据结构的。接下来要讲讲整

个的编解码过程。我想先将ouput_example.cmain函数内的编解码函数框架描述一下。这样比较清晰,而且编码者为了结

构清晰,在写ouput_example.c的过程中也基本上在main函数中只保持AVFormatContextAVStream两个数据结构

AVOutputFormat其实也在但是包含在AVFormatContext中了)。

// open video codec and allocate the necessary encode buffers

if(video_st)

open_video(oc, video_st); (8)

// write the stream header, if any

av_write_header(oc); (9)

// encode and decode process

for(; ;){

write_video_frame(oc, video_st); (10)

// break condition…here

}

//close codec

if(video_st)

close_video(oc, video_st); (11)

//write the trailer , if any

av_write_trailer(oc); (12)

// free the streams

for(i=0; ib_streams; i++){

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

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

}

//close the ouput file

if(!(fmt->flags & AVFMT_NOFILE)){

url_fclose(&oc->pb); (15)

}

av_free(oc); (16)

通过以上的一串代码,就可以清晰地看出AVFormatContex* ocAVStream* video_st是在使用ffmpeg SDK开发时贯穿始终的

两个数据结构。以下,简要介绍一下三个标为红色的函数,他们是参考代码output_example.c开发者自行定义的函数。这样

可以使整个代码结构清晰,当然你在使用ffmpeg SDK时也可以在主函数中完成对应的功能。在后面我们会专门针对这三个函

数做分析。

1. open_video(oc, video_st);

此函数主要是对视频编码器(或解码器)的初始化过程。初始化的数据结构为AVCodec* codecAVCodecContext* c包括用

到了的SDK函数有:

c = st->codec;

codec = avcodec_find_encoder(c->codec_id); //编码时,找编码器 (17)

codec = avcodec_find_decoder(c->codec_id); //解码时,找解码器 (18)

AVCodecContex是结构AVStream中的一个数据结构,因此在AVStream初始化後(5)直接复值给c

// internal open video codec

avcodec_open(c,codec); (19)

// allocate video stream buffer

// AVFrame *picture

// uint8_t *video_outbuf

video_outbuf_size=200000;

video_outbuf = av_maloc(video_outbuf_size); (20)

// allocate video frame buffer

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

上述三步比较容易理解,打开视频编解码codec、分配输出流缓存大小、分配每一帧图像缓存大小。其中AVFrame也是ffmpeg

中主要数据结构之一。这一步(8)是对编解码器的初始化过程。

2. write_video_frame(AVFormatContext *oc, AVStream *st)

这个函数中做了真正的编解码工作,其中的函数比较复杂先列出来慢慢分析。

用到的数据结构有AVCodecContext *c, SwsContext *img_convert_ctx。其中SwsContext是用来变换图像格式的。比如

yuv422变到yuv420等,当然也用到函数,见下面列表。

fill_yuv_image(tmp_picture, frame_count, c->width, c->height); (22)

sws_scale(img_convert_ctx, tmp_picture->, tmp_picture->linesize,

0, c->height, picture->data, picture->linesize); (23)

img_convert_ctx = sws_getContxt(c->width, c->height, PIX_FMT_YUV420P, (24)

c->width, c->heigth, c->pix_fmt, sws_flags, NULL, NULL, NULL);

由于参考代码中做的是一个编码。因此,它总是要求编码器输入的是yuv文件,而且是yuv420格式的。就会有了以上一些处

理过程。接下来调用编码器编码,数据规则化(打包)用到AVPacket,这也是ffmpeg中一个比较不好理解的地方。

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

AVPacket pkt;

av_init_packet(&pkt); (26)

//……handle pkt process, we will analyze later

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

encode就一定会有decode。而且ffmpeg专为解码而生,但是为什么在参考代码中只用了encoder呢?个人猜想是因为

encode只是用yuv420来编码,这样的yuv420生成比较容易,要是用到解码的化,还要在代码中附带一个其他格式的音视频文

件。在源代码libavcodec文件夹中有一个apiexample.c的参考代码,其中就做了编解码。有空的化我会分析一下。

3. close_video(AVFormatContext *oc, AVStream *st)

avcodec_close(st->codec);

av_free(picture->data[0]);

av_free(picture);

av_free(video_outbuf);

比较容易理解,不多说了。

以上一大段虽然名为介绍AVFormatContext。但基本上把ouput_example.c的视频编码部分的框架走了一遍,其一是想说明结

AVFormatContext的重要性,另一方面也是希望对使用FFMpeg SDK开发者有一个大致的框架。

其实,真正的一些编码函数,内存分配函数在SDK中都已经封装好了,只要搞清楚结构就能用了。而开发者要做的就是一些

初始化的过程,基本上就是针对数据结构1的初始化。

II. AVOutputFormat

虽然简单(初始化)但是十分重要,他是编解码器将要使用哪个codec的“指示”。在其成员数据中最重要的就是关于视频

codec的了:enum CodecID video_codec;

AVOutputFormat *fmt;

fmt = guess_format(NULL, filename, NULL); (28)

根据filename来判断文件格式,同时也初始化了用什么编码器。当然,如果是用AVInputFormat *fmt的化,就是fix用什么

解码器。(指定输出序列->fix编码器,指定输入序列->fix解码器?)

III. AVStream

AVStream作为继AVFormatContext後第二个贯穿始终的结构是有其理由的。他的成员数据中有AVCodecContext这基本的上是

对所使用的Video Codec的参数进行设定的(包括bit rate、分辨率等重要信息)。同时作为“Stream”,它包含了“流”

这个概念中的一些数据,比如:帧率(r_frame_rate)、基本时间计量单位(time_base)、(需要编解码的)首帧位置

start_time)、持续时间(duration)、帧数(nb_frames)以及一些ip信息。当然后面的这些信息中有些不是必须要初

始化的,但是AVCodecContex是一定要初始化的,而且就是作为初始化AVStream最重要的一个部分。我们在前面就谈到了

AVStream的初始化函数(5),现在来看看他是怎么做的:

// declaration

AVStream *video_st;

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

static AVStream *add_video_stream(AVFormatContex *oc, int codec_id){ (29)

AVCodecContext *c; // member of AVStream, which will be initialized here

AVStream *st; // temporary data, will be returned

st = av_new_stream(oc, 0); (30)

c = st->codec;

// 以下基本是针对c的初始化过程。包括比特率、分辨率、GOP大小等。

……

// 以下的两行需要注意一下,特别是使用MP4

if(!strcmp(oc->oformat->name, “mp4”) || !strcmp(oc->oformat->name, “mov”) || !strcmp(oc->oformat->name,

3gp”))

c->flags |= CODEC_FLAG_GLOBAL_HEADER;

// st传给video_st;

return st;

}

以上代码中,有几点需要注意的。一个是(30)c = st->codec是一定要做的,当然这是编程中最基本的问题,(30)是将st

这个AVSteam绑定到AVFormatContext* oc上。后面的c = st->codec是将c绑定到stAVCodecContext上。其二是对c的初始

化过程中,ouput_example.c里做的是一些基本的配置,当然作为使用者的你还希望对codec加入其他的一些编解码的条件。

可以参考avcodec.h里关于AVCodecContext结构的介绍,注释比较详细的。

关于AVStream的使用在前面介绍AVFormatContext时已有所涉及,在主函数中三个编解码函数中(8)(10)(11)中。观察相

关的代码,可以发现主要还是将AVStream中的AVCodecContext提取出来,再从中提取出AVCodec结构如在(8)中:

// open_video(oc, video_st);

// AVFormatContext *oc, AVStream *st

AVCodec *codec;

AVCodecContext *c;

c = st->codec;

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

// open the codec

avcodec_open(c, codec); (32)

同样,我们可以看到在(10)(write_video_frame())AVFrame也是做为传递AVCodecContext结构的载体而存在。(11

(close_video())比较简单,不熬述。

[@more@]

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/24790158/viewspace-1040903/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/24790158/viewspace-1040903/

你可能感兴趣的:(FFMpeg SDK 开发手册 1)