假如你有一个完结 “多人视频通话” 的场景需求,你会挑选从零完结仍是接第三方 SDK?假如在这个场景上你还需求支撑跨渠道,你会挑选怎么样的技术道路?

我的答案是:Flutter + 声网 SDK,这个组合能够完美解决跨渠道和多人视频通话的所有痛点,由于:

  • Flutter 天然支撑手机端和 PC 端的跨渠道才能,并具有不错的功能体现
  • 声网的 Flutter RTC SDK 相同支撑 Android、iOS、MacOS 和 Windows 等渠道,同时也是难得针对 Flutter 进行了全渠道支撑和优化的音视频 SDK

前言

在开端之前,有必要提前简略介绍一下声网的 RTC SDK 相关完结,这也是我挑选声网的原因。

声网属于是国内最早一批做 Flutter SDK 全渠道支撑的厂家,声网的 Flutter SDK 之所以能在 Flutter 上最早坚持多渠道的支撑,原因在于声网并不是运用惯例的 Flutter Channel 去完结渠道音视频才能:

声网的 RTC SDK 的逻辑完结都来自于封装好的 C/C++ 等 native 代码,而这些代码会被打包为对应渠道的动态链接库,例如 .dll.so.dylib ,终究经过 Dart 的 FFI(ffigen) 进行封装调用

这样做的好处在于:

  • Dart 能够和 native SDK 直接通信,减少了 Flutter 和原生渠道交互时在 Channel 上的功能开支;
  • C/C++ 相关完结在获得更好功能支撑的同时,也不需求过度依赖原生渠道的 API ,能够得到更灵敏和安全的 API 支撑。

假如说这样做有什么害处,那大约便是 SDK 的底层开发和维护成本会剧增,不过从用户角度来看,这无异是一个绝佳的挑选。

开端之前

接下来让咱们进入正题,既然挑选了 Flutter + 声网的完结道路,那么在开端之前必定有一些需求预备的前置条件,首要是为了满意声网 RTC SDK 的运用条件,必须是:

  • Flutter 2.0 或更高版别
  • Dart 2.14.0 或更高版别

从现在 Flutter 和 Dart 版别来看,上面这个要求并不算高,然后便是你需求注册一个声网 开发者账号 ,然后获取后续装备所需的 App ID 和 Token 等装备参数。

假如关于装备“门清”,能够疏忽跳过。

创立项目

首要能够在声网控制台的项目办理页面上点击「创立项目」,然后在弹出框选输入项目名称,之后挑选「视频通话」场景和「安全形式(APP ID + Token)」 即可完结项目创立。

基于声网 Flutter SDK 实现多人视频通话

依据法规,创立项目需求实名认证,这个必不可少;别的运用场景不必太过纠结,项目创立之后也是能够依据需求自己修改。

获取 App ID

成功创立项目之后,在项目列表点击项目「装备」,进入项目详情页面之后,会看到根本信息栏目有个 App ID 的字段,点击如下图所示图标,即可获取项目的 App ID。

基于声网 Flutter SDK 实现多人视频通话

基于声网 Flutter SDK 实现多人视频通话

App ID 也算是灵敏信息之一,所以尽量妥善保存,防止泄密。

获取 Token

为提高项目的安全性,声网引荐了运用 Token 对参加频道的用户进行鉴权,在生产环境中,一般为保证安全,是需求用户经过自己的服务器去签发 Token,而假如是测验需求,能够在项目详情页面的“暂时 token 生成器”获取暂时 Token:

在频道名输出一个暂时频道,比方 Test2 ,然后点击生成暂时 token 按键,即可获取一个暂时 Token,有用期为 24 小时。

基于声网 Flutter SDK 实现多人视频通话

这儿得到的 Token 和频道名就能够直接用于后续的测验,假如是用在生产环境上,建议仍是在服务端签发 Token ,签发 Token 除了 App ID 还会用到 App 证书,获取 App 证书相同能够在项目详情的运用装备上获取。

基于声网 Flutter SDK 实现多人视频通话

更多服务端签发 Token 可见 token server 文档 。

开端开发

经过前面的装备,咱们现在具有了 App ID频道名和一个有用的暂时 Token ,接下里便是在 Flutter 项目里引入声网的 RTC SDK :agora_rtc_engine 。

项目装备

