視頻編解碼學習十二之Android疑點

一、android.view.SurfaceControl.setDisplaySurface的作用

android.view.SurfaceControl.setDisplaySurface 是 Android 系統中一個 native 層級別的 API,主要用于 設置某個物理顯示屏(Display)的輸出 Surface,屬于 SurfaceFlinger 的一部分功能。這個 API 并不是應用開發常用的接口,而是系統或原始設備制造商(OEM)用于控制顯示輸出的低層接口。


🔧 作用解析

SurfaceControl.setDisplaySurface(SurfaceControl displayToken, Surface surface) 的主要作用是:

將一個 Surface 與特定的 Display(顯示設備)綁定,使該 Surface 成為該 Display 的渲染目標。

也就是說,這個接口決定了 某個顯示屏最終畫面要顯示到哪個 Surface 上,可以理解為是 SurfaceFlinger 告訴硬件:“你應該把圖像輸出到這個 Surface 對應的物理設備上。”


📦 參數說明

  • displayToken: 一個代表物理 Display 的 token,通常通過 SurfaceControl.getInternalDisplayToken() 或類似接口獲得。

  • surface: 目標 Surface,用于接收最終的圖像內容。比如一個 SurfaceTextureSurfaceView 所提供的 Surface


📍 使用場景

此函數多用于系統級的圖像輸出控制,比如:

  1. 投屏系統(屏幕鏡像):系統想要將主屏內容輸出到一個 HDMI 顯示器或虛擬顯示上。

  2. 多屏協作/擴展顯示:比如一臺 Android 設備連接多個顯示器時,需要綁定每個顯示器的輸出 Surface。

  3. 截圖或錄屏:系統通過一個 Surface 捕獲整個 Display 的輸出內容。

  4. 系統 UI 渲染控制:用于系統引導期間或鎖屏、開機動畫等控制顯示。


🧪 注意事項

  • 這個 API 是 隱藏的、系統級別的,普通應用無法直接調用。

  • 使用它需要 系統權限,并且通常配合 SurfaceComposerClientSurfaceFlinger 或 AIDL 接口工作。

  • 設置錯誤或參數異常可能會導致顯示黑屏、卡死或輸出內容不正常。


📚 相關源碼路徑(AOSP)

在 AOSP 源碼中,你可以看到它的實現大致在以下路徑中:

 

plaintext

復制編輯

frameworks/base/core/java/android/view/SurfaceControl.java frameworks/native/libs/gui/SurfaceComposerClient.cpp frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

具體來說,Java 層只是包裝,最終會調用到 native 層通過 binder 調用 SurfaceFlingersetDisplaySurface.

二、Display是物理顯示設備嗎?

是的,在 SurfaceControl.setDisplaySurface 的上下文中,Display 是指物理顯示設備(physical display device)。


📌 詳細說明

? 在這個上下文中的 Display 含義:

  • 指的是一個實際存在的硬件輸出設備,比如:

    • 內置屏幕(手機、平板的主屏)

    • 外接 HDMI 顯示器

    • 無線顯示器(Miracast、WiDi)

    • 虛擬顯示設備(在系統看來也被當成一個“顯示”,但可能是用于錄屏或投屏)

  • Android 系統中的 DisplayToken(在 native 層)代表的就是這種顯示設備的引用,和 Java 層 Display 類不是同一個概念,但都是為了描述某個顯示目標。


🎯 舉個例子:

你可以通過 native 層(比如 C++ 中的 SurfaceComposerClient)調用:

 

cpp

復制編輯

sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();

這個 display 就是系統主屏幕的 display token,它在調用:

 

java

復制編輯

SurfaceControl.setDisplaySurface(display, surface);

時被用來綁定輸出 surface。

這表示:“我希望把主屏幕的輸出,顯示到這個 surface 上。”


🔁 對比:Java 層的 Display

在 Java 層中,也有一個 android.view.Display 類,這個類通常是代表系統中抽象出來的顯示輸出,常用于多窗口、投屏等,但它和 SurfaceControl.setDisplaySurface 所用的 displayToken 并不直接等價。


🧠 補充:物理 Display 的獲取

在系統或 native 開發中,可以通過如下方式獲取 display:

 

cpp

復制編輯

sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); // 主屏幕 sp<IBinder> token = SurfaceComposerClient::getExternalDisplayToken(); // 外接屏幕


? 結論

是的,DisplaysetDisplaySurface() 中指的是 一個物理或系統級的顯示設備,通常代表的是 硬件屏幕輸出接口,比如內屏、HDMI、虛擬顯示等。

三、display和surface是什么關系?

這是個很核心的問題,在 Android 圖形系統(特別是 SurfaceFlingerSurfaceControl)中:


🎯 簡明結論:

Display 是“輸出目標”(比如屏幕),而
Surface 是“內容來源”(誰要被顯示)。

通過 setDisplaySurface(display, surface),你告訴系統:

“請把這個 Surface 的內容,作為這個 Display 的顯示輸出。”


🧱 各自的定義和角色

概念定義在圖形系統中的作用
Display代表一個物理顯示設備或虛擬顯示目標。最終圖像的去處(輸出目標)
Surface一個可以被寫入圖像內容的緩沖區,比如由應用或系統提供圖像內容的來源(輸入圖層或緩沖區)


📦 具體關系理解:

在 Android 中,一個典型的圖像渲染流程是這樣的:

  1. 應用或系統繪制內容 → 寫入某個 Surface

  2. SurfaceFlinger 收集所有 Surface → 合成(Compose)

  3. 合成好的最終圖像 → 輸出到某個 Display

這個流程可以簡化為:

 

rust

復制編輯

多個 Surface(圖層) --合成--> 最終幀圖像 --送出--> Display


🧩 setDisplaySurface() 的作用

這個函數的作用是:

“把這個顯示設備(Display)的最終輸出圖像放入這個 Surface 中。”

