公司其中一個主營業務是直播,目前主播直播會使用客戶端開播,客戶端中的用戶有觀衆和主播兩種身份。
但客戶端開播並不方便,例如音質沒有 PC 的好,手機長時間直播發熱,模擬器操作沒有 PC 方便等。
經過討論,讓我們組基於能跨平臺的 Electron 開發 PC 客戶端版本的開播助手,購買的第三方聲網服務正好也有相關 demo。
我們在此 demo 的基礎上增加公司的業務邏輯,其技術原理可以分爲以下幾個步驟:
- 採集:通過攝像頭、麥克風等設備採集音頻和視頻數據。
- 傳輸:通過網絡將編碼後的音頻和視頻數據傳輸給服務器。
- 服務器處理:服務器對接收的數據進行處理和分發,將數據發送給各個客戶端。
- 互動消息和消息信令:直播間的聊天消息傳輸以及各種業務消息指令(例如上麥、下麥等)。
對應的技術實現會基於聲網和騰訊 IM,以及現有的客戶端邏輯,簡單的說就是將客戶端的邏輯搬到 PC 端實現。
- 採集編解碼部分可由聲網的 SDK 方案進行 PC 端的音視頻採集。
- 傳輸部分基於聲網的 RTC 協議進行採集後的音視頻數據推流。
- 業務服務器基於現有直播業務模型進行適配,如開播流程,各種業務消息流程。
- 互動消息以及消息信令通過現有的三方服務商騰訊 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 消息之後確認業務層功能沒問題再操作聲網層,聲網層收到回調確認功能成功再結束整個功能。