iOS

# FAQ

# 登录失败(通用)

# 问题现象

登录接口调用失败

# 定位步骤

正常情况下,考虑到用户使用场景,应该从网络情况、是否有内外网限制、是否参数正确、是否多传参数或者少传参数等等方面进行排查。

# 问题排查

  1. 检查网络是否正常;
  2. 是否是外网直接访问内网地址,需要修改地址;
  3. 检查每个参数是否正确:中英文、大小写、参数内容、参数类型。
  4. 检查每个参数的含义,是否多传参数或者少传参数,如不支持国密传国密或导致失败。
  5. 实在排查不出请联系我们。

# 登出崩溃(通用)

# 问题现象

挂断后登出崩溃或者偶发登出崩溃。

# 定位步骤

登出功能正常运行一般不会出问题,排查方向可定为使用场景、环境。

# 问题排查

是否子线程,只能主线程登出,移动端建议在UI主线程,JavaSwing PC端建议在 UI 线程。

# SDK非主线程调用失败(通用)

# 问题现象

在非主线程中调用SDK方法失败或者导致其他问题。

# 定位步骤

一般情景SDK方法可以正常运行,在子线程中可能会导致出现不可知问题。

# 问题排查

是否子线程,只能主线程使用,,移动端建议在UI主线程,JavaSwing PC端建议在 UI 线程。

# 挂断后仍然无法正常结束通话(通用)

# 问题现象

成员1离开通话后,成员2仍然能看到成员1卡住的画面。此种情况大多数都是由于成员1异常离开导致的

# 定位步骤

正常情况下,成员1离开通话,服务端能立刻知道成员1离开。

异常离开通话后,成员1无法立刻通知服务端,这时服务端会等待成员1的心跳响应,1分钟(默认1分钟超时)后如果没有收到心跳回应,服务端会将成员1踢出通话。

# 问题排查

  1. 成员1没有调用term挂断
  2. 成员1先调用logout登出,然后调用term挂断
  3. 用户1直接杀进程离开会议(此种情况不可避免)

# 通话听不到声音或看不到画面,无法访问媒体设备(通用)

# 问题现象

双方通话过程中,自己说话对方听不到声音或看不到画面

# 定位步骤

一般移动端设备考虑被占用或者未授予权限,异常不能使用情况基本不考虑

# 问题排查

# 未开启麦克风/相机权限

  • iOS

进入手机设置,查看对应的应用是否开启权限。

  • Android

进入手机设置-应用-应用管理-权限,查看对应的应用是否开启权限。

  • Windows

麦克风:设置->隐私->麦克风->打开麦克风权限,同时允许本应用访问麦克风。

摄像头:设置->隐私->相机->打开相机权限,同时允许本应用访问相机。

# 麦克风/相机被占用或设备异常

  • 麦克风后打开的应用都会抢占之前打开的应用,一般系统都会提示麦克风占用

  • 摄像头后打开的应用都会抢占之前打开的应用,一般系统都会提示摄像头占用

# 视频画面出现黑边(通用)

# 问题现象

视频预览有黑边

# 定位步骤

渲染视频接口是否全屏幕渲染还是全内容渲染。

# 问题排查

当实际视频尺寸和显示视图宽高比不同时,如果是全内容渲染,那么就会留有黑边,如果是全屏幕渲染,会将画面等比例放大缩小充满显示区域,超出部分被裁剪,首先确认本端实际采集视频大小(本地预览),对端实际发送视频(预览对方)的视频宽高比和显示区域宽高比是否相同。

通话中可以通过设置本地宽高比修改发送的视频宽高比, setRatio 方法。

# 视频旋转(通用)

# 问题现象

使用过程中竖屏横屏切换,会导致视频画面方向转动。

# 定位步骤

视频方向是有转动功能的,排查方向放在设置转动上。

# 问题排查

SDK 中 JRTCMediaDevice 模块的 setVideoAngle 接口是否设置为自动模式(默认自动,可设置0,90,180,270),可以通过设置该接口适时修改画面方向或者固定画面方向。

# 音频被打断导致访客无声音画面

# 问题现象

通话中被其他音视频软件打断通话,

# 定位步骤

之前正常抢占之后无声音无画面定位需要看上报的问题

# 问题排查

上层在JRTCMediaDeviceCallBack的onVideoError接口去打开摄像头,代码如下:

@Override 
public void onVideoError(@VideoErrorType int errorType, String errorDetail) { 
    if (errorType == JRTCMediaDevice.VIDEO_ERROR_CAMERA) {
        //摄像头异常,sdk内部会去自动关闭摄像头,如果有需要可以在这里重新打开,但是避免死循环打开
        //可以重拾一次还是不行提示客户
        mediaDevice.startCamera(); 
    }
}

# iOS进入后台后音频暂停或回到前台后未继续播放

# 问题现象

iOS App 进入后台后,音频暂停;或再次回到前台时,音频没有继续播放。

# 定位步骤

优先检查 App 是否已开启 iOS 后台音频能力。

# 问题排查

在 Xcode 中打开 App Target,进入 Signing & Capabilities,检查 Background Modes 是否已勾选 Audio, AirPlay, and Picture in Picture。