常用于這些場景:

  • 錄屏:你把一個 Surface 設置為顯示的輸出,這樣它會收到完整的畫面內容

  • 投屏:將主顯示的畫面輸出到一個外接 HDMI/無線顯示的 Surface

  • 多顯示系統:為每個顯示設備配置自己的輸出 Surface


🖼? 舉個實際例子

比如你在開發一個系統級的投屏功能:

  1. 你創建一個虛擬顯示 Display(比如通過 SurfaceComposerClient.createDisplay()

  2. 你創建一個 Surface(比如基于 SurfaceTexture 或 BufferQueue)

  3. 你用 setDisplaySurface(display, surface) 將兩者綁定

這表示:這個虛擬顯示產生的畫面,將寫入這個 Surface 中,你可以用這個 Surface 編碼、傳輸或其他處理。


🧠 類比理解

可以把它類比為:

  • Display電視機

  • SurfaceHDMI 線接口的輸入源

你可以把某個視頻源(Surface)接到電視(Display)上,讓它顯示出來。


? 總結:

項目DisplaySurface
是什么顯示目標(物理或虛擬屏幕)內容緩沖區(應用寫圖像的地方)
作用顯示畫面的“去向”提供畫面的“來源”或“容器”
使用位置輸出、目標端輸入、內容端

SurfaceControl.setDisplaySurface(display, surface)
就是連接兩者的橋梁 —— 把 誰要顯示的內容(surface) 輸出到 哪個屏幕(display) 上。

四、setDisplaySurface()中,當display是主屏,surface是虛擬屏的surface,是相當于把主屏的內容復制到虛擬屏上,還是會影響主屏的顯示?

下面我來給你準確、深入地解析。


? 簡要回答:

setDisplaySurface(display=主屏, surface=虛擬屏的 surface) 表示:你將主屏的最終圖像輸出到這個 surface 中,
? 是復制主屏內容(畫面鏡像),而不是影響主屏顯示。

即:

  • 主屏繼續正常顯示

  • 你只是在“拷貝”它的畫面到另一個 Surface,用于錄制、投屏等目的


🧠 更詳細解釋:

📌 setDisplaySurface() 的行為是什么?

它不是把 surface 當作某個圖層,而是把它當成 Display 的輸出緩沖區(Framebuffer)
這意味著:

  • Display 原本輸出是發到物理屏的,現在你通過這個 API 攔截了這個輸出,讓它 輸出到你指定的 Surface

  • 被指定的 surface 會接收到合成后的整幀圖像(就是用戶當前看到的整個屏幕),相當于屏幕的“鏡像幀”拷貝


📊 舉個例子(場景對照)

場景displaysurface結果
錄屏主屏一個 Surface(比如 MediaProjection 提供的)你會收到主屏的內容幀,但不影響主屏顯示
投屏主屏外接設備的 buffer(通過網絡或 HDMI 虛擬)外設看到主屏內容,主屏依然顯示
反例主屏App 提供的 Surface,并作為圖層提交你會替換部分圖層內容,不是復制


🚧 注意事項(系統開發中):

  • 你是在設置合成后的結果的輸出目標,不是控制圖層。

  • 這個操作通常 需要系統權限或 Root 權限

  • 如果你只想“顯示”某個 Surface 的內容,應該用 SurfaceControl.setLayer() 之類的 API,而不是 setDisplaySurface()

  • 不能頻繁更改 setDisplaySurface(),它是低頻、系統級的配置操作。


📚 對應 Android 系統中的用法:

比如 Android 的錄屏、投屏服務(如 SurfaceFlinger::doComposition() 的輸出流程),就是:

  1. 使用一個虛擬 Surface 作為輸出

  2. 設置主屏的 Display 輸出到該 Surface

  3. 繼續正常渲染主屏,不會影響物理屏幕的顯示

這種方式是目前 Android 中錄屏、無線投屏的基礎機制。


? 結論重申:

setDisplaySurface(主屏, 虛擬 Surface) 是一種不影響主屏顯示的“畫面復制”操作
你只是“截獲”了主屏的合成輸出結果,輸出到了另一個 Surface 中。

五、像這樣把主屏內容復制到另一個surface中通常需要花多長時間?

這個問題非常關鍵,尤其涉及 性能時延幀同步。我們來拆解一下:


? 簡短答案:

把主屏內容復制到另一個 Surface(如錄屏、投屏用的)通常不需要額外的耗時,拷貝幾乎是“零成本”的 DMA 或硬件合成輸出操作。延遲一般控制在 1~2 幀內(16~33ms 左右)甚至更低。


🧠 為什么這么快?背后發生了什么?

這并不是“CPU 上逐像素拷貝”,而是:

🔧 GPU/HWComposer/SurfaceFlinger 一體完成:

  • SurfaceFlinger 將所有 Layer 合成為一幀時,會根據設置:

    • 將這一幀輸出給物理 Display(正常顯示)

    • 同時輸出到你指定的 Surface(投屏、錄屏)

? 輸出操作不是“復制”,而是“多路輸出”

  • 通常使用 硬件層的雙輸出/雙寫 DMA(Dual-Write DMA)GPU多目標渲染

  • 輸出到顯示器 + 輸出到 Surface 可以并行進行

  • 所以這個操作幾乎不會增加幀合成的總時長(不會影響 UI 響應)


📉 實測延遲范圍(參考值):

場景輸出方式延遲(幀)延遲(毫秒)
系統級錄屏(MediaProjection)framebuffer copy / virtual display1~2 幀~16ms - 33ms
Wireless Display(Miracast)surface + 編碼 + 網絡發送2~4 幀~30ms - 60ms
HDMI 拓展屏幕復制多路輸出接近 0 幀< 10ms


🔍 有哪些因素可能拖慢這個操作?

雖然復制本身快,但以下環節可能造成延遲:

  1. Surface 所連接的 BufferQueue 阻塞

    • 如果下游(編碼器、網絡)處理慢,surface 的消費端來不及消費,就會卡住生產者

  2. 圖像格式轉換

    • 如果目標 Surface 只接受某種格式(如 YUV),而主屏是 RGB,系統需要轉換格式,會增加 GPU 負擔

  3. 目標 Surface 在另一個進程或 Binder 中跨進程通信

    • 會帶來一次內核用戶態上下文切換,產生 1~2ms 延遲

  4. 設備性能差、GPU 壓力大

    • 可能會造成合成時間整體變慢(但這是系統問題)


🔧 優化建議(如你在開發系統服務):

  • 使用 硬件合成直出路徑(如通過 HWC2 設置 Dual output)

  • 減少 Surface Buffer 格式轉換(保持與 Display 相同)

  • 為目標 Surface 設置較高的 BufferCount(避免生產端等待)

  • 使用 setDisplaySurface() 而非 copyLayer(),避免 CPU 拷貝


? 總結

問題回答
會不會影響主屏顯示?? 不會,屬于多路輸出,不干擾主屏
是 CPU 復制嗎?? 不是,是 GPU 或 DMA 輸出
速度有多快?? 通常在 1 幀內完成,約 16~33ms 延遲
有哪些性能影響因素?Surface 堵塞、格式轉換、跨進程通信等

六、延遲在什么范圍內,才可以玩實時游戲?

你正在觸及實時圖像傳輸(如投屏游戲、云游戲、遠程控制等)系統中的核心指標之一:端到端延遲


? 簡明結論

🎮 要實現“可玩”的實時游戲體驗,總延遲應控制在 60ms 以下,理想情況在 30ms 以內。


🎯 延遲的等級劃分(針對實時交互游戲)

延遲范圍用戶感知游戲體驗
< 30ms幾乎無感知,媲美本地? 理想、順暢
30~60ms有輕微感覺,但可接受? 可玩、流暢
60~100ms有明顯延遲感?? 勉強能玩
> 100ms明顯卡頓/延遲嚴重? 不適合玩游戲


🧱 實時游戲延遲的組成部分(鏈路分析)

以“把主屏畫面復制到另一個 surface 上用于遠程實時游戲”為例,總延遲來自多個階段:

階段操作典型耗時
🖼? 渲染階段游戲幀在主屏被繪制完成16ms(60fps)
📤 復制階段主屏內容復制到目標 Surface12ms(DMA/GPU)
🎞? 編碼階段H.264/H.265 視頻編碼5~15ms(硬編)
🌐 傳輸階段網絡發送(局域網或無線)1~20ms
📥 解碼階段接收方解碼畫面5~15ms
🖥? 顯示階段解碼后幀顯示到屏幕16ms(60fps)
總計30~80ms


? 如何優化以達成實時游戲體驗?

系統設計角度:

項目推薦做法
Surface 數據復制方式使用 setDisplaySurface(),避免 CPU 復制
編碼方式使用 硬件編碼器(MediaCodec) 并調成低延遲模式
編碼參數使用低 GOP(關鍵幀間隔)、低緩存
網絡傳輸避免 Wi-Fi 干擾,使用 5GHz Wi-Fi,支持 QoS
解碼端使用硬解碼 + 提前渲染機制
顯示刷新機制使用 SurfaceView + Choreographer 提前預渲染


🎮 示例:Steam Link / Moonlight 等系統的延遲設計

系統/協議理想延遲技術手段
Moonlight + NVIDIA GameStream~30msH.265 硬編 + UDP + 低延遲模式
Steam Link4060ms軟件優化 + 局域網傳輸 + 減緩壓縮
Android 無線投屏50100mssetDisplaySurface() + MediaProjection
云游戲系統(如Xbox云游戲)80120ms云端計算 + 網絡優化


? 結論

🎮 如果你希望在屏幕復制(主屏 -> surface -> 編碼傳輸)場景中實現可玩級別的實時游戲體驗:

  • 必須確保 端到端延遲 ≤ 60ms

  • 系統設計上建議使用:

    • SurfaceControl.setDisplaySurface()(高效復制)

    • 硬件編碼(低延遲 H.264/H.265)

    • 局域網(如 5GHz Wi-Fi)

    • 減少 BufferQueue 滯后與雙緩沖

七、渲染和顯示的區別

這是一個圖形系統(無論是 Android、游戲引擎,還是桌面系統)中非常基礎但又極易混淆的兩個概念


? 簡明區分:

概念渲染(Rendering)顯示(Display)
作用生成圖像(把場景變成一幀幀像素)將生成的圖像呈現在屏幕上
階段圖像 生產 階段圖像 消費 階段
發生位置GPU(或 CPU 渲染引擎)顯示控制器 / SurfaceFlinger / HWC
產物Frame Buffer(幀緩沖)屏幕上的像素內容
頻率游戲引擎控制(如 60fps、120fps)通常與屏幕刷新率同步(如 60Hz VSync)


🧠 更深入解釋

🖼? 渲染(Rendering)是什么?

  • 是“畫圖”的過程,把游戲場景、UI、視頻幀等“抽象內容”變成 具體像素

  • 這個過程可能由:

    • 應用(Canvas、Skia、OpenGL、Vulkan)

    • 系統組件(WindowManager、SurfaceFlinger)

    • 游戲引擎(Unity、UE)

舉例:

把一個按鈕繪制成圖像,把 3D 場景變成一幀 1080p 圖像幀,這都是渲染。


📺 顯示(Display)是什么?

  • 是把“已經生成好的像素”送到屏幕上、肉眼可見的過程。

  • 通常通過:

    • SurfaceFlinger 合成所有圖層 → 輸出一幀

    • 通過 HWC / DRM 驅動 → 輸出到顯示設備

    • 屏幕掃描刷新一次(VSync)

舉例:

游戲已經生成了一幀圖像,但要等到下一次屏幕刷新(比如 16.6ms 后)才能真正出現在屏幕上。


🎯 類比理解:做 PPT 的比喻

  • 渲染 = 你在 PowerPoint 中畫了一張幻燈片

  • 顯示 = 你按下播放鍵,讓觀眾在投影儀上看到它


🔁 在 Android 中的對應:

流程階段渲染還是顯示?說明
App 使用 Skia 繪制 UI渲染使用 GPU 或 CPU 繪制到 Surface 的緩沖區中
Surface 被提交渲染圖像還沒顯示,只是準備好了
SurfaceFlinger 合成渲染(最后階段)多個圖層合成為一幀
Frame 輸出到屏幕顯示真正肉眼可見,需要 VSync 配合


🔄 典型延遲鏈(從渲染到顯示)

  1. App 發起 draw call → RenderThread 開始渲染 → Surface 中 buffer 填滿

  2. buffer 被提交給 SurfaceFlinger

  3. 等待下一次 VSync 時機,SurfaceFlinger 合成所有圖層

  4. 合成幀提交給顯示控制器 → 屏幕刷新 → 用戶看到內容

通常這整個過程叫做 "Frame pipeline",耗時約為 16.6ms ~ 33ms。


? 總結:

問題渲染顯示
是干什么的?畫出圖像把圖像呈現出來
誰負責?App / GPU / 系統組件SurfaceFlinger + HWC + 屏幕
圖像狀態“生成了但未顯示”“顯示出來了”
是否可見?? 不可見(存在于內存)? 可見(出現在屏幕)

八、如此看來,投屏做不到延遲低于30ms

你說得很敏銳 —— 常規的投屏系統確實很難做到低于 30ms 的延遲。但是否**“做不到”**,要具體看實現方式、硬件支持程度、網絡狀況等。


? 簡明結論:

🎯 傳統投屏系統(如 Miracast、AirPlay、Cast)難以低于 30ms
高度優化的投屏方案(例如 NVIDIA GameStream + Moonlight)在特定條件下可以做到 20~30ms 延遲,但也極為挑剔環境和實現。


📉 為什么傳統投屏難低于 30ms?

投屏鏈路必須經過以下階段(每步都吃延遲):

階段典型耗時說明
渲染16ms游戲/系統生成一幀圖像(60fps)
復制到 Surface1~2ms使用 setDisplaySurface 或虛擬 Display
編碼(硬編)5~10ms通常是 H.264 baseline
網絡傳輸5~15ms取決于 Wi-Fi / LAN 穩定性
解碼(硬解)5~10ms通常使用 MediaCodec 或 GPU
顯示刷新~16ms等下一次 vsync,才真正看到幀

🔗 總計:約 40~70ms(這是典型系統投屏的真實表現)


🎯 有哪些場景“接近”或“突破”30ms延遲?

系統 / 技術條件延遲范圍
? Moonlight + NVIDIA GameStream局域網、RTX GPU、優化參數20~35ms
? Steam Link(局域網)5GHz Wi-Fi + 硬編 + TV 硬解30~45ms
?? Android MediaProjection + 硬編基于 setDisplaySurface()50~80ms
?? AirPlay / Miracast無優化70~120ms

👉 注意:

  • 即使你用最快的編碼器,顯示刷新(VSync)+網絡波動會拖住延遲底線。

  • 編碼器通常也需要一些 幀緩存,比如 2 幀(就是額外 30ms)。


? 結論:能不能做到低于 30ms?

問題答案
普通投屏能否低于 30ms?? 幾乎不可能(Miracast、AirPlay 等)
特定方案能否做到?? 有條件可行(如 Moonlight + 局域網)
需要什么支持?- 低延遲編碼(快速幀間編碼)
- 高帶寬低干擾 Wi-Fi(最好是 5GHz)
- 最小化 buffer、VSync 對齊
- 接收端支持快速解碼和低延遲渲染


🛠? 補充建議:如果你自己做投屏系統

你可以考慮如下策略來盡量壓縮延遲:

  1. 使用 setDisplaySurface() 攔截主屏輸出,避免在應用層采集

  2. 使用 MediaCodec 硬編,開啟 low-latency profile

  3. GOP 設置為 1~2(低 B 幀甚至無 B 幀)

  4. 目標幀率拉高到 90fps(加快幀傳遞頻率)

  5. 接收端用 SurfaceView + async 解碼路徑

  6. 推流協議使用 RTP over UDP(而非 RTSP/TCP)

九、高分辨率的原始圖像,通過低分辨率的設備用setDisplaySurface投屏到高分辨率的設備,會丟失嗎?

觸及到了 分辨率適配像素數據保真度 的核心。


? 簡短回答:

是的,如果你使用 setDisplaySurface() 將一個低分辨率設備的原始圖像投屏到高分辨率顯示器,最終投出的畫面分辨率將受限于原設備的顯示分辨率,會失真或模糊,并不會自動變得高清。


📌 為什么會“丟失”高分辨率?

情況還原:

  • 你在一個 低分辨率設備(比如 720p 手機)上運行應用

  • 使用 setDisplaySurface()主屏輸出投到一個高分辨率設備(比如 4K 顯示器或虛擬屏)

  • 你以為能在高分屏上顯示更清晰圖像,但結果發現仍然是 720p 放大后的效果

原因在于:

解釋
? setDisplaySurface() 拷貝的是合成結果它不會重新渲染或生成高分圖像,而是復制低分屏渲染完的最終幀
?? 原始渲染內容已是低分辨率Android 上應用或系統界面是基于設備 DPI 和屏幕大小渲染的,渲染目標分辨率就是低的
? 不會自動“高清化”除非你在原設備上以更高分辨率渲染圖像(通常不可能)


🧠 類比理解:

這就像你拿一個 720p 的截圖,在 4K 顯示器上全屏播放 —— 畫面還是糊的,像素已經定死了。


? 如何判斷你最終的投屏分辨率是多少?

你可以通過以下方式驗證:

方法 1:查看系統的實際顯示分辨率

 

bash

復制編輯

adb shell dumpsys SurfaceFlinger

在輸出中查找你綁定的 Surface 的顯示分辨率,以及主屏的 DisplayDevice 信息。

方法 2:通過 MediaCodec 編碼時查看輸入幀大小

  • 如果你設置 setDisplaySurface(surface) 給一個 MediaCodecinput surface,那編碼器收到的幀分辨率就是源設備的屏幕分辨率,不會自動 upscale


? 如何實現“清晰的高分辨率投屏”?

如果你希望:

“用低分辨率設備運行 App,但在高分屏上看到更清晰的圖像”

你需要 重新設計渲染路徑,如下:

方法效果代價
? 直接 setDisplaySurface()畫面模糊簡單,低分辨率限制
? 使用虛擬 Display 并指定高分辨率高清可行要求應用支持多分屏
? App 主動渲染到高分辨率 Surface高清需改造 App 或引擎
? 投送原始圖像數據(非屏幕采集)原始質量通常用于圖像類 App


🎯 示例場景

場景是否可獲得高清圖像?原因
setDisplaySurface() 投屏手機 UI? 模糊,低分限制源圖像已是低分
用虛擬 Display 指定 1080p 渲染? 高清(如果 App 支持)渲染目標是高分
游戲使用 Vulkan 渲染到 4K Surface? 高清自定義渲染控制


? 總結:

問題答案
setDisplaySurface() 會丟失分辨率嗎?? 是,受限于源屏幕的分辨率
會自動高清化嗎?? 不會
如何獲得高清效果?? 使用虛擬 display 或高分渲染

十、創建了高分的虛擬設備,同時虛擬設備要和低分的主設備同步,會有什么影響?

這個問題觸及到了系統合成路徑、刷新策略、buffer同步等多個 Android 圖形子系統的核心點。我們分步驟來講:


🎯 場景還原

你現在有兩個 Display:

  1. 主屏(低分辨率,比如 1280x720)

  2. 虛擬屏(高分辨率,比如 1920x1080 或 4K)

你希望這兩個屏幕同步顯示相同內容(例如鏡像主屏到虛擬屏),但由于它們分辨率不同,系統需要做縮放或重新合成。


? 會產生的主要影響

類別影響情況原因說明
📉 性能開銷? 明顯增加 GPU/合成壓力系統需要額外執行一次 縮放或 re-composite
?? 延遲? 可能 增加 1 幀以上延遲同步時需要等待 buffer/合成對齊
🔁 同步?? 不一定能做到真正的幀級同步兩個 Display 的 VSync 信號可能不同步
📐 清晰度? 虛擬屏雖是高分,但內容是低分縮放上去除非應用對虛擬屏單獨高分渲染
🌀 幀率差異?? 如果兩個屏幕刷新率不同,會導致卡頓或撕裂例如主屏 60Hz,虛擬屏 30Hz 或 90Hz


🔍 細節分析:SurfaceFlinger 是怎么處理的?

👉 當你開啟鏡像模式(或自己同步繪制兩個屏幕):

  1. App 渲染的是 720p 分辨率

  2. 系統將這幀圖像:

    • 輸出到主屏,照常顯示

    • 同時縮放到虛擬屏的分辨率(如 1080p)并顯示

SurfaceFlinger 面臨的問題:

問題表現
Buffer 尺寸不匹配需要 GPU 或硬件合成器執行 scale
兩屏 vsync 不一致可能導致 tearing、掉幀
Buffer 填充與提交延遲合成管線拉長,丟幀概率升高


🧪 舉個真實例子

比如你有個 App,渲染一幀 720p 界面,顯示在兩個屏幕上:

  • 主屏 720p:完美適配

  • 虛擬屏 4K:SurfaceFlinger 要將內容拉伸到 3840x2160,GPU 消耗上升,部分設備甚至用軟件做縮放,非常慢


? 如何減少影響?

方法效果說明
使用虛擬 Display 單獨渲染高分內容? 真正高清、不卡頓代價是需要 App 支持多分屏
限制虛擬屏分辨率接近主屏?? 清晰度受限,性能輕保證合成路徑一致性
不做鏡像,而是單獨推流內容到虛擬屏? 更靈活適合投屏/游戲/視頻場景
使用硬件合成模塊(HWC2 + Display HAL)?? 設備依賴強一些 SoC 有 mirror support


? 總結結論

問題是否會發生?說明
會不會增加合成和性能開銷?? 明顯會多一次縮放、雙屏合成開銷
會不會導致同步問題、撕裂??? 可能會兩屏 vsync 不同步是常態
虛擬屏內容會更清晰嗎?? 如果僅是鏡像渲染內容決定清晰度
虛擬屏能不能獨立顯示高分圖像?? 可以需要專門渲染邏輯


🎯 建議你考慮的問題:

  • 你是做 UI 鏡像?游戲鏡像?視頻推送?

  • 你控制的是 App 層,還是系統層?能不能控制合成/渲染方式?

  • 虛擬 Display 是用 DisplayManager.createVirtualDisplay() 創建的嗎?

十一、如果不要求虛擬屏和主屏同步,只是把高分的虛擬屏投屏到高分的顯示設備上呢

如果你不要求主屏和虛擬屏同步,而是:

? 只在高分虛擬屏上渲染內容,并把它獨立投屏到一個高分顯示設備上

那么,你可以做到:


? 高清顯示、低延遲、高質量輸出

這實際上是最推薦的方式,比“主屏鏡像+縮放”更清晰、更高效,尤其適合:

  • 無線投屏(如 DLNA / 自研協議)

  • 擴展屏應用(演示模式、PPT、游戲)

  • 自研圖形應用輸出到遠程大屏幕


🔧 實現邏輯(推薦路徑):

  1. 使用 DisplayManager#createVirtualDisplay() 創建一個高分辨率虛擬顯示設備

     

    java

    復制編輯

    VirtualDisplay virtualDisplay = displayManager.createVirtualDisplay( "MyVirtualDisplay", 1920, 1080, 320, surface, // 用于接收該顯示內容的 Surface(可用于編碼) DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC );

  2. 指定該 VirtualDisplaySurface 作為編碼輸入,使用 MediaCodec 進行實時硬編碼(比如 H.264)

  3. 將編碼結果通過你自己的網絡協議(比如 RTP / RTMP / WebRTC / Socket)發送到遠端設備

  4. 在高分顯示設備上解碼并顯示該視頻流(1080p / 4K)


? 好處對比

主屏鏡像 + 縮放獨立高分虛擬屏
圖像清晰度? 受限于主屏分辨率? 高分原始渲染
渲染控制? 無法分離? 可單獨繪制
多屏邏輯? 共用 UI 配置? 獨立 UI、邏輯
延遲控制? 被系統幀同步影響? 渲染→編碼直通
可擴展性(多屏)? 很難? 可以創建多個虛擬屏


🎮 舉個典型例子

假設你做一個“云游戲客戶端”:

  • 主屏只顯示菜單,不參與畫面渲染

  • 游戲畫面渲染到虛擬屏(比如 1080p 60fps)

  • 編碼后推流到遠端顯示設備(如電視)

🟢 結果是:

  • 主屏不卡頓、不發熱

  • 虛擬屏保持高清、高幀率、高效率傳輸


?? 注意事項

問題解決方法或注意點
如何讓 App 渲染到虛擬屏?創建新的 Context.createDisplayContext() 或用 Presentation
虛擬屏沒有輸入事件?正常,不需要交互;如需交互可虛擬輸入注入
虛擬屏能 4K 嗎?? 可指定 3840x2160,但 GPU 帶寬和編碼器要跟得上


? 總結

你的目標可行性建議
高分虛擬屏 → 投屏到高分設備? 完全可行推薦使用 VirtualDisplay + MediaCodec 路徑
不同步主屏,獨立渲染內容? 高效、清晰不走主屏鏡像,性能更優
高清投屏,適配遠端顯示? 控制渲染分辨率即可編碼傳輸控制幀率、碼率

十二、點亮虛擬屏能達到像點亮主屏一樣防止手機休眠的效果嗎?

你的關注點是:

點亮虛擬屏是否可以像點亮主屏一樣,防止手機進入休眠狀態(Doze / Suspend / 黑屏)?


? 簡短結論:

不可以直接等效防止休眠虛擬屏本身不能阻止系統休眠,但可以通過特定方法間接達到防止休眠的效果。


📌 為什么虛擬屏不能自動防止休眠?

原因解釋
虛擬 Display 不被視為物理屏幕它不會像主屏一樣參與電源管理策略
系統休眠判斷基于主屏狀態即使虛擬屏在“輸出內容”,主屏息屏或用戶無操作,仍可能觸發 Doze 或 Suspend
虛擬屏沒有“用戶可見性”信號沒有接觸/亮度/喚醒事件驅動系統保持活躍狀態


? 怎么樣才能 真正防止休眠

你需要組合使用 系統喚醒機制,比如:

方法是否推薦原理
PowerManager.WakeLock? 推薦阻止 CPU 或屏幕休眠(需權限)
FLAG_KEEP_SCREEN_ON?? 僅限主屏有效虛擬屏不生效
ForegroundService + WakeLock? 常見做法在后臺長時間保持活躍
播放音頻或 MediaProjection?? 非常規方法一些廠商限制少時有效


? 推薦實現方式:使用 WakeLock

 

java

復制編輯

PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); WakeLock wakeLock = powerManager.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "YourApp:VirtualDisplayWakeLock" ); wakeLock.acquire(); // 可選超時參數