首要在 Flutter 项目的 pubspec.yaml 文件中增加以下依赖,其间 agora_rtc_engine 这儿引入的是 6.1.0 版别 。

其实 permission_handler 并不是必须的,仅仅由于「视频通话」项目必不可少需求恳求到「麦克风」和「相机」权限,所以这儿引荐运用 permission_handler 来完结权限的动态恳求。

dependencies:
  flutter:
    sdk: flutter
  agora_rtc_engine: ^6.1.0
  permission_handler: ^10.2.0

这儿需求注意的是, Android 渠道不需求特意在主工程的 AndroidManifest.xml 文件上增加 uses-permission ,由于 SDK 的 AndroidManifest.xml 已经增加过所需的权限。

iOS 和 macOS 能够直接在 Info.plist 文件增加 NSCameraUsageDescriptionNSCameraUsageDescription 的权限声明,或许在 Xcode 的 Info 栏目增加 Privacy - Microphone Usage DescriptionPrivacy - Camera Usage Description

	<key>NSCameraUsageDescription</key>
	<string>*****</string>
	<key>NSMicrophoneUsageDescription</key>
	<string>*****</string>

基于声网 Flutter SDK 实现多人视频通话

运用声网 SDK

获取权限

在正式调用声网 SDK 的 API 之前,首要咱们需求恳求权限,如下代码所示,能够运用 permission_handlerrequest 提前获取所需的麦克风和摄像头权限。

@override
void initState() {
  super.initState();
  _requestPermissionIfNeed();
}
Future<void> _requestPermissionIfNeed() async {
  await [Permission.microphone, Permission.camera].request();
}

初始化引擎

接下来开端装备 RTC 引擎,如下代码所示,经过 import 对应的 dart 文件之后,就能够经过 SDK 自带的 createAgoraRtcEngine 办法快速创立引擎,然后经过 initialize 办法就能够初始化 RTC 引擎了,能够看到这儿会用到前面创立项目时得到的 App ID 进行初始化。

注意这儿需求在恳求完权限之后再初始化引擎,并更新初始化成功状况 initStatus,由于没成功初始化之前不能运用 RtcEngine

import 'package:agora_rtc_engine/agora_rtc_engine.dart';
late final RtcEngine _engine;
///初始化状况
late final Future<bool?> initStatus;
@override
void initState() {
  super.initState();
  ///恳求完结权限后,初始化引擎,更新初始化成功状况
  initStatus = _requestPermissionIfNeed().then((value) async {
    await _initEngine();
    return true;
  }).whenComplete(() => setState(() {}));
}
Future<void> _initEngine() async {
  //创立 RtcEngine
  _engine = createAgoraRtcEngine();
  // 初始化 RtcEngine
  await _engine.initialize(RtcEngineContext(
    appId: appId,
  ));
  
}

接着咱们需求经过 registerEventHandler 注册一系列回调办法,在 RtcEngineEventHandler 里有很多回调通知,而一般情况下咱们比方常用到的会是下面这 5 个:

  • onError : 判别过错类型和过错信息
  • onJoinChannelSuccess: 参加频道成功
  • onUserJoined: 有用户参加了频道
  • onUserOffline: 有用户脱离了频道
  • onLeaveChannel: 脱离频道

///是否参加谈天
bool isJoined = false;
/// 记载参加的用户id
Set<int> remoteUid = {};
Future<void> _initEngine() async {
   
   _engine.registerEventHandler(RtcEngineEventHandler(
      // 遇到过错
      onError: (ErrorCodeType err, String msg) {
        print('[onError] err: $err, msg: $msg');
      },
      onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
        // 参加频道成功
        setState(() {
          isJoined = true;
        });
      },
      onUserJoined: (RtcConnection connection, int rUid, int elapsed) {
        // 有用户参加
        setState(() {
          remoteUid.add(rUid);
        });
      },
      onUserOffline:
          (RtcConnection connection, int rUid, UserOfflineReasonType reason) {
        // 有用户离线
        setState(() {
          remoteUid.removeWhere((element) => element == rUid);
        });
      },
      onLeaveChannel: (RtcConnection connection, RtcStats stats) {
        // 脱离频道
        setState(() {
          isJoined = false;
          remoteUid.clear();
        });
      },
    ));
}