如果未勾选,App 进入后台后系统可能会暂停音频会话,导致回到前台时音频无法按预期继续播放。开启后重新打包验证。

# 移动端坐席收到来电后无法接起

# 问题现象

集成方同时初始化坐席和访客模块,收到来电后,访客模块也收到了来电邀请,向排队机回了responseCancelInvite。

# 定位步骤

取消同时初始化进行定位

# 问题排查

只能初始化一个模块

# 播放音乐无声音(通用)

# 问题现象

使用SDK startRing播放等待铃声没有声音

# 定位步骤

需要确定使用的是本地播放还是通话内播放

# 问题排查

本地播放接口和通话内播放接口和逻辑不同,需要分别实现startRing本地播放、enableAudioInputFromFile通话内播放

# 小米9崩溃

# 问题现象

集成完毕但运行崩溃在小米9

# 定位步骤

集成完毕运行崩溃首先检查混淆,部分手机运行崩溃检查运行环境手机内核。

# 问题排查

使用v7a文件夹的so放armabi里去

# 虚拟背景墙崩溃(Windows&UOS)

# 问题现象

设置,使用虚拟背景墙崩溃。

# 定位步骤

从加载、资源、外界环境去定位

# 问题排查

  1. 选择虚拟背景墙后,界面提示5秒,首次加载慢,请耐心等待,首次时间较长。
  2. 图片分辨率超过3840*2160提示图片太大

# 访客看不到坐席画面(H5/WEB)

# 问题现象

H5界面自动播放失败

# 定位步骤

代码逻辑正常的情况下需要定位WebView、权限等限制问题。

# 问题排查

浏览器(WebView)安全性要求用户与界面有交互才允许自动播放(保护用户隐私,必须用户手动确认播放才允许播放视频/音频),有些浏览器会认为设备授权(摄像头、麦克风)行为是一次UI交互,WebView行为不确定。另外,目前H5端这方面的策略越来越严格,自动播放受限越来越严格。页面增加自动播放异常处理,弹窗交互确认,在确认按钮下增加play()接口调用

# 移动端坐席收到来电后无法接起(Android/IOS/Web)

# 问题现象

集成方同时初始化坐席和访客模块,收到来电后,访客模块也收到了来电邀请,向排队机回了responseCancelInvite。

# 定位步骤

取消同时初始化进行定位

# 问题排查

只能初始化一个模块

# PC插件的浏览器的要求及常见问题(Windows&UOS)

# 问题现象

IE11无法与坐席插件通讯

# 定位步骤

安全设置、浏览器是否能启动程序、是否能连接插件,多浏览器对比检测进行定位

# 问题排查

IE11无法与坐席插件通讯,远程地址加入IE白名单,IE安全级别设置为中/低

# 屏幕共享频繁闪现黑屏(Windwos&UOS)

# 问题现象

如题屏幕共享频繁闪现黑屏

# 定位步骤

在上层代码没问题的情况下检查集成和驱动。

# 问题排查

坐席显卡驱动未更新

# 屏幕共享闪退(IOS)

# 问题现象

ios使用屏幕共享功能应用闪退,screencapture start capture error: 录制因多任务和调整内容大小而中断

# 定位步骤

上层代码没问题的情况下,检查硬件条件、系统版本

# 问题排查

  1. 限制系统ios13以下不允许屏幕共享
  2. 使用应用外屏幕共享

参考链接: http://www.bubuko.com/infodetail-3801263.html

# 访客插件销毁后拉不起来(Windows&UOS)

# 问题现象

  1. java方式第一次拉起后 销毁 就再也拉不起来
  2. 如果不在他们业务里java同步骤1操作正常拉起。
  3. 网页版测试正常,若java拉不起,用网页同样拉不起,包检查失败,请重新检查

# 定位步骤

启动不了首先检查初始化相关功能

# 问题排查

  1. websocket 没有建立连接成功 初始化所以失败;
  2. websocket连接成功onOpen回调后再去初始化。

# 访客蓝牙接通时会被外放(IOS)

# 问题现象

蓝牙接通但是没有起到作用

# 定位步骤

查看应用界面逻辑

# 问题排查

  1. 视频建立前已经连接蓝牙,需要界面监听扬声器、蓝牙、耳机改变通知;然后再选择是否调用enableSpeak开启扬声器
  2. 视频建立后且已经开启扬声器,这时候连接蓝牙、耳机。系统会默认切换到蓝牙、耳机

# 多方通话加入失败(Windows&UOS)

# 问题现象

如题加入失败。

# 定位步骤

从代码集成,基础配置入手,没问题可以查看licence并发数是否超过上限。

# 问题排查

  1. 代码集成是否出问题,对照Sample;
  2. 配置是否出问题,对照文档;
  3. 增加licence并发数

# 录制CD异常问题(Windows&UOS)

# 问题现象

会议中停止录制服务,终端提示录制结束,视频会议也没有了,但是再次去加入会议,提示还在会议中

# 定位步骤

最后通话的录制异常回调里的isshutdown值为什么,发现无论isshutdown是否为ture,都是去关闭了通话窗口并弹出录制结束提示

# 问题排查

更新JMDS版本

# mvtm本地视频不显示(Windows&UOS)

