# 实现一对一通话

本章将介绍如何实现一对一视频通话,一对一视频通话的 API 调用时序见下图:

../../../../_images/1-1workflowandroid.jpg

# 获取设备权限

使用 checkSelfPermission (opens new window) 方法,在开启 Activity 时检查并获取 Android 移动设备的麦克风使用权限。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 获取设备权限
    requestPermission();

}

/*动态申请权限*/
private void requestPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}, 1000);
        }
    }
}

# 初始化

调用 JCMediaDevice.create (opens new window)JCCall.create (opens new window) 以初始化实现一对一通话需要的模块。

// 声明对象
JCMediaDevice mMediaDevice;
JCCall mCall;

// 初始化函数
public boolean initialize(Context context) {
    //1. 媒体类
    mMediaDevice = JCMediaDevice.create(mClient, new JCMediaDeviceCallback() {
        @Override
        public void onCameraUpdate() {

        }
        @Override
        public void onAudioOutputTypeChange(int i) {

        }
        @Override
        public void onRenderReceived(JCMediaDeviceVideoCanvas jcMediaDeviceVideoCanvas) {

        }
        @Override
        public void onRenderStart(JCMediaDeviceVideoCanvas jcMediaDeviceVideoCanvas) {

        }
    });
    //2. 通话类
    mCall = JCCall.create(mClient, mMediaDevice, new JCCallCallback() {
        @Override
        public void onCallItemAdd(JCCallItem jcCallItem) {

        }

        @Override
        public void onCallItemRemove(JCCallItem jcCallItem, int i, String s) {

        }

        @Override
        public void onCallItemUpdate(JCCallItem jcCallItem, JCCallItem.ChangeParam changeParam) {

        }

        @Override
        public void onMessageReceive(String s, String s1, JCCallItem jcCallItem) {

        }

        @Override
        public void onMissedCallItem(JCCallItem jcCallItem) {

        }
    });
}

# 媒体参数设置

一对一视频通话支持智能硬件设备集成,需要在发起通话前设置媒体参数。

// 根据模式生成配置参数
JCCall.MediaConfig mediaConfig = JCCall.MediaConfig.generateByMode(JCCall.MediaConfig.MODE_INTELLIGENT_HARDWARE_SMALL);
// 更新媒体参数
JCManager.getInstance().call.updateMediaConfig(mediaConfig);

菊风提供两种种配置模式供开发者选择,并开放相关属性供开发者进行灵活的自定义配置,具体方法请查看 媒体参数设置

# 拨打通话

调用 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) ,此时通话状态变为 STATE_PENDING (opens new window) 。您可以通过重写 onCallItemAdd (opens new window) 执行逻辑操作。

示例代码

// 1. 发起语音通话
mCall.call("userID", isVideo , new JCCall.CallParam("extraParam", "ticket"));

// 2. 重写回调
@Override
public void onCallItemAdd(JCCallItem item) {
    // 业务逻辑
    if (item.getDirection() == JCCall.DIRECTION_IN) {
        // 如果是被叫
        ...
    }else{
        // 如果是主叫
        ...
    }
}

TIP

如果主叫想取消通话,可以直接转到挂断通话部分。调用挂断接口后,通话状态变为 STATE_CANCEL。

# 创建本地视频画面

发起通话后,调用 JCCallItem (opens new window) 类中的 startSelfVideo (opens new window) 方法打开本地视频预览。需要填入参数 JCMediaDevice.RenderType (opens new window) 以选择渲染模式。

//界面UI
private FrameLayout mFLLocalVideo;
// 1. 发起视频呼叫
mCall.call("222", true, null);
// 2. 获取当前活跃通话
JCCallItem mCallItem = mCall.getActiveCallItem();
// 3. 打开本地视频预览,这里选择的是自适应模式
mCallLocalCanvas = item.startSelfVideo(JCMediaDevice.RENDER_FULL_AUTO);
// 4.将获取到的视频渲染视图添加到画布中
mFLLocalVideo.addView(mCallLocalCanvas.getVideoView(), 0);

# 应答通话

  1. 被叫收到 onCallItemAdd (opens new window) 回调,在回调中调用 JCCallItem (opens new window) 中的 getVideo (opens new window) 方法获取 video 属性来判断是视频呼入还是语音呼入,从而做出相应的处理。

    @Override
    public void onCallItemAdd(JCCallItem item) {
        // 1. 如果是视频呼入且在振铃中
        if (item.getDirection() == JCCall.DIRECTION_IN && item.getVideo()) {
         // 2. 做出相应的处理,如在界面上显示“振铃中”
            ...
        }
    }
    
  2. 调用 answer (opens new window) 接听通话。

    mCall.answer(item, true);
    

通话接听后,通话状态变为 STATE_CONNECTING。

TIP

如果被叫要在此时拒绝通话,请调用挂断通话的接口。这种情况下调用挂断后,通话状态变为 STATE_CANCELED。

# 创建远端视频画面

视频通话中,通常需要看到其他用户。被叫接听通话后,双方将建立连接,此时,主叫和被叫都将会收到通话更新的回调(onCallItemUpdate),通话状态变为 STATE_TALKING。

调用 JCCallItem (opens new window) 类中的 startOtherVideo (opens new window) 获取远端视频画面。返回对象为 JCMediaDeviceVideoCanvas (opens new window)

//界面UI
private FrameLayout mFLRemoteVideo;
@Override
public void onCallItemUpdate(JCCallItem item) {
// 如果对端在上传视频流(uploadVideoStreamOther)
// mRemoteCanvas 为 JCMediaDeviceVideoCanvas 对象实例,请在方法前声明。
if (item.getState() == JCCall.STATE_TALKING && mRemoteCanvas == null && item.getUploadVideoStreamOther()) {
    // 1.启动对端视频渲染
    JCMediaDeviceVideoCanvas mRemoteCanvas = item.startOtherVideo(JCMediaDevice.RENDER_FULL_AUTO);
    // 2.将远端视频画面添加到界面UI中
    mFLRemoteVideo.addView(mRemoteCanvas.getVideoView(),0);
    }
}

# 挂断通话

主叫或者被叫均可以挂断通话。

  1. 调用 getActiveCallItem (opens new window) 获取当前活跃的通话对象。

    mCall.getActiveCallItem();
    
  2. 调用 term (opens new window) 挂断当前活跃通话。

    mCall.term(item, reason, description);
    

示例代码

// 1. 获取当前活跃通话
JCCallItem item = mCall.getActiveCallItem();
// 2. 挂断当前活跃通话
mCall.term(item, JCCall.REASON_NONE, null);

# 销毁本地和远端视频画面

通话挂断后,收到移除通话的回调 onCallItemRemove (opens new window) ,通话状态变为 STATE_OK (opens new window) ,此时您需要分别调用 stopSelfVideo (opens new window)stopOtherVideo (opens new window) 销毁本地和远端视频画面。

@Override
public void onCallItemRemove(JCCallItem item, @JCCall.CallReason int reason, String description) {
    // 销毁本地视频画面
    item.stopSelfVideo();
    // 销毁远端视频画面
    item.stopOtherVideo();
}

最后更新时间: 2023/2/9 15:27:30