iOS 手游直播方案比較及Airplay錄屏詳解

手游直播是直播行業(yè)中非常重要的一個垂直領(lǐng)域. 手游直播與其他移動直播相比主要是畫面的來源不同, 手游直播其實是一種錄屏技術(shù). 游戲玩家在玩游戲的同時將畫面內(nèi)容實時地分享展示給其他觀眾, 在配上玩家自己的語音, 能夠給觀眾帶來比較有趣的觀看體驗. 手游直播與PC端游戲直播相比主要是設(shè)備的計算能力不同。
PC游戲直播有OBS等強大的第三方直播軟件, 加上PC強勁有富余的計算能力, 使得PC游戲直播的門檻相對較低。
手游直播中由于Apple對個人隱私和安全性的重視, iOS手游直播相對于Android手游直播的難度又大了很多。

一. 現(xiàn)有iOS錄屏方案分析

為了解決iOS手游直播中視頻數(shù)據(jù)采集的難題, 主要存在以下三種方案:

  1. 通過私有API獲取系統(tǒng)的IOSurface
    這種方法效率比較高, 但是從iOS9 開始, 這個私有API的漏洞就被Apple堵上了, 新系統(tǒng)中無法使用;

  2. Airplay Screen Mirroring;

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, 即使成功上架, 不久也會被強制下架。

  1. ReplayKit
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

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

KSYAirStreamKitKSYAirTunesServer和金山云直播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)部的主要流程如下:

airplay_flow

啟動錄屏?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)容。

主要的交互過程如下:

  1. 4次POST請求, 分別對應(yīng)的路徑為 /pair-setup, /pair-verify和兩次/fp-setup 這是開頭于FairPlay相關(guān)的核心加密部分, 任何一次POST的回復(fù)內(nèi)容不對都會導(dǎo)致握手失敗。
  2. 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ā)送。
  3. 一次GET請求, 路徑為/info, 這里接收端需要回復(fù)一個Binary plist格式的數(shù)據(jù), 將用戶配置的畫面分辨率, 最大幀率等信息傳遞到客戶端。
  4. 可能有多次GET_PARAMETER和SET_PARAMETER請求用于調(diào)整音量大小。
  5. 一次RECORD請求, 表明握手完成, airplay鏡像開始。
  6. 在鏡像過程中會每秒都收到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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,892評論 2 372