AI测试 在元宇宙中搭建语聊房,设计思路和实现方法分享

RTE开发者社区 · 2023年04月26日 · 最后由 今晚打老虎 回复于 2023年05月04日 · 5211 次阅读
文为声网 RTE 开发者社区作者投稿作者为@arige

目标

1. 制定目标

不妨大家想一下这个场景:

你走进一个咖啡厅,看到了一个美女或者帅哥,想要一个微信号,方便后续发展。

那我们要怎么做呢?

走到对方跟前,小声对他说:“你好,你长的很像我的前女友(前男友),可以加下你的微信吗?”。

对方有可能悄声的说,“我扫你”。

你这个时候你就需要把你的二维码展示给他。

当你们恋爱成功,求婚的时候,可能要找一个更加浪漫的环境,并且你们的对话希望给更多人看到、听到。

通过这个上面场景的想象,我们可以简单的确定一些基本的要素

  1. 可以更改的场景(咖啡厅和求婚现场)
  2. 自主塑造形象功能(毕竟外貌是一见钟情的原点)
  3. 单聊(要微信号不能一次要一群人得呀。不然可能被当成流氓给打得哟)
  4. 文件共享(把自己得二维码展示给对方,让对方加好友)
  5. 群聊、群视频(把求婚现场给其他用户看)

2. 拆解目标

为了满足上面提到得要求,我们需要以下功能:

  1. 支持 unity 的方便接入。毕竟 unity 是一个非常好的 3D 引擎,如果可以方便接入得话,在后续得场景创建和人物创造上都可以有更大的扩展空间
  2. 1 vs 1 的视频,或者 1 vs 多的直播模式。可以满足单聊和群聊的需求
  3. 同步本地播放的本地视频或者远端视频给其他用户。可以满足共享二维码的需求。

3. 效果预览

如果要完整的实现一个完整的项目会花费比较多的时间,而我发现目前声网的 Demo 会提供简单的人物模型和素材,但是他们开放度较高,开发者可以根据需要使用自己定制化的模型素材,为了实现上述提到的需求,我只做了一些微小的修改,就满足了我们的需求。本次主要看下效果,为后续在生产环境中落地做一些技术上的储备。

以下是在 demo 上调整后显示出来的效果。


这是用 unity 来实现的一个咖啡厅的样子


这是一个服装店,我们可以给人物进行换装,这个也是由 unity 来实现的。

以上两个的接入都非常的方便,我们可以很好的对接 unity,如果需要的话,后续有自己的 unity 场景也可以快速接入。


这个是我自己测试的 1 vs 1 的视频测试效果

这个是我自己测试的将本地视频分享给对方的一个效果

想象一下,是不是可以和你的对象一起看剧了呢?

简单实现

以上效果都是基于声网的 sdk 来实现的,如果你也想进行尝试或者使用,可以继续查看我的接入过程和中间遇到的问题以及解决方案。

1. 账号准备

我们既然使用了声网的 sdk,那注册一个声网的账号做一些声网的账号配置也很合理吧。

  1. 注册账号
  2. 完成实名
  3. 创建项目

  1. 配置项目

然后进入详细配置

生成 token,并将 appid、证书、token 复制保存。
注意!!!这里很重要,一定要看仔细,项目中会使用到!

至此,账号的准备已经完成了。

2. 配置项目

  1. demo 源码
  2. 通过声网官网客服,申请元宇宙 SDK、Unity 工程文件、开发指南,同时申请开通使用权限,详情可访问 shengwang.cn
  3. 将 sdk 解压并配置到项目中 一定要注意目录,如果自己的项目中不存在当前的目录要自己创建

  1. 配置 id 一般情况下,这个文件是不会上传到远程仓库的,写到这里也主要是安全考虑。将在后台中申请的 appid、证书,复制后粘贴到/Android/local.properties 里面,如下:

  2. 配置 channel 将在后台生成 token 的时候,填写的 channel 给写到类中,如下

  3. 配置项目权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 对于 Android 12.0 及以上设备,还需要添加如下权限: -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"  />

