2011年11月9日星期三

bigbluebutton客户端主程序及初始化流程

前面一篇文章已经提及到了bigbluebutton的main目录结构,这里再详细分析一下。bbb-client采用了mate框架。events目录定义了主程序逻辑相关的事件,这些事件都是继承自flash.events.Event,并且事件的bubbles属性要设置为true才行(mate框架的需要),具体的event可以在用到的时候大致看一下,就是一个数据结构,没有什么逻辑。

ApplicationEventMap.mxml

mate框架的中定义了一些EventHandlers,其中的type属性就是事件类型(字符串),可以生成generator属性指定的对象,cache属性指定对象的作用域,global就是全局作用域。FlexEvent.PREINITIALIZE是预初始化的事件,如果有全局的对象,可以在这里指定。

下面的处理的事件有下面几类,在内定义了,表示事件发生时调用其generator属性指定的method,并可以在arguments中指定参数:

PortTestEvent - 触发测试RTMP端口,端口测试成功,端口测试失败。对应的类为ModuleProxy,可以参考对应的as文件。

LogoutEvent - 用户logout调用UserService.logoutUser()和ModulesProxy.handleLogout(),测试连接断开调用UserService.disconnectTest()
UserServicesEvent - 启动服务,也是调用UserService.startService()方法

UsersConnectionEvent - 连接成功,也是调用UserService.userLoggedIn()方法

SuccessfulLoginEvent - 成功登录,调用ModulesProxy.loadAllModules()

RaiseHandEvent - 举手发言,调用UserService.raiseHand()

LowerHandEvent - 放弃发言,调用UserService.lowerHand()

BroadcastStartedEvent - 流广播开始事件(具体是什么流还不太清楚),调用UserService.addStream()

BroadcastStoppedEvent - 流广播结束事件,调用UserService.removeStream()

KickUserEvent - 踢人事件, 调用UserService.kickUser()

ConfigEvent - 配置读取后触发,依次调用ConfigManager.setConfig(),SkinningService.loadSkins()。

下面紧接的两个HTTPService好像没什么用,并没有调用。

需要重点分析的是model目录下的文件。

BigBlueButtonPreloader - 下载进度条,这里主要是flash的下载和初始化进度,之前也看到过LoadingBar.mxml,那个主要是显示模块的加载进度,模块加载后面再分析。

ConferenceParameters - 存放会议的相关信息,没什么逻辑,就是一个结构体。包括会议名称,当前的用户名,用户的角色(协调者或者普通参与者),room id,语音会议bridge的地址(asterisk或者freeswitch),语音会议bridge的外部地址(这里不太确定),欢迎提示,会议id,外部用户id(不清楚),logout地址,connection(不清楚是什么连接,和red5的?),内部用户id(不清楚),是否录制会议。

ConfigParameters - 加载conf/conf.xml配置文件,这里会构造ModuleDescriptors,后面可以再仔细分析。

LayoutOptions - 控制一些UI上的显示。

PortTest - 用来测试多种flash支持的网络协议的联通性

PortTestProxy - 对PortTest做了个包装,PortTest看代码应该是第三方的。

User - 也是一个结构体

model下面还有一个User子目录,按理来说是和用户关联比较紧密的:
Status - key,value状态信息。
StatusCollection - 存储和管理Status信息。
BBBUser - 这里维护用户的状态,包括:当前的presenter是谁,是否有媒体流,是否举手了。
Conference - 维护会议的用户,包括自己和其他人的信息,从[bindable]可以看到这里是为了显示用的。
JoinService - 发起加会请求,成功后保存会议信息。
NetConnectionDelegate - 网络连接和重连
UserSOService - 举手,踢人,连接流媒体,连接服务器
UserService - 包装 JoinService和UserSOService。

接下来分析一下modules目录,整个客户端的框架基本上就分析完了。
 bigbluebutton采用模块化的方式来组织,在common目录下面,可以看到有三个接口,一个是IBbbModuleWindow,这个是模块的窗口,通过这个接口来获取窗口位置来布局,IBigBlueButtonModule,这个用来设置模块的属性,这些属性都是从config.xml中读取的,并通过事件传递过来的。

在modules目录下面,是模块相关的代码,在src目录下面,有很多*Module.mxml,这些都是继承了IBigBlueButtonModule,而且基本上每个模块都定义了自己的EventMap,mate可以定义多个EventMap,这些都会挂接到一个总的event bus上去,每个模块会定义自己的event和对应的event映射和处理函数,也可以处理全局的一些事件。

初始化流程

1. BigBlueButton.mxml的preloader第一步被执行:org.bigbluebutton.main.model.BigBlueButtonPreloader,会下载BigBlueButton.swf文件。