# 问题现象

mvtm显示不出本地视频画面

# 定位步骤

上层代码没问题的情况下,检查运行条件

# 问题排查

内存不足

# 端口占用(Windows&UOS)

# 问题现象

启动应用提示端口占用

# 定位步骤

上次是否关闭,有无其他应用占用

# 问题排查

  1. 先关闭上次使用;
  2. 通过进程/任务管理器关闭相应端口应用。
  3. 重启计算机。

# 录制相关问题(Windows&UOS)

# 问题现象

  1. 本地录制视频失败。
  2. 录制文件被切割等配置问题。

# 定位步骤

需要明白服务器录制、本地录制中的会议内录制和会议外录制,还有相关的录制配置。

# 问题排查

  1. 本地录制生成录制文件失败,客户以看在传filePath时,确保路径已经生成,确保路径填写正确。
  2. 服务器录制是发起者的呼叫参数来控制开关:

访客呼叫参数JRTCallCenterCallParam.autoRecord;

座席回呼参数JRTCallCenterCallParam.autoRecord;

房间参数JRTCRoomJoinParam.enableRemoteRecord

视频客服中当发起者入会参数关闭了远程自动录制时,可通过调用enableRemoteRecord()接口控制远程录制开关和配置,多方中获取录制状态后即可通过该接口实现具体需要的配置,配置如下:

recordVideo                                设置录制是否包含视频,false的时候服务器为录音文件aac
frameRate                                设置录制帧率
mergeMode                                媒体录制视频合并模式
intelligentMergeMode        设置 mergeMode 为智能分屏模式下的布局样式(无屏幕共享)
scsMergeMode                        设置 mergeMode 为智能分屏模式下的布局样式(有屏幕共享)
videoWidth                                设置录制视频的宽度
videoHeight                                设置录制视频的高度
iBitrate                                设置录制码率
extraInfo                                设置录制自定义随路参数, json格式
watermarkTextMap                设置水印串
layoutType                                设置录制样式,对应业务管理平台上配置的录制样式的名字, 不传则用默认
List<RecordLayout>                设置录制成员布局列表,自定义布局需要
protocol                                设置录制文件存储环境 aws qiniu ftp等
uploadEndpoint                        设置接入地址,ftp模式下例:ftp://127.0.0.1:21
accessKey                                设置存储环境相关key, ftp模式传ftp服务器登录名
secretKey                                设置存储环境相关key, ftp模式传ftp服务器登录密码
bucketName                                设置桶名,存储环境相关key
fileName                                存储文件名, ftp模式下可以带路径如:juphoon/1.mp4;视频文件录制后缀为mp4,如xx.mp4
音频文件录制后缀为aac,如xx.aac
splitFileSize                        分割文件大小,单位为 kb
  1. 本地录制会议外录制JRTCMediaDevice.startVideoCaptureRecord(),会议外录制是指不加入会也可录制的本地录制,相应的又停止录制的方法stopVideoCaptureRecord,录制配置如下:
filePath    保存的文件路径,必须包含文件名(xxx.mp4或者xxx.avi)
streamId    视频流ID, (包括摄像头ID、文件视频源ID、屏幕ID等)
audioSource 录制文件音频源
fileType    录制文件编码封装类型
width       录制文件宽(传-1则默认屏幕共享采集属性的宽)
height      录制文件高(传-1则默认屏幕共享采集属性的高)
  1. 本地录制会议内录制JRTCRoom.enableLocalRecord();会议内录制是指加入会才也可录制的本地录制,JRTCRecordLocalParam录制配置如下:
frameRate                                 设置录制帧率, 默认18
mergeMode                                设置媒体录制视频合并模式,默认智能分屏模式
intelligentMergeMode        设置 mergeMode 为智能分屏模式下的布局样式(无屏幕共享)
scsMergeMode                          设置 mergeMode 为智能分屏模式下的布局样式(有屏幕共享)
videoWidth                                设置录制视频的宽度
videoHeight                                设置录制视频的高度
iBitrate                                设置录制码率
filePath                                设置保存的文件路径,必须包含文件名;音频文件录制后缀为mp4,如xx.mp4
音频文件录制后缀为aac,如xx.aac
recVideo                                设置是否录制视频,默认true
recAudio                                设置是否录制音频,默认true
includeSelf                                设置录制是否包含自己,默认true
watermarkTextMap                设置录制视频水印串
configFilePath                        设置录制配置文件路径 例如: xxx\record.cfg
List<RecordLayout>                设置录制成员布局列表,mergeMode 为自定义布局需要
  1. 如果配置出问题,检查无误后可咨询是否有新版本。

注:详细介绍请看相关接口的开发文档。

# 音频占用问题(iOS)

# 问题现象

  1. 收到部分三方App来电(微信来电),通话音频被中断,用户拒接或者结束通话导致音频无法恢复。

# 定位步骤

  1. app通话过程中,收到微信来电振铃声音较大,音频被中断然后拒绝微信电话,app声音无法恢复
  2. IOS 16.3系统,微信8.0.34版本
  3. 微信通知功能已经开启,且iPhone处于非静音模式
  4. 收到微信来电振铃时微信使用的是独占模式,会把app的声音采集中断,挂断后没有收到系统的中断结束通知,导致app语音无法恢复。

