线上教学方案:腾讯云互动白板(Tencent Interactive Whiteboard,TIW)+即时通信(Instant Messaging,IM)+实时音视频(Tencent RTC)实现老师线上互动教学
官方地址:
https://cloud.tencent.com/product/tiw
https://cloud.tencent.com/product/im
https://cloud.tencent.com/product/trtc
需要在控制台申请appid,key等,参考:https://cloud.tencent.com/document/product/1137/39899
app.build: 引用依赖库 rtc,im,eb,并指定app的cpu架构
android{
...
defaultconfig{
...
ndk {
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
}
...
}
...
}
...
dependencies{
...
api 'com.tencent.liteav:LiteAVSDK_TRTC:8.7.10102'
api 'com.tencent.imsdk:imsdk:4.8.50'
api 'com.tencent.edu:TEduBoardSdk:2.6.0.98'
...
}
project.build: app.build中的依赖文件下载出问题时,可以参考如下配置
buildscript {
repositories {
...
maven {url 'https://dl.bintray.com/tencentqcloudterminal/maven' }
...
}
}
...
allprojects {
repositories {
...
maven {url 'https://dl.bintray.com/tencentqcloudterminal/maven' }
...
}
}
Manifest: 添加以下权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
V2TIMManager.getInstance().initSDK(Context context, int sdkAppID, V2TIMSDKConfig config,V2TIMSDKListener listener)
sdkAppID: 上述控制台上申请的appid
config: 配置信息可为传null,目前只有配置日志等级,默认info级别
listener: 初始化回调接口,提供了网络状态和用户信息变化回调,所有V2TIMSDKListener回调都可以参考下:
| 回调方法 | 描述 | 推荐操作 |
|---|---|---|
onConnecting() |
SDK 正在连接到腾讯云服务器 | 适合在 UI 上展示“正在连接”状态。 |
onConnectSuccess() |
SDK 已经成功连接到腾讯云服务器 | 连接成功 |
onConnectFailed() |
SDK 连接腾讯云服务器失败 | 可以提示用户当前网络连接不可用。 |
onKickedOffline() |
当前用户被踢下线 | 此时可以 UI 提示用户“您已经在其他端登录了当前账号,是否重新登录?” |
onUserSigExpired() |
在线时票据过期 | 请使用新签发的 UserSig 进行登录。 |
onSelfInfoUpdated() |
登录用户的资料发生了更新 | 可以在 UI 上更新自己的头像和昵称。 |
V2TIMManager.getInstance().login(String userId, String userSig, V2TIMCallback callback);
userId: 用户id,可自定义,不超过32位
userSig: IM SDK 登录票据(token),由业务服务器生成
callback: 操作结果回调,所有V2TIMCallback回调都可以参考下表说明:
| 回调方法 | 描述 |
|---|---|
void onError(int code, String desc); |
出错时回调,code码详情 |
void onSuccess(); |
成功时回调 |
| 注意:调用 IM SDK Login 成功登录后,将会开始计算 DAU,请根据业务场景合理使用 IM SDK Login操作,避免出现 DAU 过高的情况。 |
|
| 登录时机 | 说明 |
| --- | --- |
| App 启动后首次使用 IM SDK 的能力时 | 本项目为进入连麦模式才首次登录 |
| IM SDK (V2TIMSDKListener) 抛出 onUserSigExpired 回调 | 登录票据 (token) 已过期,需要使用新的 UserSig进行登录 |
| IM SDK(V2TIMSDKListener) 抛出 onKickOffline 回调 | 当前用户被踢下线,同平台多点登录时触发,可考虑重新登录或者下线 |
V2TIMManager.getInstance().logout(null);
为了降低 DAU,项目在结束连麦后,要及时调用退出登录。
群分类: 好友工作群(Work)、陌生人社交群(Public)、临时会议群(Meeting)、直播群(AVChatRoom);项目采用public群(需要审批),详细区别参考:官方群类型介绍
申请入群:
V2TIMManager.getInstance().joinGroup(String groupID, String message, V2TIMCallback callback);
| 参数 | 说明 |
|---|---|
| groupID | 群id,由主播端创建、业务服务器下发 |
| message | 申请入群说明 |
| callback | 发送申请状态回调 |
入群结果监听:void onApplicationProcessed(String groupID, V2TIMGroupMemberInfo opUser, booleanisAgreeJoin, String opReason) 其中isAgreeJoin为true表示同意加群,反之被拒绝。同意加群后,全员(包括请求者)收到onMemberEnter回。
监听申请结果:
V2TIMManager.getInstance().setGroupListener(V2TIMGroupListener listener);
V2TIMGroupListener:该回调接口能监听所有群相关的状态,重要方法参考下表:
| 方法 | 说明 |
|---|---|
void onMemberEnter(String groupID, List<V2TIMGroupMemberInfo>memberList) |
有用户加入群(全员能够收到)memberList - 加入的成员 |
void onMemberLeave(String groupID, V2TIMGroupMemberInfomember) |
有用户离开群(全员能够收到)member - 离开的成员注意:群主只能解散群不能离开 |
void onMemberKicked(String groupID, V2TIMGroupMemberInfo opUser, List<V2TIMGroupMemberInfo>memberList) |
某些人被踢出某群(全员能够收到)opUser - 处理人memberList - 被踢成员 |
void onGroupDismissed(String groupID, V2TIMGroupMemberInfoopUser) |
群被解散了(全员能收到)opUser - 处理人 |
void onGroupRecycled(String groupID, V2TIMGroupMemberInfoopUser) |
群被回收(全员能收到)opUser - 处理人 |
void onApplicationProcessed(String groupID, V2TIMGroupMemberInfo opUser, boolean isAgreeJoin, StringopReason) |
加群请求已经被群主或管理员处理了(只有申请人能够收到)opUser -处理人isAgreeJoin - 是否同意加群opReason - 处理原因 |
备注:rtc使用的房间为im中的房间
| 名称 | 说明 |
|---|---|
| 通话模式 | 无旁路,本项目不用 |
| 直播模式 | 有旁路 |
| 接口机 | 用于连麦互动,费用高 |
| 代理机 | 用于观众拉流观看,费用低 |
TRTCCloud.sharedInstance(context);setListener(TRTCCloudListener listener)void setVideoEncoderParam(com.tencent.trtc.TRTCCloudDef.TRTCVideoEncParam trtcVideoEncParam)
void enterRoom(com.tencent.trtc.TRTCCloudDef.TRTCParams trtcParams, int scene)
进入房间的返回结果,会在TRTCCloudListener.onEnterRoom(result)中回调。更多详情参考官方文档
TRTCCloudDef.TRTC_APP_SCENE_LIVE。更多详情参考官方文档
void exitRoom()
trtcCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_DEFAULT);
trtcCloud.startLocalPreview(true, view);
trtcCloud.switchRole(TRTCCloudDef.TRTCRoleAnchor)切换到主播角色TXCloudVideoView。更多详细说明请参考官网文档
trtcCloud.stopLocalAudio();
trtcCloud.stopLocalPreview();
互动白板在有IM前提下,接入比较简单
// 创建并初始化白板控制器
//(1)鉴权配置
TEduBoardController.TEduBoardAuthParam authParam = new TEduBoardController.TEduBoardAuthParam(sdkAppId, userId, userSig);
//(2)白板默认配置
TEduBoardController.TEduBoardInitParam initParam = new TEduBoardController.TEduBoardInitParam();
mBoard = new TEduBoardController(context);
//(3)添加白板事件回调
mBoard.addCallback(callback);
//(4)进行初始化
mBoard.init(authParam, roomId, initParam);
initParam: 白板配置为默认配置,如果有笔画颜色等特殊需求,参考官方文档配置。
//(1)获取白板 View
View boardview = mBoard.getBoardRenderView();
//(2)添加到父视图中
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
FrameLayout container = findViewById(R.id.board_view_container);
container.addView(boardview, layoutParams);
TIMManager.getInstance().unInit();我正在编写一个方法,它将在一个类中定义一个实例方法;类似于attr_accessor:classFoocustom_method(:foo)end我通过将custom_method函数添加到Module模块并使用define_method定义方法来实现它,效果很好。但我无法弄清楚如何考虑类(class)的可见性属性。例如,在下面的类中classFoocustom_method(:foo)privatecustom_method(:bar)end第一个生成的方法(foo)必须是公共(public)的,第二个(bar)必须是私有(private)的。我怎么做?或者,如何找到调用我的cust
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路
使用散列定义的访问器方法动态创建对象的最简单方法是什么?例如,如果我有一个散列:{foo:"Foo",bar:"Bar"}我想要一个具有访问器方法foo、foo=、bar和bar=的对象,其初始值分别为"Foo"和"Bar"。我可以想到这样做:moduleObjectWithAccessordefself.newh;Struct.new(*h.keys).new(*h.values)endendo=ObjectWithAccessor.new(foo:"Foo",bar:"Bar")o.foo#=>"Foo"但是,我不需要它们的多个实例具有相同的特定键集,而是希望每次都使用可能不同的键
目前我正在使用这个正则表达式从YoutubeURL中提取视频ID:url.match(/v=([^&]*)/)[1]我怎样才能改变它,以便它也可以从这个没有v参数的YoutubeURL获取视频ID:http://www.youtube.com/user/SHAYTARDS#p/u/9/Xc81AajGUMU感谢阅读。编辑:我正在使用ruby1.8.7 最佳答案 对于Ruby1.8.7,这就可以了。url_1='http://www.youtube.com/watch?v=8WVTOUh53QY&feature=feedf'url
我需要动态创建一个Ruby类,即动态地从ActiveRecord::Base派生。我暂时使用eval:eval%Q{class::#{klass}是否有一种等效的、至少同样简洁的方法可以在不使用eval的情况下执行此操作? 最佳答案 您可以使用Class类,其中的类是实例。困惑了吗?;)cls=Class.new(ActiveRecord::Base)doself.table_name=table_nameendcls.new 关于ruby-无需eval即时创建Ruby类,我们在Stac
参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍 介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。 内容有: ①:Hub模型的方法介绍 ②:服务器端代码介绍 ③:前端vue3安装并调用后端方法 ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke() 去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on