1. Video Device与Video Surface
1.1 GAL_VideoDevice
GAL_VideoDevice定义Video Device,它是GAL(Graphics Abstract Layer)层设备的抽象。
- 成员name是设备名。
- 成员screen是设备的Surface,视频数据渲染在Surface的表面。这是一个GAL_Surface结构。
- 成员hidden是设备的平台特有的数据。这是一个GAL_PrivaeVideoData结构。
- 成员info是与一般的视频设备属性,如capability等。这是一个GAL_VideoInfo结构。
- 成员函数VideoInit()初始化Video Device。
- 成员函数SetVideoMode()设置视频模式。它还创建GAL_Surface实例,并连接到设备的数据缓存上。
全局变量__mg_current_video保存全局唯一的的GAL_VideoDevice实例。
GAL_VideoDevice *__mg_current_video = NULL;
1.2 VideoBootStrap
VideoBootStrap负责创建GAL_VideoDevice实例。
- 成员name是设备名。
- 成员desc是设备描述信息
- 成员函数available()检查是否支持指定的Video Device。
- 成员函数create()创建指定平台类型的设备,也就是GAL_VideoDevice实例。
不同平台提供了自己的VideoBootStrap实例。全局数组 bootstrap[]中保存了这些实例。这里使用基于开源软件gvfb的模拟环境,使用的实例是PCXVFB_bootstrap。
VideoBootStrap *bootstrap[] = {
&DUMMY_bootstrap,
&PCXVFB_bootstrap,
&X11_bootstrap,
...
};
VideoBootStrap PCXVFB_bootstrap = {
"pc_xvfb",
"PCX Virtual FrameBuffer",
PCXVFB_Available,
PCXVFB_CreateDevice
};
1.3 GAL_Surface
GAL_Surface是GAL_VideoDevice的绘制表面。
-
成员flags是Surface的标志,其中部分bit指定Surface的类型。类型是如下值。
#define GAL_SWSURFACE 0 /* Surface is in system memory */ #define GAL_HWSURFACE 1 /* Surface is in video memory */
-
成员pixels指向数据缓存。对于硬件Video Device,就是显存。
-
成员format是数据格式,这是个GAL_PixelFormat结构。
-
成员w、h分别是Surface的宽、高,pitch是字节对齐后的宽度。
-
成员clip_rect是剪裁区域。
1.4 PCXVFB
PCXVFB_bootstrap创建基于开源软件gvfb的GAL_VideoDevice实例。
- minigui进程在子进程中启动gvfb进程。
- 在minigui的进程和gvfb的进程之间创建基于unix socket的通信通道。
- gvfb进程创建一块共享内存,并且把共享内存的ID,通过unix socket传给minigui进程。这样建立起minigui与gvfb之间的连接。
共享内存的布局如下:
共享内存包括包括两部分,一是映射的数据显存,二是显存宽、高、颜色深度等信息的数据头。数据头hdr在前,显存video data紧随其后。
- gvfb进程更新数据头,告知显存信息。
- minigui向显存中写入数据,让gvfb显示它。
XVFBHeader定义数据头。
- 成员width、height/pitch、depth是显存的位置、大小、颜色深度信息。
- 成员fb_offset是显存在共享内存中的偏移。
GAL_PrivateVideoData的成员shmrgn指向共享内存起始地址。因为数据头在内存头部,所以成员hdr与shmrgn指向同一地址。
除了视频相关的 功能,gvfb还负责捕捉输入事件,通过Unix socket通道,将输入消息传给minigui进程。这部分在“用户输入系统INPUT”中说明。
1.5 PCXVFB_CreateDevice()
PCXVFB_CreateDevice()基于gvfb创建GAL_VideoDevice实例。
- 调用malloc(),创建GAL_VideoDevice实例。
- 调用malloc(),创建实例的成员hidden,也就是GAL_PrivateVideoData。
- 设置GAL_VideoDevice的成员函数,如VideoInit()、SetVideoMode()等。
- 对于PCXVFB,对应的成员函数是PCXVFB_VideInit()、PCXVFB_SetVideoMode()。
1.6 PCXVFB_VideoInit()
PCXVFB_VideoInit()初始化GAL_VideoDevice实例。
-
调用GetMgEtcValue(),从配置文件中依次读取
- gvfb的可执行文件路径 /use/local/bin/gvfb
- 窗口名 XVFB-for-MiniGUI-3.0-(Gtk-Version)
- 希望的显存模式 360x480-32bpp。调用函数GAL_ParseVideoMode()将这个字符串分解成宽360、高480、颜色深度32。
-
调用socket(),创建一个UNIX socket,保存在全局变量__mg_pcxvfb_server_sockfd中。
int __mg_pcxvfb_server_sockfd;
-
调用bind(),将socket绑定在地址/tmp/pcxvfb_socketXXX,XXX为进程ID号。
-
调用listen()将socket置于监听状态。
-
调用shm_init_lock(),创建和初始化以进程ID为key的semaphore。其中,semget()创建semaphore,semctl()将semaphore的值设置为1。
-
调用fork()启动gvfb子进程。
在gvfb子进程中,
- 调用execl_pcxvfb()运行gvfb。其中,
- 调用GetMgEtcValue(),与minigui进程一样,调用GetMgEtcValue(),从配置文件中依次读取gvfb的可执行文件路径、窗口名、 运行模式。
- 用这些配置参数,调用execlp()运行gvfb。
- gvfb进程应该创建共享内存,映射到显存,并通过unix socket连接minigui进程。
在minigui进程中,
-
调用my_select()在UNIX socket上等待gvfb子进程连接。成功返回后,调用accept()接收连接,得到新的数据socket,保存在全局变量__mg_pcxvfb_client_sockfd中。
int __mg_pcxvfb_client_sockfd;
-
调用my_select(),在数据socket __mg_pcxvfb_client_sockfd上等待。 成功返回后,调用read()读取gvfb进程写入的共享内存id。
-
调用shmat(),得到共享内存的地址。将这个地址保存在GAL_PrivateVideoData的成员shmrgn中。同时让成员hdr也指向它。
1.7 PCXVFB_SetVideoMode()
PCXVFB_SetVideoMode()设置GAL_VideoDevice的模式。更重要的是,它还设置显存地址。
- 将Video Device的类型设置为GAL_HWSURFACE,表示这是一个硬件显存
- 将成员pixels设置为显存在共享内存中的偏移位置。如前所说,这是gvfb暴露给minigui进程的显存地址。
// pcxvfb.c
GAL_Surface *PCXVFB_SetVideoMode (_THIS, GAL_Surface *current,
int width, int height, int bpp, Uint32 flags)
{
current->flags = GAL_HWSURFACE | GAL_FULLSCREEN;
current->w = this->hidden->hdr->width;
current->h = this->hidden->hdr->height;
current->pitch = this->hidden->hdr->pitch;
current->pixels = this->hidden->shmrgn + this->hidden->hdr->fb_offset;
current->format->MSBLeft = this->hidden->hdr->MSBLeft;
...
}
1.8 GAL_VideoInit()
GAL_VideoInit()创建GAL_VideoDevice设备并初始化。
-
调用GAL_GetVideo()得到创建设备。其中,
- 遍历全局数组bootstrap[],找到指定名字的VideoBootStrap实例。
- 调用VideoBootStrap的成员函数available(),检查其是否可用。这里调用的是PCXVFB_Available()。
- 如果可用,调用VideoBootStrap的成员函数create()创建实例。这里调用的是PCXVFB_CreateDevice()。
- 最后设置实例的属性,如名字等。这里名字是”pc_xvfb”。
-
将设备实例保存在全局变量__mg_current_video中。
-
调用GAL_VideoDevice的成员函数VideoInit(),初始化设备。这里调用的是PCXVFB_VideoInit()。
-
调用GAL_CreateRGBSurface()创建Surface,设置为GAL_VideoDevice的成员screen。
1.9 GAL_CreateRGBSurface()
GAL_CreateRGBSurface() 创建Surface。
- 调用malloc(),创建GAL_Surface实例。
- 调用GAL_AllocFormat(),创建GAL_PixelFormat结构。GAL_Surface实例的成员format设置成这个值。
- 调用GAL_CalculatePitch(),计算surface每行的对齐宽度。
- 调用GAL_SetClipRect(),将surface的剪裁区域置空。
- 让GAL_Surface的成员pixels暂时指向本地静态内存。
1.10 GAL_GetVideoMode()
GAL_GetVideoMode()得到合适的模式,包括宽、高、颜色深度。
-
调用GAL_VideoModeOK()检查希望的模式是否支持,得到实际支持的颜色深度。如果不支持,返回失败。如果支持,颜色深度与指定的颜色深度相同,则返回成功。
- GAL_VideoModeOK()首先检查模式,包括宽、高、颜色深度,找到最近的颜色深度。如果不支持这个模式,则返回false;如果支持,则对比全局数组GAL_closest_depths[4][8]中预定义的模式,选择最接近的颜色深度。
-
如果深度不同,则遍历GAL_closest_depths[4][8],得到接近的深度。
-
调用GAL_ListModes()得到支持的长宽模式。
-
根据指定的GAL_PixelFormat,调用GAL_VideoDevice的成员函数ListModes(),这里是PCXVFB_ListModes()。但PCXVFB_ListModes()什么都没做。
1.11 GAL_SetVideoMode()
GAL_SetVideoMode()设置模式,并连接设备的显存。
- 调用GAL_GetVideoMode(),得到支持的模式。
- 调用GAL_VideoDevice的成员函数SetVideoMode(),设置模式并连接设备显存。这里调用PCXVFB_SetVideoMode()。
网友评论