公司其中一个主营业务是直播,目前主播直播会使用客户端开播,客户端中的用户有观众和主播两种身份。

  但客户端开播并不方便,例如音质没有 PC 的好,手机长时间直播发热,模拟器操作没有 PC 方便等。

  经过讨论,让我们组基于能跨平台的 Electron 开发 PC 客户端版本的开播助手,购买的第三方声网服务正好也有相关 demo。

  

  我们在此 demo 的基础上增加公司的业务逻辑,其技术原理可以分为以下几个步骤:

  1. 采集:通过摄像头、麦克风等设备采集音频和视频数据。
  2. 传输:通过网络将编码后的音频和视频数据传输给服务器。
  3. 服务器处理:服务器对接收的数据进行处理和分发,将数据发送给各个客户端。
  4. 互动消息和消息信令:直播间的聊天消息传输以及各种业务消息指令(例如上麦、下麦等)。

  对应的技术实现会基于声网和腾讯 IM,以及现有的客户端逻辑,简单的说就是将客户端的逻辑搬到 PC 端实现。

  1. 采集编解码部分可由声网的 SDK 方案进行 PC 端的音视频采集。
  2. 传输部分基于声网的 RTC 协议进行采集后的音视频数据推流。
  3. 业务服务器基于现有直播业务模型进行适配,如开播流程,各种业务消息流程。
  4. 互动消息以及消息信令通过现有的三方服务商腾讯 IM 接入适配当前的直播业务流程。

  一开始比较疑惑,为什么直播间中的交互要通过 IM 完成,后面了解到主播和观众需要用长连接绑定在一起,这样有交互才能通知到各个人员的界面中。

  这个工作量是非常巨大的,但是我们计划分成多期完成,第一期就做最简单的音频上麦和下麦。

一、分工

  在拿到原始的 Electron 项目后,一开始还没想好如何分工,大家对 Electron 都比较陌生。

  前后也讨论了两次,最后决定边做边调整分工内容,首先需要了解下 Electron 项目。

1)项目说明

  当前的 Electron 项目其实就是将页面打包到浏览器中发布成一个客户端,这是一个特殊的浏览器,可以不受限制的使用更多的功能。

  而我们的大部分开发其实和普通网页开发无差别,代码存储在下图的 renderer 目录中,分工的主要内容也是此目录中。

  

  main 目录的文件会控制主进程,负责管理应用的生命周期,显示原生界面,执行特殊操作并管理渲染器进程。

  下面的代码就是在打开一个浏览器窗口,可配置其宽高,并且能自动打开控制台。

  开发环境与线上环境 loadURL() 调用的参数有所不同。

function createMainWindow() {
  const window = new BrowserWindow({
    width: 1200,
    height: 720,
    autoHideMenuBar: true,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
      webSecurity: false,
    },
    //titleBarStyle: 'hidden',
    //titleBarOverlay: false,
  });
  window.setMinimumSize(1200,720)
  window.setBackgroundColor('#000000')
  window.webContents.openDevTools({
    mode: 'detach',
    activate: true,
  });
  if (isDevelopment) {
    window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
  } else {
    window.loadURL(
      formatUrl({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file',
        slashes: true,
      })
    );
  }
  window.on('closed', () => {
    mainWindow = null;
  });
  window.webContents.on('devtools-opened', () => {
    window.focus();
    setImmediate(() => {
      window.focus();
    });
  });

  return window;
}

2)明确范围

  初次分工是让组内的一名成员负责登录功能,我去完成主界面的逻辑和项目基础功能。

  项目基础功能包括通信、常量、缓存等功能的封装,其中最重要的是通信功能。

  为了能和客户端使用相同的接口,需要在通信时也加上与客户端相同的鉴权逻辑,并且还得带上所有接口都需要的必传参数。

  同事的登录逻辑也会依赖通信,所以通信模块极为重要,也就最先完成封装,其次是对腾讯 IM 的封装。

  官方文档写的比较清晰,但是在实际集成时,还是遇到不少问题,期间与客户端的同事没少打交道,他们也提供了很大的支持。

  直播间的交互依赖 IM,IM 基于 websocket 长连接,目前已将 IM 的操作封装到 utils/chat.ts 中,简化初始化、发送消息、接收消息、加入直播间等功能。

  下面是一条发送上麦的消息,其中 reqId 和 user-agent 是必传项。

{
  code:"audio", 
  body:{
    reqId:1,
    "user-agent": ""
    method:"onSeat", 
    seatNum:-1, 
    auto: true
  }
}

  下面是一条接收进入直播间的消息,如果是响应消息,那么会包含 reqId;如果是主动推送的消息,那么就不会包含此属性。

  注意,有些响应还会包含 method 字段,并且如果是响应消息,那么 code 都将是 req。

{
  toUserId: "443343545",
  code: "req",
  roomId: "88434342",
  data: {
    userId: "546657523",
    nickName: "211",
method: "onSeat", reqId: 1
}, print: true }

  如果要对某个响应做回调,可以根据 code 和 method 的组合来设计回调方法的名称,然后注册到一个哈希对象中。

  public addReceiveCallback(name:string, callback:any) {
    this.hashReceive[name] = callback;
  }

  不过,由于响应消息的 code 都是 req,所以在发送消息时,需要将 reqId 与 code 映射,这样就能在响应时通过 reqId 读取 code。

  经过这类基础封装后,分工就明确了,直播间中的交互以及登录都交由另一个同事完成,大家也能做到互不干扰。

