手游直播是直播行業(yè)中非常重要的一個垂直領(lǐng)域. 手游直播與其他移動直播相比主要是畫面的來源不同, 手游直播其實是一種錄屏技術(shù). 游戲玩家在玩游戲的同時將畫面內(nèi)容實時地分享展示給其他觀眾, 在配上玩家自己的語音, 能夠給觀眾帶來比較有趣的觀看體驗. 手游直播與PC端游戲直播相比主要是設(shè)備的計算能力不同。
PC游戲直播有OBS等強大的第三方直播軟件, 加上PC強勁有富余的計算能力, 使得PC游戲直播的門檻相對較低。
手游直播中由于Apple對個人隱私和安全性的重視, iOS手游直播相對于Android手游直播的難度又大了很多。
一. 現(xiàn)有iOS錄屏方案分析
為了解決iOS手游直播中視頻數(shù)據(jù)采集的難題, 主要存在以下三種方案:
通過私有API獲取系統(tǒng)的IOSurface
這種方法效率比較高, 但是從iOS9 開始, 這個私有API的漏洞就被Apple堵上了, 新系統(tǒng)中無法使用;Airplay Screen Mirroring;
Airplay 是 Apple 提供的一種遠程播放技術(shù), 可以將iPhone、iPod touch、iPad及Mac上的音頻,照片,ppt, 視頻和系統(tǒng)界面鏡像等內(nèi)容傳送到同一局域網(wǎng)中支持Airplay的設(shè)備(如:音箱、Apple TV)中播放. 其中Airplay Screen Mirroring 就是用于屏幕投影的功能,有iOS系統(tǒng)自身將屏幕內(nèi)容進行采集壓縮,通過網(wǎng)絡(luò)投屏的其他設(shè)備上. 但 Airplay 是Apple的私有協(xié)議組, 并且為了安全考慮, 傳輸過程中音視頻數(shù)據(jù)都是用Apple私有的Fairplay加密的, 因此要想獲得這些數(shù)據(jù), 必須破解Airplay的協(xié)議和并且破解Fairplay加密方式。
另外出于個人隱私保護和影視版權(quán)保護的考慮,Apple不允許錄屏功能的軟件上架 AppStore, 即使成功上架, 不久也會被強制下架。
- ReplayKit
Apple 注意到了廣大用戶對手游直播的呼聲, 從iOS 9開始提供了ReplayKit, 給了用戶主動對外分享屏幕內(nèi)容的能力. 對與Replaykit, Apple在不斷的增強, iOS9的時候還只能把特定App的畫面錄制成MP4的片段, 到iOS10 能夠獲取特定App的原始圖像和聲音, 到iOS11, 能夠從系統(tǒng)級啟動錄制, 獲得所有APP包括桌面的畫面. 但是Replaykit是以APP擴展的方式存在的, 真正接受到畫面, 進行壓縮發(fā)送的部分, 并不是一個完整的應(yīng)用程序, 而是一個在后臺運行的擴展. Airplay則可以作為一個在后臺運行的App, 相對來說, 完整App的穩(wěn)定性和可控程度在現(xiàn)階段可能要優(yōu)于App擴展。
ReplayKit 的方案大家可以參考 KSYReplayKit 或者Apple 官方的文檔。
二. KSYAirStreamer SDK
KSYAirStreamer SDK基于Airplay Screen Mirroring的錄屏SDK,使用Airplay方案錄屏, 其實就要求直播APP將自己偽裝成一個Airplay的接收設(shè)備, 實現(xiàn)Airplay的協(xié)議,解密出Fairplay加密的數(shù)據(jù), 再將數(shù)據(jù)轉(zhuǎn)發(fā)出去。
KSYAirStreamer_iOS 就提供了這樣一個模擬接收設(shè)備的SDK, 開始錄屏?xí)riOS系統(tǒng)與SDK建立連接, SDK收到畫面后, 編碼發(fā)送到直播服務(wù)器. 其中編碼和推流功能使用金山云直播SDK實現(xiàn)。
正常的Airplay鏡像, 是當(dāng)與手機在同一個局域網(wǎng)中有一臺Apple TV時, 通過手機的控制中心(在屏幕下方邊緣處上撥時出現(xiàn)), 可以看到候選的Airplay 設(shè)備, 選中對應(yīng)的設(shè)備,即可開始鏡像, 此時系統(tǒng)頂部的狀態(tài)欄會變成藍色, 并有AirPlay的logo。
使用KSYAirStreamer 進行錄屏直播時, 則省去了用戶手動選擇設(shè)備的過程, 因為我們已經(jīng)知道設(shè)備的名稱, 可以直接主動的選擇對應(yīng)的設(shè)備. 這一步主要是通過調(diào)用私有API, 直接對控制中心進行操作實現(xiàn)的. 也是無法上AppStore的理由之一。
2.1 KSYAirStreamKit
KSYAirStreamKit 將KSYAirTunesServer和金山云直播SDK中的音頻采集和推流等組件 組裝在一起共同實現(xiàn)了iOS錄屏直播的功能。
需要錄屏?xí)r, 構(gòu)造一個KSYAirStreamKit的實例, 并通過kit 設(shè)置關(guān)于錄屏和推流的一些配置,最后調(diào)用startService / stopService 方法, 即可啟 / 停錄屏。
_kit = [[KSYAirStreamKit alloc] init];
_kit.airCfg = [[KSYAirTunesConfig alloc] init];
_kit.videoBitrate = 800; //kbps
_kit.streamUrl = @"rtmp://xxx.xxx/live/stream";
[_kit startService];
....
[_kit stopService];
2.2 Airplay鏡像配置參數(shù)
啟動鏡像前, 需要根據(jù)實際情況, 設(shè)置airplay鏡像的參數(shù), 這一步和直播時設(shè)置攝像頭采集的參數(shù)是類似的. 通過KSYAirTunesConfig類可以配置的參數(shù)內(nèi)容如下:
參數(shù)名 | 類型 | 說明 | 默認值 |
---|---|---|---|
airplayName | NSString* | Airplay接收設(shè)備的名稱 | ksyair |
videoSize | int | 接收設(shè)備的尺寸, 指定的是MAX(寬,高) | 960 |
framerate | int | 希望接收到iOS發(fā)送端的視頻幀率 | 30 |
airTunesPort | short | 設(shè)置airtunes 服務(wù)的監(jiān)聽端口, 0 表示系統(tǒng)自動分配 | 0 |
airVideoPort | short | 設(shè)置視頻數(shù)據(jù)的接收端口(帶重試) | 7100 |
macAddr | NSData * | 設(shè)備的mac地址, 默認隨機生成,(長度為6字節(jié)) | 隨機 |
其中airplayName設(shè)定的字符串就是, 在控制中心中能夠看到的設(shè)備的名稱. 當(dāng)同一個局域網(wǎng)需要有多個設(shè)備同時使用錄屏SDK時, 建議這個名稱加上設(shè)備相關(guān)的后綴, 避免沖突。
videoSize 和正常的分辨率設(shè)置不太一樣, 只有一個數(shù)字. 豎屏?xí)r高度為videoSize, 寬度根據(jù)屏幕比例計算得到,橫屏?xí)r則寬度為videoSize, 高度又系統(tǒng)自動計算. 實際測試發(fā)現(xiàn), iOS 10以上的系統(tǒng)中,部分設(shè)備可能不支持低分辨率. 比如iPhone 6s等設(shè)備已經(jīng)不支持720(416 * 720,或720 * 416)以下分辨率輸出, 請按實際情況做分辨率調(diào)整. 設(shè)置這些分辨率容易導(dǎo)致系統(tǒng)異常, 可能需要重啟手機才能恢復(fù)。
2.3 推流參數(shù)設(shè)置
KSYAirStreamKit 中通過streamerBase實例來實現(xiàn)推流, 對應(yīng)參數(shù)的配置請參考wiki中的描述。
2.4 聲音采集
KSYAirStreamKit 中通過 KSYAUAudioCapture來實現(xiàn)的音頻采集. 正常的Airplay鏡像時, 其實是音頻和畫面同步被投影到AppleTV等設(shè)備上的. 但是當(dāng)我們錄屏?xí)r, 接收端也在同一個設(shè)備上, 這時候保持原有邏輯音頻就會出現(xiàn)回環(huán)了. 因此實際上, 錄屏?xí)r, 只有畫面部分走Airplay協(xié)議發(fā)送, 而音頻還是通過外放(Speaker)播放, 這樣系統(tǒng)或者游戲等App的聲音, 能和主播的聲音一起, 通過 KSYAUAudioCapture一起采集進來, 觀眾聽到的就是比較完整的場景聲音了。
2.5 狀態(tài)變化和消息通知
整個錄屏推流過程中, 有兩個狀態(tài)變化需要關(guān)注. 一個是來自數(shù)據(jù)源的KSYAirTunesServer的狀態(tài)變化, 另一個是推流狀態(tài)的變化。
2.5.1 Airplay鏡像的狀態(tài)變化
Airplay鏡像 其實也是iOS系統(tǒng)和我們SDK通過網(wǎng)絡(luò)進行連接交互的一個過程, 在連接階段可能出錯, 在連接成功后也可能受到網(wǎng)絡(luò)變化等因素的影響斷開連接. KSYAirTunesServer 通過 KSYAirDelegate代理協(xié)議來提供狀態(tài)變化的通知, KSYAirStreamKit也轉(zhuǎn)發(fā)了此代理協(xié)議.
didStartMirroring 和 didStopMirroring 方法負責(zé)通知Airplay鏡像的啟動和停止。
mirroringErrorDidOcccur 負責(zé)通知當(dāng)前遇到的錯誤。
錯誤類型 | 說明 | 錯誤碼值 |
---|---|---|
KSYAirErrorCodePortConflict | 端口沖突, 請檢查airTunesPort/airVideoPort的配置 | 0 |
KSYAirErrorCodeNetworkDisconnection | 網(wǎng)絡(luò)未連接 | 1 |
KSYAirErrorCodeAirPlaySelectTimeout | 連接AirPlay超時 | 2 |
KSYAirErrorCodeConnectBreak | 連接斷開(網(wǎng)絡(luò)切換) | 3 |
KSYAirErrorCodeOther | 其他未知錯誤 | 4 |
2.5.2 推流狀態(tài)變化通知
推流的狀態(tài)變化和直播SDK中的內(nèi)容一致, 請參考wiki
KSYAirStreamKit 中已經(jīng)實現(xiàn)了基本的重連邏輯
三. Airplay 協(xié)議淺析
KSYAirStreamer SDK中的核心類KSYAirTunesServer實現(xiàn)了Airplay Screen Mirroring接收端的協(xié)議。
3.1 Airplay 錄屏SDK 難點分析
3.1.1 Airplay 協(xié)議文檔缺失
AirPlay是Apple的私有協(xié)議族, 沒有公開的官方文檔作為參考. 目前能夠參考的是github上nto大神整理的一份非官方的協(xié)議spec文檔, 但是完全照著文檔的描述是無法兼容新的iOS系統(tǒng)(iOS9/10)的, 新系統(tǒng)對協(xié)議有改變,而這份文檔的最后更新日期是2012年, 這些變更內(nèi)容都沒有包含。
3.1.2 Airplay 敏感數(shù)據(jù)加密的破解
有人會說Airplay畢竟是通過網(wǎng)絡(luò)交互的協(xié)議, 當(dāng)年 nto能夠整理出一份spec, 現(xiàn)在也可以通過抓包, 分析的方式得到新的交互過程, 缺少了文檔不過是增加一些破解協(xié)議的難度而已. 但是以保護用戶隱私著稱的Apple, 不會明文傳輸音視頻數(shù)據(jù)的. Airplay協(xié)議中有著比較完善的DRM保護, 傳輸?shù)囊粢曨l都是用AES算法加密過的, 而AES的key 則是在握手的過程中通過Fairplay協(xié)議保護的, 這一部分是Apple重點保護的內(nèi)容, 目前沒有人能真正破解。
3.1.3 單機Airplay 錄屏中音頻采集的坑
使用Airplay的方案做錄屏, 其實是接收端和客戶端在同一設(shè)備上的,我們可以稱之為單機Airplay. 正常的投屏過程中, 客戶端會把音頻投到接收設(shè)備上. 單機Airplay的話, 投屏的音頻, 按邏輯還得繼續(xù)投屏, 這時候就出現(xiàn)回環(huán)了. 因此錄屏是必須通過一定的手段避免通過airplay傳遞音頻, 強制保證音頻還是通過外放(Speaker)播放. 再加上直播時還需要采集主播的聲音. 這些與音頻相關(guān)的操作都和iOS的AudioSession相關(guān), 非常容易出現(xiàn)不穩(wěn)定的情況。
3.2 Airplay 協(xié)議可行性分析
Airplay的可行性主要是通過市面上已有的一些商業(yè)方案來保證的, 我們能看到reflector 2, 360 Mirroring 等收費的商業(yè)軟件, 還有小米電視等機頂盒設(shè)備都有一些成功案例. 因此一定有人攻克以上的難點。
3.3 Airplay 協(xié)議構(gòu)成
AirPlay并不是完全重新開始寫的一個協(xié)議, 是好幾個現(xiàn)有的協(xié)議的組合, 其中有的協(xié)議是完全標(biāo)準的, 有一部分協(xié)議進行了一些修改,有的則是完全私有的。
- Multicast DNS (aka Bonjour) 用于發(fā)布服務(wù), 啟動后, 在iOS的控制中心菜單中就能看到對應(yīng)的設(shè)備;
- HTTP / RTSP / RTP 用于流媒體服務(wù), 傳輸音視頻數(shù)據(jù), 進行播放控制等;
- NTP 時間同步;
- FairPlay DRM加密 完全私有的加密協(xié)議。
Airplay 在iOS上有很多種應(yīng)用場景, 可以單獨傳輸音頻, 可以傳輸照片, 幻燈片, 可以直接傳輸本地播放的視頻, 也可以直接投影屏幕內(nèi)容. 不同應(yīng)用場景協(xié)議交互的步驟也不同. 錄屏應(yīng)用主要是需要實現(xiàn)其中投影屏幕內(nèi)容的部分交互協(xié)議。
3.4 Airplay Screen Mirroring
Airplay 錄屏SDK內(nèi)部的主要流程如下:
啟動錄屏?xí)r, 通過iOS上的 NSNetService API 實現(xiàn)Airplay的服務(wù)的發(fā)布, 服務(wù)發(fā)布的txtRecod里的字段比較關(guān)鍵, 其中有些字段決定了后續(xù)交互的內(nèi)容, 比如加密方式等。
整個投影過程中有兩個主要的網(wǎng)絡(luò)連接, 接收端需要監(jiān)聽兩個端口(airTunesPort,airVideoPort)。
其中, 通過airTunesPort建立的TCP連接主要完成的是建連握手部分, 通過airVideoPort建立的TCP連接用于傳輸 H.264編碼的視頻數(shù)據(jù), 視頻數(shù)據(jù)是被AES加密過的。
3.5 Airplay 握手建連
建連握手部分是經(jīng)過修改的RTSP協(xié)議, 與HTTP協(xié)議比較類似, 都是客戶端(iOS系統(tǒng))發(fā)起請求, 接收端SDK針對請求內(nèi)容進行回應(yīng)的方式. 每個請求包括:方法+路徑+header+content等內(nèi)容。
主要的交互過程如下:
- 4次POST請求, 分別對應(yīng)的路徑為 /pair-setup, /pair-verify和兩次/fp-setup 這是開頭于FairPlay相關(guān)的核心加密部分, 任何一次POST的回復(fù)內(nèi)容不對都會導(dǎo)致握手失敗。
- SETUP請求 不同版本的iOS系統(tǒng)中, SETUP方法的次數(shù)可能不同.
在SETUP方法中能得到比較多的信息, 比如后面視頻AES解密需要用到的ekey和eiv. 接收端需要將自己的監(jiān)聽的接收端口airVideoPort回復(fù)給客戶端.這些信息需要對這次請求的content使用Apple的Binary plist格式進行解碼才能提取, 回復(fù)內(nèi)容也要編碼為Binary plist格式. 后續(xù)完成握手后, 客戶端會通過 airVideoPort 端口建立 TCP連接, 然后將屏幕畫面通過H.264編碼后再通過AES加密處理后, 向連接持續(xù)發(fā)送。 - 一次GET請求, 路徑為/info, 這里接收端需要回復(fù)一個Binary plist格式的數(shù)據(jù), 將用戶配置的畫面分辨率, 最大幀率等信息傳遞到客戶端。
- 可能有多次GET_PARAMETER和SET_PARAMETER請求用于調(diào)整音量大小。
- 一次RECORD請求, 表明握手完成, airplay鏡像開始。
- 在鏡像過程中會每秒都收到POST請求,路徑為/feedback, 相當(dāng)于心跳, 用于保持TCP的長連接。
3.6 Airplay Screen Mirroring 視頻數(shù)據(jù)傳輸
視頻數(shù)據(jù)傳輸是客戶端通過TCP 單方向的往接收端灌加密后的數(shù)據(jù). 數(shù)據(jù)內(nèi)容分為頭部和載荷. 頭部包含了載荷的類型, 長度, 時間戳等信息. 載荷部分有兩種, 一種是H.264的參數(shù)集, sps, pps等; 另一種就是AES加密后的H.264裸流, 沒有填充到封裝格式中, 用SETUP請求中得到的key和iv解密后,即可送到視頻解碼器中解碼。
4. 總結(jié)
以上主要匯總了一下當(dāng)前iOS平臺上錄屏的一些方案. 介紹了 基于Airplay Screen Mirroring的錄屏SDK - KSYAirStreamer 的基本結(jié)構(gòu)和使用方法。最后介紹了KSYAirStreamer中實現(xiàn)的Airplay協(xié)議的一些細節(jié)。希望能夠幫到有錄屏需求的同學(xué).。
KSYAirStreamer 的demo大家可以到 github 查看代碼和體驗功能。
轉(zhuǎn)載請注明:
作者金山視頻云,首發(fā)簡書 Jianshu.com
也歡迎大家使用我們的直播SDK:
iOS融合版(推流 + 播放):https://github.com/ksvc/KSYLive_iOS
有關(guān)音視頻的更多精彩內(nèi)容,請參考https://github.com/ksvc
金山云SDK相關(guān)的QQ交流群:
- 視頻云技術(shù)交流群:574179720
- 視頻云iOS技術(shù)交流:621137661