扣丁書屋

Flutter 音視頻開發新思路

引言

音視頻功能因為需要依賴于 GPU 或其他系統底層軟硬件,來計算處理和展現結果,所以相關技術總是相對偏向于底層,包括音視頻的基礎框架,乃至代碼功能邏輯也一般都沉淀在底層。我們可以在各個系統平臺構建我們的音視頻功能和應用界面,但是 Flutter 是系統平臺之上的跨平臺框架,似乎天然與偏底層的音視頻有著一定的隔閡,所以在 Flutter 上開發音視頻,似乎會陷入到了僅能開發音視頻界面,而無法更深入一步的困境。

Flutter 音視頻開發的困境

我們希望通過 Flutter 統一各端的界面 UI 和功能邏輯,Flutter 不僅僅能夠用來構建界面,也能夠用于中間層功能邏輯的跨端,這樣做的好處不言而喻,不僅 UI 界面在多端是統一的,功能邏輯在多端也是統一的,這樣能有更好的功能一致性和可維護性。閑魚在這方面也做了很多實踐和努力,比如對消息業務的架構升級,抹平了 IM 場景雙端邏輯的差異,提升了整體穩定性和研發效率,具體可見《Flutter IM 跨端架構設計和實現》。

圖示為 Flutter 音視頻的分層結構圖

在音視頻方面,我們也希望音視頻的相關功能邏輯能上移到 Flutter 進行統一開發,但是由于音視頻的底層依賴,目前大量的音視頻功能邏輯都還是實現在 Native 或者更底層,僅通過 Plugin 傳輸指令和數據,通過 External Texture 將紋理外接到 Flutter 展示,Flutter 僅僅用于構建界面。所以我們不得不面臨一個尷尬的現狀:雖然通過 Flutter 技術,我們能夠做到音視頻模塊界面的統一,但功能邏輯依然分散在各端底層,需要分別開發維護,不僅研發效率不高,各端功能表現也容易不一致。

為了解決這個問題,將更多的功能邏輯上移到 Flutter,讓 Flutter 音視頻的也能做到邏輯跨端,我們需要一種新的思路。

新思路

任何一個功能模塊,都是由更細粒度的子模塊構建出來的。例如一個拍攝功能,我們可以拆分出相機、麥克風、幀處理、音頻處理、預覽、拍照、視頻錄制等子功能模塊,每個子模塊都承擔一定的職責,對外透出屬性和操作接口,和其他模塊配合處理數據和邏輯,最終構建出完整的功能。如果要在 Flutter 中實現這個拍攝功能,一般是在底層串聯實現這些功能,將視頻幀作為紋理外接到 Flutter 側展示,Flutter 側構建界面,并通過 MethodChannel 下發指令進行拍照、視頻錄制等操作控制。

這時候我們想到,如果子模塊能夠被 Flutter 側操作控制,是否能夠直接在 Flutter 側完整構建出整個功能呢?

為了方便地控制底層音視頻子模塊,我們需要在 Flutter 側建立起 Native 子模塊的映射,即在 Flutter 側存在一個和 Native 一樣的對象,Native 側子模塊的屬性和接口方法都會映射到 Flutter,我們稱這樣的子模塊為節點(Node)。Native Node 和 Flutter Node 之間需要有一條數據傳輸通道,對于 Flutter Node 的操作,可以通過數據通道通知到 Native 進行實際的響應。由于音視頻的實際處理和操作是在 Native 底層的,因此數據也是產生在底層的,在早期的想法中,我們希望 Native 產生的數據(例如視頻幀)可以上拋到 Flutter,Flutter Node 接收到數據進行一些邏輯處理之后再交給底層 Native Node 處理,如下圖所示(空心箭頭代表指令消息傳輸,實心箭頭代表音視頻數據傳輸)。

雖然理論上通過 FFI 也可以做到,但是頻繁的數據通信還是可能會存在性能問題和不穩定因素,同時我們也意識到,內存數據僅在底層流通是更合理的,Flutter 側只需關注邏輯結果,因此 Node 之間的通信結構簡化為下圖所示。

構成音視頻功能模塊的子模塊稱之為音視頻元能力節點(Node),負責實現特定的音視頻能力,這些元能力需要提前實現和注冊;根據不同的功能需要,使用不同的元能力節點構建音視頻管線(Pipeline),節點之間遵循一定的規則進行連接和標準數據傳輸;音視頻管線(Pipeline)在 Flutter 側進行構建,最終在 Native 側實際生成真正的管線和節點連接。通過在 Flutter 側使用不同的音視頻元能力,構建不同的管線,并對管線和節點進行操作控制,即可在 Flutter 側實現出不同的音視頻功能。

