#29 声网sdk接入

Open
opened 11 months ago by lyq · 6 comments
天问 commented 11 months ago

声网语聊房对接

背景

  • 场景: 多人语音派对房 ,当前用的是TRTC
  • 移动端80%流量,Flutter开发,PC端规划做。
  • 后台是PHP或java
  • 语音审核:人审
  • 直播间分享:暂时无
  • 跨房连麦PK:暂时无
  • IM消息:自研

声网方案

说明:

  • 服务端主要工作
    • 生成加入频道时的必要信息;(频道号、uid、Token等信息)
    • 维护合法的房间和用户列表;(IM、Restful API、 NCS)
    • 踢人、房间解散等违规处理;(Restful API)
  • 客户端主要工作
    • 加入频道
    • 上下麦
    • 开闭麦mute&unMute
    • 音量波纹提示

资料

功能和注意事项

  • 频道模式&音频属性
    • 要使用直播模式,即setChannelProfile(channelProfileLiveBroadcasting);
    • 直播模式,只有主播才能往频道发流,观众不能发流只能收流。 因此麦上讲话的都是主播角色。
    • 音频属性要设置为setAudioProfile(audioProfileMusicHighQualityStereo);setAudioScenario(audioScenarioGameStreaming)
  • 加入频道
  • 切换角色
    • 主播角色既能发流也能收流,观众只能收流 。 因此麦上讲话的人都是主播,收听的人都是观众。
    • 主播和观众互相切换的API: setClientRole(角色)
  • 声音波纹提示
    • enableAudioVolumeIndication方法开启波纹提示,建议设置提示间隔时间是 500ms,一般音量大于20的时候给到波纹提示
    • 本地回调中,uid为0则表示本地主播;其它则是远端瞬时音量最高的3位主播的uid
  • 声网防炸房方案 https://doc.shengwang.cn/doc/rtc/flutter/best-practice/prevent-stream-bombing
  • 断线重连 频道状态管理https://doc.shengwang.cn/doc/rtc/flutter/basic-features/channel-connection
# 声网语聊房对接 ### 背景 - 场景: 多人语音派对房 ,当前用的是TRTC - 移动端80%流量,Flutter开发,PC端规划做。 - 后台是PHP或java - 语音审核:人审 - 直播间分享:暂时无 - 跨房连麦PK:暂时无 - IM消息:自研 ### 声网方案 说明: - 服务端主要工作 - 生成加入频道时的必要信息;(频道号、uid、Token等信息) - 维护合法的房间和用户列表;(IM、Restful API、 NCS) - 踢人、房间解散等违规处理;(Restful API) - 客户端主要工作 - 加入频道 - 上下麦 - 开闭麦mute&unMute - 音量波纹提示 ### 资料 - 声网Flutter SDK及示例下载链接 https://doc.shengwang.cn/doc/rtc/flutter/resources - 快速开始语音互动 https://doc.shengwang.cn/doc/rtc/flutter/basic-features/audio-quick-start - Flutter SDK API参考 https://doc.shengwang.cn/api-ref/rtc/flutter/API/rtc_api_overview - Token生成 https://doc.shengwang.cn/doc/rtc/flutter/basic-features/token-authentication - 建议使用AccessToken2 生成Token - PHP或者java的参考示例连接中都有对应的github仓库 - 声网WebDemo地址(可以用来测试) https://webdemo-na.agora.io/basicLive/index.html - 比如要验证生成的Token是否正确,则可通过这个demo来加入房间测试 - 验证和客户端声音互通 ### 功能和注意事项 - 频道模式&音频属性 - 要使用直播模式,即setChannelProfile(**channelProfileLiveBroadcasting**); - 直播模式,只有主播才能往频道发流,观众不能发流只能收流。 因此麦上讲话的都是主播角色。 - 音频属性要设置为setAudioProfile(**audioProfileMusicHighQualityStereo**);setAudioScenario(**audioScenarioGameStreaming**) - 加入频道 - 频道号、uid、Token需要自己业务服务器分配 ,加入频道时传的token一定要和频道号、uid匹配,否则加入频道不会成功。 - 对应的API https://doc.shengwang.cn/api-ref/rtc/flutter/API/toc_channel#api_irtcengine_joinchannel2 - uid建议使用uint32位的,不建议使用string型uid - 加入频道成功要以本地加入成功的回调来判断 [onJoinChannelSuccess](https://doc.shengwang.cn/api-ref/rtc/flutter/API/toc_channel#callback_irtcengineeventhandler_onjoinchannelsuccess) - 切换角色 - 主播角色既能发流也能收流,观众只能收流 。 因此麦上讲话的人都是主播,收听的人都是观众。 - 主播和观众互相切换的API: **setClientRole(角色)** - 声音波纹提示 - enableAudioVolumeIndication方法开启波纹提示,建议设置提示间隔时间是 500ms,一般音量大于20的时候给到波纹提示 - 本地回调中,uid为0则表示本地主播;其它则是远端瞬时音量最高的3位主播的uid - 声网防炸房方案 https://doc.shengwang.cn/doc/rtc/flutter/best-practice/prevent-stream-bombing - 断线重连 频道状态管理https://doc.shengwang.cn/doc/rtc/flutter/basic-features/channel-connection
天辰 commented 11 months ago
Owner