# 问题排查

  1. 收到音频被中断的通知后,判断是否收到系统来电,如果不是系统来电引发的中断,需要App层弹窗让用户选择是否恢复通话;
  2. 示例代码:
#import <JRTCSDK/JRTCSDK.h>
#import <JRTCSDK/JRTCSystemPhone.h>

@interface JRTCManager () <JRTCClientCallback, JRTCMediaDeviceCallback, JRTCSystemPhoneCallback> {
    BOOL _systemCall; //记录是否系统来电
}
//实现接口
@end

@property (nonatomic, strong) JRTCClient* client;
@property (nonatomic, strong) JRTCMediaDevice* mediaDevice;

@implementation JRTCManager

-(void)init {
    //初始化client
    _client = [JRTCClient create:self initParam:nil];
    //初始化_mediaDevice
    _mediaDevice = [JRTCMediaDevice create:_client callback:self initParam:nil];
    //设置系统通话监听,也可以上层自行使用系统api监听系统通话
    [JRTCSystemPhone.shared addCallback: self];
}

//监听系统通话
- (void)onSystemPhoneStateChanged:(SystemPhoneState)newState oldState:(SystemPhoneState)oldState {
    switch (newState) {
        case SystemPhoneStateRing: //系统来电
        case SystemPhoneStateOffhook: //系统通话建立/系统电话呼出
            _systemCall = YES; 
            break;
        case SystemPhoneStateIdle: //空闲
        default:
            _systemCall = NO;
            break;
    }
}

//监听音频中断恢复通知
- (void)onAudioInerruptAndResume:(bool)interrupt {
    if (interrupt) { //音频中断
        if (_systemCall) {
            //收到系统来电,系统通话结束音频可自行恢复,不需要做处理
        } else {
            //收到三方App来电,部分通话软件通话结束后导致无法恢复音频
            //可以在这里弹框通知用户是否恢复音频
            //如果用户选择恢复,重新打开音频设备
            [self restartAudio];
        }
    } else { 
        //音频恢复
    }
}

//重新打开音频设备
- (void)restartAudio {
    [_mediaDevice stopAudio]; //关闭音频设备
    //设置全局音频模式
    NSError *error = nil;
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
    [audioSession setActive:YES error: &error];
    [audioSession setActive:NO error: &error];
    [_mediaDevice startAudio];//打开音频设备
}

@end

# 系统摄像头占用问题(Android)

# 问题现象

  1. 部分手机视频通话过程中被抢占摄像头,无法收到SDK的onVideoError通知。

# 定位步骤

  1. 部分手机摄像头被占用无法收到异常通知。

# 问题排查

  1. 渲染摄像头视图所在页面监听前后台切换,并记录当前前后台状态;
  2. 当页面在后台收到 onVideoError 通知,记录变量;
  3. 当页面回到前台判断是否有收到onVideoError 通知,如果有收到,则强行重启摄像头;如果没有,则判断当前摄像头是否已经开启,如果开启,则强行重启摄像头。
  4. 示例代码:
public CameraVideoActivity extends AppCompatActivity implements JRTCClientCallback, JRTCMediaDeviceCallback {

    private JRTCClient mClient;
    private JRTCMediaDevice mMediaDevice;

    private boolean mToBack = false; //记录当前页面是否在后台
    private boolean mVideoError = false; //记录是否收到视频异常通知
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_video);
        mClient = JRTCClient.create(this, this, null);
        mMediaDevice = JRTCMediaDevice.create(mClient, this, null);
    }

    //监听视频异常
    @Override
    public void onVideoError(String error) {
                //大部分手机在摄像头被占用,能收到视频异常回调,需要通过变量记录下
        if (mToBack) {
                mVideoError = true;
        } else {
            //如果在前台出现视频异常,那就一般不是摄像头被占用,或者被系统停止导致的;
            //可以在这里给予用户UI上提示;
                //一般手机设备默认摄像头不会出现损坏,所以在前台收到视频异常,
            //一般是其他视频错误导致,比如渲染异常等,需要提供日志反馈给开发定位。
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        //页面回到前台
        mToBack = false;
        if (mVideoError) {
            //如果收到了视频异常的的回调,SDK内部已经关闭了摄像头,只要重新打开摄像头就行
            mMediaDevice.startCamera();
            mVideoError = false; //重置变量
        } else if (mMediaDevice.isCameraOpen()) {
            //如果没有收到视频异常的的回调,则强行重新打开摄像头
            mMediaDevice.stopCamera();
            mMediaDevice.startCamera();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        //页面退到后台
        mToBack = true;
    }
}

# 部分手机退到后台导致无声或者摄像头画面静止问题(Android)

# 问题现象

  1. 部分手机在通话界面弹出申请悬浮窗权限;
  2. 用户点击去开启,跳转到系统应用设置界面开关权限;
  3. 麦克风无法采集到声音;
  4. 摄像头画面静止,无法采集到画面。

# 定位步骤

  1. Android 高版本对后台应用麦克风、摄像头等隐私权限加强了限制,当应用退到后台,一段时间后或者有应用权限变更,导致系统停止麦克风和摄像头采集。

# 问题排查

  1. 视频/音频通话建立时,需要增加前台服务通知来保活;
  2. 示例代码:
    1. 在 AndroidManifest. xml 里增加声明;

    2. <!-- 前台服务权限 -->
      <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
      
      <!-- 保活服务 -->
      <!-- targetVersion 30或者以上需要添加 android:foregroundServiceType 这个配置 -->
      <service android:name="com.xxx.XXXAliveService"
          android:foregroundServiceType="camera|microphone"
          android:enabled="true"/>
      
    3. XXXAliveService.java文件,添加前台服务通知;

    4. public IBinder onBind(Intent intent) {
              return null;
          }
      
          @Override
          public int onStartCommand(Intent intent, int flags, int startId) {
              createNotificationChannel();
              return super.onStartCommand(intent, flags, startId);
          }
      
          private void createNotificationChannel() {
              //Android 8.0 通知必须要有 channelId,可以随意字符串,只要和NotificationChannel中参数一致;
              NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "audioDevice")
                      .setContentTitle("菊风")
                  .setContentText("视频通话中")
                      .setDefaults(Notification.DEFAULT_SOUND)
                  .setWhen(System.currentTimeMillis());
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                  NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
                  NotificationChannel channel = new NotificationChannel("audioDevice", "keepAlive", NotificationManager.IMPORTANCE_DEFAULT);
                  notificationManager.createNotificationChannel(channel);
              }
              Notification notification = builder.build();
              //targetVersion 30以下
              startForeground(1000, notification);
              //targetVersion 30或者以上
              startForeground(1000, notification, FOREGROUND_SERVICE_TYPE_CAMERA | FOREGROUND_SERVICE_TYPE_MICROPHONE);
          }
      
          @Override
          public void onDestroy() {
              stopForeground(true);
              super.onDestroy();
          }
      }
      
    5. 在音频/视频通话建立时开启前台服务通知

    6. //context 可以是界面Activity对象也可以是,Application 对象
      Intent service = new Intent(context, XXXAliveService.class);
      startForegroundService(service);
      
    7. 在视频通话结束后停止前台服务通知

    8. //context 可以是界面Activity对象也可以是,Application 对象
      Intent service = new Intent(context, XXXAliveService.class);
      this.stopService(service);
      

# Android 工程 targetVersion 29导致屏幕共享失败

# 问题现象

使用屏幕共享过程中抛出 SecurityException 异常

java.lang.SecurityException: Media projections require a foreground service 
of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION

# 定位步骤

在 targetSdkVersion 大于等于29时,系统加强了对屏幕采集的限制,必须先启动相应的前台 Service,才能正常调用 getMediaProjection 方法,否则会抛异常;

# 问题排查

如果工程配置 targetSdkVersion 大于等于29(需要使用Android X),目前上架 google 应用市场和国内应用市场要求targetSdkVersion 大于等于30;

  1. 首先确保菊风提供的 sdk 中包含类 com.justalk.cloud.zmf.CaptureScreenService ,部分提供较早的 sdk 还没有兼容,所以没有该类,如果没有需要菊风侧 sdk 升级
  2. 需要在 AndroidManifest.xml 配置如下:
//如果Android tagetVersion 设置29及以上,则需要添加前台服务权限,屏幕共享需要
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

//注册activity,屏幕共享需要
<activity
          android:name="com.justalk.cloud.zmf.ZmfActivity"
          android:theme="@android:style/Theme.Dialog" />
//如果Android tagetVersion 设置29或以上,则还需要注册service,屏幕共享需要
<service 
         android:name="com.justalk.cloud.zmf.CaptureScreenService"
         android:enabled="true"
         android:foregroundServiceType="mediaProjection"/>

# Andorid 15 及以上手机屏幕共享黑屏或者蒙影

# 问题现象

如果客户端是Android 15 或者以上手机,服务器录制文件屏幕共享画面黑屏或者蒙影

# 定位步骤

Android 15以及以上手机默认针对银行类/支付类App开启屏幕共享保护,当打开屏幕共享的时候,一般系统会有提示。

# 问题排查

系统设置可以把该保护关闭。

# PC端程序崩溃(Java Swing ),赞同场景

# 问题现象

程序崩溃

# 定位步骤

  1. 需要提供jvm异常文件,类似如下:需要配置 jvm 运行参数 -XX:ErrorFile

暂时无法在飞书文档外展示此内容

  1. 需要提供core文件,需要配置 jvm 运行参数 -XX:+CreateMinidumpOnCrash
  2. 如果是 Windows 端,还可以提供下 Windows 下的系统崩溃日志

# 问题排查

如果jvm异常堆栈指向菊风库,可以提供崩溃堆栈和菊风的日志找菊风开发定位

# NoCandidate:JSM.JSMS 错误

# 问题现象

创建会议失败,报错 MtcMpcallCreateDidFailNotification mCookie:2 info={"MtcMpcallErrorKey":"NoCandidate:JSM.JSMS"}

# 定位步骤

该报错表示没有会场资源

# 问题排查

检查 RoomLicence 是否正常启动

# 小程序看远端视频画面卡住

# 问题现象