用户能够依据上面的回调来判别 UI 状况,比方当时用户处于频道内时显现对方的头像和数据,其他用户参加和脱离频道时更新当时 UI 等。

接下来由于咱们的需求是「多人视频通话」,所以还需求调用 enableVideo 翻开视频模块支撑,同时咱们还能够对视频编码进行一些简略装备,比方经过 VideoEncoderConfiguration 装备 :

  • dimensions: 装备视频的分辨率尺度,默许是 640×360
  • frameRate: 装备视频的帧率,默许是 15 fps

  Future<void> _initEngine() async {
    
    // 翻开视频模块支撑
    await _engine.enableVideo();
    // 装备视频编码器,编码视频的尺度(像素),帧率
    await _engine.setVideoEncoderConfiguration(
      const VideoEncoderConfiguration(
        dimensions: VideoDimensions(width: 640, height: 360),
        frameRate: 15,
      ),
    );
    await _engine.startPreview();
  }

更多参数装备支撑如下所示:

参数 描述
dimensions 视频编码的分辨率(px)默许值为 640 360
codecType 视频编码类型,比方 1 规范 VP8;2 规范 H.264;3:规范 H.265
frameRate 视频编码的帧率(fps),默许值为 15
bitrate 视频编码码率,单位为 Kbps
minBitrate 最低编码码率,单位为 Kbps
orientationMode 视频编码的方向形式,例如: 0(默许)方向一致;1固定横屏;2固定竖屏
degradationPreference 带宽受限时,视频编码降级偏好,例如:为 0(默许)时带宽受限时,视频编码时优先下降视频帧率,保持分辨率不变;为 1 时带宽受限时,视频编码时优先下降视频分辨率,保持视频帧率不变;为 2 时带宽受限时,视频编码时同时下降视频帧率和视频分辨率
mirrorMode 发送编码视频时是否敞开镜像形式,只影响远端用户看到的视频画面,默许封闭
advanceOptions 高档选项,比方视频编码器偏好,视频编码的压缩偏好等

终究调用 startPreview 敞开画面预览功能,接下来只需求把初始化好的 Engine 装备到 AgoraVideoView 控件就能够完结烘托。

烘托画面

接下来便是烘托画面,如下代码所示,在 UI 上参加 AgoraVideoView 控件,并把上面初始化成功的 _engine ,经过 VideoViewController 装备到 AgoraVideoView ,就能够完结本地视图的预览。

依据前面的 initStatus 状况,在 _engine 初始化成功后才加载 AgoraVideoView

Scaffold(
  appBar: AppBar(),
  body: FutureBuilder<bool?>(
      future: initStatus,
      builder: (context, snap) {
        if (snap.data != true) {
          return Center(
            child: new Text(
              "初始化ing",
              style: TextStyle(fontSize: 30),
            ),
          );
        }
        return AgoraVideoView(
          controller: VideoViewController(
            rtcEngine: _engine,
            canvas: const VideoCanvas(uid: 0),
          ),
        );
      }),
);

这儿还有别的一个参数 VideoCanvas ,其间的 uid 是用来标志用户id的,这儿由于是本地用户,这儿暂时用 0 表示 。

假如需求参加频道,能够调用 joinChannel 办法参加对应频道,以下的参数都是必须的,其间:

  • token 便是前面暂时生成的 Token
  • channelId 便是前面的渠道名
  • uid 和上面相同逻辑
  • channelProfile 挑选 channelProfileLiveBroadcasting ,由于咱们需求的是多人通话。
  • clientRoleType 挑选 clientRoleBroadcaster ,由于咱们需求多人通话,所以咱们需求进来的用户能够交流发送内容。
Scaffold(
  
  floatingActionButton: FloatingActionButton(
    onPressed: () async {
      // 参加频道
      _engine.joinChannel(
        token: token,
        channelId: channel,
        uid: 0,
        options: ChannelMediaOptions(
          channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
          clientRoleType: ClientRoleType.clientRoleBroadcaster,
        ),
      );
    },
  ),
);
基于声网 Flutter SDK 实现多人视频通话
基于声网 Flutter SDK 实现多人视频通话

