Android 自訂螢幕佈局¶
arctos links Android SDK 提供方便的自訂螢幕佈局功能。我們可以在程式碼中建立螢幕佈局,通訊的主持人只要呼叫一個簡單的方法即可即時完成切換。
自訂螢幕佈局¶
基本觀念¶
螢幕佈局是透過建立一個模板(VideoLayoutTemplate)實例來完成。在自訂的模板當中,我們會設定不同圖層(Layer),每個圖層則會指定繪製範圍(Rect),並且賦予圖層不同的標籤(Label),藉此指定要在此圖層上繪製哪個來源資料。SDK 會自動判斷資料來源類別,並根據模板中定義的圖層順序繪製到對應標籤的圖層上。
主持人若要切換螢幕佈局,只需要呼叫 ArctosLinks.ConferenceManager.hostSwitchScreenTemplate() 方法,傳入模板(VideoLayoutTemplate)的實例,即可改變螢幕佈局。更動後的螢幕佈局,將立即改變當前會議室所有人看到的畫面。
模板 (VideoLayoutTemplate)¶
「模板 (VideoLayoutTemplate)」 指影像的版面配置。當影像資料需要進行繪製時,它會依照 VideoLayoutTemplate 的設定,將資料繪製到畫面的指定範圍。
實作上 VideoLayoutTemplate 是個抽象類別。APP 必須自行設計出繼承自 VideoLayoutTemplate 的類別,複寫 layers 屬性。然後,將新類別的實體提供給 arctos links SDK 使用。此抽象類別的定義如下:
從上面的程式碼可以看出,實際上 VideoLayoutTemplate 抽象類別是多個「圖層 (layer)」的集合。實際上,每個圖層擁有自己的「繪製範圍 (Rect)」、「圖片縮放方式 (ScaleType)」與資料類別(YUV 或 RGB)的設定。
圖層 (Layer)¶
Layer 包含了兩個屬性,分別是代表了圖層繪製範圍的 area,以及圖片縮放方式的 scaleType:
關於繪製範圍,首先我們把整個畫布理解成一個 2D 的平面,而此平面的 x, y 軸範圍皆介於 -1 到 1 之間。這個平面的四個頂點為左下角(-1, -1)、右下角(1, -1)、左上角(-1, 1)、右上角(1, 1):

我們可以把圖層想像為不同大小的方形紙張,放置到這個平面上的指定位置。這個紙張所標示出來的方形區塊,就是我們的「繪製範圍(Rect)」。
繪製範圍 (Rect)¶
實作上,Rect 由四個浮點數屬性定義:
四個屬性分別代表 Rect 標示出的範圍的左、上、右、下的 x 軸或 y 軸座標。例如,以下這個 Rect 所標示出的範圍顯示如圖:

Warning
在創建 Rect 實體的時候,請確保 right 大於 left,top 大於 bottom。如果設定錯誤,可能會造成意料之外的翻轉或是變形現象。
在 Rect 類別中,也有一些預先定義好的 Rect 常數可以使用。大致上,你可以從其名稱了解它的大概位置和範圍。使用方式可以參考後面的範例說明。
val FULL_SCREEN = Rect(left = -1f, top = 1f, right = 1f, bottom = -1f)
val TOP_LEFT = Rect(left = -1f, top = 1f, right = 0f, bottom = 0f)
val TOP_RIGHT = Rect(left = 0f, top = 1f, right = 1f, bottom = 0f)
val BOTTOM_LEFT = Rect(left = -1f, top = 0f, right = 0f, bottom = -1f)
val BOTTOM_RIGHT = Rect(left = 0f, top = 0f, right = 1f, bottom = -1f)
val HALF_LEFT = Rect(left = -1f, top = 1f, right = 0f, bottom = -1f)
val HALF_RIGHT = Rect(left = 0f, top = 1f, right = 1f, bottom = -1f)
圖片縮放方式 (ScaleType)¶
想像一下,當我們要把輸入畫面繪製到圖層上時,若是輸入資料的長寬比,與圖層自己定義的範圍 Rect 擁有的長寬比不同時,我們就需要考慮圖片的縮放方式,以避免變形問題。
我們的 Layer 參考了 Android 的 ImageView ScaleType ,定義了以下五種縮放方式:
- CENTER_CROP:預設的縮放方式。視需要等比例放大輸入畫面的長寬,直到完全蓋滿
Layer範圍。輸入畫面的一部分可能會被截掉,畫面不會變形。 - FIT_XY:視需要延展、放大或縮小輸入畫面的長寬,直到完全蓋滿
Layer範圍。輸入畫面不會被截掉,但畫面可能變形。 - FIT_CENTER:視需要等比例縮小輸入畫面的長寬,直到輸入畫面全部都會顯示在
Layer範圍。若有部分Layer範圍無法覆蓋到,畫面會置中對齊。輸入畫面不會被截掉,畫面不會變形。 - FIT_START:視需要等比例縮小輸入畫面的長寬,直到輸入畫面全部都會顯示在
Layer範圍。若有部分Layer範圍無法覆蓋到,畫面會靠左側或上側對齊。輸入畫面不會被截掉,畫面不會變形。 - FIT_END:視需要等比例縮小輸入畫面的長寬,直到輸入畫面全部都會顯示在
Layer範圍。若有部分Layer範圍無法覆蓋到,畫面會靠右側或下側對齊。輸入畫面不會被截掉,畫面不會變形。
Info
一般視訊通訊中,以 CENTER_CROP 與 FIT_CENTER 兩種縮放方式最為常見。
圖層順序性¶
圖層加入的順序會決定每個圖層繪製的順序。最先加入的圖層會最早被繪製。然而,若圖層的範圍有所重疊,則先繪製的圖層會被後繪製的圖層給蓋過去。
實作上,在 VideoLayoutTemplate 的 layers 屬性,實際上是個有順序性的列表(LinkedHashMap)。因此在覆寫此欄位的時候,請把要先繪製的圖層(例如背景)放在列表的前面,要比較後繪製的圖層放在列表的後面。更多使用方式,請參考後續章節的範例。
圖層資料格式¶
每個圖層只能接受一種格式的輸入,它可以是 RGB 格式或是 YUV 格式。我們定義了以下兩種 Layer 的延伸類別:
class YuvLayer @JvmOverloads constructor(area: Rect, scaleType: ScaleType = ScaleType.CENTER_CROP) : Layer(area, scaleType)
class RgbLayer @JvmOverloads constructor(area: Rect, scaleType: ScaleType = ScaleType.CENTER_CROP) : Layer(area, scaleType)
在設計自己的 VideoLayoutTemplate 時,請根據實際需求決定該處圖層是要接受哪種資料格式。
在 arctos links SDK 中,若該圖層要顯示相機或視訊串流,會使用 YuvLayer。若要顯示白板筆畫,則會使用 RgbLayer。
標籤 (Label)¶
在創建 VideoLayoutTemplate 時,要分別賦予每個圖層一個對應的字串標籤。例如以下的自訂模板:
class VideoLayoutFullScreen : VideoLayoutTemplate() {
override val layers: LinkedHashMap<String, Layer> = linkedMapOf(Label.LOCAL to YuvLayer(FULL_SCREEN, CENTER_CROP))
}
這個模板就是將標籤 Label.LOCAL 對應到一個 YuvLayer。
在 SDK 中,我們根據現在的視訊會議室場景,定義了以下五種標籤。圖層必須指定對應到這五個標籤的其中一種:
Label.LOCAL主持人本地的畫面,通常是手機的前、後相機畫面Label.SOURCE_B會議室中的第一位參與者Label.SOURCE_C會議室中的第二位參與者Label.SOURCE_D會議室中的第三位參與者Label.WHITEBOARD白板畫筆繪製的圖層
特殊圖層:白板圖層¶
白板圖層定義了畫筆功能作畫的位置。在使用上,必須遵守兩個規則:
- 標籤(
Label)必須是Label.WHITEBOARD - 圖層本身必須是
RgbLayer
白板圖層的使用方式可以參考復面的範例。
範例¶
以下方式會建立一個全螢幕模板:
class VideoLayoutFullScreen : VideoLayoutTemplate() {
override val layers: LinkedHashMap<String, Layer> = linkedMapOf(
Label.LOCAL to YuvLayer(FULL_SCREEN, CENTER_CROP)
)
}
以下方式將畫面切分成左、右兩邊:
class VideoLayoutDivided : VideoLayoutTemplate() {
override val layers: LinkedHashMap<String, Layer> = linkedMapOf(
(Label.LOCAL to YuvLayer(HALF_LEFT, CENTER_CROP)),
(Label.SOURCE_B to YuvLayer(HALF_RIGHT, CENTER_CROP))
)
}
以下方式建立一個「畫中畫」模板, 自己的畫面在左上角的一個小區塊,通訊對方的畫面佔據整個背景,並且加入白板圖層。
Warning
注意此模板的圖層順序。我們希望 LOCAL 小畫面可以疊在 SOURCE_B 上方,因此 LOCAL 圖層在比較後面的位置。然後因為白板圖層的筆畫會在所有畫面的最上方,因此 WHITEBOARD 圖層在最後面的位置。
class VideoLayoutViewInView : VideoLayoutTemplate() {
override val layers: LinkedHashMap<String, Layer> = linkedMapOf(
(Label.SOURCE_B to YuvLayer(Rect.FULL_SCREEN, Layer.ScaleType.FIT_CENTER)),
(Label.LOCAL to YuvLayer(
Rect(left = -1.0f, top = 1.0f, right = -0.3f, bottom = 0.3f),
Layer.ScaleType.CENTER_CROP
)),
(Label.WHITEBOARD to RgbLayer(Rect.FULL_SCREEN, Layer.ScaleType.FIT_CENTER))
)
}
切換螢幕佈局¶
在完成自訂模板後,主持人可直接使用 ArctosLinks.ConferenceManager.hostSwitchScreenTemplate() 方法,傳入一個定義好的模板(VideoLayoutTemplate)之實例,即可改變會議室中所有人看到的螢幕佈局。
import com.arctos.sdk.links.core.application.ArctosLinks
private fun hostSwitchScreenTemplate(layout: VideoLayoutTemplate) {
runCatching {
ArctosLinks.getInstance(Context)
.conferenceManager
.hostSwitchScreenTemplate(layout)
.getOrThrow()
}.onSuccess {
Log.d(TAG, "Successfully switched screen template")
}.onFailure {
Log.d(TAG, "Failed to switch screen template: ${it.message}")
}
}