盛京银行小程序视频通话过程中收到微信消息,点击弹出的消息横幅打开微信聊天窗口,关闭聊天窗口后小程序看远端视频画面卡住。

# 定位步骤

打开聊天窗口类似于退后台,这时候推拉流都停止,但是关闭聊天窗口后并没有恢复拉流,只恢复了推流。

# 解决方案

live-player拉流组件的auto-pause-if-open-native(当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放)和auto-pause-if-navigate(当跳转到本小程序的其他页面时,是否自动暂停本页面的实时音视频播放)两个属性值设置成 false 后解决

# H5摄像头权限失败

# 问题现象

调用 startCameraVideo 失败,或收到 onCameraError 回调。

常见错误码:

  • 111013C21 / C21_OPEN_CAMERA_NOT_PERMISSION:开启摄像头未获得摄像头权限

# 定位步骤

正常情况下,考虑到用户使用场景,应该从浏览器权限、页面协议、系统权限、摄像头设备、设备占用、采集参数是否正确等方面进行排查。

# 问题排查

  • 检查页面是否使用 HTTPS 或 localhost,普通 HTTP 页面通常无法调用摄像头;
  • 检查浏览器地址栏中的摄像头权限是否被拒绝;
  • 检查系统隐私设置是否允许当前浏览器访问摄像头;
  • 检查摄像头是否被其他 App、会议软件、其他浏览器标签页占用;
  • 检查设备是否真的存在摄像头,可通过 getCameras() 或浏览器设备设置确认;
  • 检查是否在用户点击按钮后触发采集授权,移动端浏览器更依赖用户手势;
  • 检查 setCameraProperty(width, height, frameRate) 设置的分辨率和帧率是否被设备支持;
  • 移动端检查前后摄像头 facingMode 是否可用;
  • 检查浏览器控制台中 getUserMedia 的具体异常,如 NotAllowedErrorNotFoundErrorNotReadableError

# H5麦克风权限失败

# 问题现象

调用 startAudioInput 失败,或收到 onAudioInputError 回调。

常见错误码:

  • 111005C21 / C21_START_AUDIO_INPUT_NOT_MIC_PERMISSION:打开音频输入设备未获得麦克风权限

# 定位步骤

正常情况下,考虑到用户使用场景,应该从浏览器权限、页面协议、系统权限、麦克风设备、设备占用、音频输入状态等方面进行排查。

# 问题排查

  • 检查页面是否使用 HTTPS 或 localhost;
  • 检查浏览器地址栏中的麦克风权限是否被拒绝;
  • 检查系统隐私设置是否允许当前浏览器访问麦克风;
  • 检查麦克风是否被其他 App、会议软件、其他浏览器标签页占用;
  • 检查麦克风是否被系统禁用、静音或未插好;
  • 检查调用 startAudioInput 前媒体类型是否允许音频,MediaType.VIDEO 下不支持打开音频输入;
  • 检查浏览器控制台中 getUserMedia 的具体异常;

# H5本地视频预览黑屏或不显示

# 问题现象

摄像头已授权,但页面本地视频区域黑屏、无画面,或 startCameraVideo 成功但画面不出。

# 定位步骤

H5 工程通过动态创建 video 标签并设置 srcObject 渲染本地流。排查重点包括 DOM 容器、视频流、播放状态、浏览器渲染和页面样式。

# 问题排查

  • 检查传入 startCameraVideovideoBox 是否真实存在;
  • 检查 videoBox 及父容器是否有有效宽高,避免宽高为 0;
  • 检查页面 CSS 是否隐藏了视频容器;
  • 检查 startCameraVideo 是否返回了有效的 canvas 对象;
  • 检查本地 MediaStream 是否包含 video track;
  • 检查 video 标签是否触发 oncanplayonplaying
  • 检查浏览器是否阻止自动播放,可尝试在用户点击事件中调用 canvas.resume()
  • 检查是否调用过 stopVideostopAllMedia 导致媒体流被停止;
  • 检查 iOS Safari、微信内置浏览器是否需要用户手势恢复播放;

# H5远端视频播放失败

# 问题现象

订阅远端视频后,对端画面不显示,或调用 startVideo 返回空、画面黑屏。

常见错误码:

  • 111027C21 / C21_START_VIDEO_FAIL:开启预览视频失败
  • 112024C22 / C22_REQUEST_OTHER_VIDEO_FAIL:请求房间中其他用户的视频流失败

# 定位步骤

远端视频流程通常是先调用 requestVideo 获取远端 mediaStream,再调用 startVideo(streamUrl, renderType, mediaStream, videoBox) 渲染。

# 问题排查

  • 检查远端成员是否真的开启了视频;
  • 检查 requestVideo 是否成功;
  • 检查房间或通话是否已加入,未入会时请求远端视频会失败;
  • 检查是否请求了自己的视频流,SDK 会拒绝 request self video;
  • 检查 participant.streamUrl 是否为空,streamUrl 为空会导致 startVideo 失败;
  • 检查 mediaStream 是否存在 video track;
  • 检查传入的远端视频容器 videoBox 是否存在且有尺寸;
  • 检查远端是否中途关闭视频或取消发送视频流;
  • 检查 iOS Safari、微信内置浏览器是否需要用户手势恢复播放;