相同的道理,经过前面的 RtcEngineEventHandler ,咱们能够获取到参加频道用户的 uid(rUid) ,所以仍是 AgoraVideoView ,可是咱们运用 VideoViewController.remote 依据 uid 和频道id去创立 controller ,配合 SingleChildScrollView 在顶部显现一排能够左右滑动的用户小窗效果。

用 Stack 嵌套层级。

Scaffold(
  appBar: AppBar(),
  body: Stack(
    children: [
      AgoraVideoView(
      
      ),
      Align(
        alignment: Alignment.topLeft,
        child: SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Row(
            children: List.of(remoteUid.map(
                  (e) =>
                  SizedBox(
                    width: 120,
                    height: 120,
                    child: AgoraVideoView(
                      controller: VideoViewController.remote(
                        rtcEngine: _engine,
                        canvas: VideoCanvas(uid: e),
                        connection: RtcConnection(channelId: channel),
                      ),
                    ),
                  ),
            )),
          ),
        ),
      )
    ],
  ),
);

这儿的 remoteUid 便是一个保存参加到 channel 的 uid 的 Set 对象。

终究运转效果如下图所示,引擎加载成功之后,点击 FloatingActionButton 参加,能够看到移动端和PC端都能够正常通信交互,而且不管是通话质量仍是画面流通度都适当优秀,能够感受到声网 SDK 的完结度仍是适当之高的。

赤色是我自己加上的打码。

基于声网 Flutter SDK 实现多人视频通话
基于声网 Flutter SDK 实现多人视频通话
基于声网 Flutter SDK 实现多人视频通话

在运用该例子测验了 12 人同时在线通话效果,根本和微信视频会议没有差别,以下是完好代码:


class VideoChatPage extends StatefulWidget {
  const VideoChatPage({Key? key}) : super(key: key);
  @override
  State<VideoChatPage> createState() => _VideoChatPageState();
}
class _VideoChatPageState extends State<VideoChatPage> {
  late final RtcEngine _engine;
  ///初始化状况
  late final Future<bool?> initStatus;
  ///是否参加谈天
  bool isJoined = false;
  /// 记载参加的用户id
  Set<int> remoteUid = {};
  @override
  void initState() {
    super.initState();
    initStatus = _requestPermissionIfNeed().then((value) async {
      await _initEngine();
      return true;
    }).whenComplete(() => setState(() {}));
  }
  Future<void> _requestPermissionIfNeed() async {
    await [Permission.microphone, Permission.camera].request();
  }
  Future<void> _initEngine() async {
    //创立 RtcEngine
    _engine = createAgoraRtcEngine();
    // 初始化 RtcEngine
    await _engine.initialize(RtcEngineContext(
      appId: appId,
    ));
    _engine.registerEventHandler(RtcEngineEventHandler(
      // 遇到过错
      onError: (ErrorCodeType err, String msg) {
        print('[onError] err: $err, msg: $msg');
      },
      onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
        // 参加频道成功
        setState(() {
          isJoined = true;
        });
      },
      onUserJoined: (RtcConnection connection, int rUid, int elapsed) {
        // 有用户参加
        setState(() {
          remoteUid.add(rUid);
        });
      },
      onUserOffline:
          (RtcConnection connection, int rUid, UserOfflineReasonType reason) {
        // 有用户离线
        setState(() {
          remoteUid.removeWhere((element) => element == rUid);
        });
      },
      onLeaveChannel: (RtcConnection connection, RtcStats stats) {
        // 脱离频道
        setState(() {
          isJoined = false;
          remoteUid.clear();
        });
      },
    ));
    // 翻开视频模块支撑
    await _engine.enableVideo();
    // 装备视频编码器,编码视频的尺度(像素),帧率
    await _engine.setVideoEncoderConfiguration(
      const VideoEncoderConfiguration(
        dimensions: VideoDimensions(width: 640, height: 360),
        frameRate: 15,
      ),
    );
    await _engine.startPreview();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Stack(
        children: [
          FutureBuilder<bool?>(
              future: initStatus,
              builder: (context, snap) {
                if (snap.data != true) {
                  return Center(
                    child: new Text(
                      "初始化ing",
                      style: TextStyle(fontSize: 30),
                    ),
                  );
                }
                return AgoraVideoView(
                  controller: VideoViewController(
                    rtcEngine: _engine,
                    canvas: const VideoCanvas(uid: 0),
                  ),
                );
              }),
          Align(
            alignment: Alignment.topLeft,
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                children: List.of(remoteUid.map(
                      (e) => SizedBox(
                    width: 120,
                    height: 120,
                    child: AgoraVideoView(
                      controller: VideoViewController.remote(
                        rtcEngine: _engine,
                        canvas: VideoCanvas(uid: e),
                        connection: RtcConnection(channelId: channel),
                      ),
                    ),
                  ),
                )),
              ),
            ),
          )
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // 参加频道
          _engine.joinChannel(
            token: token,
            channelId: channel,
            uid: 0,
            options: ChannelMediaOptions(
              channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
              clientRoleType: ClientRoleType.clientRoleBroadcaster,
            ),
          );
        },
      ),
    );
  }
}

