远程用户开关音视频回调问题

场景描述(步骤):
1、初始化RtcEngine,设置场景为Constants.CHANNEL_PROFILE_COMMUNICATION,设置分辨率、fps,开启视频模块,RtcEngine.startPreview();
2、muteLocalAudioStream(true)不发送本地音频;
3、用户加入频道,muteLocalVideoStream(false)发送视频数据;
4、渲染本地视频;

操作:加入频道后,远程用户调用muteLocalAudioStream(boolean)开关音频,本地用户拿对应的onRemoteAudioStateChanged回调方法的值;

现象:
1、远程用户发送音频时(执行muteLocalAudioStream(false)),本地用户收到回调方法打印值如下日志中前两行,必现,每次都这样,包括ios也是;
2、接下来远程用户在调该方法开关音频时,回调如下日志中的后面几行,reason都是5或6,表示mute开关;

为何第一次会出现reason为0,state为1和2的情况?这样本地用户如何判断远程用户的音频状态??

日志:
onRemoteAudioStateChanged_uid: 231, state: 1, reason: 0, elapsed: 9031
onRemoteAudioStateChanged_uid: 231, state: 2, reason: 0, elapsed: 9082
onRemoteAudioStateChanged_uid: 231, state: 0, reason: 5, elapsed: 44018
onRemoteAudioStateChanged_uid: 231, state: 2, reason: 6, elapsed: 172079
onRemoteAudioStateChanged_uid: 231, state: 0, reason: 5, elapsed: 173062
onRemoteAudioStateChanged_uid: 231, state: 2, reason: 6, elapsed: 174700

用户A和用户B,一开始用户A加入频道时,用户B的 muteLocalVideoStream 调用状态是怎样的?A加入频道后,B分别是怎么去更改本地 mute 状态的?然后A收到的 onRemoteAudioStateChanged 的打印分别是什么?

我们一般不建议通过 onRemoteAudioStateChanged 来判断远程用户的音频状态,因为这个回调,只有当【音频状态有变更】时才会触发。比如一开始用户B调用了 muteLocalAudioStream(true),用户A加入频道,是不会收到 onRemoteAudioStateChanged 回调的。只有当用户A在频道里时,用户B的音频状态有变更,用户A才会触发 onRemoteAudioStateChanged 回调。

先前的帖子里已经和你同步过了哈,一般是建议自己通过业务逻辑实现【本地用户如何判断远程用户的音频状态】,这样才能实现精准判断。例如:可以通过 RTM 的用户属性来实现,当本地的 mute 状态变更时,调用 addOrUpdateLocalUserAttributes 添加或更新本地用户的 mute 相关属性,远端用户加入频道后,可以通过 getUserAttributesByKeys 方法获取指定用户指定属性名的属性,从而获知 mute 状态。

引用 用户A和用户B,一开始用户A加入频道时,用户B的 muteLocalVideoStream 调用状态是怎样的?A加入频道后,B分别是怎么去更改本地 mute 状态的?然后A收到的 onRemoteAudioStateChanged 的打印分别是什么?

首先B用户初始化RtcEngine,muteLocalAudioStream(true)关闭本地音频,加入频道;然后用户A加入频道,然后B用户调用muteLocalAudioStream(false),发送自己的音频,然后A用户收到回调onRemoteAudioStateChanged,打印参数为:
onRemoteAudioStateChanged_uid: 231, state: 1, reason: 0, elapsed: 9031
onRemoteAudioStateChanged_uid: 231, state: 2, reason: 0, elapsed: 9082

然后B再次调用muteLocalAudioStream(true)关闭本地音频,打印日志如下:
onRemoteAudioStateChanged_uid: 231, state: 0, reason: 5, elapsed: 44018

再打开:onRemoteAudioStateChanged_uid: 231, state: 2, reason: 6, elapsed: 172079
。。。

也就是只有A用户进来之后,B用户第一次改变的时候,回调不一样,剩下的再次改变就都一样了:
onRemoteAudioStateChanged_uid: 231, state: 0, reason: 5, elapsed: 44018
onRemoteAudioStateChanged_uid: 231, state: 2, reason: 6, elapsed: 172079
onRemoteAudioStateChanged_uid: 231, state: 0, reason: 5, elapsed: 173062
onRemoteAudioStateChanged_uid: 231, state: 2, reason: 6, elapsed: 174700

引用 我们一般不建议通过 onRemoteAudioStateChanged 来判断远程用户的音频状态,因为这个回调,只有当【音频状态有变更】时才会触发。比如一开始用户B调用了 muteLocalAudioStream(true) ,用户A加入频道,是不会收到 onRemoteAudioStateChanged 回调的。只有当用户A在频道里时,用户B的音频状态有变更,用户A才会触发 onRemoteAudioStateChanged 回调。
先前的帖子里已经和你同步过了哈,一般是建议自己通过业务逻辑实现【本地用户如何判断远程用户的音频状态】,这样才能实现精准判断。例如:可以通过 RTM 的用户属性来实现,当本地的 mute 状态变更时,调用 addOrUpdateLocalUserAttributes 添加或更新本地用户的 mute 相关属性,远端用户加入频道后,可以通过 getUserAttributesByKeys 方法获取指定用户指定属性名的属性,从而获知 mute 状态。

既然这样,那么用户在频道内改变状态时,都可以通过更新指定属性名的属性来提供给远程用户判断?那这样的话,这些回调一般用于什么作用;

上例中,A用户加入后,B用户确实是有音频状态的变更,只有第一次改变的回调不一样

明白你的意思了,根据你的描述,那这里确实可能有点问题,我们这边先内部测试确认下哈。

