You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android应用中Azure Communication Service用户间呼叫连接失败问题咨询

Azure Communication Service Android视频通话连接问题排查

我正在开发基于Azure Communication Service的Android视频通话应用,参考官方教程修改代码。教程里用CLI/Portal生成的单用户访问令牌作为呼叫ID发起呼叫,但我给两个用户分别配置Portal生成的令牌,修改代码传入两个令牌作为呼叫参与者后,模拟器和真机测试都无法建立连接。

教程原启动呼叫代码

private void startCall() {
Context context = this.getApplicationContext();
EditText callIdView = findViewById(R.id.call_id);
String callId = callIdView.getText().toString();
ArrayList<CommunicationIdentifier> participants = new ArrayList<CommunicationIdentifier>();
List<VideoDeviceInfo> cameras = deviceManager.getCameras();


StartCallOptions options = new StartCallOptions();
if(!cameras.isEmpty()) {
    currentCamera = getNextAvailableCamera(null);
    currentVideoStream = new LocalVideoStream(currentCamera, context);
    LocalVideoStream[] videoStreams = new LocalVideoStream[1];
    videoStreams[0] = currentVideoStream;
    VideoOptions videoOptions = new VideoOptions(videoStreams);
    options.setVideoOptions(videoOptions);
    showPreview(currentVideoStream);
}
participants.add(new CommunicationUserIdentifier(callId));

call = callAgent.startCall(
        context,
        participants,
        options);

//Subscribe to events on updates of call state and remote participants
remoteParticipantUpdatedListener = this::handleRemoteParticipantsUpdate;
onStateChangedListener = this::handleCallOnStateChanged;
call.addOnRemoteParticipantsUpdatedListener(remoteParticipantUpdatedListener);
call.addOnStateChangedListener(onStateChangedListener);
}

我的修改后代码

private void startCall(String whoCall, String whoCalled) {
    Context context = this.getApplicationContext();

    ArrayList<CommunicationIdentifier> participants = new ArrayList<CommunicationIdentifier>();
    List<VideoDeviceInfo> cameras = deviceManager.getCameras();

    StartCallOptions options = new StartCallOptions();
    if(!cameras.isEmpty()) {
        currentCamera = getNextAvailableCamera(null);
        currentVideoStream = new LocalVideoStream(currentCamera, context);
        LocalVideoStream[] videoStreams = new LocalVideoStream[1];
        videoStreams[0] = currentVideoStream;
        VideoOptions videoOptions = new VideoOptions(videoStreams);
        options.setVideoOptions(videoOptions);
        showPreview(currentVideoStream);
    }

    participants.add(new CommunicationUserIdentifier(whoCall));
    participants.add(new CommunicationUserIdentifier(whoCalled));

    call = callAgent.startCall(
            context,
            participants,
            options);

    //Subscribe to events on updates of call state and remote participants
    remoteParticipantUpdatedListener = this::handleRemoteParticipantsUpdate;
    onStateChangedListener = this::handleCallOnStateChanged;
    call.addOnRemoteParticipantsUpdatedListener(remoteParticipantUpdatedListener);
    call.addOnStateChangedListener(onStateChangedListener);

    callButton.setVisibility(View.INVISIBLE);
    funStuffButton.setVisibility((View.VISIBLE));
    hangupButton.setVisibility(View.VISIBLE);
}

其中whoCall与whoCalled为从Portal获取的两个用户访问令牌,通过单选按钮设置呼叫对象。我存在以下疑问:

  • 是否需要双方使用同一个令牌?若如此,如何实现用户间呼叫?
  • 是否需要修改令牌在呼叫初始化中的使用逻辑?

解决方案与排查思路

核心误解纠正

用户访问令牌≠用户ID,你混淆了这两个核心概念:

  1. 令牌是客户端的身份认证凭证,每个用户必须使用自己的专属令牌初始化CallAgent,而非作为参与者ID传入。
  2. 呼叫参与者参数需要传入的是对方的Communication User ID,不是令牌。

代码错误修正

你的修改代码把两个令牌都加入参与者列表是完全错误的,正确逻辑如下:

  1. 初始化CallAgent(每个客户端单独执行)
// 用当前用户的专属令牌初始化CallAgent,完成身份认证
CommunicationTokenCredential credential = new CommunicationTokenCredential("当前用户的访问令牌");
CallAgent callAgent = new CallClient().createCallAgent(context, credential).get();
  1. 发起呼叫(仅传入对方用户ID)
private void startCall(String targetUserId) {
    Context context = this.getApplicationContext();
    ArrayList<CommunicationIdentifier> participants = new ArrayList<>();
    List<VideoDeviceInfo> cameras = deviceManager.getCameras();

    StartCallOptions options = new StartCallOptions();
    if(!cameras.isEmpty()) {
        currentCamera = getNextAvailableCamera(null);
        currentVideoStream = new LocalVideoStream(currentCamera, context);
        LocalVideoStream[] videoStreams = new LocalVideoStream[1];
        videoStreams[0] = currentVideoStream;
        VideoOptions videoOptions = new VideoOptions(videoStreams);
        options.setVideoOptions(videoOptions);
        showPreview(currentVideoStream);
    }

    // 仅添加对方的用户ID(不是令牌)
    participants.add(new CommunicationUserIdentifier(targetUserId));

    call = callAgent.startCall(
            context,
            participants,
            options);

    // 事件订阅逻辑保持不变
    remoteParticipantUpdatedListener = this::handleRemoteParticipantsUpdate;
    onStateChangedListener = this::handleCallOnStateChanged;
    call.addOnRemoteParticipantsUpdatedListener(remoteParticipantUpdatedListener);
    call.addOnStateChangedListener(onStateChangedListener);

    callButton.setVisibility(View.INVISIBLE);
    funStuffButton.setVisibility(View.VISIBLE);
    hangupButton.setVisibility(View.VISIBLE);
}

排查步骤

  1. 确认令牌与用户ID的绑定关系:从Portal生成令牌时,每个令牌都对应唯一的Communication User ID,需记录该ID作为呼叫目标,而非使用令牌本身。
  2. 检查CallAgent初始化:确保两个客户端分别用自己的令牌初始化CallAgent,禁止共用令牌。
  3. 查看错误日志:在Android Studio的Logcat中搜索ACS相关错误信息(如认证失败、参与者不存在),直接定位问题原因。
  4. 验证权限配置:确认应用已申请INTERNET、CAMERA、RECORD_AUDIO等必要权限,且运行时已授权。
  5. 回归基础流程测试:先确保单个客户端能正常完成初始化,再逐步扩展到双端呼叫测试。

内容的提问来源于stack exchange,提问作者pbhuter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:07:34