總結起來,基于這種思路,我們需要做到以下幾點:

  • ? 合適的元能力抽象和功能映射(Flutter <-> 底層)
  • ? 高效地數據通信(Flutter <-> 底層)
  • ? 管線構建與狀態管理機制
  • ? 節點連接與數據流轉標準

由此我們沉淀了 Flutter 的流式音視頻開發框架 PowerMedia。

PowerMedia

PowerMedia 分為 Core 核心框架和 Node 元能力節點兩部分,下圖是 PowerMedia 的整體架構圖。

在核心框架中,PowerMedia 實現了對管線構建和狀態管理的支持,并建立了高效的通信通道,支持 Flutter 和 Native 之間 Node 以及 Pipeline 的通信,異步消息傳輸使用 Flutter 的 MethodChannel,針對部分需要同步通信的場景,我們也基于 FFI 實現了高性能的同步通信通道 PowerPlatformChannel。

音視頻元能力節點(Node)的抽象與實現,則與最終功能的底層實現密切相關。節點可以分為生產型(Producer)、消費型(Consumer)和生產消費型(Producer and Consumer)三種,分別代表了節點本身是生產數據、消費數據還是處理數據。我們按照 MIME 規范對管線中傳輸的底層數據進行了標準化收斂,節點需要聲明支持的數據類型,只有類型匹配的節點才能互相連接。實際代碼中,對于視頻幀數據,在 iOS 中流轉的是 CVPixelBuffer 的封裝對象,而 Android 中是 OpenGL Texture 的封裝對象,框架也管理了底層數據的生命周期。Node 在 Flutter 側透出的屬性和接口,可以讓我們以“面向對象”的方式進行音視頻功能編碼。

PowerMeida 中 Flutter Node 和 Native Node 的關系,可以類比為 Flutter 中 Widget 與 Element/RenderObject 的關系,或者 Weex/RN 中組件標簽與 Native 中組件實際 View 的關系。Flutter Node 的創建與使用僅僅是一種邏輯描述,真正的功能需要 Native Node 來實現。我們通過 PowerMedia 在 Flutter 中構建 Pipeline 管線圖,這會在 Native 實際創建管線圖,構建這兩張圖的過程也可以類比為 Flutter 中構建三棵樹(雖然兩者不完全等價,但是思想接近)。

簡而言之,我們在 Flutter 通過構建 Pipeline 管線圖實現功能框架,通過對 Pipeline 和 Node 的功能邏輯編碼,實現具體的功能。

下圖顯示了 PowerMedia 框架在整個音視頻架構中的位置,針對特定的音視頻功能,我們可以實現和沉淀相應的音視頻元能力節點,而更多、更穩定的元能力節點,有助于我們更方便地在 Flutter 實現更多的的音視頻功能,這也是一種正向的生態演進。

拍攝實踐

我們以上文提到的拍攝的例子,來說明我們如何通過 PowerMedia 在 Flutter 實現拍攝功能。

一個基本的拍攝功能,需要可以預覽相機畫面,可以拍照和錄制視頻,當然也可以有一定的濾鏡、美顏能力?;谶@些能力要求,我們需要對拍攝模塊進行拆分,抽象出完成這些功能所需的音視頻元能力:

  1. 相機(Camera):負責采集視頻畫面
  2. 麥克風(Microphone):負責采集音頻數據
  3. 裁剪(Cropper):負責裁剪視頻幀,調整畫幅
  4. 渲染(Renderer):對視頻幀進行處理,實現濾鏡、美顏等能力
  5. 預覽(Previewer):負責視頻幀上屏預覽
  6. 幀保存(FrameSaver):負責將視頻幀保存到本地,實現拍照功能
  7. 視頻錄制(VideoRecorder):負責將視頻幀和音頻幀保存寫入到視頻文件

將上述音視頻元能力按照 PowerMedia 的接口規范實現為元能力節點(Node),并注冊給 PowerMedia。

下面簡單以 Camera 和 Renderer 節點為例看下 Flutter 側部分代碼:

基于上述元能力節點,可以在 Flutter 側構建出如下的 Pipeline 管線圖:

部分 Pipeline 構建代碼如下:

如果要實現的拍攝模塊,不需要濾鏡、美顏等功能,我們只要不添加和連接 Renderer 節點即可,那就會構建出另外一張 Pipeline 管線圖。