如果你只需要防止 CPU 休眠而不需要點亮屏幕:

 

java

復制編輯

PowerManager.PARTIAL_WAKE_LOCK

記得使用完后 wakeLock.release(),否則會造成耗電。


🧠 示例場景:你在開發投屏服務

場景是否能阻止休眠?備注
只創建虛擬屏 + 開始編碼推流? 不能雖然圖像在輸出,但主屏休眠會中斷編碼
虛擬屏 + WakeLock + 前臺服務? 可以這是推薦做法,適配性最強
虛擬屏 + 屏幕共享(MediaProjection)?? 不一定部分系統可阻止休眠,但不可依賴


? 特別提醒:Doze 模式下不保證行為穩定

某些廠商(如華為、小米)在電池策略中,會強制掛起后臺虛擬屏服務,除非:

  • 你是前臺服務(通知欄常駐)

  • App 被加入電池白名單

  • 獲取了 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 權限(需要手動授權)


? 總結

問題是否成立建議方案
虛擬屏會自動阻止系統休眠嗎?? 不會加 WakeLock
虛擬屏保持內容輸出會延長設備喚醒時間嗎??? 不一定不穩定,視廠商實現
如何確保系統不黑屏 / 不掛起 App?? 前臺服務 + WakeLock
虛擬屏是否能用于長時間輸出到顯示設備?? 可以,但需配合防休眠機制