注意:

Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA

需要动态申请权限,否则无法正常完成视频功能。

sdk 本身是不会去申请权限的,所以需要我们自己在合适的位置帮 sdk 申请好权限。

  1. 混淆配置


咖啡厅

做形象


服装店

这里是直接使用了声网给的效果,如果自己项目需要的话,可以去自己去调整。

其中换装和捏脸都需要 unity 上的一些修改和 native 与 unity 的通信。整个可以参考换装和捏脸

3. 视频 1vs1

前面已经完成了整体项目的构建。并且已经把 unity 内容跑起来了,那么接下来我们看下,如果要实现 1vs1 的视频要怎么处理。为了方便处理,我单独写了一个页面来处理视频 1vs1,效果如下:


视频聊天

这个流程图,建议大家多看几遍,在我们遇到问题的时候,这个图会给我们启发,帮助我们解决问题。

详细流程:

  1. 创建 RtcEngine 对象

该对象管理了整个的视频等场景,是一个非常核心的对象。

try {
           RtcEngineConfig config = new RtcEngineConfig();
           config.mContext = getBaseContext();
           config.mAppId = appId;
           config.mEventHandler = mRtcEventHandler;
           config.mAudioScenario = Constants.AudioScenario.getValue(Constants.AudioScenario.DEFAULT);
           mRtcEngine = RtcEngine.create(config);
       } catch (Exception e) {
           throw new RuntimeException("Check the error.");
       }
  1. 创建 RtcEngine 属性
// 视频默认禁用,你需要调用 enableVideo 启用视频流。
      mRtcEngine.enableVideo();
      // 录音默认禁用,你需要调用 enableAudio 启用录音。
      mRtcEngine.enableAudio();
      // 开启本地视频预览。
      mRtcEngine.startPreview();
  1. 将本地摄像头内容显示到 local_video_view_container 上
FrameLayout container = findViewById(R.id.local_video_view_container);
        // 创建一个 SurfaceView 对象,并将其作为 FrameLayout 的子对象。
        SurfaceView surfaceView = new SurfaceView (getBaseContext());
        container.addView(surfaceView);
        // 将 SurfaceView 对象传入声网,以渲染本地视频。
        mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, KeyCenter.RTC_UID));
  1. 设置当前的模式
ChannelMediaOptions options = new ChannelMediaOptions();

     // 将用户角色设置为 BROADCASTER。
     options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
     // 视频通话场景下,设置频道场景为 BROADCASTING。
     options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;

其中 clientRoleType 有两种,如果是 CLIENT_ROLE_BROADCASTER 就是可以播和收,如果是 CLIENT_ROLE_AUDIENCE 就只能收看,当前就成了主播模式。

  1. 加入频道
// 使用临时 Token 加入频道。
       // 你需要自行指定用户 ID,并确保其在频道内的唯一性。
       int res = mRtcEngine.joinChannel(token, channelName, KeyCenter.RTC_UID, options);
       if (res != 0)
       {
           // Usually happens with invalid parameters
           // Error code description can be found at:
           // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
           // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
           Log.e("video","join err:"+RtcEngine.getErrorDescription(Math.abs(res)));

       }

joinChannel 方法有返回值,可以看到我们加入频道是否成功了,如果不成功的话,我们可以看下错误原因,并对照解决;如果成功了,就可以观察 IRtcEngineEventHandler 对象的回调方法,重点关注下 onError(int err) 和 onJoinChannelSuccess(String channel, int uid, int elapsed) 如果收到 onJoinChannelSuccess 方法的回调,我们就可以关注 onUserJoined(int uid, int elapsed) 方法,我们可以在这个方法里开始显示远端内容。

  1. 显示远端内容
 @Override
      // 监听频道内的远端主播,获取主播的 uid 信息。
      public void onUserJoined(int uid, int elapsed) {
          Log.e(TAG, "onUserJoined->" + uid);
          runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  // 从 onUserJoined 回调获取 uid 后,调用 setupRemoteVideo,设置远端视频视图。
                  setupRemoteVideo(uid);
              }
          });
      }
