-
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()
p>
以下就根据,以上数据结构及函数在
ffmpeg
< br>测试代码
output_example.c
中出现的前
后顺进行分析。
交待完毕进入正题。
一.
FFMpeg
中的数据结构:
I. AVFormatContext
一般在使用
ffmpeg sdk
的代
码中
AVFormatContext
是一个贯穿始终的数据结
构
,
很多函数都要用到
它作为参数。<
/p>
FFmpeg
代码中对这个数据结构的注释是:
< br>format I/O context
此结构包含了一个视频流的格
式内容。其中存有了
AVInputFormat
(
or AVOutputFormat
同一时间
AVFormatContext
内只能存在其中一
个)
,
和
AVStream
、
AVPacket
这几个重要的数据结构以及
一些其他的相关信息
,
比如
title
,author,copyright
等。还有一些可能在编解码中会用到的信息,诸如
:
duration, file_size, bit_rate
等。参考
avformat.h
头文
件。
Useage:
声明:
AVFormatContext *oc; (1)
初始化:
由于
AVFormatConext
结构包含许多信息因此初始化过程是分步完成,而且有
些变量如果
没有值可用,也可不初始化。但是由于一般声明都是用指针因此一个分配内存
过程不可少:
oc =
av_alloc_format_context(); (2)
< br>结构中的
AVInputFormat*
(或
AVOutputFormat*
)是一定要初始化的,基本上这是编译码
要使用什
么
codec
的依据所在:<
/p>
oc->oformat =
fmt; or oc->iformat = fmt; (3)
其中
AVOutputFormat*
fmt
或
AVInputFormat* fmt
。
(
AVInputFormat and AVO
utputForm
at
的初始
化在后
面介绍。随后在参考代码
output_example.c
中
有一行:
snprintf(oc-filename,
sizeof(oc-
>filename), “%s”, filename);
(4)
还不是十分清楚有什么作用,估计是先要在输出文件
中写一些头信息。
在完成以上步骤後,
(初始化完毕
AVInpu
tFormat*
(或
AVOutputFormat*
)
以及
AVFormatContext
p>
)
接下来就是要利用
oc
< br>初始化本节开始讲到的
AVFormatContext
中的第二个重要结构。
AVStream
(假设已
经有了声明
AVStream *video_st
。参考代码用了一个函数来完成初始化,当然也可以在主函数中做,传递
进函数的参数是
oc
和
fmt->video_co
dec
(这个在下一节介绍
(29)
)
:
vdeo_st =
add_video_stream(oc, fmt->video_codec); (5)
此函数会在后面讲到
AVStrea
m
结构时分析。
< br>AVFormatContext
最后的一个设置工作是:
if(
av_set_paramters(oc,NULL) < 0){ (6)
//handle error;
}
dump_format(oc, 0, filename, 1); (7)
作用就是看看先前的初始化过程中设置的参数是否符合规范,
否则将报错。
上面讲的都是初始化
的过程
,
包括
AVFormatCon
text
本身的和利用
AVFormatContext
初始化其他数
据结构的。接下来要讲讲整个的编解码过程。我想先将<
/p>
ouput_example.c
中
ma
in
函数内的编解码函数框
架描述一下。这样比较清晰,而且编
码者为了结构清晰,在写
ouput_example.c
的过
程中也基本上在
main
函数中只保持
AVFormatContext
和
AVStream
两个数据结构
(
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; 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*
oc
和
AVStream*
video_st
是在使用
ffmpeg SD
K
开发时贯穿始终的两个数据结构。以下,简要介绍一下三个标为红色的函数,他们是参
考代码
output_example.c
开发者自行定义的函
数。这样可以使整个代码结构清晰,当然你在使用
ffmpeg SDK
时
也可以在主函数中完成对应的功能。在后面我们会专门针对这三个函
数做分析。
1. open_video(oc, video_st);
此函数主要是对视频编码器(或解码器)的初始化过程。初始
化的数据结构为
AVCodec*
codec
和
AVCodecContext* c
包括用到了的
SDK
函数有:
c = st->codec;
codec =
avcodec_find_encoder(c->codec_id);
//
编码时,找编码器
(17)
codec =
avcodec_find_decoder(c->codec_id);
//
解码时,找解码器
(18)
AVCodecContex<
/p>
是结构
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
文件,而且是
yuv4
20
格式
的。就会有了以上一些处理过程。接下来调用编码器编
码,数据规则化(打包)用到
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
就一定会有
d
ecode
。
而且
ffmpeg
专为解码而生
,
但是为什么在参考代码中只用
了
encoder
呢?个人猜想是因为
encode
只是用
yuv420
来编
码,这样的
yuv420
生成比较容易,要是用到解码的化,<
/p>
还要在代码中附带一个其他格式的音视频文件
。
< br>在源代码
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);
比较容易理解,不多说了。
以上一大段虽然名为介绍
AVFor
matContext
。但基本上把
ouput_exampl
e.c
的视频编码部分的框架
走了一遍,其一是想说明结构
p>
AVFormatContext
的重要性,另一方面也是希望对使
用
FFMpeg
SDK
开发
者有一个大致的框架。
其实,真正的一些编码函数,内存分配函数在
SDK
中都已
经封装好了,只要搞清楚结构就能用了。
而开发者要做的就是一些初始化的过程,基本上
就是针对数据结构
1
的初始化。
II.
AVOutputFormat
虽然简单(初始化)但是十
分重要,他是编解码器将要使用哪个
codec
的
“
指示
”
。在其成员数据中
最
重要的就是关于视频
codec
的了:
enum
CodecID video_codec;
AVOutputFormat *fmt;
fmt = guess_format(NULL, filename,
NULL); (28)
根据
fi
lename
来判断文件格式
,
同时也
初始化了用什么编码器
。
当然,
如果是
用
AVInputFormat *fmt
的化,就是
fix
用什么解码器。(指定输出序列
->fi
x
编码器,指定输入序列
->fix
解
码器?)
III.
AVStream
AVStream
作为继
AVFormatContext
後第二个贯穿始终的
结构是有其理由的。他的成员数据中有
AVCodecContext
< br>这基本的上是对所使用的
Video Codec
的参数
进行设定的
(包括
bit rate
、
分辨率等重要
信息)。同时作为
“Stream”
,它包含了
“
流
”
这个概念中的一些数据,比如:帧率(<
/p>
r_frame_rate
)、基本时间计量单位(
time_base
)、(需要
编解码的)首帧位置
(
start_time
)、持续时间(
duration
)、帧数(
nb_frames
)以及一些
ip
信息。当然
后面的这些信息中有些不是必须要初
< br>始化的,但是
AVCodecContex
是一定要初始
化的,而且就是作为初始化
AVStream
最重要的一个部<
/p>
分。我们在前面就谈到了
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
大小等
。
……
//
以下的两行需要注意一下,特别是使用
< br>MP4
的
< br>if(!strcmp(oc->oformat-
>name,
||
!strcmp(oc->oformat-
>name, “3gp”))
c->flags |=
CODEC_FLAG_GLOBAL_HEADER;
//
将
st
传给
video_st;
return st;
}
以上代码中,有几点需要注意的。一个是
(30)
和
c = st->codec
< br>是一定要做的,当然这是编程中最基
本的问题,
(30)
是将
st
这个
AVSteam
绑定到
AVFormatContext*
oc
上。后面的
c = st->codec
< br>是将
c
绑定
到
< br>st
的
AVCodecContext
< br>上。其二是对
c
的初始化过程中,
ouput_example.c
里做的是一些基本的配置,
当然作为使用者的你还希望对
codec
加入其他的一些编解码
的条件。
可以参考
avcodec.h
里关于
AVCodecConte
xt
结构的介绍,注释比较详细的。
关于
AVStream
的
使用在前面介
绍
AVFormatContext
时已有所涉及,在主函数中
三个编解码函数中
(8)
、
(10)<
/p>
和
(11)
中。观察
相关的代码,可以发现主要还是将
AVStream
中的<
/p>
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
也是做为传递
AVCo
decContext
结构
的载体而存在。(
< br>11
)
< br>(close_video())
比较简单,不熬述。
IV. AVCodecContext
“mp4”)
||
!strcmp(oc
->oformat-
>na
me,
“mov”)
此结构在
Ffmpeg
SDK
中的注释是
:
main
external api structure
其重要性可见一斑
。
而且在
avcodec
它的定义
处,对其每个成员变量,都给出了十分详细的介绍。应该说
AVCodecContex
t
的初始化是
Codec
使
用
中最
重要
的一
环。
虽然
在
前面
的
AVStream
中
已经
有所
提
及,
但是
这里
还是
要在
说一
遍
。
AVCodecCo
ntext
作为
Avstream
的一
个成员结构,必须要在
Avstream
初始化後
(30)
再对其初始化
(AVStream
的
初
始
化
用
到
AVFormatContex)
。
虽
然
成
< br>员
变
量
比
较
多
,
但
是
这
里
只
说
p>
一
下
在
outpu
t_example.c
中用到了,其他的请查阅
avcode
c.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; // 400 kbits/s
c->width = 352;
c->height = 288; // CIF
//
帧率做分母,秒做分子,那么
t
ime_base
也就是一帧所用时间。(时间基!)
c->time_ =
STREAM_FRAME_RATE;
c->time_
= 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
multi
pler
参数:
mb_lmin &
mb_lmax
、
constant
quantization parameter rate control method:
cqp
等。
值
得一提的是在
AVCodecContext
中有两个成员数
据结构:
AVCodec
、
AVFra
me
。
AVCodec
记录了所要使<
/p>
用的
Codec
信息并且含有
5
个函数
:
init
、
encoder
、
close
、
decode
、
flush
来完成编解码工作
(参见
avcode.h 2072
行)
。
AVFrame
中主要是包含了编码後的帧信息
,包括本帧是否是
key frame
、
*data[4]
定义的
Y
、
Cb
和
Cr
信息等,
随后
详细介绍。
< br>初始化後
,
可以说
AVCode
cContext
在
(8)&(10)
中大显身手
。
先在
(8)open_v
ideo()
中初始化
AVCodec
*codec
以及
AVFrame*
picture
:
// AVCodecContext *c;
codec =
avcodec_find_encoder(c->codec_id);
……
picture =
alloc_picture(PIX_FMT_YUV420P, c->width,
c->height);
後在
wr
iter_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
、
< br>
像素格式信息。还有前面提到过的
< br>5
个函数(
init
、
encode
、
close
、
decoder
、
flush<
/p>
)。顺便提
一下,虽然在参考代码
out
put_example.c
中的编码函数用的是
avcode
c_encode_video
(),我怀疑在其
中就是调用了
AVCodec
的
encode
函数,他们传递的参数和返回值都是一致的,当然还没有得到确认,有
兴趣可以看看
ffmpeg
源代码。在参考代码中,
AVCodec
的初始化後的使用都是依附于
AV
CodecContex
,
前者是后者的成员。在
AVCodecContext
初始化後(
add_
video_stream()
)
,
A
VCodec
也就能很好的初始
化了:
//
初始化
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
中的数据是要经
常存取的。为了加速,要采取这样的代码手段。
-
-
-
-
-
-
-
-
-
上一篇:投影机厂商名单
下一篇:船公司简介及英文名称