十三、通過電腦adb命令,用app_process拉起的進程,系統休眠后也會不工作,加 WakeLock確定能解決嗎?

涉及到 app_process 啟動的進程是否能在系統休眠后繼續運行 —— 我們來深入拆解


🎯 問題背景總結:

你用的是類似如下命令:

 

bash

復制編輯

adb shell app_process /system/bin com.example.YourMainClass

  • 這種方式繞過了普通 Activity / Service 啟動路徑

  • 通常不受 Android 應用生命周期限制,但也不受系統 power manager 自動保護

現在你想確認:

如果系統休眠(息屏 / Doze / Suspend),這個進程是否會停止?如果加 WakeLock,能否解決?


? 簡潔結論:

是的,理論上你可以使用 WakeLock 保持 CPU 運行,即使系統休眠,app_process 啟動的進程也可以持續工作。

?? 但前提是:

  • 你必須獲取 正確類型的 WakeLock

  • 運行環境有權限申請 WakeLock

  • 該進程沒有被 SELinux 限制調用 PowerManager


? 你該怎么做?

🧩 步驟一:讓進程獲取 WakeLock

即使你通過 app_process 啟動,只要你創建了 Java 進程并能訪問系統服務,你可以這樣寫代碼:

 

java

復制編輯

PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::KeepAlive"); wakeLock.acquire();

  • PARTIAL_WAKE_LOCK 是關鍵!它不要求屏幕亮,但能防止 CPU 進入休眠。

  • 你不需要 SCREEN_DIM_WAKE_LOCK 等類型,因為你可能沒有界面。