进阶调整

终究咱们再来个进阶调整,前面 remoteUid 保存的仅仅长途用户 id ,假如咱们将 remoteUid 修改为 remoteControllers 用于保存 VideoViewController ,那么就能够简略完结画面切换,比方 「点击用户画面完结巨细切换」 这样的需求。

如下代码所示,简略调整后逻辑为:

  • remoteUid 从保存长途用户 id 变成了 remoteControllersMap<int, VideoViewController>
  • 新增加了 currentController 用于保存当时大画面下的 VideoViewController ,默许是用户自己
  • registerEventHandler 里将 uid 保存更改为 VideoViewController 的创立和保存
  • 在小窗处增加 InkWell 点击,在单击之后切换 VideoViewController 完结画面切换
class VideoChatPage extends StatefulWidget {
  const VideoChatPage({Key? key}) : super(key: key);
  @override
  State<VideoChatPage> createState() => _VideoChatPageState();
}
class _VideoChatPageState extends State<VideoChatPage> {
  late final RtcEngine _engine;
  ///初始化状况
  late final Future<bool?> initStatus;
  ///当时 controller
  late VideoViewController currentController;
  ///是否参加谈天
  bool isJoined = false;
  /// 记载参加的用户id
  Map<int, VideoViewController> remoteControllers = {};
  @override
  void initState() {
    super.initState();
    initStatus = _requestPermissionIfNeed().then((value) async {
      await _initEngine();
      ///构建当时用户 currentController
      currentController = VideoViewController(
        rtcEngine: _engine,
        canvas: const VideoCanvas(uid: 0),
      );
      return true;
    }).whenComplete(() => setState(() {}));
  }
  Future<void> _requestPermissionIfNeed() async {
    await [Permission.microphone, Permission.camera].request();
  }
  Future<void> _initEngine() async {
    //创立 RtcEngine
    _engine = createAgoraRtcEngine();
    // 初始化 RtcEngine
    await _engine.initialize(RtcEngineContext(
      appId: appId,
    ));
    _engine.registerEventHandler(RtcEngineEventHandler(
      // 遇到过错
      onError: (ErrorCodeType err, String msg) {
        print('[onError] err: $err, msg: $msg');
      },
      onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
        // 参加频道成功
        setState(() {
          isJoined = true;
        });
      },
      onUserJoined: (RtcConnection connection, int rUid, int elapsed) {
        // 有用户参加
        setState(() {
          remoteControllers[rUid] = VideoViewController.remote(
            rtcEngine: _engine,
            canvas: VideoCanvas(uid: rUid),
            connection: RtcConnection(channelId: channel),
          );
        });
      },
      onUserOffline:
          (RtcConnection connection, int rUid, UserOfflineReasonType reason) {
        // 有用户离线
        setState(() {
          remoteControllers.remove(rUid);
        });
      },
      onLeaveChannel: (RtcConnection connection, RtcStats stats) {
        // 脱离频道
        setState(() {
          isJoined = false;
          remoteControllers.clear();
        });
      },
    ));
    // 翻开视频模块支撑
    await _engine.enableVideo();
    // 装备视频编码器,编码视频的尺度(像素),帧率
    await _engine.setVideoEncoderConfiguration(
      const VideoEncoderConfiguration(
        dimensions: VideoDimensions(width: 640, height: 360),
        frameRate: 15,
      ),
    );
    await _engine.startPreview();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Stack(
        children: [
          FutureBuilder<bool?>(
              future: initStatus,
              builder: (context, snap) {
                if (snap.data != true) {
                  return Center(
                    child: new Text(
                      "初始化ing",
                      style: TextStyle(fontSize: 30),
                    ),
                  );
                }
                return AgoraVideoView(
                  controller: currentController,
                );
              }),
          Align(
            alignment: Alignment.topLeft,
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Row(
                ///增加点击切换
                children: List.of(remoteControllers.entries.map(
                  (e) => InkWell(
                    onTap: () {
                      setState(() {
                        remoteControllers[e.key] = currentController;
                        currentController = e.value;
                      });
                    },
                    child: SizedBox(
                      width: 120,
                      height: 120,
                      child: AgoraVideoView(
                        controller: e.value,
                      ),
                    ),
                  ),
                )),
              ),
            ),
          )
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // 参加频道
          _engine.joinChannel(
            token: token,
            channelId: channel,
            uid: 0,
            options: ChannelMediaOptions(
              channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
              clientRoleType: ClientRoleType.clientRoleBroadcaster,
            ),
          );
        },
      ),
    );
  }
}

