本文主要介绍使用 TIM 作为同步通道。
平台支持
iOS | Android | Windows | Mac OS | Web | 小程序 |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
注意事项
因为实时录制会以 TIMSDK 作为信令通道,同时是以自定义消息的 extension:'TXWhiteBoardExt' 为标识,所以在同步信令的时候需要注意以下四点:
1. 使用 TIMSDK 作为信令通道。
2. 关闭互动白板内置的信令通道(一定需要关闭,否则会收不到 onTEBSyncData 回调)。
3. 同步信令的时候发送的消息类型为自定义消息类型。
4. 自定义消息的 extension 字段必须为 TXWhiteBoardExt。
同步原理
监听 onTEBSyncData 事件(事件名以各端实际为准),将事件中回调的数据通过信令通道进行广播。
收到白板信令后,调用互动白板 addSyncData 接口(接口名以各端实际为准)进行同步。
代码集成
Mac/iOS
说明
// 1. 将 TEduBoardInitParam 的 timSync 参数初始为 NO (关闭互动白板内置的信令通道)TEduBoardInitParam *initParam = [[TEduBoardInitParam alloc] init];initParam.timSync = NO; // 关闭互动白板内置的信令通道(一定需要关闭,否则会收不到onTEBSyncData回调)_boardController = [[TEduBoardController alloc] initWithAuthParam:authParam roomId:_classId initParam:initParam];// 2. 监听白板信令数据回调 onTEBSyncData,将数据发送给其他白板用户- (void)onTEBSyncData:(NSString *)data {V2TIMMessage *message = [[V2TIMManager sharedInstance] createCustomMessage:data desc:nil extension:@"TXWhiteBoardExt"];if (message.customElem) {message.customElem.extension = @"TXWhiteBoardExt";}[[V2TIMManager sharedInstance] getConversation:@"groupid" succ:^(V2TIMConversation *conv) {if ([conv type] == V2TIM_GROUP) {BOOL onlineUserOnly = ![[conv.groupType lowercaseString] isEqualToString:@"avchatroom"];[[V2TIMManager sharedInstance] sendMessage: messagereceiver: nilgroupID: conv.groupIDpriority: V2TIM_PRIORITY_HIGHonlineUserOnly:onlineUserOnlyofflinePushInfo:nilprogress:nil succ:^{// 发送 IM 消息成功} fail:^(int code, NSString *desc) {// 发送 IM 消息失败,建议进行重试}];}} fail:^(int code, NSString *desc) {// 获取回话失败}];}// 3. 监听IM的消息回调,在收到其他用户的白板信令时,将消息传递给白板// 注意: 自己操作触发的信令,不需要再同步给本人[_boardController addSyncData:data];
Android
说明
// 1. 将 TEduBoardInitParam 的 timSync 参数初始为 NO (关闭互动白板内置的信令通道)TEduBoardController.TEduBoardInitParam initParam = new TEduBoardController.TEduBoardInitParam();initParam.timSync = false; // 关闭互动白板内置的信令通道(一定需要关闭,否则会收不到onTEBSyncData回调)// 2. 监听白板信令回调 onTEBSyncData,将信令发送给其他白板用户@Overridepublic void onTEBSyncData(String data) {final V2TIMMessage message = V2TIMManager.getMessageManager().createCustomMessage(data.getBytes(), "", "TXWhiteBoardExt".getBytes());if (message.getCustomElem() != null) {message.getCustomElem().setExtension("TXWhiteBoardExt".getBytes());}V2TIMManager.getConversationManager().getConversation("groupid", new V2TIMValueCallback<V2TIMConversation>() {@Overridepublic void onSuccess(V2TIMConversation v2TIMConversation) {if (v2TIMConversation.getType() == V2TIMConversation.V2TIM_GROUP) {boolean onlineUserOnly = !(v2TIMConversation.getGroupType().toLowerCase().equals("avchatroom"));V2TIMManager.getMessageManager().sendMessage(message, null, "groupid", 1, onlineUserOnly, null, new V2TIMSendCallback<V2TIMMessage>() {@Overridepublic void onSuccess(V2TIMMessage v2TIMMessage) {// 发送 IM 消息成功}@Overridepublic void onError(int i, String s) {// 发送 IM 消息失败,建议进行重试}@Overridepublic void onProgress(int i) {}});}}@Overridepublic void onError(int i, String s) {// 获取回话失败}});}// 3. 在收到其他用户的白板信令时,将信令透传给白板// 注意: 自己操作触发的信令,不需要再同步给本人mBoard.addSyncData(data);
Windows
// 引入IM SDK头文件#include "TIMCloud.h"// 这里为了演示方便,使用ostringstream来构造JSON串,生产环境建议使用第三方JSON库来生成JSON串#include <iostream>#include <sstream>#include <string>// 1. 将 TEduBoardInitParam 的 timSync 参数初始为 NO (关闭互动白板内置的信令通道)TEduBoardInitParam initParam;initParam.timSync = false; // 关闭互动白板内置的信令通道(一定需要关闭,否则会收不到onTEBSyncData回调)boardCtrl->Init(authParam, ROOM_ID, initParam); // 使用上面构造的初始化参数// 2. 监听白板信令回调 onTEBSyncData,将信令发送给其他白板用户virtual void onTEBSyncData(const char * data) override {//使用自定义信令通道,发送 data 给其他白板用户std::string message = data;std::ostringstream json;json << "{";json << "\\"" << kTIMMsgElemArray << "\\":";json << "[{";json << "\\"" << kTIMMsgPriority << "\\": " << kTIMMsgPriority_High << ","; // 设置消息优先级为高json << "\\"" << kTIMElemType << "\\": " << kTIMElem_Custom << ","; // 消息类型为自定义消息json << "\\"" << kTIMCustomElemExt << "\\": \\"TXWhiteBoardExt\\","; // 扩展字段信息json << "\\"" << kTIMCustomElemData << "\\": \\"" << message << "\\","; // 消息内容为白板数据json << "}]";json << "}";int ret = TIMMsgSendNewMsg("课堂id", kTIMConv_Group, json.str().c_str(), [](int32_t code, const char *desc, const char *json_param, const void *user_data) {if (ERR_SUCC == code) { // 消息发送成功//信令发送成功后调用 addAckData(data),确认数据发送状态} else { // 消息发送失败,建议进行重试}}, nullptr);if (ERR_SUCC != ret) { // 消息发送失败,建议进行重试}}// 3. 在收到其他用户的白板信令时,将信令透传给白板// 注意: 自己操作触发的信令,不需要再同步给本人boardCtrl->AddSyncData(data);
Web
// Web没有内置TIM通道,不需要额外关闭内置TIM通道。// 1. 在 onTEBSyncData 回调里,将数据发送给其他白板用户teduBoard.on(TEduBoard.EVENT.TEB_SYNCDATA, data => {const message = tim.createCustomMessage({to: '课堂ID',conversationType: TIM.TYPES.CONV_GROUP,priority: TIM.TYPES.MSG_PRIORITY_HIGH, // 因为im消息有限频,白板消息的优先级调整为最高payload: {data: JSON.stringify(data),description: '',extension: 'TXWhiteBoardExt',},});// 发送消息tim.sendMessage(message).then(() => {// 发送成功//信令发送成功后调用 addAckData(data),确认数据发送状态}, (error) => {// 发送失败,建议进行重试});});// 2. 监听im的消息接收事件,在收到其他用户的白板信令时,将信令透传给白板(addSyncData)// 注意: 自己操作触发的信令,不需要再同步给本人;以下代码this.im,this.userId,teduBoard请以实际的业务变量为准this.tim.on(TIM.EVENT.MESSAGE_RECEIVED, (event) => {const messages = event.data;const groupId = String(this.classInfo.classId);messages.forEach((message) => {// 群组消息if (message.conversationType === TIM.TYPES.CONV_GROUP) {if (message.to === groupId) { // 如果是当前群组const elements = message.getElements();if (elements.length) {elements.forEach(async (element) => {if (element.type === 'TIMCustomElem') { // 自定义消息if (element.content.extension === 'TXWhiteBoardExt') { // 是白板的自定义消息if (message.from != this.userId) { // 并且发消息的人不是自己// 将白板信令设置给白板teduBoard.addSyncData(data);}}}});}} else {// 其他群组消息自行处理,在互动白板的场景中可以忽略其他群组的消息}} else if (message.conversationType === TIM.TYPES.CONV_C2C) { // C2C消息// c2c消息在互动白板的场景中可以直接忽略}});});