🔐 權限要求:

你必須在代碼中擁有:

 

xml

復制編輯

<uses-permission android:name="android.permission.WAKE_LOCK"/>

但對于 app_process 啟動的進程,沒有 AndroidManifest.xml,所以你需要:

  • 在 shell 用戶或 system 用戶下運行(擁有 WAKE_LOCK 權限)

  • 或使用 pm grant 賦予權限(如果你用 app 包運行)

 

bash

復制編輯

adb shell pm grant com.example.yourapp android.permission.WAKE_LOCK


?? 注意:不解決以下幾種“休眠”問題

休眠類型WakeLock 有效?說明
屏幕息屏? 有效PARTIAL_WAKE_LOCK 可以保持后臺運行
Doze 模式? 有效(需前臺進程)但某些廠商會強殺
App standby? 有效前提是你不通過應用層啟動
深度掛起(suspend)?? 設備依賴,不總有效某些設備強制 system suspend,WakeLock 無效(尤其是長時間息屏后)


🧠 補充:你應該避免的情況

錯誤做法后果
SCREEN_DIM_WAKE_LOCK 無界面時無效、被忽略
不獲取 WakeLock系統一旦 Doze 或 CPU suspend,app_process 啟動的進程會被凍結或終止
依賴 Java Thread.sleep 來保持會被系統掛起,不會真正運行


