提升Android手機主要視頻應用全屏播放的觀看體驗

爲了給用戶帶來多媒體方面的體驗福利,一刻也不能停,現在要提升主要視頻應用全屏播放的觀看體驗。

“提升主要視頻應用全屏播放的觀看體驗”老闆撂下一句話後拂袖而去,剩下的自己體會。經過人工智能的大腦快速處理,提取了幾個比較關鍵的技術點。

1.視頻應用,如何判斷到視頻應用在工作?

2.全屏播放,如何判斷視頻應用在播放且全屏幕播放?

3.主要視頻應用,不是所有的視頻應用如何區分?

4.隱藏的比較深的點,體驗效果的生命週期,某個視頻應用在全屏播放的時候觀看體驗得到增強,言外之意,非全屏播放的時候要恢復之前的狀態,比如視頻從全屏切換到非全屏,視頻退出,視頻進入後臺,視頻被殺死,視頻崩潰等異常情況?

5.如何避免快速切換有效果/無效果/有效果,帶來的視覺上的衝擊?

逐個分析突破

1.視頻應用,如何判斷到視頻應用在工作?

這些視頻應用基本上都是第三方的比如騰訊視頻,優酷,愛奇藝等,全部由第三方開發商實現,不受控制,需要從系統入手,對播放比較熟悉的話首先想到的是這些播放器會使用到一些系統控件進行播放,顯示等。比如MediaPlayer,MediaCodec,SurfaceView,TextureView等,MediaPlayer第三方很少用基本都用自己的框架,MediaCodec只有在硬解碼的時候纔會用,SurfaceView或TextureView顯示控件基本都會用,很少能繞過去,從這兩個view入手,基本可以hack住想要的視頻應用,而且雖然android有很多視頻顯示的控件,但基本上都是繼承自這兩個view。

clipboard.png

clipboard.png

通過這兩個控件就可以hack到應用裏了。

2.全屏播放,如何判斷視頻應用在播放且全屏幕播放?

首先定義下什麼是全屏播放呢,一定是橫屏嗎,像騰訊,優酷等的電視劇電影綜藝等,不一定,比如快手,抖音一類的短視頻很多都是豎屏的全屏。
其次我們可以從SurfaceView和TextureVIew裏拿到Surface的大小,也就是視頻區域的寬高。
這樣我們通過一個簡單的算法就可以判斷是否是全屏了。
video height > screen height || video width > screen height

clipboard.png

3.主要視頻應用,不是所有的視頻應用如何區分?

通過1,2操作後所有的視頻應用在跑到相應邏輯後都會使能體驗效果,比如在系統設置裏有一些user guide視頻的播放,比如多任務欄裏一些動畫的播放等,這些都不是我們想要的,也會給用戶帶來困惑造成不確定感。
沒有別的辦法只能通過名單來控制了,由於hack在app的java層,可以很容易的得到當前進程的包名,所以方案是通過包名過濾特定的名單。
比如。
//video apps
"com.tencent.qqlive.player.meizu","com.youku.phone.player.meizu", "com.meizu.media.video", "com.qiyi.video","tv.pps.mobile", "com.tencent.qqlive", "com.youku.phone","com.letv.android.client", "com.pplive.androidphone", "com.storm.smart","com.funshion.video.mobile", "com.cmcc.cmvideo", "com.mobile.videonews.li.video","com.sohu.sohuvideo", "com.baidu.video", “com.hunantv.imgo.activity",“tv.danmaku.bili",
//short videos
"com.flyme.videoclips", "com.smile.gifmaker", "com.ss.android.ugc.aweme","com.ss.android.ugc.live", "com.ss.android.article.video", "com.hupu.games","com.kuaiest.video", “com.tencent.weishi”,
//live videos
"com.duowan.mobile", "air.tv.douyu.android", "com.duowan.kiwi","com.panda.videoliveplatform", "com.huajiao", “com.tencent.now"

