一、分析一个模块的源码步骤
- 1、首先应该看
SWITCH_MODULE_LOAD_FUNCTION(mod_conference_load);
也就是要从mod_conference_load函数中,看到这个模块中写了哪些可供外部调用的api或app。
SWITCH_ADD_API(api_interface, "conference", "Conference module commands", conf_api_main, p);
SWITCH_ADD_APP(app_interface, global_app_name, global_app_name, NULL, conference_function, NULL, SAF_NONE);
SWITCH_ADD_APP(app_interface, "conference_set_auto_outcall", "conference_set_auto_outcall", NULL, conference_auto_function, NULL, SAF_NONE);
SWITCH_ADD_CHAT(chat_interface, CONF_CHAT_PROTO, chat_send);
- 2、再找到要详细分析的api或app的函数,根据调用参数和返回结果情况去寻找匹配的源码。
- 3、模块中其他的函数基本上都是供api或app函数调用的,因此可以在看到被调用后再去详细分析。
二、详细分析conference api
-
SWITCH_STANDARD_API(conf_api_main);
即为该api函数。 - 每个api实际传入的参数是输入参数cmd,作用的会话session,输出流stream。
- 先将传入的参数cmd通过strdup函数复制到lbuf字符串保存好,然后将该字符串用空格分割后存入字符串数组argv,并得到字符串统计数argc。
- conference后接的第一个参数是argv[0],接下来就是根据这第一个参数去进行字符串匹配,然后执行相应的动作。
- 若是argv[0]是一个会议室号码,则表示要对特殊的会议室进行操作;否则就是对所有的会议进行操作或者普通的api命令使用。
-
conference_find
函数,传入会议室name号码,返回该会议室结构体对象conference_obj_t
,以便后续操作。 - 找到对应的会议室结构体对象后,转到实际的执行api命令的函数
conf_api_dispatch
。该函数中,利用api_command_t conf_api_sub_commands[]
这样一个命令参数匹配结构体数组,找到匹配的子命令以及回调函数。 - 比如vid-floor子命令的结构体数据为
{"vid-floor", (void_fn_t) & conf_api_sub_vid_floor, CONF_API_SUB_MEMBER_TARGET, "vid-floor", "<member_id|last> [force]"}
。要想了解这个子命令的详细执行过程,可以转到回调函数conf_api_sub_vid_floor
。force可强制设定某个成员一直获取vid_floor,即一直显示该成员的画面。 -
conference_set_floor_holder
函数将某成员设为临时拥有vid_floor,然后还是自动根据声音大小切换。 -
conference_set_video_floor_holder
函数强制使某成员一直拥有vid_floor。conference的一个状态值CFLAG_VID_FLOOR_LOCK
标记该vid_floor拥有者锁住不可自动变。 - 以上都是通过成员对象即member结构体对象来操作,而成员对象是对应于一个会议的加入顺序设定的链表中的id值,通过该member对象获取channel号码的方式是使用函数
switch_channel_get_name(member->channel);
此外还可以通过member->session获取到该会话。 -
member_update_status_field(member)
函数将对应member对象的状态改为TALKING和VIDEO (FLOOR),即有权讲话和展示视频的状态。 -
switch_core_session_refresh_video(member->session);
函数更新该session的msg状态SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ,然后调用switch_core_session_receive_message(session, &msg)
更新会话接收消息的策略。(后面跟踪不到对应状态会干什么了。。。) -
conference_loop_fn_vid_floor_toggle
函数切换(toggle)vid_floor,放在一个结构体数组中struct _mapping control_mappings[19]
三、发起一个会议的流程
conference_app中通过参数或其他方式传入一个会议室号码,然后先检查该会议室是否存在并返回一个会议室对象conference=conference_find(conf_name, NULL);
,如果不存在的话则创建一个conference = conference_new(conf_name, xml_cfg, session, NULL);
1)然后为这个会议室开启一个线程运行launch_conference_thread(conference);
。该线程调用一个执行函数conference_thread_run
,内部有一个while循环去处理会议成员之间的通信数据包。
2)视频会议会创建一个特别的视频线程launch_conference_video_thread(conference_obj_t *conference);
,然后调用执行函数conference_video_thread_run
,并修改会议对象视频状态conference->video_running = 1;
。该函数内部有一个while循环,会一直通过switch_core_session_read_video_frame(session, &vid_frame, SWITCH_IO_FLAG_NONE, 0);
函数读取每个vid_floor_hold成员的视频帧vid_frame,根据一个for循环遍历所有的成员并通过switch_core_session_write_video_frame(imember->session, vid_frame, SWITCH_IO_FLAG_NONE, 0);
函数将该vid_frame转发给所有成员。
3)两个会议成员之间的视频转发流程
-
launch_conference_video_bridge_thread(conference_member_t *member_a, conference_member_t *member_b)
函数传入两个会议成员对象,函数内调用两个线程,分别执行a到b的视频帧转发launch_thread_detached(conference_video_bridge_thread_run, pool, &conference->vh[0]);
,和b到a的视频帧转发launch_thread_detached(conference_video_bridge_thread_run, pool, &conference->vh[1]);
。然后都转到线程调用的函数conference_video_bridge_thread_run
,该函数内有个while循环并通过函数switch_core_session_read_video_frame
和switch_core_session_write_video_frame
来进行视频帧的转发。
四、1.6版本通过relate api设置成员之间的联系,使一个成员可以绑定看另一个成员的视频
-
conference 3000 relate 1 2 sendvideo
即将成员1视频发给成员2,不管成员1是否已获得vid_floor
网友评论