# H5远端音频无声

# 问题现象

远端视频能看到,但听不到对方声音;或调用 startAudioOutput 后没有声音。

常见错误码:

  • 112022C22 / C22_ENABLE_REMOTE_AUDIO_OUTPUT_FAIL:开关远端音频输出失败
  • 111058C21 / C21_AUDIO_ERROR_CALLBACK:音频异常回调

# 定位步骤

H5 工程远端音频通过 JRTCMediaDeviceAudioContainer 播放,并且实际创建的是隐藏的 media 标签。排查重点包括远端音频流、浏览器自动播放策略、音频容器、输出设备和系统音量。

# 问题排查

  • 检查远端成员是否开启了音频;
  • 检查 mediaStream.getAudioTracks() 是否有音频轨道;
  • 检查 startAudioOutput(result.mediaStream, userId, audioBox) 入参是否正确;
  • 建议传入有效的 audioBox,不传可能无法播放音频;
  • 检查浏览器是否限制自动播放,移动端建议在用户点击后触发入会、订阅或播放;
  • 检查是否调用过 stopAudioOutputenableAudio(false)
  • 检查系统音量、浏览器标签页是否静音;
  • 检查 iOS Safari、微信内置浏览器是否需要用户手势恢复播放;

# H5切换摄像头失败

# 问题现象

调用 switchCamera 失败,无法切换前后摄像头或指定摄像头。

常见错误码:

  • 111018C21 / C21_SWITCH_CAMERA_FAIL:切换摄像头失败

# 定位步骤

H5 工程要求摄像头已经打开,并且设备列表中存在可切换摄像头。切换时会先停止当前 track,再重新调用 getUserMedia

# 问题排查

  • 检查调用 switchCamera 前是否已经成功打开摄像头;
  • 检查 getCameras() 是否有两个以上摄像头;
  • 检查旧摄像头资源是否已释放;
  • 检查摄像头是否被其他应用占用;

# 角色值判断(通用)

# 问题现象

通话或会议场景中,需要根据角色值判断当前成员是访客、座席、主访客、主座席,还是普通成员。

# 定位步骤

角色值本质上是按位组合的结果,通常需要通过二进制位和按位与运算来判断角色类型,再结合基础成员值确定最终身份。

# 问题排查

基础值说明:

十进制 二进制 十六进制 含义
131072 100000000000000000 0x20000 主角色位
128 10000000 0x80 访客位
64 1000000 0x40 座席位
6 110 0x06 普通音视频成员基础值

常见角色值对照:

角色值 二进制 十六进制 组成 角色说明
131206 100000000011000110 0x20086 131072 + 128 + 6 主访客
131142 100000000010000110 0x20046 131072 + 64 + 6 主座席
134 10000110 0x86 128 + 6 普通访客
70 1000110 0x46 64 + 6 普通座席
6 110 0x06 6 普通成员

现场判断顺序:

  1. 先看角色值里有没有 131072。如果 角色值 & 131072 = 131072,就说明带主角色位,是主角色。
  2. 再看角色值里有没有 128。如果 角色值 & 128 = 128,就说明是访客。
  3. 再看角色值里有没有 64。如果 角色值 & 64 = 64,就说明是座席。
  4. 如果只有基础值 6,没有主角色位、访客位、座席位,那就是普通成员。

现场可直接参考下面的判断结果:

  • 131072 且带 128:主访客。
  • 131072 且带 64:主座席。
  • 不带 131072 但带 128:普通访客。
  • 不带 131072 但带 64:普通座席。
  • 只有 6:普通成员。

按位与运算示例:

  • 131206 & 131072 = 131072,所以 131206 带主角色位。
  • 131142 & 131072 = 131072,所以 131142 带主角色位。
  • 134 & 131072 = 0,所以 134 不是主角色。
  • 134 & 128 = 128,所以 134 是访客。
  • 131206 & 128 = 128,所以 131206 是访客,也就是主访客。
  • 70 & 64 = 64,所以 70 是座席。
  • 131142 & 64 = 64,所以 131142 是座席,也就是主座席。

# 多方通话/多方通话2.0(Mpcall)中普通服务器录制

# 问题现象

通话中,如何开启普通服务器录制以及监听服务器录制状态变化

# 定位步骤

通过监听通话属性接口是否上报录制相关的属性变化

# 问题排查

以 Android 端举例

多方通话监听 JRTCRoomCallback 接口的 onRoomPropertyChanged 回调

//定义个全局变量记录当前服务器录制状态,默认初始化状态
@JRTCEnum.RemoteRecordState int mCurrentRemoteRecordState = JRTCEnum.RECORD_STATE_NONE;

/**
 * 房间属性变化回调
 * <p>
 * 当房间的属性发生变化时,会收到此回调,例如房间中有成员发起屏幕共享、录制状态发生变化等。
 *
 * @param changeParam {@link PropChangeParam} 变化标识集合
 * @param room        当前 JRTCRoom 对象
 */