2. 建立ApplicationEventMap,设置事件处理表,在FlexEvent.PREINITIALIZE中,还会创建一些全局对象,包括ModulesProxy,UserService,ConfigManager,在ModulesProxy中会依次创建ModulesDispatcher, PortTestProxy和ModuleManager,ModulesDispatcher用来发送模块相关的一些事件,PortTestProxy用来测试rtmp和rtmpt端口是否是通的,ModuleManager用来管理模块的(加载,删除,传递配置信息);UserService用来加会的。ConfigManager作为一部全局的对象来存储配置信息。ModuleManager的构造函数中创建了ConfigParameters,在ConfigParameters的构造函数中,回去获取conf/config.xml,这是个异步的过程。

3. BigBlueButton.mxml的最下面的MainApplicationShell会创建一个初始化的布局,也就是打开页面后看到的进度条还有下面的一些调试窗口按钮等。ResourceUtil 初始化会加载conf/locale.xml,并且会根据默认的语言来加载对应的swf资源。

4. 配置加载成功回调到ConfigParameters::handleComplete,会将模块配置信息放到ModuleDescriptor中,并回调ModuleManager::handleComplete,在这中间会先触发PortTestEvent.TEST_RTMP,然后检查模块依赖,看所需要的模块是否都在配置列表中,最后触发ConfigEvent.CONFIG_EVENT事件。


5.ConfigEvent.CONFIG_EVENT事件有几个响应的地方,一个是在ApplicationEventMap中,会调用ConfigManager::setConfig存储配置,会调用SkinningService::loadSkins更换皮肤。第二个是在MainApplicationShell.mxml中,设置LOG窗口可见性,不过这里好像屏蔽掉了,是通过另外一个事件configLoadedEvent来触发,设置多个窗口的可见性。第三个是在MainToolbar.mxml中,设置语言选择栏可见性和帮助的URL链接。


6.PortTestEvent.TEST_RTMP事件有几个响应的地方,一个是ApplicationEventMap中,会调用ModuleProxy::testRTMP测试端口,另外一个是在LoadingBar.mxml中,这里只是用来显示。这里连的uri是在config.xml中指定的 。如果测试成功,会触发PortTestEvent.PORT_TEST_SUCCESS,否则会触发PortTestEvent.PORT_TEST_FAILED。如果失败,则继续进行RTMPT的测试。如果成功,则调用ModulesProxy::portTestSuccess,在其中调用modulesManager.startUserServices(),这里会传递连接成功的协议类型,并替换掉原先配置中的协议类型(rtmpt没开源,按理应该不支持,可能为了以后扩展用吧),接下来会触发UserServicesEvent.START_USER_SERVICES。会调用到UserService.startService()。

7. startService中会调用joinService.load来加载配置中的中host指定的地址,如果不是调试,host="http://192.168.1.101/bigbluebutton/api/enter",具体参数是怎么传到这个API的还不太清楚,抓包分析猜测是通过seesionid。上面这个API会返回会议的信息。然后会触发ConferenceCreatedEvent.CONFERENCE_CREATED_EVENT,并通过NetConnectionDelegate创建一条长连接到指定的地址。

8. ConferenceCreatedEvent.CONFERENCE_CREATED_EVENT的处理也有两个地方,一个是在viewsWindow.mxml中,这里是视图上的显示,另外一个是APIEventMap,这里只是将meeting信息存储在UserManager中。

9. 第6步中的连接创建成功后,会触发UsersConnectionEvent.CONNECTION_SUCCESS,这个事件也有两个处理的地方,一个是ConnectionLostWindow.mxml中,这里会关掉连接断开的窗口,并重新刷新页面;另一个地方是ApplicationEventMap.mxml,会调用UserService.userLoggedIn(),在这里会调用UsersSOService.join(),会获取当前与会的人员信息,并触发ParticipantJoinEvent.PARTICIPANT_JOINED_EVENT。随后会触发SuccessfulLoginEvent.USER_LOGGED_IN。

10. SuccessfulLoginEvent.USER_LOGGED_IN事件的处理,一个是UI上的显示toolbar,另一个是ModulesProxy.loadAllModules(),从这一步开始起,就依次从配置中给定的模块的uri加载模块了。在模块加载过程中,会依次收到MODULE_LOAD_PROGRESS事件通知,这个会更新UI的进度条显示,当所有的模块都加载完成后,然后会检查locale的版本信息,(这里好像遗漏了locale资源的加载步骤,后面再看吧),然后调用module的start()方法,最后触发ALL_MODULES_LOADED,这个事件的响应处理在ui上就是移除掉进度条。而模块的窗口显示和其他UI按钮的显示,则由模块自己来触发,比如OpenWindowEvent,这个事件触发后会由MainCanvas.mxml处理,调用模块窗口的getPrefferedPosition()获取窗口位置来布局。

至此,bigbluebutton客户端的框架加载流程已经分析完,其中具体模块的加载和具体的应用有关系,下周打算分析其中的语音模块,接下来会分析presenter模块和video模块,并且打算尝试修复presenter的BUG(好像0.8版本并没有改进)。

没有评论:

发表评论