mplayer代码阅读

来源:互联网 发布:万国数据业内排名 编辑:程序博客网 时间:2024/05/16 15:21

注:本文来自小嵌的Blog,http://blog.mcuol.com/User/AT91RM9200/Article/55918_1.htm

本文只关注FBDEV相关的内容,其他的或不提,或略过。[由于时间比较紧,许多地方肯定还有不足的地方,欢迎指教]

首先从Mplayer.c中的main函数开始,
注意到
//================== Init VIDEO (codec & libvo) ==========================
current_module=”preinit_libvo”;
if(!(video_out=init_best_video_out(video_driver_list)))


--


mpctx->video_out=init_best_video_out(video_driver_list);


--


这将跳进Video_out.c
for(i=0;video_out_drivers[i];i++){
if(!video_driver->preinit(vo_subdevice))
如果你的FBDEV打开的话,即若有
#ifdef HAVE_FBDEV
&video_out_fbdev,
&video_out_fbdev2,
那么,video_driver->preinit(vo_subdevice)这句话将跳进Vo_fbdev.c中执行preinit操作。
Preinit中进行了
fb_dev_fd = open(fb_dev_name, O_RDWR)
和一些ioctl对framebuffer设备的信息进行读取:
ioctl(fb_dev_fd, FBIOGET_VSCREENINFO, &fb_vinfo)
至此,FBDEV相关的pre初始化操作基本结束。


。。。省略其他无关的信息
进入
/*========================== PLAY VIDEO =============
这里面最重要的一个操作就是解出一帧:
//——————– Decode a frame: ———————-
// get it!
current_module=”video_read_frame”;
具体如何解赞不提,但应关注一下结果:
in_size=video_read_frame(sh_video,&next_frame_time,&start,force_fps);
其中video_read_frame是Video.c中的函数,这个操作得到了这一帧的数据信息在
内存区的起始值start和长度in_size;


由于现在得到的帧还只是未解码的信息,
下一步就是要进行解码操作:
// decode:
current_module=”decode_video”;
blit_frame=decode_video(sh_video,start,in_size,drop_frame);
执行时将跳进Dec_video.c中的decode_video函数,
mpi=mpvdec->decode(sh_video, start, in_size, drop_frame);
之后,得到了这一帧的原始的mp_image_t的图像格式,基本上可以说是最终可用的数据了。
下一步就是如何将数据刷进Fb设备中了。
vf=sh_video->vfilter;
ret = vf->put_image(vf,mpi); // apply video filters and call the leaf vo/ve
这段实际上就是我们想要的。
原来的作者曾有一句注释掉的,则更为清晰:
//vo_draw_image(video_out,mpi);
意思则是说将mpi数据直接刷到已经初始化好的video_out中去。
Vf_vo.c中实现了上述一段的主要功能:
#define video_out ((vo_functions_t*)(vf->priv)) 定义了video_out实际为vo_functions_t类型,
而vf->put_image(vf,mpi)的实现中
video_out->draw_slice(mpi->planes,mpi->stride,mpi->w,mpi->h,mpi->x,mpi->y);
调用draw_slice函数的原型为在Video_out.h中的
/*
* Draw a planar YUV slice to the buffer:
* params:
* src[3] = source image planes (Y,U,V)
* stride[3] = source image planes line widths (in bytes)
* w,h = width*height of area to be copied (in Y pixels)
* x,y = position at the destination image (in Y pixels)
*/
uint32_t (*draw_slice)(uint8_t *src[], int stride[], int w,int h, int x,int y);
也就是此时将调用Vo_fbdev.c中的
static uint32_t draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y)
{
uint8_t *d;
uint8_t *s;


d = center + (fb_line_len * y + x) * fb_pixel_size;


s = src[0];
while (h) {
memcpy(d, s, w * fb_pixel_size);
d += fb_line_len;
s += stride[0];
h–;
}


return 0;
}
从而完成从mpi到framebuffer的内存拷贝!


============================================
mplayer大体框架及流程可参考网络其他资料。这里只是记录一些小细节。       mplayer解码视频数据时基本可以分为如下几个步骤:解析参数、demux、选择音视频codec(包括选择初始化vo  vf等)、解码数据、输出到显示设备,依次循环解码显示。
1、先看了视频codec的选择部分。大体过程如 下:reinit_video_chain()--> init_best_video_codec(sh_video,video_codec_list,video_fm_list)-->init_video(sh_video, NULL, NULL, status, &selected)--> find_video_codec(sh_video->format,sh_video->bih ? ((unsigned int              sh_video->codec, force))-->find_codec(fourcc, fourccmap, start, 0, force);   其中归根结底是在find_codec中进行匹配,匹配方式是通过FOURCC进行的,FOURCC可以对视频数据进行标识,在/etc /codec.conf中定义了所有的解码器,其中有两个关键字fourcc与format都代表了FOURCC字段,从代码 common_init()-->parse_codec_cfg(MPLAYER_CONFDIR "/codecs.conf")中可以看出,format与fourcc字段都被添加到fourcc中,都标识视频编码格式,选择过程即 init_video通过两次循环,首先查找status低于untested的编码格式与fourcc匹配的所有编码器,第二次循环式在这些编码器重查 找第一个status=working的编码器,并执行init初始化如果成功,则选择此codec,如果不成功,继续选择下面的。 所以存在这种情况,即有多个解码器都可以解码此文件,则mplayer从第一个开始选。
2、vf--vo流程:
         选择vo:首先通过reinit_video_chain()-->init_best_video_out(video_driver_list) 选择一个合适的vo,(注 preinit成功的第一个vo即选中)。     
         init_best_video_codec(sh_video,video_codec_list,video_fm_list)-->init_video(sh_video, NULL, NULL, status, &selected)-->init(sh_video)中调 mpcodecs_config_vo(sh,sh->disp_w,sh->disp_h,sh->bih ? sh->bih->biCompression : sh->format)中调用vf->query_format(vf, out_fmt)来匹配codec中的out输出格式,如果有不需要转化支持的(VFCAP_CSP_SUPPORTED_BY_HW),则选择。否则选 择第一个满足VFCAP_CSP_SUPPORTED的输出格式。并且在下面代码中还会调用vf_config_wrapper(vf, sh->disp_w, sh->disp_h, screen_size_x, screen_size_y, vocfg_flags,out_fmt) == 0)-->vf->config(vf, width, height, d_width, d_height, flags, outfmt);(位置在libmpcodecs\vf_vo.c),代码中包含++vo_config_count使得后面显示的时候不会因 vo_config_count==0而退出。
       下面是解码播放视频时,update_video(int *blit_frame)-->filter_video(sh_video, decoded_frame,sh_video->pts)-->vf->put_image(vf, mpi, pts)。  put_image会调用vo实现的显示方法。
        其中,vf->put_image(vf, mpi, pts)是在libmpcodecs/vf_vo.c中定义的,在里面赋值具体使用drawslice 还是别的显示方式。