前言
ffmeg源码讲解的博客很多,可是完全讲解清楚原理的,觉得还不太多,这里就稍微分析一下,无论是编码器,和解码器,都是注册一下,然后在特定的流,对应特定的编解码器。具体我们借助官方的提供的ffpalyer,稍微分析下。
正文
首先是使用,这里超级简单
ffplayer.exe +"文件名"
这是最简单的方法,这里我们目光不是控制,仅仅大概了解ffpeg的流程,这里我们就不详细介绍。
下面我们开始代码阅读
int main(int argc, char **argv)
{
......
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
#if CONFIG_AVFILTER
avfilter_register_all();
#endif
av_register_all();
avformat_network_init();
......
is = stream_open(input_filename, file_iformat);
......
event_loop(is);
......
}
这里显示用的SDL,这是一个开源的可以用来渲染支持surface的一个多平台的玩意,代码量比较小,如果有机会可以研究下,不过这里暂时不再纠结这个问题,反正,得到的每一帧数据,都是用SDL来渲染的,知道这些就够了。我们关注我们的重点。
前边几个是条件编译,我做过详细阅读。这些其实不是关键,最核心的几个函数是
av_register_all(); //这是吧所有的编解码结构体给注册完成
is = stream_open(input_filename, file_iformat);//开启四个线程,完成视频音频,和读取文件的工作
event_loop(is); //更新页面相应键盘。
首先看下注册的函数
void av_register_all(void)
{
static AVOnce control = AV_ONCE_INIT;
ff_thread_once(&control, register_all);
}
static void register_all(void)
{
avcodec_register_all();
/* (de)muxers */
REGISTER_MUXER (A64, a64);
REGISTER_DEMUXER (AA, aa);
REGISTER_DEMUXER (AAC, aac);
REGISTER_MUXDEMUX(AC3, ac3);
......
}
void avcodec_register_all(void)
{
static AVOnce control = AV_ONCE_INIT;
ff_thread_once(&control, register_all);
}
static void register_all(void)
{
/* hardware accelerators */
REGISTER_HWACCEL(H263_VAAPI, h263_vaapi);
......
REGISTER_ENCODER(A64MULTI5, a64multi5);
REGISTER_DECODER(AASC, aasc);
}
看到代码的第一反应,着都是干啥的呢,乌七八糟的,不过耐心看,其实这些整整齐齐的东西,会觉得很可爱。
#define REGISTER_MUXER(X, x) \
{ \
extern AVOutputFormat ff_##x##_muxer; \
if (CONFIG_##X##_MUXER) \
av_register_output_format(&ff_##x##_muxer); \
}
void av_register_output_format(AVOutputFormat *format)
{
AVOutputFormat **p = last_oformat;
while(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
p = &(*p)->next;
if (!format->next)
last_oformat = &format->next;
}
这些结构非常简单,也就是把extern AVOutputFormat ff_##x##_muxer;
这个变量给注册到一个叫做last_oformat
中。
其他都是同理,这些触发器,这里暂时不说,关键看下我们的编解码器。
#define REGISTER_DECODER(X, x) \
{ \
extern AVCodec ff_##x##_decoder; \
if (CONFIG_##X##_DECODER) \
avcodec_register(&ff_##x##_decoder); \
}
其实这里很容易理解,还是注册到一个全局的链表,这里暂时不要纠结到底在哪里,不用纠结,可是这个变量到底是啥呢?我们试着找一找,毕竟假如这个是h264,我应该定义一个exterel的ff_h264_decoder
AVCodec ff_h264_decoder = {
.name = "h264",
.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(H264Context),
.init = h264_decode_init,
.close = h264_decode_end,
.decode = h264_decode_frame,
.capabilities = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
AV_CODEC_CAP_FRAME_THREADS,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING,
.flush = flush_dpb,
.init_thread_copy = ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),
.update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),
.profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles),
.priv_class = &h264_class,
};
这里解码也是这个东东,总之,就是吧全局的解码器,集中到一个列表中,便于查找。
is = stream_open(input_filename, file_iformat)
这个是控制开启解码库,等等读取文件等操作,限于篇幅,暂时不做详细解析。
static void event_loop(VideoState *cur_stream)
{
SDL_Event event;
double incr, pos, frac;
for (;;) {
double x;
//这里一直刷新,用来更新通过stream_open函数打开的线程更新出来的视频帧。
refresh_loop_wait_event(cur_stream, &event);
switch (event.type) {
case SDL_KEYDOWN:
......//这里是处理键盘操作的。
}
}
}
这里代码逻辑比较复杂,就不追踪,有机会好好分析这里。
后记
这里其实没有过多的介绍,不过对于整个代码架构稍微有些了解,下一篇我们研究一些stream_open函数的具体做了啥。