使用声网 SDK 进行直播

很多人对 Instagram 和 Facebook 的直播功能十分好奇,如果你是一个 flutter 开发者,或打算用 Flutter 进行开发,可以使用声网实时通信 SDK 在 flutter 中添加实时聊天功能。声网是一个 RTC SDK,为 Android、iOS、Web、Windows、macOS、Unity、 electron和 flutter 等平台提供 SDK,类型包括视频 SDK、语音 SDK、交互式游戏 SDK、实时消息 SDK、云录制等。

我们会在这个项目中使用声网视频 SDK 和实时消息 SDK。

我们开始吧!

首先,使用下列两种方法创建一个新的 Flutter 项目:

  • Command Line
  • Android Studio

我在本项目中使用 Android Studio。因此,先创建一个新项目(点击这里查看创建 flutter 项目的教程),然后根据个人喜好为该项目命名。

现在,清理 main.dart 文件,添加一个带 Logo 的启动画面。因为我们使用的是声网 SDK,创建的软件与 Instagram 类似,所以我将其命名为 Agoragram。


启动画面

先从启动画面入手,点击这里获取代码。设计如下所示:

定时器结束时,我把路由改为 HomeScreen,之后我会将 HomeScreen 作为基本路由。

启动页面搭建完成之后,我们需要构建一个登录页面。我将 Firebase 身份验证用作登录和注册页面的后端,因此要先创建一个 Firebase 项目,将其与 flutter 链接,大家可以点击这里查看引用。

将 Firebase 集成到 Flutter 项目后,我们要构建 login.dart 文件。下文会提到用于登录的微件设计:

  • 一段文本;
  • 用来分别输入电子邮件和密码的两个文本域。

注意,我使用了一些在 dart 文件中定义的函数,因为我已在另一个文件中提出了所有 Firebase 请求,具体细节我会分享在文末,大家可以点击这里查看登录代码。

登录后,创建一个注册微件。注册需要头像、用户名、姓名、电子邮件和密码。另外,我们需要创建一个输入值无效提示,在用户的输入值无效时弹出提示(点击这里查看代码)。

我在上方的页面下方添加了一个页脚,单击该页脚可以跳转到登录页面。我们需要一个可以显示人相的头像,另外,用户名必须具有唯一性。

完成登录或注册后,我将名称、电子邮件、头像等数据保存在 Firebase 服务器和共享的“Preference”中(以便随时获取详细信息)。


主界面(Home Screen)

我们要构建的下一个页面是主界面,我添加了 2 个包含图像和评论的帖子,都是 app 里自带的(本教程不讨论这部分内容),帖子前面有用户的头像和姓名,我们可以在图片下方添加心形、评论、分享和保存等图标,我们还可以为图像添加描述。以上是计划构建的基本布局。

这是主界面中单个帖子的布局。为便于管理 UI,我为帖子使用了一个类函数。

因为我们要构建的是直播推流,所以需要两个 UI,分别用来主持直播和观看直播,如下所示:

上图的“test”是我们用户名,“y”是另一个用户的用户名,为了使设计如“y”所示,我们需要堆叠一些微件。我的想法是,只要用户主持一场现场直播,就在 Firebase 中创建一个文档,并显示在此处,所以我们需要一个滚动视图,用来横向管理直播用户。为便于轻松管理数据,我也为直播用户使用了类函数。点击这里查看构建主界面的代码。完成后的主界面如下所示:

然后,开始使用直播功能。我们希望发起直播的人(我们称此类用户为主播用户)可以向其他用户分享摄像头视图和声音。观看直播的其他人只能观看,不能共享摄像头和音频。主机有添加观众的功能,但我们先不构建这个功能。

我用声网 SDK 来构建视频共享平台,其他 RTC SDK 比起来,声网 SDK 还是非常好用的。我构建这个 app 使用的是 GitHub 库的引用,欢迎大家点击查看,请按照声网平台的步骤进行操作:

第一步:创建一个帐户

1、在 www.agora.io 上创建一个开发者帐户;

2、单击右上角的注册按钮;

3、填写注册信息,创建帐户;

4、创建完帐户后,登录。

第二步:开发者控制面板

1、注册完成后就可以进入开发者面板,这里你可以访问所有开发者资源;

2、点击左上角的“Projects”选项卡,能看到一个创建好的默认项目;

3、找到 App ID,它是你使用声网服务的 API 密钥。

有了这个 App ID,我们才能在项目中运行声网 SDK。