void onRoomPropertyChanged(PropChangeParam changeParam, JRTCRoom room) {
    if (changeParam.remoteRecordState) {
        //表示远程录制状态变化
        @JRTCEnum.RemoteRecordState int remoteRecordState = room.getRemoteRecordState();
        if (this.mCurrentRemoteRecordState == JRTCEnum.RECORD_STATE_NONE && remoteRecordState == JRTCEnum.RECORD_STATE_READY) {
            //录制准备就绪,此时可以开启录制
            room.enableRemoteRecord(true, new JRTCRecordRemoteParam());
            //同理可以通过 room.enableRemoteRecord(false, null);停止呀远程录制
        } else if (remoteRecordState == JRTCEnum.RECORD_STATE_RUNNING) {
            //表示录制进行中
    
        } else if (
            this.mCurrentRemoteRecordState == JRTCEnum.RECORD_STATE_RUNNING &&remoteRecordState == JRTCEnum.RECORD_STATE_READY) {
            //表示录制停止,此时又可以调用,room.enableRemoteRecord(true, new JRTCRecordRemoteParam()); 开启一个新的录制
        }
        this.mCurrentRemoteRecordState = remoteRecordState;
    }
}

多方2.0(Mpcall)监听 JRTCCallCallback 接口的 onCallPropertyChanged 回调

//定义个全局变量记录当前服务器录制状态,默认初始化状态
@JRTCEnum.RemoteRecordState int mCurrentRemoteRecordState = JRTCEnum.RECORD_STATE_NONE;

/**
 * 通话属性改变,重点关注屏幕共享
 * <p>
 * @param propChangeParam 通话改变的属性
 */
void onCallPropertyChanged(PropChangeParam changeParam) {
    if (changeParam.remoteRecordState) {
        //表示远程录制状态变化
        @JRTCEnum.RemoteRecordState int remoteRecordState = room.getRemoteRecordState();
        if (this.mCurrentRemoteRecordState == JRTCEnum.RECORD_STATE_NONE && remoteRecordState == JRTCEnum.RECORD_STATE_READY) {
            //录制准备就绪,此时可以开启录制
            this.call.enableRemoteRecord(true, new JRTCRecordRemoteParam());
            //同理可以通过 this.call.enableRemoteRecord(false, null);停止呀远程录制
        } else if (remoteRecordState == JRTCEnum.RECORD_STATE_RUNNING) {
            //表示录制进行中
    
        } else if (this.mCurrentRemoteRecordState == JRTCEnum.RECORD_STATE_RUNNING && remoteRecordState == JRTCEnum.RECORD_STATE_READY) {
            //表示录制停止,此时又可以调用,this.call.enableRemoteRecord(true, new JRTCRecordRemoteParam()); 开启一个新的录制
        }
        mthis.CurrentRemoteRecordState = remoteRecordState;
    }

}

# 通话中如何监听屏幕共享状态变化

# 问题现象

通话中,成员开启屏幕共享,如何监听并渲染对方屏幕画面

# 定位步骤

通过监听通话属性接口是否上报屏幕共享相关的属性变化

# 问题排查

以 Android 端举例

多方通话监听 JRTCRoomCallback 接口的 onRoomPropertyChanged 回调

String mCurrentShareUserId = ""; //当前发起共享屏幕的用户ID
JRTCMediaDeviceVideoCanvas mShareVideoCanvas;//当前屏幕供选渲染视图对象
/**
 * 房间属性变化回调
 * <p>
 * 当房间的属性发生变化时,会收到此回调,例如房间中有成员发起屏幕共享、录制状态发生变化等。
 *
 * @param changeParam {@link PropChangeParam} 变化标识集合
 * @param room        当前 JRTCRoom 对象
 */
void onRoomPropertyChanged(PropChangeParam changeParam, JRTCRoom room) {
    if (changeParam.screenShare) {
        //表示通话中屏幕共享状态变化
        String screenShareUserId = room.getShareUserId();
        if (!TextUtils.isEmpty(screenShareUserId)) {
            //表示有人发起了屏幕共享
            if (TextUtils.isEmpty(mCurrentShareUserId)) {
                //有人发起屏幕共享
            } else {
                //原先的屏幕共享被顶,一个通话只能有一路屏幕共享,比如,成员A发起了共享,成员B再去发起,成员A的屏幕共享就会被成员B顶替
                //但是视频流ID还是同一个
            }
            if (TextUtils.equals(screenShareUserId, this.mClient.getUserId())) {
                //表示自己发起了屏幕共享
            } else{
                //通话中其他成员开启了屏幕共享,此时可以取渲染对方的屏幕视频画面,先去订阅屏幕视频流
                room.requestScreenVideo(new JRTCVideoSize(1280, 720));
                mShareVideoCanvas = this.mMediaDevice.startVideo(room.getShareStreamId(), JRTCMediaDevice.RENDER_FULL_CONTENT);
            }
        } else if (!TextUtils.isEmpty(mCurrentShareUserId) && TextUtils.isEmpty(screenShareUserId)) {
            //表示屏幕共享结束,此时可以取消订阅屏幕共享流,以及停止渲染
            room.unRequestScreenVideo();
            if (mShareVideoCanvas != null) {
                this.mMediaDevice.stopVideo(mShareVideoCanvas);
            }
        }
    
    }
}

同理,多方通话2.0(Mpcall)已经座席、访客模块,监听 onCallPropertyChanged 回调