2011年11月17日星期四

bigbluebutton中的audio

官网的FAQ中提到,0,71a里面的默认的VOIP语音有2-4秒的时延,原因有两点,一个是服务器audio转码(nellymoser -> mulaw),另一个原因是TCP链路。但在网络状况比较好的情况下,还有问题,那应该是转码带来的开销太大。大致的流程是:
flash(nellymose) -> Red5phone(mulaw)->asterisk/freeswitch -> Red5phone(mulaw)->flash(nellymose)。

里面提到了几个方案:
1. 用speex代替nellymoser,因为asterisk和freeswitch都支持speex,所以不需要转码。另外可以看到bbb的开发人员也在尝试为freeswitch添加nellymoser的支持(http://lists.freeswitch.org/pipermail/freeswitch-dev/2011-March/004742.html),另外freeswitch也在添加对rtmp协议的支持,http://freeswitch-users.2379917.n2.nabble.com/Conference-and-Speex-Wideband-audio-quality-problem-td5356620.html,如果flash能够直接连上freeswitch,那么开销会进一步降低。asterisk似乎也添加了对rtmp的支持,但好像还有些问题https://issues.asterisk.org/view.php?id=15484。另外最新版本的asterisk和freeswitch都已经支持speex.

2. 用native code来替代java做转码。

3. 使用java的客户端直接连到freeswitch或者asterisk。

4. 使用push-to-talk的模式(http://zhidao.baidu.com/question/100105828),感觉和微信差不多。

5. 通过电话call-in。

最终在0.8版本中采用的方案是1,具体见http://groups.google.com/group/bigbluebutton-dev/browse_thread/thread/a36d61df9b9fc45b?pli=1,根据大家的反馈来看效果还不错。

2011年11月15日星期二

[转]向依赖关系宣战——依赖倒置、控制反转和依赖注入辨析

最近在了解red5,由于其使用了spring framework,也大致了解了一下,其中提到到了一些设计模式,这些名词不熟悉,不过对其使用已经比较熟悉,看到一篇不错的文章。节选其中的一部分内容如下:

向依赖关系宣战——依赖倒置、控制反转和依赖注入辨析

虽然“依赖倒置”和“控制反转”在设计层面上都是消解模块耦合的 有效方法,也都是试图令具体的、易变的模块依赖于抽象的、稳定的模块的基本原则,但二者在使用语境和关注点上存在差异:“依赖倒置”强调的是对于传统的、 源于面向过程设计思想的层次概念的“倒置”,而“控制反转”强调的是对程序流程控制权的反转;“依赖倒置”的使用范围更为宽泛,既可用于对程序流程的描述 (如流程的主从和层次关系),也可用于描述其他拥有概念层次的设计模型(如服务组件与客户组件、核心模块与外围应用等),而“控制反转”则仅适用于描述流 程控制权的场合(如算法流程或业务流程的控制权)。

依赖注入的核心思想是:
  1. 抽象接口隔离了使用者和实现之间的依赖关系,但创建具体实现类的实例对象仍会造成对于具体实现的依赖。
  2. 采用依赖注入可以消除这种创建依赖性。使用依赖注入后,某些类完全是基于抽象接口编写而成的,这可以最大限度地适应需求的变化。

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版本并没有改进)。

2011年11月7日星期一

bigbluebutton flex客户端的UI视图

今天周末,时间有限,打算泛泛的看一下代码,做点记录。这篇就大致写一下代码结构好了,bbb-client的主要代码在org.bigbluebutton下,里面有五个目录

common - 公共目录,包括...
core - 读取配置和配置管理
main - 住程序和模块框架的代码
modules - 各个功能模块的代码
util - 工具类代码,包括日志模块,多语言加载模块等

初看有点地方都不太合理,比如core中的文件很少,也不算什么核心代码。后面再看吧。

先看main里面的代码吧,看起来这是个不错的选择,因为程序框架的主要逻辑都在这里,而功能模块的分析也要先了解主程序的运行才方便理解,其他地方的一些基础代码可以在看主程序时顺带看看就好。

main里面有五个目录:

api -
event - 自定义事件,用来解耦模块
maps - 消息映射
model - 控制逻辑
views - 视图

先挑简单的看,先看视图。
BrandingLogo.mxml - 这个可以放自定义的Logo,当然,这里得自己加代码,最简单的就是放个

ConnectionLostWindow.mxml - 这个是重连时显示的窗口,其中的ProgressBar有一个属性indeterminate,这个为true时,在现实进度的时候,就没有百分百的显示,因为断开连接后是没法去估计什么时候能重连上的。其中为重连事件注册了一个回调函数,在重连上后,重新打开页面,关闭这个popup窗口。这里有行没用的代码,var pageURL:String = mx.core.Application.application.url.split("/")[2];

LanguageSelector.mxml - 这里是个combobox,其中dataProvider绑定了一个数组变量,flex这种数据绑定的总是要显示的去在变量前加[bindable],难道编译器就不能把这块给屏蔽掉吗,说实话不太喜欢flex,感觉语法不简练,要多敲不少和逻辑没太大关系的代码,就感觉在手写html文件一样写代码,你要写的代码逻辑都淹没在一堆和主逻辑不相关的标记中了,有了flex build的智能提示是可以少敲点代码,但看起来还是很不爽。这里在语言选择变化后会调用到ResourceUtil来修改语言显示。

LoadingBar.mxml - 这里就是最开始加载时显示的进度条了,可以看到这有几个事件绑定:

ModuleLoadEvent.MODULE_LOADING_STARTED - 模块开始加载
ModuleLoadEvent.MODULE_LOAD_PROGRESS     - 模块加载进度
ModuleLoadEvent.ALL_MODULES_LOADED          - 所有模块成功加载
PortTestEvent.TEST_RTMP                                         - 开始RTMP端口测试
PortTestEvent.TUNNELING_FAILED                        - Tunneling测试失败

再看几个事件处理函数
moduleLoadingStarted
这里面会统计模块的总数,并在进度条显示总共会加载多少个模块,其中可以看到引用多语言文件中的bbb.mainshell.statusProgress.loading = Loading {0} modules,这里面有个{0}可以被替换到,看来properties中还可以使用模板语法。另外还初始化了每个模块的加载进度为0。

moduleLoadProgress
这里会更新总的加载进度。这里有个疑问,各个模块能同时加载吗?记得调试的时候看flex也就只有一个线程,不知道是否支持多线程。

allModulesLoaded
模块加载成功,移除进度条

testRTMP
显示在测试RTMP连接

tunnelingFailed
显示连接失败

LoggedOutWindow.mxml
这个就是点击了logout按钮后弹出来的,里面就只有一个OK按钮,点击之后就调用logout的API,"/bigbluebutton/api/signOut",这里感觉有点不太合理,点击退出就就没cancel,只能ok。另外这里显示的字符串也没有加入到多语言支持。

LogWindow.mxml
这个用来显示日志,里面有个TextArea,还有控制面板,里面可以过滤日志,关闭日志显示,清除日志,手工刷新日志。这里有点要提到的是,里面的成员变量并没有采取和普通的变量区别的写法,有些地方也没有加this,看起来总觉得不那么舒服。
另外测试的时候发现filter后的标记在clear后也不会清除掉。Clear掉后再refresh,还是会出现之前的日志。这里还有些bug的。
还有,这里log窗口是有滚动条的,我记得presenter的窗口好像没有,也许可以参考着加上。
需要说明的是,这里的日志并不会自己滚动,而是需要手动刷新,而从TextArea中获取更新的值也有点意思,从flex文档中可以看到“The TextArea control is a multiline text field with a border and optional scroll bars.”,另外这里用的是MDIWindow,Highlighter也是来是flexlib中,displayLogMessages()肯定是在窗口显示出来前被调用的。

MainToolbar.mxml
这里是toolbar的UI,没什么特殊逻辑,就是一些事件处理,但是这里loggedIn的参数都没用到,感觉有点乱。有点要提到的是,这里toolbar的按钮是可以动态的扩展的,也就是说,自己写了一个扩展模块,可以通过事件添加按钮到toolbar。

MainCanvas.mxml
可以向canvas上添加窗口,窗口要实现IBbbModuleWindow接口,可以在实现时说明窗口的布局方式。这里还实现了对video窗口的布局。默认是空的,窗口都是动态加载的。

MicSettings.mxml
这个是麦克风设置的窗口。

OldLocaleWarnWindow.mxml
用来给出locale的版本不对的提示,在从远端获取了conf.xml配置后,会比较其中的多语言版本号,和本地的比较。

MainApplicationShell.mxml
这就是程序的主窗口了,其中包括MainToolbar, MainCanvas(初始化时有进度条和logo,加载完后被移除)和控制面板(copyright, video layout,日志显示按钮,和重置布局按钮)。

上面其他的一些窗口都是在程序运行时,动态的创建和销毁的。

initializeShell()中只创建了一个全局的Dispatcher对象,该对象用来分发事件。具体的流程可能还要看看mate framework。

2011年11月4日星期五

关于clock skew问题的简短对话


clock skew解决的那个我没看懂 16:51
怎么解决的啊?解释一下? 16:51
Jeromy Fu (jianfu - ENGINEER.SOFTWARE ENGINEERING):
比较绕 16:51
两种情况


Jeromy Fu (jianfu - ENGINEER.SOFTWARE ENGINEERING):
一种,发送端快,接收端慢,那么rel_owd会减少,因为每次计算base owd都是取最小值,所以对结果没影响,right? 16:53
Wilson Chen (weichen2 - ENGINEER.SOFTWARE ENGINEERING):
减少就对结果没影响吗?也有吧? 16:53
把本来的拥塞当成非拥塞了 16:54
Jeromy Fu (jianfu - ENGINEER.SOFTWARE ENGINEERING):
因为计算queuing_owd = current_owd - base_owd
16:54
Wilson Chen (weichen2 - ENGINEER.SOFTWARE ENGINEERING):
base owd和current owd同步减少? 16:54
Jeromy Fu (jianfu - ENGINEER.SOFTWARE ENGINEERING):
16:54
Wilson Chen (weichen2 - ENGINEER.SOFTWARE ENGINEERING):
y 16:55
Jeromy Fu (jianfu - ENGINEER.SOFTWARE ENGINEERING):
另外一种情况,发送端慢,接收端快,那么rel_owd会变大 16:58
如果接收端收到数据包后,再把收到的时间戳带回去到发送端(ack包里),那发送端就能计算逆向的owd, reverse_owd
16:59
对发送端来说,收到ack时,他的角色和第一种情况的接收端是一样的,也就是说,可以准备计算reverse_owd
17:01
Wilson Chen (weichen2 - ENGINEER.SOFTWARE ENGINEERING):
了解 17:01
这个比较复杂 17:01
Jeromy Fu (jianfu - ENGINEER.SOFTWARE ENGINEERING):
那好了,发送端和接收端的时钟偏移是一样的,只是方向不一样而已 17:02
叠加上去就可以了 17:02
Wilson Chen (weichen2 - ENGINEER.SOFTWARE ENGINEERING):
有道理,就是靠reverse_owd

bigbluebutton客户端flex分析- 初始化流程(1)

  作为flex的初学者,大致浏览的一下flex官方的视频教程,接下来就打算了解bigbluebutton的客户端了。进程远没有我当初想象中的那么顺 利,经常的都会碰到一些问题。相信在解决一个个问题的过程中,会慢慢的熟悉flex和这个产品。通读文档肯定是来不及,时间紧迫,很多不清楚的地方就查阅 一下资料,然后自己推测或猜测的,可能会有些不对的地方,但有需要的时候一定会查阅资料弄清楚问题。


  首先从启动开始,客户端已经通过flex build编译通过,但是启动的时候并没有显示加载的进度条,而是卡在了初始画面,为了找到原因,只有探索源码。第一步,要知道flex程序的入口在哪里,一个flex程序可以有很多mxml和as文件,但是包含Application只有一个,这个Application在mxml中用来标识,把鼠标移到到其上,可以看到其对应的as类mx.core.Application。

  flex程序感觉写起来要敲很多的字符,并不简洁,也不明了。可以看到BigBlueButton实际上就只做的下面几个事情:

  1. 声明pageTitle为"BigBlueButton"。
  2. 声明应用程序的布局采用绝对布局的方式。
  3. 构造了一个ResourceUtil对象,处理i18n。
  4. 声明了IDragManager,可能是支持窗口拖动?不太清楚,先跳过。
  5. 声明了HistoryManager,绑定浏览器的前进后退操作?不太清楚,先跳过
  6. 声明了一个私有的handleChatEvent,没有看到有引用的地方,函数体里面也是打了一条trace,不太清楚,先跳过
  7. 然后声明了一个ApplicationEventMap,这个是mate框架相关的,定义了一些事件处理的映射关系。不太清楚,先跳过
  8. 声明了一个MainApplicationShell,估计也是和mate框架相关,定义了app的视图。不太清楚,先跳过
  9. 在application中,可以看到有一个preloader的属性,其中指向了"org.bigbluebutton.main.model.BigBlueButtonPreloader",这里有点类似js, BigBlueButtonPreloader是一个as文件。

  由此可以看到,入口的地方应该是顺着preloader往下走,接下来看看BigBlueButtonPreloader都干了些什么。

  可以看到有一个“import flash.display.Sprite;”查阅了一下“在不使用 Flash 动画时用这个 Sprite ,Sprite 是轻量级的容器,不包括时间轴的容器,比 MC 小一些,更少占用内存(系统资源)。”

  下面这两个应该就是显示进度条相关的,RSL(Runtime Shared Libraries)。
  import mx.events.RSLEvent;
  import mx.preloaders.DownloadProgressBar;

  然后定义了一个继承自DownloadProgressBar的类并自定义了一些显示的字符串。这里好像没看到有什么特殊的逻辑。

  接下来再看MainApplicationShell,这里主要是定义了UI视图,最外层的是一个VBox,可以看到VBox有一个creationComplete属性,指向一个initialzeShell()函数。

  再下面的一系列的声明了一系列的事件监听器,先跳过。

  再下面是,这个也先跳过,再后面分析了mate框架就明白了。

  里面是as脚本,定义了上面的事件监听器函数,基本上都是根据事件来更新UI显示的。

  再往下就是一些UI的布局了:MainToolbar,然后是MainCanvas,在MainCanvas中定义了LoadingBar和BrandingLogo,其中BrandingLogo(.mxml)中可以添加自己Logo图片。再往下就是ControlBar,下面是copyright和一个log按钮(可以打开调试窗口看日志)和reset layout窗口。

  看到这里,都还没什么头绪,为什么会freeze住,但是从上面发现可以打开调试窗口,显示日志,接下来再来一次,进度条显示为"connecting to the server",难道server没连上?打开调试窗口,可以看到,语言和模块都已经加载成功,并且已经能够联通rtmp服务器,最下面显示:

11/3/2011 15:44:02.980 [DEBUG] JoinService:load(...) http://64.104.177.100/bigbluebutton/api/enter
11/3/2011 15:44:03.396 [DEBUG] Join FAILED =
  FAILED
  Could not find conference null.


加会失败?为什么刚开始登录的时候就会加会?从日志里面看到对应的url地址为:http://64.104.177.100/bigbluebutton/api/enter,想到之前修改过config.xml,其中有一项是,这里我是修改过的,最初的值不太记得了,但是好像官网上说如果要调试运行,要将host修改一下,好像和join-mock有关系,但是当时感觉官网没说太清楚,再查阅一下吧,又看了一下,只是提到了要用这个替换掉,另外有些帖子说join-mock文件没更新,连不上freeswitch。猜测这个join-mock应该是模拟http://64.104.177.100/bigbluebutton/api/enter的返回值,但是为什么用flex编译调试就有问题,直接从浏览器打开就没问题呢?看来还得继续再分析。

首先第一步就要看哪里会读取config.xml配置了。搜索到了,这个在ApplicationEventMap.mxml文件中引用了,下面还有一行,不清楚是做什么的,joinResultMock.xml这个文件不存在。


另外再搜索join-mock.xml,可以看到这个在JoinServiceTest.as文件中引用,这个看起来像是Testcase的程序,怎么会跑到这里来?再搜索一下JoinServiceTest这个类,看看哪里有引用到,查了一下,只有在UserTestSuite.as中引用到了。

后来发现config.xml还有一处地方引用到了,ConfigParameters.as,应该就是这里了。可以看到这里是远程的获取config.xml文件,从这里可以知道,我们可以动态的去配置config.xml,不需要重启服务,只需要刷新页面就可以让配置生效了。另外这里host配置项目读取后放置到public的变量host中去了,接下来就要去看哪里引用了这个类。ConfigParameters引用的地方倒是比较好找,但后面就不容易了,由于flex本质上也是一个事件驱动的,再加上这个项目使用了mate这个事件框架(我的理解是mate本质上是封装了flex底层的事件模型,使得各个模块之间通过信号事件进行交互,来达到模块间解耦的目的,还没仔细看,现在的理解是这样的)。

这样单看代码不是特别容易,只好结合调试一起来看,但事件机制导致在不是特别熟悉代码的情况下调试也不是那么容易,因此要利用一切可以利用的信息,比如,在UI上面显示的一些状态提示字符。在最后一会,屏幕上显示了“Connecting to the server”,搜索了工程,发现这个在"en_US/bbbResources.properties"中 。这个文件是多语言中的en_US版本,看来多语言的支持是做在了客户端,并不需要从服务器去更新。接下来搜索“bbb.mainshell.statusProgress.connecting”,发现在LoadingBar.mxml中:

private function testRTMP(e:PortTestEvent):void{

//- Cannot get locale string this early in loading process
       this.label = 'Connecting to the server';  
      //ResourceUtil.getInstance().getString('bbb.mainshell.statusProgress.connecting');
}

这里应该就是对应的位置,可以看到多语言的资源还没加载完,所以这里用英文显示了,如果要做汉化,这里也是需要修改的地方。

可以看到最终浏览器中的flex应用给出的提示是"Sorry, we cannot connect to the server.",再打开log窗口,看到连的服务器是"64.104.177.100",刚换了一下网络,从公司网络切换到家里的网络了,地址已经变了,这个错误和之前出现的并不一样。


有点奇怪的是,本已经通过bbb-conf --setip修改了配置中的ip地址了,但是为什么还是错误的ip地址,难道不是从远程获取的(远程是修改了/var//www/bigbluebutton/conf下面的,并没有修改设置的dev目录下的,我已经将配置改为使用dev环境)?但是明明是URLRequest获取的啊,难道有cache?但是可以看到,代码中获取时加上了时间参数,以避免有cache,_urlLoader.load(new URLRequest(file + "?a=" + date.time)); 只有再调试一把了,看看Network Monitor窗口里的显示(多多少少觉得之前的flex视频教程的基础部分没白看,多少都能用到一些)。居然没有,还不清楚这个Network Monitor是怎么工作的,不过我猜测也就是hack了flex的SDK里面的网络操作。另外构造URLRequest的时候,并没有加上域名或ip的前缀,难道真的是访问本地文件(好像不应该访问远程的,因为现在调试运行时,远程ip都还没有)?先看看SDK说明。看URLRequest里最后一个例子提到:如果没有域名或ip,那这个文件应该就是当前flash的文件所在目录的相对位置,但是bigbluebutton.swf在bin目录下,下面的conf/config.xml配置的是192.168.1.102,应该是没有使用这个配置,我怀疑调试运行的时候,默认的工作目录就是BigBlueButton.mxml所在的目录。当然这是我的猜测,网上还没找到相应的说明。先修改了试试先。如果改了还不行就可能是cache的原因了。

经过修改后,重新运行发现配置生效了。真是在摸索中前进,都是苦力活。好了,现在回到了之前的bug,


11/3/2011 22:02:17.954 [DEBUG] JoinService:load(...) http://192.168.1.101/bigbluebutton/api/enter
11/3/2011 22:02:18.063 [DEBUG] Join FAILED =
  FAILED
  Could not find conference null.


接下来看日志,看起来JoinService是一个加会的服务,怎么会跑到这里来,一开始就加会了?哦,好像是的,demo打开的那个web页面是html的,不是flash的。


那这个问题看来是清楚了,加会但是参数不对,会议根本就不存在。接下来要看看的就是加会的api和对于的官网文档里面提到的那个join-mock文件怎么使用的了。

至此为止,为什么用'conf/join-mock'来替换掉之前的那个url(http://192.168.1.101/bigbluebutton/api/enter)就很清楚了,join-mock直接返回了一个mock的会议信息。

好了,经过验证已经可以成功加会了。是的,如果我按照官网的说明,根本不会遇到这些乱七八糟的问题,但是,通过解决这个问题,让我对bbb-client端的代码有了一个初步的熟悉,特别是我这种一行flex代码都没写过的人来说,我感觉已经找到那么一点门路了。

接下来还得继续写,因为还没有开始写正题 - 初始化的流程,但上面是一个不错的开端。再继续努力吧。





 




2011年11月2日星期三

重新写博客了

几年后重新看之前的博客,自己都不想看,记录的这些琐碎的技术细节。好了,作为一名技术人员,一定要能够积累自己的产品,工作五年了,却没有一个拿的出手的产品,很失败。最近的目标是学习bigbluebutton,并能做一定定制开发。现在先从客户端开始入手,flex的,没经验,有学习成本,但是我决定要做好。期待我自己的定制化产品。