private void setupRemoteVideo(int uid) {
      FrameLayout container = findViewById(R.id.remote_video_view_container);
      SurfaceView surfaceView = new SurfaceView (getBaseContext());
      surfaceView.setZOrderMediaOverlay(true);
      container.addView(surfaceView);
      mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid));
  }

最终,远端的视频会显示在 remote_video_view_container 上。

4. 将本地播放的视频显示给远端用户

同步本地播放给远端

将本地视频怎么同步给远端的用户呢?其实从本质上来讲,将本地摄像头和将本地播放的视频给远端用户看,对远端用户都是一样的,不一样的是本地用户给远端用户的数据源是哪个。一个是摄像头,一个是播放器。

  1. 设置本地 video 的时候做下修改
FrameLayout container = findViewById(R.id.local_video_view_container);
       // 创建一个 SurfaceView 对象,并将其作为 FrameLayout 的子对象。
       SurfaceView surfaceView = new SurfaceView (getBaseContext());
       container.addView(surfaceView);
       // 将 SurfaceView 对象传入声网,以渲染本地视频。
       VideoCanvas videoCanvas = new VideoCanvas(surfaceView, Constants.RENDER_MODE_HIDDEN, Constants.VIDEO_MIRROR_MODE_AUTO,
               Constants.VIDEO_SOURCE_MEDIA_PLAYER,  mediaPlayer.getMediaPlayerId(), KeyCenter.RTC_UID);
       mRtcEngine.setupLocalVideo(videoCanvas);
  1. 在 int res = mRtcEngine.joinChannel(token, channelName, KeyCenter.RTC_UID, options);是 0 的时候,调用如下方法:
int res = mRtcEngine.joinChannel(token, channelName, KeyCenter.RTC_UID, options);
      if (res != 0)
      {
          // Usually happens with invalid parameters
          // Error code description can be found at:
          // en: https://docs.agora.io/en/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
          // cn: https://docs.agora.io/cn/Voice/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_i_rtc_engine_event_handler_1_1_error_code.html
          Log.e("video","join err:"+RtcEngine.getErrorDescription(Math.abs(res)));

      }else {
          mediaPlayer = mRtcEngine.createMediaPlayer();
          mediaPlayer.registerPlayerObserver(this);
          mediaPlayer.open(MetaChatConstants.VIDEO_URL, 0);
      }

3.监听 onPlayerStateChanged 回调并在 state 是 PLAYER_STATE_OPEN_COMPLETED 的时候执行 play 方法,代码如下:

public void onPlayerStateChanged(io.agora.mediaplayer.Constants.MediaPlayerState state, io.agora.mediaplayer.Constants.MediaPlayerError error) {
       if(state == io.agora.mediaplayer.Constants.MediaPlayerState.PLAYER_STATE_OPEN_COMPLETED){
           mediaPlayer.play();
       }
   }

至此,就完成了功能上的使用。

其它功能

除了上面提到的功能外,声网还提供了一些其他的功能,在需要的时候可以直接使用,或者少量修改就可以用的。

比如说空间音效功能,该功能基于声学原理,模拟声音在不同空间环境中的传播、反射、吸收效果。可以为用户提供旅游中路人聊天声、海浪声、风声等,让用户更沉浸式体验

再比如说实时共赏影音功能。该功能可以几乎无延时的实现,远程观影、听歌等功能。甚至可以实现 k 歌的能力。

更多的功能期待大家一起发掘!

参考资料

共收到 2 条回复 时间 点赞

很完整的介绍,学习了。

请问现在模型可以通过实体扫描的方式生成吗

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册