完好代码如上图所示,运转后效果如下图所示,能够看到画面在点击之后能够完美切换,这儿首要提供一个大体思路,假如有爱好的能够自己优化并增加切换动画效果。

基于声网 Flutter SDK 实现多人视频通话

别的假如你想切换前后摄像头,能够经过 _engine.switchCamera(); 等 API 简略完结。

总结

从上面能够看到,其实跑完基础流程很简略,回顾一下前面的内容,总结下来便是:

  • 恳求麦克风和摄像头权限
  • 创立和经过 App ID 初始化引擎
  • 注册 RtcEngineEventHandler 回调用于判别状况
  • 翻开和装备视频编码支撑,而且启动预览 startPreview
  • 调用 joinChannel 参加对应频道
  • 经过 AgoraVideoViewVideoViewController 装备显现本地和长途用户画面

当然,声网 SDK 在多人视频通话领域还具有各类丰富的底层接口,例如虚拟布景、美颜、空间音效、音频混合等等,这些咱们后面在进阶内容里讲到,更多 API 效果能够查阅 Flutter RTC API 获取。

额定拓宽

终究做个内容拓宽,这部分和实践开发可能没有太大关系,纯粹是一些技术弥补。

假如运用过 Flutter 开发过视频类相关项目的应该知道,Flutter 里能够运用外界纹理和 PlatfromView 两种方法完结画面接入,而由此对应的是 AgoraVideoView 在运用 VideoViewController 时,是有 useFlutterTextureuseAndroidSurfaceView 两个可选参数。

这儿咱们不讨论它们之间的优劣和差异,仅仅让咱们能够更直观理解 Agora SDK 在不同渠道烘托时的差异,作为拓宽知识点弥补。

基于声网 Flutter SDK 实现多人视频通话

首要咱们看 useFlutterTexture,从源码中咱们能够看到:

  • 在 macOS 和 windows 版别中,Agora SDK 默许只支撑 Texture 这种外界纹理的完结,这首要是由于 PC 端的一些 API 限制导致。
  • Android 上并不支撑装备为 Texture ,只支撑 PlatfromView 形式,这儿应该是基于功能考虑。
  • 只要 iOS 支撑 Texture 形式或许 PlatfromView 的烘托形式可挑选,所以 useFlutterTexture 更多是针对 iOS 收效。

基于声网 Flutter SDK 实现多人视频通话

而针对 useAndroidSurfaceView 参数,从源码中能够看到,它现在只对 android 渠道收效,可是假如你去看原生渠道的 java 源码完结,能够看到其实不管是 AgoraTextureView 装备仍是 AgoraSurfaceView 装备,终究 Android 渠道上仍是运用 TextureView 烘托,所以这个参数现在来看不会有实践的效果。

基于声网 Flutter SDK 实现多人视频通话
基于声网 Flutter SDK 实现多人视频通话

终究,就像前面说的 , 声网 SDK 是经过 Dart FFI 调用底层动态库进行支撑,而这些调用现在看是经过 AgoraRtcWrapper 进行,比方经过 libAgoraRtcWrapper.so 再去调用 lib-rtc-sdk.so ,假如关于这一块感爱好的,能够继续深入探索一下。

基于声网 Flutter SDK 实现多人视频通话

基于声网 Flutter SDK 实现多人视频通话