Tencent和agora直播逻辑整理

第一步:依赖替换:

  tencent_trtc_cloud: 2.5.2

替换为

  agora_rtc_engine: ^6.0.0

lib\Base.dart 中定义全局变量:

 static TRTCCloud? trtcCloud;

实现了一个销毁 trtc 的方法:

  static Future<void> destroyTrtcCloud() async {
    print('销毁语音插件');
    VBase.roomId = 0;
    VBase.trtcCloud = null;
    await VBase.trtcCloud?.stopLocalAudio();
    await VBase.trtcCloud?.exitRoom();
    await TRTCCloud.destroySharedInstance();
  }

这里将直播间id清空,trtc设置为null等操作。

lib\service.dart 中定义exitRoom操作,和上面 VBase 类一样:

abstract class VRoom {
  static TRTCCloud? trtcCloud;
  static bool trtcCloudIsActive = false;

  static Future<void> exitRoom(String name) async {
    if (VRoom.trtcCloudIsActive) {
      // VRoom.trtcCloud.unRegisterListener(onTrtcListener);
      await VRoom.trtcCloud?.stopLocalAudio();
      await VRoom.trtcCloud?.exitRoom();
      await TRTCCloud.destroySharedInstance();
      print('销毁房间语音信息');
    }
  }
}

注意这里VRoom类中定义的 exitRoom 和 destroyTrtcCloud 方法类似,可能重复了。

在 lib\chat\PlayerWidget.dart 中,这里定义了trtcCloud 和 player:

  final TXAudioEffectManager player;
  final TRTCCloud trtcCloud;
  
bool get _isPlaying => _playerState == PlayerState.playing;// 播放中
  bool get _isPaused => _playerState == PlayerState.paused; // 暂停

player直播间播放音乐的插件,

//先检测是否有音乐
    var isHavData = await VStore.hasData('music');

//有音乐,设置音乐播放列表
    if(isHavData){
      var data = await VStore.getData('music');
      setState(() {
        _fileList = data['list'] as List<dynamic>;
        _fileIndex = data['index'];
      });
      // play(_fileIndex);
    }


                          player.seekMusicToPosInMS(_musicId, position.round());

设置音量:

                player.setAllMusicVolume((_volume * 100).toInt());

播放下一首,先停止,在播放:

  _playNextMusic() async {
    if(tabs[0]['list'].length > 0) {
      setState(() {
        if (_fileIndex < tabs[0]['list'].length - 1) {
          _fileIndex++;
        }else{
          _fileIndex = 0;
        }
        _position = const Duration(milliseconds: 0);
      });
      await player.stopPlayMusic(_musicId);
      print('播放1:${_fileIndex}');
      play(_fileIndex);
    }
  }

注意,直播时候 trtcCloud 注册音乐播放器监听器 onTrtcListener

  void _initStreams() {
    trtcCloud?.registerListener(onTrtcListener);
  }

这个 trtcCloud 在直播间页面调用播放音乐插件时候传入,接下来着重看一下直播间页面。

直播间页面 lib\chat\VoiceChatRoomPage.dart

  late TRTCCloud trtcCloud;
  late bool trtcCloudIsActive = false;
  
  // 定义原生方法
    MethodChannel _channel = const MethodChannel('trtcCloudChannel');

调用sdk,配置appid等参数,新建一个 trtcCloud 对象:

    trtcCloud = (await TRTCCloud.sharedInstance())!;

进入直播间逻辑:

注意在调用 VoiceChatRoomPage 进入直播间会传递参数:

{'room_id': roomId, 'data': result['data']}

