文 / Grey Skold
譯 / 屈健甯
原文
https://medium.com/pinterest-engineering/managing-videos-on-android-f59da9601d5f2016年Pinterest安卓應用上釋出的視訊子產品,其目标是使得應用能夠提供無縫的視訊體驗。包括支援在每個螢幕上同時播放多個視訊,并且通過滾動出螢幕自動暫停播放的方式來動态地控制視訊的播放狀态以及同時播放的視訊數量。
很快我們發現其實需要應對的技術挑戰有很多,例如:
- 管理目前所有可用視訊的播放狀态
- 了解視訊在螢幕上的可見率
- 為我們的開發人員提供易于使用的視訊元件
随着工作的進行,我們逐漸調整視訊架構來滿足這些需求,下面我們将在最新的視訊子產品中深入探讨如何應對這些挑戰。
視訊管理
從更高的層次上來看,我們需要建構一個元件,這個元件需要感覺螢幕上所有可用的視訊執行個體(即視圖)以及其相關的surfaces(即視訊片段)。管理surfaces對于監控應用于surfaces的子對象的生命周期狀态(即onStart()等)至關重要,并且避免在使用者層上添加過多代碼來将最新狀态更改應用到視圖。
為了跟蹤這些關鍵的生命周期事件,Android架構向我們提供了螢幕顯示内容的目前狀态以及視覺上影響我們應用程式的任何更改。我們監測的關鍵生命周期事件是UI附件調用(例如onAttachedToWindow())以及主機螢幕何時更改其顯示狀态(例如onPause()等)。
使用這些回調方法,我們嘗試記錄已提供有效視訊URL的所有視訊。這将為我們提供目前範圍内可用的視訊的初始清單。
在視訊架構的第一個疊代中,我們依靠用戶端代碼本身調用這些調用,但是我們發現這是不可擴充的。因為它在建構視訊功能時增加了更多的複雜性。取而代之的方法是,我們通過建構需要傳入基礎視訊元件的方法,提取了在VideoManager之後注冊視訊的回調方法。從那裡,VideoManager将在幕後進行适當的計算。由于它現在才可以“開箱即用”地工作,是以消除了觀衆對視訊記錄過程已經具有預定義知識的需求。
改進前
// FooBarFragment.class for FooBar feature
override fun onResume() {
super.onResume()
// Required by consumers to implement
videoView?.apply {
viewability = Viewability. FullyVisible
onActivate()
onViewCompletelyVisible()
}
}
override fun onPause() {
// Required by consumers to implement
videoView?.apply {
viewability = Viewability. NotVisible
onDeactivate()
}
super.onPause()
}
改進後
// BaseFragment.class implemented by all screens
override fun onResume() {
super.onResume()
videoManager.onResume( this)
}
override fun onPause() {
videoManager.onPause( this)
super.onPause()
}
// VideoManager.class internally
override fun onResume(videoSurface: VideoViewSurface) {
videoSurface.videoViews.forEach {
registerVideo(it)
}
}
override fun onPause(videoSurface: VideoViewSurface) {
videoSurface.videoViews.forEach {
unregisterVideo(it)
}
}
保留這個視訊清單讓我們可以根據應用程式的目前可見性來動态地設定播放狀态。同時這個方法還提供了基于在視訊記錄時傳遞的某些中繼資料屬性動态更改之類其他功能的靈活性。
例如,我們可能希望所有視訊廣告都自動播放,但僅限于在同一片段上自動播放1個有機視訊(即創作者生成的内容)。通過檢查在單個視訊上記錄的中繼資料,我們可以将這些限制應用于UI層。
我們還提取了所有Pinterest特定的分析代碼,用以來聚焦在視訊管理器(管理和播放視訊)功能上,同時讓這個管理元件和應用程式之間保持獨立。
計算可視性
可視性定義為在螢幕上顯示的UI元件的可見區域的百分比。此度量對于我們了解目前顯示給使用者的内容至關重要。有了這些資訊,我們就能為合作夥伴收集有關其内容參與度的資訊。
在常見情況下,由于VideoManager保留對所有活動視訊的引用,是以我們可以跟蹤視圖的确切坐标(即getLocationInWindow())和裝置的螢幕尺寸(以像素為機關)(請參見DisplayMetrics),以推斷其在螢幕上的可見性。
我們還通過以下方式處理重疊的UI元件:
- 向消費者提供包括一系列``障礙物’'視圖的選項,這些視圖可能會覆寫我們的基礎視訊(例如工具欄,浮動按鈕等)
- 顯示彈出視窗的回調(即onWindowFocusChanged())螢幕滾動元件或UI元件不在螢幕上(請參閱RecyclerView監聽器)
- 螢幕上顯示視訊表面時的其他回調(即onResume()等)
為開發人員打造的内容
雖然我們希望減少開發人員面臨的視訊管理複雜性,但是這其中最大的困難就是采用新的視訊界面。是以,我們都抽象出了視訊設定的複雜性以及Google PlayerView提供的UI元件的使用:
// FooBar video feature, requires custom FooBarVideoView.class of 100+ lines
object : FooBarVideoView(
context, // application context
analytics, // Analytics object
url, // video url
uid, // unique ID
false // isAd flag
) {
// configuration flag for custom setup (mute, autoplay, controller, etc.)
override val videoConfiguration = VideoConfiguration.FOO_BAR
}.apply {
shouldLoop = true
videoAspectRatio = aspectRatio
render(videoMetaData) // loads video, videoMetaData contains: url, isAd, uid
}
// Foobar video feature, no custom class required just set flags
PinterestVideoView(context).apply {
// Optional params for setup/customization
this.analytics = pinterestAnalytics
this.mute = false
this.autoPlay = true
this.alwaysAutoplay = true
this.alwaysPlay = true
this.showMute = true
this.looping = true
this.bufferingRule = SHOW_BUFFERING_ALWAYS
}.apply {
render(videoMetaData) // loads video, videoMetaData contains: url, isAd, uid
}
視訊基礎架構的另一個複雜性是實際的VideoManager體系結構本身。在我們的重寫中,我們将大多數舊元件合并為僅支援正常運作的VideoManager的核心部分。
我們新的VideoManager體系結構為事件群組件之間的互相關系提供了清晰的層次結構。這不僅在紙面上看起來不錯,而且僅重構一項就删除了約4,500行代碼(不到原始實作大小的1/3)
展望
建立适當的“視訊管理”是一個漫長而艱巨的過程,但是多年來,我們已經建構了一些真正經過改進的東西,以幫助簡化我們的開發流程和Pinner體驗。将來,我們希望開源我們的工作,以便其他開發人員可以為正在進行的處理動态視訊回放做出貢獻。我們将繼續疊代我們的視訊用戶端架構,以應對新的挑戰,以期為Pinners和開發人員提供令人愉悅的視訊體驗。
————————————————
版權聲明:本文為CSDN部落客「LiveVideoStack_」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:
https://blog.csdn.net/vn9PLgZvnPs1522s82g/article/details/101729271「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。