? 建議實踐方案

如果你正在做的是:

  • 開發一個通過 ADB 啟動的常駐服務(例如性能監控、遠程指令、投屏、流媒體處理)

  • 不使用標準 Activity/Service 框架

那么最佳組合是:

功能技術方案
后臺運行app_process + PARTIAL_WAKE_LOCK
防休眠使用 WakeLock.acquire() 持續持有
防殺進程推薦使用 nohupapp_process & + 守護進程機制
穩定性增強可搭配使用 ForegroundService(如用 shell 啟 app 服務)


? 結論總結:

問題是否成立?備注
app_process 啟動的進程休眠后會暫停?? 是的CPU suspend 后會凍結
WakeLock 能防止進程被凍結?? 有效使用 PARTIAL_WAKE_LOCK
所有設備都一定有效??? 不一定廠商可能有特殊電源策略
無界面程序也能拿到 WakeLock?? 可以只要有權限并運行在有能力用戶下

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79856.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79856.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79856.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

家用或辦公 Windows 電腦玩人工智能開源項目配備核顯的必要性(含 NPU 及顯卡類型補充)

一、GPU 與顯卡的概念澄清 首先需要明確一個容易誤解的概念&#xff1a;GPU 不等同于顯卡。 顯卡和GPU是兩個不同的概念。 【概念區分】 在討論圖形計算領域時&#xff0c;需首先澄清一個常見誤區&#xff1a;GPU&#xff08;圖形處理單元&#xff09;與顯卡&#xff08;視…