那名單做成靜態的還是在線的比較好?如何條件允許的話做成在線的當然最好。原理比較簡單把這個配置文件txt也好jason也好csv也好放在你的域名的某個目錄下,通過http或https下載就可以了。
具體到這裏最好採用jason或者csv的格式,或者其他比較好操作的格式,方便管理。
比如
Jason:
{“bussiness":{"bussiness1":{"package_name":"["com.meizu","com.tencent"]"}}}
Csv:
Name1,name2,name3,name4,name5,name6,…

然後配置你的服務就可以了,需要更新的時候刷新下cdn。

這裏有個比較特殊的地方,在終端如何去管理這個名單,由於下載更新比較耗時而且爲了省時且不頻繁訪問服務器需要在本地管理備份,但是hack到別人的應用裏,所以不能在hack的代碼裏處理,這個時候需要一個service,而且是system service,它是一個單獨的進程開機運行,用來管理一些唯一的資源,比如這個白名單的下載更新本地管理,效果模式的管理等等。
然後在hack裏實現client與之通信,具體如何實現一個system service這裏不表,後邊由於生命週期管理的需要也會需要這個service的,後邊再討論。
service取名VideoService,有點像Android AudioService的意思它是用來管理audio相關邏輯的可以通過AudioManager訪問,android沒有VideoService,這裏添加VideoService後也可以擴展到其他video相關邏輯的場景,同理設計了可以通過VideoManager訪問的機制。

4.隱藏的比較深的點,體驗效果的生命週期,某個視頻應用在全屏播放的時候觀看體驗得到增強,言外之意,非全屏播放的時候要恢復之前的狀態,比如視頻從全屏切換到非全屏,視頻切換,視頻退出,視頻進入後臺,視頻被殺死,視頻崩潰等異常情況?

說的直白一點,需要效果的時候一定要有效果,不需要的時候千萬不能有效果。這裏就涉及到用戶的交互了。
在介紹體驗效果的生命週期之前,是不是有個疑問,效果的打開關閉時在哪裏做的,是怎麼做的?
打開關閉時在SurfaceView和TextureView裏做的,利用了它們的生命週期。
SurfaceView的生命週期, surfaceCreated, surfaceChanged, surfaceDestroyed
TextureView的生命週期,onSurfaceTextureAvaliable, onSurfaceTextureSizeChanged, onSurfaceTextureDestroyed, onSurfaceTextureUpdated
通過view生命週期的創建,更新,銷燬以及包含的寬高信息,來做到相應的打開,關閉效果。
比如SurfaceView,surfaceCreated的時候,如果判斷是全屏,就打開它,用戶從全屏切換到了小窗口觸發了surfaceChanged,判斷是否全屏,不是就關閉它,或者用戶退出視頻觸發了surfaceDestroyed,就關閉它。

這兩個view的生命週期和用戶場景交互式密切相關的,正是由於這種機制,實現了體驗效果的生命週期的管理,除了剛纔說的全屏/小窗口切換,視頻退出場景,還有幾種比較典型的場景介紹下。以surfaceview爲例

(1)視頻在全屏幕狀態,直接退到桌面;然後再點擊app進入全屏
觸發surfaceDestroyed,關閉效果;觸發surfaceCreated, surfaceChanged,判斷是否全屏,是打開效果
(2)調出多任務欄
觸發surfaceDestroyed,關閉效果

clipboard.png

clipboard.png

(3)在設置裏殺死視頻
如果要進入的設置裏去殺死視頻,需要先回到桌面進入設置或通過多任務進入設置,無論回到桌面還是進入多任務,都會觸發surfaceDestroyed關閉效果,所以在設置裏殺死視頻,體驗效果是關閉狀態。
(4)在多任務裏殺死視頻
同理,進入多任務,會觸發surfaceDestroyed關閉效果,所以在多任務殺死視頻,體驗效果是關閉狀態。
(5)視頻進入後臺後,被一些省電或內存管理機制殺掉
同理,視頻先進入後臺,會觸發surfaceDestroyed關閉效果,所以視頻進入後臺後,被一些省電或內存管理機制殺掉,體驗效果是關閉狀態。
(6)正在全屏播放時,視頻崩潰或用被kill
體驗效果不能正常關閉,因爲這個時候之前的生命週期不能正常走到。
需要爲這種情況單獨設計一種機制,本身設計是c/s結構的,所以很容易想到利用binder die來監聽client die。不同的是根據binder die的規則,視頻app也要作爲一個binder的service。
在VideoManager裏實現,然後VideoManager運行在視頻app的進程。
private final Binder mICallback = new Binder();
然後通過binder調用將這個mICallback對象傳到VideoService,在VideoService裏實現IBinder.DeathRecipient
mICallback.linkToDeath(this, 0)
當die發生的時候binderDied()回調會被執行,可以在這個回調裏關閉效果,完成這種情況下生命週期的設計。

5.如何避免快速切換有效果/無效果/有效果,帶來的視覺上的衝擊?

這裏也涉及到用戶的交互,怎麼理解呢?可以看兩個比較典型的場景
(1)瀏覽短視頻的時候,比如抖音,從第一個開始快速滑動到第n個,這個中間可能發生了很多次打開/關閉效果,在很短的時間內,不停地切換效果會造成用戶視覺上的體驗不是很好
(2)另外一種不停地切換效果的情況是,頻繁全屏/小窗切換
這種快速切換帶來的頻繁打開/關閉應該儘量避免。
所以在VideoService裏設計了一個worker thread來專門串行處理各個client發過來請求,如果關閉打開關閉在很短的時間內發生,可以選擇忽略。

clipboard.png

最終方案大致如下

clipboard.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章