VirtualDisplay
一、介紹
代表一個虛擬顯示幕, 虛擬顯示幕的內容被渲染到您必須提供給createVirtualDisplay()的Surface ,
二、使用
1、createVirtualDisplay
通常我們使用DisplayManager.createVirtualDisplay()來創建虛擬顯示,
一下是創建虛擬顯示的具體方法:
createVirtualDisplay(@NonNull String name,
int width, int height, int densityDpi, @Nullable Surface surface, int flags);
createVirtualDisplay(@NonNull String name,
int width, int height, int densityDpi, @Nullable Surface surface, int flags,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler)
引數:
name – 虛擬顯示幕的名稱,必須非空,
width – 虛擬顯示的寬度(以像素為單位),必須大于 0,
height – 虛擬顯示幕的高度(以像素為單位),必須大于 0,
densityDpi – 以 dpi 為單位的虛擬顯示密度,必須大于 0,
surface – 虛擬顯示幕的內容應該被渲染到的表面,如果最初沒有,則為 null,
flags – 虛擬顯示標志的組合: VIRTUAL_DISPLAY_FLAG_PUBLIC 、 VIRTUAL_DISPLAY_FLAG_PRESENTATION 、 VIRTUAL_DISPLAY_FLAG_SECURE 、 VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY或VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR ,
callback - 當VirtualDisplay的狀態改變時呼叫的回呼
handler – 應在其上呼叫偵聽器的處理程式,如果應在呼叫執行緒的 Looper 上呼叫偵聽器,則為 null,
虛擬顯示的內容被渲染到應用程式提供的Surface ,虛擬顯示的行為取決于提供給此方法的標志, 默認情況下,虛擬顯示被創建為私有的、非展示的和不安全的, 使用某些標志可能需要權限,
2、分析引數flags
1.VIRTUAL_DISPLAY_FLAG_PUBLIC
虛擬顯示標志:創建公共顯示,公共虛擬顯示幕
設定此標志時,虛擬顯示是公開的,公共虛擬顯示幕的行為與大多數連接到系統的其他顯示幕(例如外部或無線顯示幕)的行為類似, 應用程式可以在顯示幕上打開視窗,系統可以將其他顯示幕的內容鏡像到它上面,
2.VIRTUAL_DISPLAY_FLAG_PRESENTATION
虛擬顯示標志:創建演示顯示,演示虛擬顯示
當該標志被設定時,虛擬顯示幕被注冊為presentation display category中的presentation display category , 應用程式可以自動將其內容投影到演示顯示以提供更豐富的第二螢屏體驗,
3.VIRTUAL_DISPLAY_FLAG_SECURE
虛擬顯示標志:創建安全顯示,安全的虛擬顯示幕
設定此標志后,虛擬顯示被視為安全, 來電者承諾采取合理措施,例如無線加密,以防止顯示內容被截取或記錄在持久性介質上,
另外:創建安全的虛擬顯示幕需要 CAPTURE_SECURE_VIDEO_OUTPUT 權限, 此權限保留供系統組件使用,不適用于第三方應用程式
4.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
虛擬顯示標志:只顯示該顯示幕本身的內容; 不要鏡像另一個顯示幕的內容,
此標志與VIRTUAL_DISPLAY_FLAG_PUBLIC結合使用, 通常,如果公共虛擬顯示幕沒有自己的視窗,它們將自動鏡像默認顯示幕的內容, 當這個標志被指定時,虛擬顯示幕將只顯示它自己的內容,如果它沒有視窗,它將被消隱,
5.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
虛擬顯示標志:當沒有顯示內容時,允許將內容鏡像到私人顯示幕上,此標志僅用于在創建私有顯示時覆寫默認行為,
另外:創建自動鏡像虛擬顯示幕需要 CAPTURE_VIDEO_OUTPUT 或 CAPTURE_SECURE_VIDEO_OUTPUT 權限, 這些權限保留供系統組件使用,第三方應用程式無法使用, 或者,可以使用適當的MediaProjection來創建自動鏡像虛擬顯示,
3、MediaProjection
授予應用程式捕獲螢屏內容和/或錄制系統音頻的能力的令牌, 授予的確切功能取決于 MediaProjection 的型別,
螢屏捕獲會話可以通過MediaProjectionManager.createScreenCaptureIntent啟動, 這允許捕獲螢屏內容,但不能捕獲系統音頻,
示例:
MediaProjectionManager mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if (mProjectionManager != null) {
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), 1);
}
在onActivityResult回呼中處理
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
String path=GetRealPath.getPath(this,uri);
if(requestCode==1){
if(resultCode==RESULT_OK){
MediaProjection mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
VirtualDisplay virtualDisplay =mMediaProjection.createVirtualDisplay("screen_shut", mWidth, mHeight, mDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,mSurface,null,null);
}
}
}
createVirtualDisplay引數:
name: 是生成的VirtualDisplay實體的名稱;
width, height: 分別是生成實體的寬高;
dpi: 生成實體的像素密度,必須大于0,一般都取1;
surface: 這個比較重要,是你生成的VirtualDisplay的載體,我的理解是,VirtualDisplay的內容是一幀幀的螢屏截圖(所以你看到是有寬高,像素密度等設定),所以MediaProjection獲取到的其實是一幀幀的圖,然后通過surface來順序播放這些圖片,形成視頻,
MediaProjection最好在前臺服務中創建并在清單檔案中設定Service為foregroundServiceType="mediaProjection",以上僅僅為示例
三、使用ImageReader獲取虛擬顯示幕中的影像
1、ImageReader
ImageReader 類允許應用程式直接訪問渲染到Surface影像資料;影像資料封裝在Image物件中,可以同時訪問多個這樣的物件,最多可達maxImages建構式引數指定的數量, 通過其Surface發送到 ImageReader 的新影像將排隊,直到通過acquireLatestImage或acquireNextImage呼叫訪問, 由于記憶體限制,如果 ImageReader 沒有以等于生產速率的速率獲取和釋放影像,則影像源最終將在嘗試渲染到 Surface 時停止或丟棄影像,
2、實體化newInstance
靜態方法直接呼叫
ImageReader.newInstance(
@IntRange(from = 1) int width,
@IntRange(from = 1) int height,
@Format int format,
@IntRange(from = 1) int maxImages);
width – 此閱讀器將生成的影像的默認寬度(以像素為單位),
height – 此閱讀器將生成的影像的默認高度(以像素為單位),
格式 - 此閱讀器將生成的影像格式, 這必須是ImageFormat或android.graphics.PixelFormat常量之一, 請注意,并非所有格式都受支持,例如 ImageFormat.NV21,
maxImages – 用戶希望同時訪問的最大影像數, 這應該盡可能小以限制記憶體使用, 一旦用戶獲得了 maxImages 影像,必須先釋放其中一個影像,然后才能通過,
3、獲取VirtualDisplay中的影像
創建virtualdisplay時傳入imageReader.getSurface()
virtualDisplay=mDisplayManager.createVirtualDisplay("getImage", mWidth, mHeight, mDensity, mImageReader.getSurface(),
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
imageReader.getSurface():獲取可用于為此ImageReader生成Images的Surface ,
注冊一個偵聽器,以便在 ImageReader 中有新影像可用時呼叫,
注意這里的新影像,也就是說如果你的虛擬顯示中始終沒有新的操作或者說界面沒有變化,ImageReader是監聽不到新的影像的,log可能不會列印,但不意味著虛擬顯示中的影像消失了,
//一般而言,如果你的Handler是要來重繪操作UI的,那么就需要在主執行緒下跑,
Handler mHandler=new Handler(Looper.getMainLooper());
mImageReader.setOnImageAvailableListener(new ImageAvailableListener(),mHandler);
private class ImageAvailableListener implements ImageReader.OnImageAvailableListener {
@Override
public void onImageAvailable(ImageReader reader) {
if (reader != mImageReader) {
return;
}
//SystemClock.sleep(2000);
Image image = reader.acquireNextImage();
if (image != null) {
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
final ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
//保存圖片到本地
ImageSaveUtil.saveBitmap2file(bitmap,activity,String.valueOf(System.currentTimeMillis()));
Log.d(TAG, "New image available from virtual display." + i);
image.close();
i++;
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/356703.html
標籤:其他