構建完 Pipeline 之后,我們通過操作 Pipeline 來控制整個功能模塊的生命周期狀態,通過操作相應節點的接口方法,實現特定的功能邏輯如開關閃光燈、添加濾鏡、拍照、錄像等。例如下圖即 Flutter 側封裝的利用 Renderer 節點來應用濾鏡的方法:

通過 PowerMedia,我們在 Flutter 實現了拍攝模塊的功能邏輯,當然界面部分還需要額外編碼實現。

總結起來,通過 PowerMedia 構建拍攝功能,我們得到了如下收益:

1 . 沉淀了多個可復用的音視頻元能力節點。新功能需求需要的元能力 Node 是需要從零實現的,但是已經實現后的元能力節點,可以沉淀下來,因為其職責單一,可以復用到其他功能模塊的構建中。例如我們在圖片編輯功能模塊的實現中,就復用了 Cropper、Renderer、FrameSaver、Previewer 這幾個 Node,大大減少了開發工作量,提升了效率。后續其他功能需要使用相機或者麥克風能力,也可以直接復用 Node 能力。

2 . 拍攝功能邏輯上移到了 Flutter,原先需要在各端分別開發的功能邏輯,只需在 Flutter 開發即可,既提升了開發效率,也能夠保證邏輯表現上的一致性,也有利于后續功能維護。

3 . 開發體驗上更加友好,對于 Node 的操作更加符合面向對象的編程體驗。

最后提一下,實現一個音視頻功能模塊所需的元能力的抽象是沒有標準答案的,理論上來說我們可以將整個功能全部實現在底層,將其包裝成一個元能力節點注冊給 PowerMedia,但顯然這樣做的話,沒有太大意義。同樣的,我們可以非常細粒度的拆分音視頻基礎能力,例如將 Renderer 節點再細分為 Makeup 節點和 Filter 節點,單獨負責美顏功能和濾鏡功能;將視頻錄制細分為視頻編碼、音頻編碼、合流和視頻文件保存節點。

理論上,更細粒度的元能力節點,更符合單一職責的原則,穩定性更好,可復用性更強,功能解耦的同時,也賦予了 Flutter 側邏輯對于整個功能模塊更大的控制力,更能體現 PowerMedia 的優勢。但實踐過程中,元能力節點的粒度需要綜合考慮成本、性能和穩定性,來選擇最佳的方案。

結語

音視頻在現代 App 中越來越重要,對于 Flutter 開發來說,音視頻場景也是不可或缺的一環,功能邏輯跨端與 UI 跨端同樣重要,這樣才能做到真正的跨平臺開發,提升研發效率。文中提到的技術框架 PowerMedia 已經在閑魚多個場景應用落地,我們也還在不斷完善演進中,如何能使用 PowerMedia 解決更多的 Flutter 音視頻場景問題,提升框架的整體性能和穩定性,是我們后續重點演進和提升的方向,同時我們也在不斷沉淀高質量的音視頻元能力 Node,豐富節點生態。希望通過本文,能給大家做 Flutter 音視頻帶來一些不一樣的視角和啟發。


https://mp.weixin.qq.com/s/ZpHfkz8CQmPhKY4--WYTWw

最多閱讀

如何有效定位Flutter內存問題? 1年以前  |  11632次閱讀
Flutter的手勢GestureDetector分析詳解 2年以前  |  7522次閱讀
Flutter插件詳解及其發布插件 2年以前  |  6228次閱讀
在Flutter中添加資源和圖片 3年以前  |  5086次閱讀
Flutter 狀態管理指南之 Provider 2年以前  |  4286次閱讀
發布Flutter開發的iOS程序 3年以前  |  4264次閱讀
Flutter for Web詳細介紹 2年以前  |  4135次閱讀
在Flutter中發起HTTP網絡請求 3年以前  |  3901次閱讀
使用Inspector檢查用戶界面 3年以前  |  3823次閱讀
Flutter Widget框架概述 3年以前  |  3229次閱讀
Flutter路由詳解 2年以前  |  3064次閱讀
JSON和序列化 3年以前  |  3005次閱讀
Flutter框架概覽 3年以前  |  2960次閱讀
推薦5個Flutter重磅開源項目! 1年以前  |  2928次閱讀
為Flutter應用程序添加交互 3年以前  |  2910次閱讀
處理文本輸入 3年以前  |  2813次閱讀
使用自定義字體 3年以前  |  2808次閱讀
編寫國際化Flutter App 3年以前  |  2804次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>