二、研发

  客户端需要将软件安装到本地电脑中,所以需要有签名和更新机制。

  以 MacOS 为例,默认是不能安装非应用市场的软件,如果要将软件发布应用市场,那么就必须签名。

  而更新是为了能让用户享受最新版本的功能和优化。由于第一阶段,用户都是运营能控制的,所以就省去了签名和更新。

1)启动

  启动项目,在背后执行的命令是 electron-webpack dev。

npm start

  electron-webpack 的目标是通过一次简单的安装消除所有初步设置(项目结构、热更新、TS、babel 等),以便重新开发应用程序。

2)打包

  开始打包,在背后执行的命令是 yarn compile && electron-builder

npm run dist

  electron-builder 用于打包和构建适用于 macOS、Windows 和 Linux 的 Electron 应用程序。

  注意,打包成 Windows 软件时,需要在 Windows 系统中完成打包。

  在 Windows 上安装环境时,发现 Node.js、VSCode 等软件,最新版都不再支持 Win7。

{
    "compile": "electron-webpack",
    "dist": "yarn compile && electron-builder",
    "dist:mac": "yarn dist --dir -c.compression=store -c.mac.identity=null",
    "dist:win32": "yarn compile && electron-builder --publish never --win --ia32",
    "dist:win64": "yarn compile && electron-builder --publish never --win --x64"
}

  在第一次打包时,会下载依赖的 electron-v.xxxx.zip 文件,由于国内的网络环境,下载速度缓慢,经常会失败。

  在提示的错误中,会包含下载地址,最简单的办法是放到浏览器中下载,下载完成后放到 Mac 的指定目录。

open ~/Library/Caches/electron/

  Windows 目录可以参考 C:\Users\Administrator\AppData\Local\electron\Cache,其中Administrator是登录的账号,有可能不同。

  

  在打包 Windows 时,也要下载相关的 Electron 压缩包(下载地址可从错误提示中获取),例如 electron-v18.2.3-darwin-x64.zip。

⨯ Get "https://npmmirror.com/mirrors/electron-builder-binaries/winCodeSign-2.6.0/winCodeSign-2.6.0.7z": 
proxyconnect tcp: dial tcp :0: connectex: The requested address is not valid in
its context.github.com/develar/app-builder/pkg/download.(*Downloader).follow.func1......

  在打包时还会出现其他的包不能安装,按照提示将包下载到本地,然后复制到指定位置。

  例如将 winCodeSign.gz 复制到 ~/Library/Caches/electron-builder/winCodeSign 中并解压缩。

  最后按照各种提示进行操作即可,例如开放权限等。

  

3)IM

  直播间的通信基于腾讯 IM,以上麦为例,在上麦时客户端会向 IM 服务器发送上麦消息。

  服务端在完成上麦处理后,让 IM 服务端发送响应消息给客户端。

  上麦初始化后,需要加入声网的频道,再进行座位下发消息,经由 IM 服务器中转给客户端。

4)日志

  在模板 src/index.ejs 页面中,已经加载自研的监控 SDK,console.log 和 console.error 的数据都会上报到服务器中。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>PC直播助手</title>
    <script src="https://www.xxx.com/js/shin.js" crossorigin="anonymous"></script>
    <script>
      shin.setParam({
        token: "pc-live",
        version: '1.0.0',
        src: 'https://api.xxx.com/ma.gif'
      });
    </script>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

  除此之外,还可以引入 electron-log 库,将打印信息存储在本地文件中。

  最新版本是 5.X,但是每次引入会报错,所以就选用了 4.4.8 版本,内置了 error、warn、info 和 debug 等方法。

import logger from 'electron-log';

logger.info('第二条测试日志', {
  userId: '657676',
  userSig: 'eJwszc3K*wsAAP--qFMw4w__',
  groupId: '88434345'
})

  在调用日志方法后,默认就会在指定目录中生成 renderer.log 文件,其中 live-assistant 是在 package.json 中配置的 name 属性。

open ~/Library/Logs/live-assistant/

  renderer.log 文件中的默认日志格式如下,格式可自定义。

[2024-02-22 18:02:20.549] [info]  第二条测试日志 {
  userId: '5456565',
  userSig: 'eJwszc3K*wsAAP--qFMw4w__',
  groupId: '885657564'
}
[2024-02-22 18:18:14.223] [info]  第三条测试日志 {
  userId: '34545656',
  userSig: 'eJwszc3K*wsAAP--qFMw4w__',
  groupId: '88567688'
}

5)声网

  整个直播间功能依赖声网和 IM 共同作用,IM 是展示 UI 层,可以让观众在直播间内看到主播是否上麦、开闭麦状态。

  声网是功能层,实际控制主播是否进入频道,是否能发流。

  所以整个流程应该是先发送 IM 消息,接收到服务端 IM 消息之后确认业务层功能没问题再操作声网层,声网层收到回调确认功能成功再结束整个功能。

 

 posted on 2024-03-21 10:12  咖啡机(K.F.J)  阅读(129)  评论(0编辑  收藏  举报