Python----神經網絡(《Deep Residual Learning for Image Recognition》論文和ResNet網絡結構)

一、論文 1.1、論文基本信息 標題&#xff1a;Deep Residual Learning for Image Recognition 作者&#xff1a;Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun 單位&#xff1a;Microsoft Research 會議&#xff1a;CVPR 2016 主要貢獻&#xff1a;提出了一種深度殘…

Qt/C++開發監控GB28181系統/錄像文件查詢/錄像回放/倍速播放/錄像文件下載

一、前言 搞定了實時預覽后&#xff0c;另一個功能就是錄像回放&#xff0c;錄像回放和視頻點播功能完全一致&#xff0c;唯一的區別就是發送點播的sdp信息中攜帶了開始時間和結束時間&#xff0c;因為是錄像文件&#xff0c;所以有這個時間&#xff0c;而實時視頻預覽這個對應…

在Spark搭建YARN

&#xff08;一&#xff09;什么是SparkONYarn模式 Spark on YARN&#xff08;Yet Another Resource Negotiator&#xff09;是 Spark 框架在 Hadoop 集群中運行的一種部署模式&#xff0c;它借助 Hadoop YARN 來管理資源和調度任務。 架構組成 ResourceManager&#xff1a;作…

SpringAI

機器學習&#xff1a; 定義&#xff1a;人工智能的子領域&#xff0c;通過數據驅動的方法讓計算機學習規律&#xff0c;進行預測或決策。 核心方法&#xff1a; 監督學習&#xff08;如線性回歸、SVM&#xff09;。 無監督學習&#xff08;如聚類、降維&#xff09;。 強化學…

如何用Redis實現分布式鎖?RedLock算法的核心思想?Redisson的看門狗機制原理?

