Flutter 實時視頻渲染:Texture與PlatformView

前兩期 RTC Dev Meetup:Flutter 開發技術專場 的回顧視頻已被上傳至了 RTC 開發者社區,歡迎你們瀏覽與交流。 咱們整理了其中一個演講內容——由聲網 Agora 高級架構師 張乾澤分享的「Flutter 實時音視頻實踐」。如下爲演講實錄。緩存

我今天給你們介紹的是 Flutter 實時音視頻開發實踐方面的經驗。我叫張乾澤,畢業於英國牛津大學,以前在 SAP 主要擔任移動端架構師,2017年加入聲網Agora,目前是聲網Agora 的高級架構師,主要負責 RTC 技術在娛樂、直播、教育等行業的應用研發工做。架構

首先咱們講到 RTC 應用,什麼樣是 RTC 應用呢?RTC 是 Real-time Communications。你們平時在平常生活中已經接觸到很是很是多的RTC應用,好比說直播,你們平時會用手機看熊貓直播、鬥魚直播,實時看主播的遊戲畫面、他們唱歌的畫面、跳舞的畫面。還有教育,經過互聯網能夠實現遠程教育,好比學生和美國的老師能夠直接進行實時互動。框架

在醫療方面,咱們最近也看到不少的案例。之前你要到醫院掛號排隊,等一上午,醫生看一下,而後明天再來複診時從新掛號,又是一個上午的時間過去了。如今有了 RTC 後,你能夠經過視頻直接在家裏看醫生,醫生在視頻中問你一些問題,若是病症簡單,你甚至都不須要去醫院。ide

今天咱們就拿常見的視頻通話的應用,來看看如何基於 Flutter 來開發實現,以及其中會有什麼樣的困難。性能

架構邏輯與實現思路

首先講一下 Flutter 的架構邏輯。若是比較熟悉 Flutter 的朋友可能會知道,它的渲染方式跟 React Native 最不同的地方就是它對於本身的一些原生控件,沒有利用系統自己提供 View。那麼開發應用的時候,怎麼渲染出來的呢?優化

它本身裏面會創建一個 Layer Tree,Layer Tree 裏面是它的一個樹狀結構,它把數據或者渲染的地方發到 Skia,Skia 獲得 GPU 的 Vsync 信號的時候,再把這些數據傳給 GPU 統一渲染。徹底沒有經過相似 UI View 這樣的 Native 的 View 的。編碼

在知道這個概念以後,我就開始着手開發這個應用。這個應用由於它自己就提供了一些經常使用的組件,好比 App 的一個框架,一些按鈕和導航欄,Flutter 有現成的組件可讓你直接把這些東西畫上去,只須要經過一些很是簡單的配置便可。設計

Flutter 自己也提供了與 Native SDK 的通信的方式,叫作 Channel Massage,跟 React Native 調用 Native 的思路很是像。因此咱們有了聲網 Agora SDK,會幫咱們處理音頻編解碼、音頻的傳輸,而後咱們能夠利用 Flutter 自己的組件把 UI 也畫好,好像這個東西就 Work 了。當一切都彷佛變得那麼美好的時候,我卡在了視頻上。Flutter 怎麼渲染視頻?3d

在 Native 平臺都有系統組件來渲染視頻,但 Flutter 沒有這樣的東西。我如今要把實時視頻渲染出來。orm

因而我查看了 Flutter 的源碼,其中有一個組件叫 Video player,這是放視頻的。其中有一個 Texture Widget,它在這種模式下提供了一種機制,可讓你進行視頻的渲染。

實現思路一:Texture Widget

首先視頻是由一幀幀圖像組成的。Flutter 的 Texture 提供了一個能夠放在 Layer Tree 裏的組件,組件中的數據源須要由你經過 Native 端來提供。

在這裏以 iOS 爲例,iOS 就須要提供 CVPixelBufferRef。這是一個數據,對應的就是視頻中的一幀畫面。把這個數據做爲數據源提供給Texture Widget,而後Texture Widget就能夠把你提供的這些數據顯示出來,最終就變成了一個視頻。

上圖是大體的實現架構。首先要在 Dart 上實現一個 Native 的 Plugin。Plugin Registar是Plugin的一個入口,有一個屬性叫作 TextureRegistry,你能夠在這裏註冊一個你本身的Texture類,這個類可能實現了一個協議叫Flutter Texture。你實現這個協議過程當中有兩個方法須要實現:一個方法叫作 CVPixelBufferRef,就是要提供數據源到你的Texture。另外一個方法你須要本身主動去調,叫作textureFrameAvailable,它的做用是告訴 TextureRegistry 如今能夠根據提供的數據來更新畫面了,就是我提供數據。

其中有一個問題,如今有 TextureWidget,若是更新數據源的話,它怎麼才能知道我要更新哪一個Texture呢?其實,在註冊這Texture的時候,它會返回一個 TextureID。你在建立 Texture Widget 的時候須要把這個 TextureID 傳進去。這樣的話 Texture 的組件就會跟你提供的這個實踐進行綁定,知道你提供的數據源就是爲這個 Texture 提供的數據。以此類推,咱們也能夠有不少個 Texture。

回到咱們以前的設計來看(如上圖),咱們這邊是一個視頻通話應用,咱們有不少本地的視頻,有不少的遠端的視頻,這些視頻均可以是不一樣的 Texture Widget,放在Dart的頁面容器中,而後咱們分別爲它們提供數據源,進行數據渲染。

