# 实现一对一通话
本章将介绍如何实现一对一视频通话,一对一视频通话的 API 调用时序见下图:
# 初始化
首先继承 JCMediaDeviceCallback (opens new window) 对象和 JCCallCallback (opens new window) 对象,并实现这两个对象中的纯虚函数。
class JCManager : public JCMediaDeviceCallback, public JCCallCallback
{
public:
//新增通话回调
virtual void onCallItemAdd(JCCallItem* item);
//移除通话回调
virtual void onCallItemRemove(JCCallItem* item, JCCallReason reason, const char* description);
//通话状态更新回调
virtual void onCallItemUpdate(JCCallItem* item, JCCallItemChangeParam changeParam);
//通话中收到消息回调
virtual void onMessageReceive(const char* type, const char* content, JCCallItem* item);
//上报服务器拉取的未接来电
virtual void onMissedCallItem(JCCallItem* item);
//摄像头变化回调
virtual void onCameraUpdate();
public:
//JCMediaDevice 对象
JCMediaDevice* mediaDevice;
//JCCall 对象
JCCall* call;
};
TIP
回调中的对象只能在该回调中使用,不能保存,上层可通过对应的方法获取通话对象。
然后调用 createJCMediaDevice (opens new window) 和 createJCCall (opens new window) 以初始化一对一通话需要的模块
bool JCManager::initialize()
{
//1. 媒体类
mediaDevice = createJCMediaDevice(client, this);
//2. 通话类
call = createJCCall(client, mediaDevice, this);
}
其中:
JCMediaDevice create 方法中的 this 为 JCMediaDeviceCallback (opens new window) 的派生类,该类于将媒体设备相关的事件通知给上层。因此需要先创建 JCMediaDeviceCallback 的派生类,然后在该派生类中实现 JCMediaDeviceCallback 的纯虚函数。
JCCall create 方法中的 this 为 JCCallCallback (opens new window) 的派生类,该类用于将通话相关的事件通知给上层。因此需要先创建 JCCallCallback 的派生类,然后在该派生类中实现 JCCallCallback 的纯虚函数。
# 拨打通话
调用 call (opens new window) 发起语音通话,需要填写的参数有:
userID
填写对方的用户ID。video
选择是否为视频通话, true 表示拨打视频通话, false 表示拨打语音通话。callParam
通话参数对象,此参数可为空,详细定义见:JCCallParam (opens new window)。
// 发起语音呼叫
JCCallParam mCallParam;
JCManager::shared()->call->call("userID", isVideo, mCallParam);
拨打通话后,主叫和被叫均会收到新增通话的回调 onCallItemAdd (opens new window) ,此时通话状态变为 JCCallStatePending 。您可以在上层实现 onCallItemAdd (opens new window) 方法并处理相关的逻辑。
示例代码
// 收到新增通话回调
void JCManager::onCallItemAdd(JCCallItem* item) {
// 业务逻辑
if (item->direction == JCCallDirectionIn) {
// 如果是呼入
...
} else {
// 如果是呼出
...
}
}
TIP
如果主叫想取消通话,可以直接转到挂断通话部分。调用挂断接口后,通话状态变为 JCCallStateCancel。
# 创建本地视频画面
发起通话后,调用 JCCallItem (opens new window) 类中的 startSelfVideo (opens new window) 方法创建本地视频画面,该方法会返回一个 JCMediaDeviceVideoCanvas (opens new window) 对象。该对象用于将视频渲染到画布上,并管理渲染的方式。(调用此方法会打开摄像头):
void JCManager::onCallItemAdd(JCCallItem* item) {
JCMediaDeviceVideoCanvas* mCallLocalCanvas;
if (mCallLocalCanvas == NULL && item->getUploadVideoStreamSelf())
{
// 创建本地视频画面
mCallLocalCanvas = item->startSelfVideo((void*)mWndCallLocalVideo.m_hWnd, (JCMediaDeviceRenderMode)JCMediaDeviceRenderModeFullContent);
}
}
# 应答通话
主叫发起呼叫成功后,被叫会收到 onCallItemAdd (opens new window) 回调,此时可以通过回调中的 JCCallItem (opens new window) 对象中的 getVideo() 方法以及 getDirection() 方法判断是视频呼入还是语音呼入,从而做出相应的处理:
void JCManager::onCallItemAdd(JCCallItem* item) { // 1. 如果是视频呼入且在振铃中 if (item->getDirection() == JCCallDirectionIn && item->getState() == JCCallStatePending) { // 2. 做出相应的处理,如在界面上显示“振铃中” ... } }
调用 answer (opens new window) 接听通话,视频通话既可语音应答也可视频应答:
// 获取活跃通话 JCCallItem* item = JCManager::shared()->call->getActiveCallItem(); // 应答通话 JCManager::shared()->call->answer(item, item->getVideo());
通话应答后,通话状态变为 JCCallStateConnecting。
TIP
如果被叫要在此时拒绝通话,可以直接转到挂断通话部分。调用挂断接口后,通话状态变为 JCCallStateCanceled。
# 创建远端视频画面
被叫接听通话后,双方将建立连接,此时,主叫和被叫都将会收到通话更新的回调(onCallItemUpdate),通话状态变为 JCCallStateTalking。
调用 JCCallItem (opens new window) 类中的 startOtherVideo (opens new window) 方法创建远端视频画面,该方法会返回一个 JCMediaDeviceVideoCanvas (opens new window) 对象,该对象用于将视频渲染到画布上,并管理渲染的方式。
void JCManager::onCallItemUpdate(JCCallItem* item, JCCallItemChangeParam changeParam) {
JCMediaDeviceVideoCanvas *mCallRemoteCanvas;
// 如果对端在上传视频流(uploadVideoStreamOther)
if (mCallRemoteCanvas == NULL && item->getUploadVideoStreamOther())
{
// 创建远端视频画面
mCallRemoteCanvas = item->startOtherVideo(mWndCallRemoteVideo.m_hWnd, (JCMediaDeviceRenderMode)JCMediaDeviceRenderModeFullContent);
...
}
}
# 挂断通话
主叫或者被叫均可以挂断通话。
首先调用 getActiveCallItem (opens new window) 获取当前活跃的通话对象;
当前活跃通话对象获取后,调用 term (opens new window) 挂断当前活跃通话:
void JCSampleDlg::OnBnClickedButtonTermcall() { // 1. 获取当前活跃通话 JCCallItem* item = JCManager::shared()->call->getActiveCallItem(); if (item != NULL) { // 2. 挂断当前活跃通话 JCManager::shared()->call->term(item, JCCallReasonNone, "term"); } }
# 销毁本地和远端视频画面
通话挂断后,会触发 JCCallCallback (opens new window) 中的 onCallItemRemove(通话移除回调),通话状态变为 JCCallStateOk,此时您需要调用 stopSelfVideo (opens new window) 和 stopOtherVideo (opens new window) 方法销毁本地和远端视频画面。
void JCManager::onCallItemRemove(JCCallItem* item, JCCallReason reason, const char* description) { //移除通话回调
// 本端视频销毁
if (mCallLocalCanvas != NULL && !item->getUploadVideoStreamSelf())
{
item->stopSelfVideo();
mCallLocalCanvas = NULL;
mWndCallLocalVideo.Invalidate();
}
// 远端视频销毁
if (mCallRemoteCanvas != NULL && !item->getUploadVideoStreamOther())
{
item->stopOtherVideo();
mCallRemoteCanvas = NULL;
mWndCallRemoteVideo.Invalidate();
}
}
至此,您就完成了基础的一对一视频通话功能。