一、Redis分布式鎖基礎實現 public class RedisDistributedLock {private JedisPool jedisPool;private String lockKey;private String clientId;private int expireTime 30; // 默認30秒public boolean tryLock() {try (Jedis jedis jedisPool.getResource()) {// NX表示不…

前端面試寶典---js垃圾回收機制

什么是垃圾回收 垃圾回收是指一種自動內存管理機制&#xff0c;當聲明一個變量時&#xff0c;會在內存中開辟一塊內存空間用于存放這個變量。當這個變量被使用過后&#xff0c;可能再也不需要它了&#xff0c;此時垃圾回收器會自動檢測并回收這些不再使用的內存空間。垃圾回收…

阿里媽媽LMA2新進展:集成大語言模型與電商知識的通用召回大模型URM

近日&#xff0c;阿里媽媽在國際頂級學術會議 —— 國際萬維網大會&#xff08;International World Wide Web Conference, 簡稱WWW&#xff09;上共同主持了計算廣告算法技術相關的Tutorial&#xff08;講座&#xff09;&#xff0c;介紹了計算廣告領域的技術發展脈絡&#xf…

數字孿生實時監控汽車零部件工廠智能化巡檢新范式

在汽車制造業面臨數字化轉型時&#xff0c;汽車零部件工廠也面臨著提升生產效率、降低運營成本和增強市場競爭力的多重挑戰。傳統的巡檢方式已經難以滿足現代工廠對高效、精準管理和實時決策的需求。數字孿生系統的出現&#xff0c;為汽車零部件工廠提供了一種創新的智能化巡檢…

【計算機網絡】3數據鏈路層②

1. 數據鏈路層所處的地位 數據鏈路層使用的信道主要有兩種: ①點對點信道:PPP協議 ②廣播信道:有線局域網,CSMA/CD協議;無線局域網,CSMA/CA協議 對比項點對點信道 vs 單播廣播信道 vs 廣播核心是否一致? 一致(一對一傳輸)? 一致(一對所有傳輸)差異點前者是物理層…

c++中的函數(默認參數,占位參數,重載)

1&#xff0c;函數默認參數 在c中&#xff0c;函數的形參列表中的形參是可以有默認值得 語法&#xff1a;返回值類型 函數名 &#xff08;參數 默認值&#xff09;{} 示例&#xff1a; #include<iostream> using namespace std;//函數默認參數 // 就是如果傳了就…

【原創】使用阿里云存放一個臨時共享的文件

在某些場合&#xff0c;需要臨時將一個文件存儲到一個可被公網訪問的地方&#xff0c;某個服務需要訪問一下這個文件。這個文件基本上就是一次壽命&#xff0c;也就是你上傳一下&#xff0c;然后被訪問一下&#xff0c;這個文件的壽命就結束了。 對于這種需求&#xff0c;自建…

Python中列表(list)知識詳解(2)和注意事項以及應用示例

在 Python 中列表&#xff08;list&#xff09; 的包括其結構、常見操作&#xff08;更新、添加、刪除、查找、隊列棧行為等&#xff09;&#xff0c;下面將逐一的進行講解并附相關的示例。 一、列表的基礎知識 1. 定義與特點 定義方式&#xff1a;用 [] 包裹的有序可變集合 …

vscode extention踩坑記

# npx vsce package --allow-missing-repository --no-dependencies #耗時且不穩定 npx vsce package --allow-missing-repository #用這行 code --install-extension $vsixFileName --force我問ai&#xff1a;為什么我的.vsix文件大了那么多 ai答&#xff1a;因為你沒有用 --n…

移動端巡檢點檢,讓設備管理更便捷高效

在企業設備管理的日常工作中&#xff0c;巡檢點檢是保障設備正常運行的重要環節。傳統的巡檢方式依賴紙質記錄、人工操作&#xff0c;效率低、易出錯&#xff0c;已難以滿足現代企業的管理需求。隨著技術發展&#xff0c;越來越多設備管理系統引入移動端功能&#xff0c;為設備…

laravel 中使用的pdf 擴展包 laravel-snappy(已解決中文亂碼)

Centos7 安裝 wkhtmltopdf 1、先查看系統是 32 位的還是 64 位的 uname -a2、通過 composer 安裝 wkhtmltopdf 32位: $ composer require h4cc / wkhtmltopdf-i386 0.12.x $ composer require h4cc / wkhtmltoimage-i386 0.12.x 64位: $ composer require h4cc/wkhtmltopdf-…

Rust:重新定義系統編程的安全與效率邊界

在軟件工程領域&#xff0c;內存安全漏洞每年造成數千億美元損失&#xff0c;而C/C生態中60%的漏洞源于指針誤用。正是在這樣的背景下&#xff0c;Rust憑借其革命性的內存安全機制異軍突起。作為一門現代系統級編程語言&#xff0c;Rust不僅解決了困擾開發者數十年的內存管理難…

C++學習細節回顧(匯總二)

一.初始化列表相關 1.初始化順序受申明順序影響 2.在必要時可以部分不采用初始化列表&#xff0c;避免受特性1影響 二.非類型模板參數 template< class T , size_t N 10 > 三.特化–特殊化處理 template< class T > bool less(T left , T right) { return left&…

勾選某一行的勾選框,更改當前行的顏色,ALV數據發生變化的事件

文章目錄 屏幕ALV的創建定義變量注冊事件方法定義方法實現frm_data_change 效果 屏幕 ALV的創建 DATA: g_gui_custom_container TYPE REF TO cl_gui_custom_container. DATA: g_gui_alv_grid TYPE REF TO cl_gui_alv_grid.DATA: gt_listheader TYPE slis_t_listheader, &quo…

AI-02a5a6.神經網絡-與學習相關的技巧-批量歸一化

批量歸一化 Batch Normalization 設置合適的權重初始值&#xff0c;則各層的激活值分布會有適當的廣度&#xff0c;從而可以順利的進行學習。那么&#xff0c;更進一步&#xff0c;強制性的調整激活值的分布&#xff0c;是的各層擁有適當的廣度呢&#xff1f;批量歸一化&#…