是否是感受如今一切準備就緒了?其實還有一個問題。Flutter官方的 Github 提供了一些示例代碼,可是它提供的功能是讓你播放一個靜態緩存,咱們這裏提供的是一個實時的視頻流,不是一個視頻文件。不可能以讀取一個文件的方式,把裏面的數據一幀一幀地轉化以後,傳給 Texture Widget。咱們須要有一個方法拿到這些實時的數據,把它做爲數據源傳給Texture。

這時候咱們就須要使用聲網Agora SDK。首先給你們普及一個概念,你們想象一下平時在看一個直播的時候,直播那邊有一個攝像頭,他玩一些遊戲,他跳一些舞、唱一些歌,是怎樣讓我在手機裏面看到這些數據的呢?總結來說是一個很是簡單的流程,如上圖所示。首先主播有一個攝像頭,進行數據的採集。這些數據是裸數據。在拿到這些數據後,咱們須要進行編碼。在編碼過程當中,咱們能夠對數據進行處理和格式化,好比說讓它有一個特定的分辨率和幀率。直播不會把攝像頭的全部的數據都傳過來,他會對數據進行壓縮、處理,這就是編碼的過程。在編碼完成後,咱們就有一個視頻數據,它會經過聲網的雲服務,快速傳輸到你的手機上。手機客戶端上也有聲網的 SDK,在拿到數據後,會把數據進行解碼,並提供一個方法,將這些視頻數據傳到UI View 上,最終顯示爲你所看到的視頻。

如上圖所示,基於剛纔的架構,咱們添加了一個聲網 SDK。SDK 有一個方法叫作 AgoraVideoSink。它提供了一個回調,會把收到的全部視頻數據按照你想要的格式傳給你。咱們在獲得回調數據後,再把他傳給Flutter Texture。咱們能夠設定一個更新頻率,每當獲得一次回調後,經過 notify 告訴 TextureRegistery 將新的數據更新到 Texture。經過這樣的一個過程,咱們就能實現一個 Flutter 的實時音視頻 App 了。

實現思路二:PlatformView

因爲 Texture 會涉及到不少渲染的流程,因此不少人都會以爲它有些複雜。因此在 Flutter 1.0版本中,Google 給出了一個新的東西,叫作 PlatformView。

它提供了一種方法,讓咱們能夠建立 UI View,並加到 Dart 的 LayerTree 裏。在 Dart 中的類對應到 iOS 和 Android 平臺分別是UIKitView 和 AndroidView。

PlatformView 該怎麼用呢?在 PluginRegistar 中新增了 ViewFactory,ViewFactory 只有 CreateView 這一個方法須要實現。你能夠在這個方法裏首先提供一個 Identifier,在實現該方法後,能夠返回一個你想要的 PlatformView,並與 Dart 組件綁定在一塊兒。

由於咱們的 SDK 支持傳遞 Native 的 View,而後將視頻渲染到上面。如上圖,咱們基於剛纔的架構作一下改動。聲網 SDK 中提供了一個 CustomViewFoctory,它的做用就是建立一個原生的 View,而後 SDK 的 AgoraRtcEngine 會與 View 綁定,在收到數據流的時候會自動將它渲染到這個 View 上。由於每一個 View 會對應一個 ViewID。那麼咱們只須要經過 ViewID 就能夠獲得這個 View,而後把它渲染出來。

性能對比

總結一下,在 Flutter 中實現實時音視頻有兩種方式,一種是 Texture,另外一種是 PlatformView。咱們將它們與 Native 的實現方式進行了性能對比,結果以下圖所示。

三者相比,Texture 的實現方式性能比較差。PlatformView 的性能與 Native 相近。就目前的 Demo 來看,形成Texture 的方法性能較差的緣由可能有兩個:

第一,在收到視頻數據以後,會馬上更新 Texture,但這樣作的必要性不大,若是進行一些優化的話,還能夠提高一些性能;

第二,咱們是經過一個回調來獲取數據,而後將它提供給 Flutter 的 Texture,這個過程是經過 CPU 來完成的,但一般來說,渲染的工做須要由 GPU 來作,因此數據從 GPU 到 CPU 又再到 GPU,這個過程很是耗費資源。

總結一下,我我的對於 Texture 和 PlatformView 的優缺點比較。我我的以爲Texture這個東西實現「更Dart」,什麼叫「更Dart」?就是沒有不少原生的參與,好比 UI View ,更符合 Dart 的生態環境,在設計上更加純粹。可是它也有缺點。若是用它作一些事情,不作特殊處理的話,可能很難避免 CPU、GPU 中間數據拷貝,形成性能損失。若是你要用它去作一個地圖,那你就得把地圖數據渲染放在Flutter上,用Flutter的邏輯所有從新實現一遍。這個對於大多數開發者來講,對於正在蓬勃發展中的一個生態來講是很是不利的。因此我我的認爲這也是 Google 在 Flutter 1.0加入了PlatformView 的緣由。

使用 PlatformView 有什麼優勢呢?我剛纔說了,它能夠把以前實現 Native View 的功能直接放在Dart裏。對於開發者來講,更加簡單易用。也能夠避免前者存在在 CPU、GPU 數據拷貝帶來的性能損失問題。但問題,它在設計上不那麼純粹,會引入一下不可控的因素。


詳細 PPT 與回顧視頻,請點擊這裏獲取

相關文章
相關標籤/搜索