接下来要添加依赖项,请打开 pubspec.yaml,输入以下内容:

agora_rtc_engine: 1.0.5   # for video
agora_rtm: 0.9.11         # for chatting 

你可以在存储 App ID 的我的项目中找到 setting.dart 文件,将 App ID 粘贴在该文件中。

const APP_ID = "";  // Enter the App ID in between the double quotes

获取项目的所有依赖项:

flutter pub get

直播的预期 UI 如下图所示:

图一

图二

我们需要使用声网引擎来初始化视频和音频,对于评论,我们使用的是声网 RTM。 我们有查看加入直播场景中的直播用户的功能,还可以静音、切换摄像头,还可以点击按钮结束直播,布局如上图二。

可以点击这里查看完整的 dart 文件,我会说明使用的内容和原因。

Future<void> initialize() async {

  await _initAgoraRtcEngine();
  _addAgoraEventHandlers();
  await AgoraRtcEngine.enableWebSdkInteroperability(true);
  await AgoraRtcEngine.setParameters(
      '''{\"che.video.lowBitRateStreamParameter\":{\"width\":320,\"height\":180,\"frameRate\":15,\"bitRate\":140}}''');
  await AgoraRtcEngine.joinChannel(null, widget.channelName, null, 0);
}

/// Create agora sdk instance and initialize
Future<void> _initAgoraRtcEngine() async {
  await AgoraRtcEngine.create(APP_ID);
  await AgoraRtcEngine.enableVideo();
}

/// Add agora event handlers
void _addAgoraEventHandlers() {

  AgoraRtcEngine.onJoinChannelSuccess = (
    String channel,
    int uid,
    int elapsed,
  ) async{

    final documentId = widget.channelName;
    channelName= documentId;
    FireStoreClass.createLiveUser(name: documentId,id: uid,time: widget.time,image:widget.image);
    // The above line create a document in the firestore with username as documentID

    await Wakelock.enable();
    // This is used for Keeping the device awake. Its now enabled

  };

  AgoraRtcEngine.onLeaveChannel = () {
    setState(() {
      _users.clear();
    });
  };

  AgoraRtcEngine.onUserJoined = (int uid, int elapsed) {
    setState(() {
      _users.add(uid);
    });
  };

  AgoraRtcEngine.onUserOffline = (int uid, int reason) {
    setState(() {
      _users.remove(uid);
    });
  };
}

我们在 initState() 内部调用 initialize 函数,用来初始化声网引擎。我还使用了 Wakelock 软件包,让设备保持唤醒状态,否则设备会在特定时间后自动锁定,不过要记得在直播结束后禁用 Wakelock。

@override
void initState() {
  super.initState();
  // initialize agora sdk
  initialize(); // Initialise agora engine
  _createClient(); // initialise agora RTM
}

如果要为声网 RTM 创建客户端,我们还需要在 initState() 中添加该函数。

另外,我们还需获取 Rendered Widget 来显示画面,可以使用下列命令:

AgoraRenderWidget(0, local: true, preview: true),

目前 Layout 已经设置完成。我们只需要主机用户的屏幕,启动视频频道需要 channelName,channelName 也应具有唯一性,避免造成交叉混淆。因为我们已经将用户名设为唯一,所以直接用用户名作为频道名称。

接下来,我们用声网 RTM 搭建评论功能。在主机文件的 initState() 中调用 createClient() 函数,此函数定义了一个用户发送消息的 RTM 通道。

void _createClient() async {
  _client =
  await AgoraRtmClient.createInstance('<Your App ID Here>');
  _client.onMessageReceived = (AgoraRtmMessage message, String peerId) {
    _log(user: peerId,  info: message.text, type: 'message');
  };
  _client.onConnectionStateChanged = (int state, int reason) {
    if (state == 5) {
      _client.logout();
     setState(() {
        _isLogin = false;
      });
    }
  };
  await _client.login(null, widget.channelName );
  _channel = await _createChannel(widget.channelName);
  await _channel.join();
}

不要忘记在 AgoraRtmClient.createInstance() 中添加你的 App ID。

我用 channel.getMembers().length 的 RTM 功能查看加入直播的用户数量,此功能可以返回加入频道的用户数量,代码如下:

Future<AgoraRtmChannel> _createChannel(String name) async {
  AgoraRtmChannel channel = await _client.createChannel(name);
  channel.onMemberJoined = (AgoraRtmMember member) async {
    var img = await FireStoreClass.getImage(username: member.userId);
    userMap.putIfAbsent(member.userId, () => img);
    var len;
    _channel.getMembers().then((value) {
      len = value.length;
      setState(() {
        userNo= len-1 ;
      });
    });

    _log(info: 'Member joined: ',  user: member.userId,type: 'join');
  };
  channel.onMemberLeft = (AgoraRtmMember member) {
    var len;
    _channel.getMembers().then((value) {
      len = value.length;
      setState(() {
        userNo= len-1 ;
      });
    });

  };
  channel.onMessageReceived =
      (AgoraRtmMessage message, AgoraRtmMember member) {
    _log(user: member.userId, info: message.text,type: 'message');
  };
  return channel;
}

void _log({String info,String type,String user}) {
  
  var image = userMap[user];
  Message m = new Message(
      message: info, type: type, user: user, image: image);
  setState(() {
   _infoStrings.insert(0, m);
  }); 
}

如果有用户加入频道,我会获取用户的头像并将其添加到地图中,这样就只用从服务器中获取一次图像,无需在用户评论时多次调用 image 函数。

每当有用户加入频道,聊天框中都会显示一条消息,表示有用户加入频道。 每当有用户发表评论,就会显示一条带有用户图像、用户名的消息。

以上是我认为的主持人频道要使用的主要功能,大家可以点击这里查看完整设计。


下一步是用户加入频道观看实时直播,因此我们要对主持人之外的用户的界面做一些更改。用户不需要使用麦克风和摄像头图标,下图就是用户加入直播后的界面显示:

屏幕的左上角显示主机用户的头像,右上角显示用户数,下方有 comment 和 heart 图标。我不太会制作 heart 动画,只能制作一个与其相似度高达 90% 的动画。

一起来看看我们需要做的更改,我们要在 _initAgoraRtcEngine() 中静音用户音频并关闭本地视频。

Future<void> _initAgoraRtcEngine() async {
  await AgoraRtcEngine.create(APP_ID);
  await AgoraRtcEngine.enableVideo();
  await AgoraRtcEngine.muteLocalAudioStream(muted);  // Changes
  await AgoraRtcEngine.enableLocalVideo(!muted);     // Changes 
}

对于 heart 动画,我正在使用一个带数学运算的自定义 dart 文件,可以点击这里查看自定义的 dart 文件。 在 join.dart 中调用 HeartAnim,我用的是下列代码:

Widget heartPop(){
  final size = MediaQuery.of(context).size;
  final confetti = <Widget>[];
  for (var i = 0; i < _numConfetti; i++) {
    final height = _random.nextInt(size.height.floor());
    final width = 20;
    confetti.add(HeartAnim(height % 200.0,
        width.toDouble(),1));
  }


  return Container(
    child: Padding(
      padding: const EdgeInsets.only(bottom:20),
      child: Align(
        alignment: Alignment.bottomRight,
        child: Container(
          height: 400,
          width: 200,
          child: Stack(
            children: confetti,
          ),
        ),
      ),
    ),
  );
}

这样可以一次性制作出 10 个大小和图形不同的心形图标,但因为要以动画形式显示心形图标,所以要让心形图标循环 4 秒钟:

void popUp() async{
  setState(() {
    heart=true;
  });
  Timer(Duration(seconds: 4), () =>
  {
    _timer.cancel(),
    setState(() {
      heart=false;
    })
  });
  _timer = Timer.periodic(Duration(milliseconds: 125), (Timer t) {
    setState(() {
      _height += _random.nextInt(20);
    });
  });
}

上面的代码将在剩余的 4 秒钟内调用 heartPop。

以上就是加入页面的所有更改,点击这里查看代码。


总结

以上就是本文的所有内容。我说明了所有需要注意的代码,剩余的代码要么是微件,要么不言自明。希望这篇文章可以帮到大家。

大家可以点击这里查看 GitHub 仓库,可以试着创建应用或 Fork 这个仓库。如果大家有更好的方法或发现了文章中的任何错误,欢迎随时联系我。



原文作者:Akarsh Ashok
原文链接:https://medium.com/@akarshashok12/live-broadcast-using-agora-sdk-5befba0fbbed
推荐阅读
相关专栏
SDK 教程
164 文章
本专栏仅用于分享音视频相关的技术文章,与其他开发者和声网 研发团队交流、分享行业前沿技术、资讯。发帖前,请参考「社区发帖指南」,方便您更好的展示所发表的文章和内容。