一个是直播间id,而 data 参数,在进入直播间之前先调用后台post接口:

    var result = await VService.enterRoom(
                      roomId: roomId.toString(), password: message);

  static Future<Map<String, dynamic>?> enterRoom(
      {required String roomId, String password = ''}) async {
    var res = await httpClient.post(
      API.enterRoom,     //'/api/voice/enterRoom';
      data: FormData.fromMap({
        'room_id': roomId,
        'password': password,
      }),
    );



var result = await VService.getRoomStatus(roomId.toString());

  static Future<dynamic> getRoomStatus(String roomId) async {
    var res = await httpClient.post(
      API.getRoomStatus, // '/api/voice/getRoomStatus';
      data: FormData.fromMap({
        'room_id': roomId,
      }),
    );
    return res.data;
  }

直播间角色:

        ? TRTCCloudDef.TRTCRoleAnchor  节目主持人,即主播
        : TRTCCloudDef.TRTCRoleAudience;  观众

调用 _fluWakeLock 实例的 enable 方法,用于启用设备的唤醒锁。启用唤醒锁可以防止设备在应用程序处于活动状态时进入睡眠模式,保持设备的唤醒状态,以确保应用程序可以继续运行或执行某些特定的任务。

    FluWakeLock _fluWakeLock = FluWakeLock();
    _fluWakeLock.enable();
    
  _destroyRoom() async {
    print('销毁房间');
    try {
      VService.leaveRoom(_roomId.toString());
      SocketUtils().sendChatData(RoomMessageType.LEAVE, {'room_id': _roomId});
      if (trtcCloudIsActive) {
        print('销毁语音控件');
        trtcCloud?.unRegisterListener(onTrtcListener);
        await trtcCloud?.stopLocalAudio();
        await trtcCloud?.exitRoom();
        await TRTCCloud.destroySharedInstance();
      }
    } catch (e) {
      print('销毁房间错误:$e');
    }
  }

传参后进入 VoiceChatRoomPage 直播间界面,

  _connectOtherRoom() {
    print('开始调用跨房通话1');
    var object = new Map();
    if (_roomId == _pk['a_room_id']) {
      object['roomId'] = _pk['b_room_id'];
      object['userId'] = _pk['b_user_id'].toString();
    } else {
      object['roomId'] = _pk['a_room_id'];
      object['userId'] = _pk['a_user_id'].toString();
    }
    trtcCloud?.connectOtherRoom(jsonEncode(object));
    // trtcCloud?.connectOtherRoom(jsonEncode(object));
    // _channel.invokeMethod('connectOtherRoom', {
    //   "param": jsonEncode(object)
    // });
  }

直播推流:


  _startPushStream() async {
    print("开始音频服务");
    print('房间号:$_roomId,用户号:${VBase.uid}, sign:$_userSign');
    trtcCloud = (await TRTCCloud.sharedInstance())!;
    _player = trtcCloud.getAudioEffectManager();
    _playerWidget = PlayerWidget(_player, trtcCloud);

    TRTCParams params = TRTCParams();
    params.sdkAppId = VBase.sdkAppId;
    params.strRoomId = _roomId.toString();
    params.roomId = _roomId;
    params.userId = VBase.uid.toString();
    // params.roomId = 100000;
    params.role = _roomDetailData.voicePermission ? TRTCCloudDef.TRTCRoleAnchor : TRTCCloudDef.TRTCRoleAudience;
    params.userSig = _userSign;
    params.privateMapKey = _roomDetailData.privilege;
    // trtcCloud?.setSystemVolumeType(type: TRTCCloudDef.
  _handleAudioSetting(bool muteAll) {
    print("全员禁音:${muteAll.toString()}");
    // EasyLoading.showToast(muteAll ? '全员禁音' : '已取消全员禁音');
    trtcCloud?.muteAllRemoteAudio(muteAll);
    // if (!muteAll){
    //   trtcCloud?.switchRole(TRTCCloudDef.TRTCRoleAnchor);
    //   trtcCloud?.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC);
    // }
  }

  _handleMicSetting(bool mute) {
    print("禁麦:${mute.toString()}");
    // EasyLoading.showToast(muteAll ? '全员禁音' : '已取消全员禁音');
    _handleLocalAudio(!mute);
    // if (!muteAll){
    //   trtcCloud?.switchRole(TRTCCloudDef.TRTCRoleAnchor);
    //   trtcCloud?.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC);
    // }
  }
  _handleVoiceReverb() {
    showModalBottomSheet(
        context: context,
        isDismissible: true,
        isScrollControlled: true,
        shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15))),
        builder: (BuildContext context) {
          return Container(
            decoration: const BoxDecoration(
                color: Colors.white, borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15))),
            height: dp(550),
            child: Column(
              children: [
                InkWell(
                    onTap: () {
                      trtcCloud.getAudioEffectManager().setVoiceChangerType(0);
## Tencent和agora直播逻辑整理 第一步:依赖替换: ``` tencent_trtc_cloud: 2.5.2 ``` 替换为 ``` agora_rtc_engine: ^6.0.0 ``` 在 *lib\Base.dart* 中定义全局变量: ``` static TRTCCloud? trtcCloud; ``` 实现了一个销毁 trtc 的方法: ``` static Future<void> destroyTrtcCloud() async { print('销毁语音插件'); VBase.roomId = 0; VBase.trtcCloud = null; await VBase.trtcCloud?.stopLocalAudio(); await VBase.trtcCloud?.exitRoom(); await TRTCCloud.destroySharedInstance(); } ``` 这里将直播间id清空,trtc设置为null等操作。 在 *lib\service.dart* 中定义exitRoom操作,和上面 **VBase** 类一样: ``` abstract class VRoom { static TRTCCloud? trtcCloud; static bool trtcCloudIsActive = false; static Future<void> exitRoom(String name) async { if (VRoom.trtcCloudIsActive) { // VRoom.trtcCloud.unRegisterListener(onTrtcListener); await VRoom.trtcCloud?.stopLocalAudio(); await VRoom.trtcCloud?.exitRoom(); await TRTCCloud.destroySharedInstance(); print('销毁房间语音信息'); } } } ``` 注意这里**VRoom**类中定义的 exitRoom 和 destroyTrtcCloud 方法类似,可能重复了。 在 lib\chat\PlayerWidget.dart 中,这里定义了trtcCloud 和 player: ``` final TXAudioEffectManager player; final TRTCCloud trtcCloud; bool get _isPlaying => _playerState == PlayerState.playing;// 播放中 bool get _isPaused => _playerState == PlayerState.paused; // 暂停 ``` player直播间播放音乐的插件, ``` //先检测是否有音乐 var isHavData = await VStore.hasData('music'); //有音乐,设置音乐播放列表 if(isHavData){ var data = await VStore.getData('music'); setState(() { _fileList = data['list'] as List<dynamic>; _fileIndex = data['index']; }); // play(_fileIndex); } ``` ``` player.seekMusicToPosInMS(_musicId, position.round()); ``` 设置音量: ``` player.setAllMusicVolume((_volume * 100).toInt()); ``` 播放下一首,先停止,在播放: ``` _playNextMusic() async { if(tabs[0]['list'].length > 0) { setState(() { if (_fileIndex < tabs[0]['list'].length - 1) { _fileIndex++; }else{ _fileIndex = 0; } _position = const Duration(milliseconds: 0); }); await player.stopPlayMusic(_musicId); print('播放1:${_fileIndex}'); play(_fileIndex); } } ``` 注意,直播时候 **trtcCloud** 注册音乐播放器监听器 **onTrtcListener**: ``` void _initStreams() { trtcCloud?.registerListener(onTrtcListener); } ``` 这个 **trtcCloud** 在直播间页面调用播放音乐插件时候传入,接下来着重看一下直播间页面。 **直播间页面** *lib\chat\VoiceChatRoomPage.dart* ``` late TRTCCloud trtcCloud; late bool trtcCloudIsActive = false; // 定义原生方法 MethodChannel _channel = const MethodChannel('trtcCloudChannel'); ``` 调用sdk,配置appid等参数,新建一个 **trtcCloud** 对象: ``` trtcCloud = (await TRTCCloud.sharedInstance())!; ``` **进入直播间逻辑:** 注意在调用 VoiceChatRoomPage 进入直播间会传递参数: ``` {'room_id': roomId, 'data': result['data']} ``` 一个是直播间id,而 data 参数,在进入直播间之前先调用后台post接口: ``` var result = await VService.enterRoom( roomId: roomId.toString(), password: message); static Future<Map<String, dynamic>?> enterRoom( {required String roomId, String password = ''}) async { var res = await httpClient.post( API.enterRoom, //'/api/voice/enterRoom'; data: FormData.fromMap({ 'room_id': roomId, 'password': password, }), ); var result = await VService.getRoomStatus(roomId.toString()); static Future<dynamic> getRoomStatus(String roomId) async { var res = await httpClient.post( API.getRoomStatus, // '/api/voice/getRoomStatus'; data: FormData.fromMap({ 'room_id': roomId, }), ); return res.data; } ``` 直播间角色: ``` ? TRTCCloudDef.TRTCRoleAnchor 节目主持人,即主播 : TRTCCloudDef.TRTCRoleAudience; 观众 ``` 调用 `_fluWakeLock` 实例的 `enable` 方法,用于启用设备的唤醒锁。启用唤醒锁可以防止设备在应用程序处于活动状态时进入睡眠模式,保持设备的唤醒状态,以确保应用程序可以继续运行或执行某些特定的任务。 ``` FluWakeLock _fluWakeLock = FluWakeLock(); _fluWakeLock.enable(); ``` ``` _destroyRoom() async { print('销毁房间'); try { VService.leaveRoom(_roomId.toString()); SocketUtils().sendChatData(RoomMessageType.LEAVE, {'room_id': _roomId}); if (trtcCloudIsActive) { print('销毁语音控件'); trtcCloud?.unRegisterListener(onTrtcListener); await trtcCloud?.stopLocalAudio(); await trtcCloud?.exitRoom(); await TRTCCloud.destroySharedInstance(); } } catch (e) { print('销毁房间错误:$e'); } } ``` 传参后进入 VoiceChatRoomPage 直播间界面, ``` _connectOtherRoom() { print('开始调用跨房通话1'); var object = new Map(); if (_roomId == _pk['a_room_id']) { object['roomId'] = _pk['b_room_id']; object['userId'] = _pk['b_user_id'].toString(); } else { object['roomId'] = _pk['a_room_id']; object['userId'] = _pk['a_user_id'].toString(); } trtcCloud?.connectOtherRoom(jsonEncode(object)); // trtcCloud?.connectOtherRoom(jsonEncode(object)); // _channel.invokeMethod('connectOtherRoom', { // "param": jsonEncode(object) // }); } ``` 直播推流: ``` _startPushStream() async { print("开始音频服务"); print('房间号:$_roomId,用户号:${VBase.uid}, sign:$_userSign'); trtcCloud = (await TRTCCloud.sharedInstance())!; _player = trtcCloud.getAudioEffectManager(); _playerWidget = PlayerWidget(_player, trtcCloud); TRTCParams params = TRTCParams(); params.sdkAppId = VBase.sdkAppId; params.strRoomId = _roomId.toString(); params.roomId = _roomId; params.userId = VBase.uid.toString(); // params.roomId = 100000; params.role = _roomDetailData.voicePermission ? TRTCCloudDef.TRTCRoleAnchor : TRTCCloudDef.TRTCRoleAudience; params.userSig = _userSign; params.privateMapKey = _roomDetailData.privilege; // trtcCloud?.setSystemVolumeType(type: TRTCCloudDef. ``` ``` _handleAudioSetting(bool muteAll) { print("全员禁音:${muteAll.toString()}"); // EasyLoading.showToast(muteAll ? '全员禁音' : '已取消全员禁音'); trtcCloud?.muteAllRemoteAudio(muteAll); // if (!muteAll){ // trtcCloud?.switchRole(TRTCCloudDef.TRTCRoleAnchor); // trtcCloud?.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC); // } } _handleMicSetting(bool mute) { print("禁麦:${mute.toString()}"); // EasyLoading.showToast(muteAll ? '全员禁音' : '已取消全员禁音'); _handleLocalAudio(!mute); // if (!muteAll){ // trtcCloud?.switchRole(TRTCCloudDef.TRTCRoleAnchor); // trtcCloud?.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC); // } } ``` ``` _handleVoiceReverb() { showModalBottomSheet( context: context, isDismissible: true, isScrollControlled: true, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15))), builder: (BuildContext context) { return Container( decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15))), height: dp(550), child: Column( children: [ InkWell( onTap: () { trtcCloud.getAudioEffectManager().setVoiceChangerType(0); ```
天辰 commented 11 months ago
Owner
腾讯文档: https://cloud.tencent.com/document/product/647/79628?from_column=20420 声网文档: https://qifocus.notion.site/e646cf065ddf48cf879959e520f83810 demo项目: https://git.yoqi.me/lyq/agora_rtc_engine_example
天辰 commented 11 months ago
Owner

声网接入

引入包:

  agora_rtc_engine: 6.2.0
  permission_handler: ^10.2.0

权限检测:


配置appid:

//config/agora.config.dart
String get appId {
return const String.fromEnvironment('TEST_APP_ID',
    defaultValue: '<TEST_APP_ID>');
}

String get token {
return const String.fromEnvironment('TEST_TOKEN',
    defaultValue: '<TEST_TOKEN>');
}

String get channelId {
return const String.fromEnvironment(
    'TEST_CHANNEL_ID',
    defaultValue: '<TEST_CHANNEL_ID>',
);
}

注意这里的token需要每次在业务系统获取,并全局保存。

token 有效期 24 小时,文档:使用 Token 鉴权 | 文档中心 | 声网 (shengwang.cn)

agora 创建RtcEngine 对象:

RtcEngine createAgoraRtcEngine() {
  return impl.RtcEngineImpl.create();
}

RtcEngineEx createAgoraRtcEngineEx() {
  return impl.RtcEngineImpl.create();
}

Future<void> initialize(RtcEngineContext context);


这个对象在全局保存。

获取用户uid:

Future<UserInfo> getUserInfoByUid(int uid);

获取用户信息:

Future<UserInfo> getUserInfoByUserAccount(String userAccount);

加入频道:

// 加入频道
await _engine.joinChannel(
    token: token,
    channelId: channel,
    options: const ChannelMediaOptions(
        // 设置用户角色为观众
        clientRoleType: ClientRoleType.clientRoleAudience,
        // 设置低延时级别
        audienceLatencyLevel: AudienceLatencyLevelType.audienceLatencyLevelLowLatency),
    uid: 0,
);


//退出直播间
Future<void> leaveChannel({LeaveChannelOptions? options});

设置通话音量:


设置全员静音:


## 声网接入 引入包: ``` agora_rtc_engine: 6.2.0 permission_handler: ^10.2.0 ``` 权限检测: ``` ``` 配置appid: ``` //config/agora.config.dart String get appId { return const String.fromEnvironment('TEST_APP_ID', defaultValue: '<TEST_APP_ID>'); } String get token { return const String.fromEnvironment('TEST_TOKEN', defaultValue: '<TEST_TOKEN>'); } String get channelId { return const String.fromEnvironment( 'TEST_CHANNEL_ID', defaultValue: '<TEST_CHANNEL_ID>', ); } ``` 注意这里的**token**需要每次在业务系统获取,并全局保存。 token 有效期 24 小时,文档:[使用 Token 鉴权 | 文档中心 | 声网 (shengwang.cn)](https://doc.shengwang.cn/doc/rtc/flutter/basic-features/token-authentication) agora 创建RtcEngine 对象: ``` RtcEngine createAgoraRtcEngine() { return impl.RtcEngineImpl.create(); } RtcEngineEx createAgoraRtcEngineEx() { return impl.RtcEngineImpl.create(); } Future<void> initialize(RtcEngineContext context); ``` 这个对象在全局保存。 获取用户uid: ``` Future<UserInfo> getUserInfoByUid(int uid); ``` 获取用户信息: ``` Future<UserInfo> getUserInfoByUserAccount(String userAccount); ``` 加入频道: ``` // 加入频道 await _engine.joinChannel( token: token, channelId: channel, options: const ChannelMediaOptions( // 设置用户角色为观众 clientRoleType: ClientRoleType.clientRoleAudience, // 设置低延时级别 audienceLatencyLevel: AudienceLatencyLevelType.audienceLatencyLevelLowLatency), uid: 0, ); //退出直播间 Future<void> leaveChannel({LeaveChannelOptions? options}); ``` 设置通话音量: ``` ``` 设置全员静音: ``` ```
天辰 commented 11 months ago
Owner

这个月底就和腾讯终止合作,需要在一周左右时间完成声网sdk对接

这个月底就和腾讯终止合作,需要在一周左右时间完成声网sdk对接
天辰 commented 11 months ago
Owner

可以用这个接口测试获取的动态token http://api-test.tinger.net.cn/api/vchat/token uid:4443 channelName:1234

可以用这个接口测试获取的动态token http://api-test.tinger.net.cn/api/vchat/token uid:4443 channelName:1234
天问 commented 11 months ago
Owner
后台服务: https://doc.shengwang.cn/faq/quality-issues/android-background#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88
Sign in to join this conversation.
No Milestone
No assignee
2 Participants
Loading...
Cancel
Save
There is no content yet.