# 实现一对一通话
本章将介绍如何实现一对一视频通话,一对一视频通话的 API 调用时序见下图:
# 初始化
调用 JCMediaDevice.create (opens new window) 和 JCCall.create (opens new window) 以初始化实现一对一通话需要的模块。
/// 声明对象
JCMediaDevice mMediaDevice;
JCCall mCall;
/// 初始化函数
public bool initialize() {
/// 1. 媒体类
mMediaDevice = JCMediaDevice.create(mClient, this);
/// 2. 通话类
mCall = JCCall.create(mClient, mMediaDevice, this);
}
其中:
- JCMediaDevice create 方法中的 this 为实现 JCMediaDeviceCallback (opens new window) 接口的对象,用于将媒体设备相关的事件通知给上层。
JCMediaDeviceCallback 中的主要方法如下。
//摄像头变化
public void onCameraUpdate()
{
}
//音频输出变化
public void onAudioOutputTypeChange(string audioOutputType)
{
}
- JCCall create 方法中的 this 为实现 JCCallCallback (opens new window) 接口的对象,用于将通话相关的事件通知给上层。
JCCallCallback 中的主要方法如下。
//新增通话回调
public void onCallItemAdd(JCCallItem item)
{
}
//移除通话
public void onCallItemRemove(JCCallItem item, JCCallReason reason)
{
}
//通话状态更新回调(当上层收到此回调时,可以根据 JCCallItem 对象获得该通话的所有信息及状态,从而更新该通话相关UI)
public void onCallItemUpdate(JCCallItem item, JCCallItem.ChangeParam changeParam)
{
}
//通话中收到消息回调
public void onMessageReceive(string type, string content, JCCallItem item)
{
}
//未接通话回调
public void onMissedCallItem(JCCallItem item)
{
}
# 拨打通话
调用 call (opens new window)发起语音通话,需要填写的参数有:
userID
填写对方的用户ID。video
选择是否为视频通话, true 表示拨打视频通话, false 表示拨打语音通话。callParam
通话参数对象,此参数可为空,详细定义见:JCCallParam (opens new window)。
// 发起语音呼叫
mCall.call("userID", isVideo , new JCCall.CallParam("extraParam", "ticket"));
拨打通话后,主叫和被叫均会收到新增通话的回调 onCallItemAdd (opens new window) ,此时通话状态变为 Pending 。您可以通过重写 onCallItemAdd (opens new window) 执行逻辑操作。
示例代码
/// 1. 发起语音通话
mCall.call("userID", isVideo , new JCCall.CallParam("extraParam", "ticket"));
/// 2. 重写回调
public void onCallItemAdd(JCCallItem item) {
/// 业务逻辑
if (item.direction == JCCallDirection.In) {
/// 如果是被叫
...
}else{
/// 如果是主叫
...
}
}
TIP
如果主叫想取消通话,可以直接转到挂断通话部分。调用挂断接口后,通话状态变为 Cancel。
# 创建本地视频画面
发起通话后,调用 JCCallItem (opens new window) 类中的 startSelfVideo (opens new window) 方法打开本地视频预览。需要填入参数 JCMediaDevice.RenderType (opens new window) 以选择渲染模式。
示例代码
/// 1. 发起视频呼叫
mCall.call("222", true, null);
/// 2. 获取当前活跃通话
JCCallItem mCallItem = mCall.getActiveCallItem();
/// 3. 打开本地视频预览,这里选择的是自适应模式
JCMediaDeviceVideoCanvas localCanvas = mCallItem.startSelfVideo(JCMediaDeviceRenderMode.FULLAUTO);
ImageBrush image = new ImageBrush(localCanvas.videoView);
image.Stretch = Stretch.Uniform;
this.label.Background = image;
...
# 应答通话
被叫收到 onCallItemAdd (opens new window) 回调,在回调中根据 JCCallItem (opens new window) 属性来判断是视频呼入还是语音呼入,从而做出相应的处理。
public void onCallItemAdd(JCCallItem item) {
/// 1. 如果是视频呼入且在振铃中
if (item.direction == JCCallDirection.In && item.video) {
/// 2. 做出相应的处理,如在界面上显示“振铃中”
...
}
}
调用 answer (opens new window) 接听通话。
mCall.answer(item, true);
通话接听后,通话状态变为 Connecting。
TIP
如果被叫要在此时拒绝通话,请调用挂断通话的接口。这种情况下调用挂断后,通话状态变为 Canceled。
# 创建远端视频画面
被叫接听通话后,双方将建立连接,此时,主叫和被叫都将会收到通话更新的回调(onCallItemUpdate),通话状态变为 Talking。
调用 JCCallItem (opens new window) 类中的 startOtherVideo (opens new window) 获取远端视频画面。返回对象为 JCMediaDeviceVideoCanvas (opens new window)。
示例代码
public void onCallItemUpdate(JCCallItem item) {
// 如果对端在上传视频流(uploadVideoStreamOther)
// mRemoteCanvas 为 JCMediaDeviceVideoCanvas 对象实例,请在方法前声明。
if (item.state == JCCallState.Talking && mRemoteCanvas == null && item.getUploadVideoStreamOther()) {
JCMediaDeviceVideoCanvas remoteCanvas = item.startOtherVideo(JCMediaDeviceRenderMode.FULLAUTO);
ImageBrush image = new ImageBrush(remoteCanvas.videoView);
image.Stretch = Stretch.Uniform;
this.label.Background = image;
}
}
# 挂断通话
主叫或者被叫均可以挂断通话。
调用 getActiveCallItem (opens new window) 获取当前活跃的通话对象:
mCall.getActiveCallItem();
调用 term (opens new window) 挂断当前活跃通话:
mCall.term(item, reason, description);
示例代码
/// 1. 获取当前活跃通话
JCCallItem item = mCall.getActiveCallItem();
/// 2. 挂断当前活跃通话
mCall.term(item, JCCallReason.None, null);
# 销毁本地和远端视频画面
通话挂断后,收到移除通话的回调 onCallItemRemove (opens new window) ,通话状态变为 Ok ,此时您需要分别调用 stopSelfVideo (opens new window) 和 stopOtherVideo (opens new window) 销毁本地和远端视频画面。
示例代码
public void onCallItemRemove(JCCallItem item, JCCallReason reason, String description) {
/// 销毁本地视频画面
item.stopSelfVideo();
/// 销毁远端视频画面
item.stopOtherVideo();
}