通过信令系统去判断状态变化,这是一种可靠判断方式。通过 RTC 回调去判断,可能会存在回调丢失之类的可能性,这是不可靠的一种方式。所以建议通过 RTM 来做校验,而不是依赖 onRemoteAudioStateChanged 回调去判断远端用户的麦克风状态。

你也可以参考这个 demo,实现对远端麦克风状态的监控:

你试试看把调用顺序改为:B用户初始化RtcEngine,加入频道,muteLocalAudioStream(true)关闭本地音频;然后用户A加入频道,B用户调用muteLocalAudioStream(false),发送自己的音频,看下A用户此时收到回调的打印是怎样的?是不是就是正常的了?

引用 你试试看把调用顺序改为:B用户初始化RtcEngine,加入频道,muteLocalAudioStream(true)关闭本地音频;然后用户A加入频道,B用户调用muteLocalAudioStream(false),发送自己的音频,看下A用户此时收到回调的打印是怎样的?是不是就是正常的了?

您好,按照上述修改
当A用户加入频道时,会直接收到B用户的状态改变(即B用户加入频道后muteLocalAudioStream(true)关闭本地音频,但此操作实在A用户进来之前操作的,此时A用户也能收到该回调),然后B用户再次打开、关闭,依然是第一次改变状态时先打印的0;如下日志:

// 用户A加入:
joinChannel_join result: 0
onJoinChannelSuccess_channel: 99900099910, uid: 231, elapsed: 401
onUserJoined_uid: 2311, elapsed: 420
onRemoteVideoStateChanged_uid: 2311, state: 1, reason: 0, elapsed: 420
onRemoteVideoStateChanged_uid: 2311, state: 2, reason: 0, elapsed: 511
onFirstLocalVideoFrame_width: 640, height: 480, elapsed: 529

// 用户B改变音频状态:
onRemoteAudioStateChanged_uid: 2311, state: 1, reason: 0, elapsed: 24473
onRemoteAudioStateChanged_uid: 2311, state: 2, reason: 0, elapsed: 24554
onRemoteAudioStateChanged_uid: 2311, state: 0, reason: 5, elapsed: 29661
onRemoteAudioStateChanged_uid: 2311, state: 2, reason: 6, elapsed: 31271
onRemoteAudioStateChanged_uid: 2311, state: 0, reason: 5, elapsed: 32387
onRemoteAudioStateChanged_uid: 2311, state: 2, reason: 6, elapsed: 33320
onRemoteAudioStateChanged_uid: 2311, state: 0, reason: 5, elapsed: 34101
onRemoteAudioStateChanged_uid: 2311, state: 2, reason: 6, elapsed: 34863

好的,muteLocalAudioStream 理论上应该是在加入频道后才能去调用的,稍后我测试确认一下 reason 的问题。

好的,我这边确实是在加入后调用的,也会触发回调,包括加入前的调用,回调也有

用户在频道内开关音视频通过属性来判断,比如设置localUserAttribute来变更自己的状态,此时其他人可以主动调用getAttribute来获取,但是该用户设置属性时,RTM没有触发回调吧,只有设置频道属性才有回调

是的,没有回调。目前只能主动调用 getUserAttributesgetUserAttributesByKeys 去获取用户属性。

另外 reason 的这个问题我也本地复现了,我这边先内部确认下具体原因。

那这样感觉很繁琐呀,目的是这样:

1、用户A加入频道,需要知道当前频道内所有用的音视频开关状态;
2、用户A加入频道后,再有其他用户加入时,需要知道该用户的音视频开关状态;
3、当频道内有任何用户音视频开关发生改变时(打开或关闭)时,用户A需要马上知道,并做UI处理;

针对以上需求,您这边建议呢,还是通过修改频道属性来实现吗?

是的,一般都是通过 RTM 的用户属性或频道属性来实现的,我们 demo 也是那么做的~

好的,多谢

麻烦问下,在rtc频道中需要使用rtm自定义消息,初始化完成RtcEngine和RtmClient,还需要用指定频道id去用RtmClient去创建一个RtmChannel频道,然后在将rtm加入该频道,再将rtc也加入这个频道,是这个步骤吗?

对的,一般需要在代码逻辑中,绑定好 RTC 和 RTM 加入频道的动作:

  • 同一个用户使用相同的频道名,分别加入 RTC 和 RTM 频道;
  • 在你的代码逻辑中,将加入两个频道的动作进行绑定;

关于这个问题,我内部确认了一下,目前 SDK 是那么设计的哦,引擎内部把收到第一帧音频的原因都归于 REMOTE_AUDIO_REASON_INTERNAL(0)

您好,那么关于这样的判断,比如A用户加入频道时,此时频道已经有B、C、D用户,当A进入后,B用户第一次改变自己的音视频状态,此时A用户拿到回调怎么判断B用户开启还是关闭?

所以对于首帧音频,只能判断 state,无法判断 reason。如果需要判断 reason,建议通过 rtm 来做。

您好,如果我进入频道默认不订阅所有人的音视频流,但我需要订阅指定用户时,该怎么做(RtcEngine和RtcChannel两个地方)

RtcEngine:
joinChannel 后,调用 muteAllRemoteAudioStreams 停止接收所有音频流,调用 muteAllRemoteVideoStreams 停止接收所有视频流即可。

RtcChannel:
同上,相关 API 参考: muteAllRemoteVideoStreamsmuteAllRemoteAudioStreams

这个muteRemote方法,停止/接受远程用户流,在调用后,对方是否也会停止发送,调用后只有调用端生效吗,不影响该方法指定的远程用户继续向别的用户发流吗?