日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android PC投屏简单尝试- 自定义协议章(Socket+Bitmap)

發布時間:2025/3/15 Android 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android PC投屏简单尝试- 自定义协议章(Socket+Bitmap) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

代碼地址 :https://github.com/deepsadness/MediaProjectionDemo

效果預覽

投屏效果預覽

簡單說明:

  • 使用Android MediaProjection Api來完成視頻的截圖
  • 通過WebSocket進行鏈接。將圖片傳遞給網頁
  • 想法來源

    看到vysor,覺得特別好玩,于是就想著自己能不能試著做一個類似的功能出來。搜索了相關實現。發現網上已經有網友針對vysor做了分析。于是就照著思路,按圖索驥,當作對MediaProjection Api的練習,來完成這個小項目

    主要思路

    1. 獲取屏幕的截屏

    • 創建VirtualDisplay
      Android在Api 21以上為我們已經提供了系統的Api可以進行操作。
      主要是這幾個類的相互配合
      MediaProjection和VirtualSurface,還有截圖的話,使用ImageReader,三個類配合使用。

      配套使用示意圖.png

    public RxScreenShot createImageReader() {//注意這里使用RGB565報錯提示,只能使用RGBA_8888mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1000);mSurfaceFactory = new ImageReaderSurface(mImageReader);createProject();return this;}private void createProject() {mediaProjection.registerCallback(mMediaCallBack, mCallBackHandler);//通過這種方式來創建這個VirtualDisplay,并將數據傳遞給ImageReader提供surfacemediaProjection.createVirtualDisplay(TAG + "-display", width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,mSurfaceFactory.getInputSurface(), null, null);}
    • 獲取屏幕截圖
      可以通過ImageReader類。配套Image來獲獎獲得的數據轉成Bitmap
    /* 封裝成了Observable對象。 */ public class ImageReaderAvailableObservable extends Observable<ImageReader> {public static ImageReaderAvailableObservable of(ImageReader imageReader) {return new ImageReaderAvailableObservable(imageReader, null);}public static ImageReaderAvailableObservable of(ImageReader imageReader,Handler handler) {return new ImageReaderAvailableObservable(imageReader, handler);}private final ImageReader imageReader;private final Handler handler;private ImageReaderAvailableObservable(ImageReader imageReader, Handler handler) {this.imageReader = imageReader;this.handler = handler;}@Overrideprotected void subscribeActual(Observer<? super ImageReader> observer) {Listener listener = new Listener(observer, imageReader);observer.onSubscribe(listener);//設置準備好的監聽事件imageReader.setOnImageAvailableListener(listener, handler);}static class Listener implements Disposable, ImageReader.OnImageAvailableListener {private final AtomicBoolean unsubscribed = new AtomicBoolean();private final ImageReader mImageReader;private final Observer<? super ImageReader> observer;Listener(Observer<? super ImageReader> observer, ImageReader imageReader) {this.mImageReader = imageReader;this.observer = observer;}@Overridepublic void onImageAvailable(ImageReader reader) {if (!isDisposed()) {//將準備好的reader發送出去,進行處理observer.onNext(reader);//注意:這里如果不調用onCompleted事件。其實這個監聽會不斷回調事件 // observer.onComplete();}}@Overridepublic void dispose() {if (unsubscribed.compareAndSet(false, true)) {mImageReader.setOnImageAvailableListener(null, null);}}@Overridepublic boolean isDisposed() {return unsubscribed.get();}} }/* 調用開始截屏的方法 */ public Observable<Object> startCapture() {return ImageReaderAvailableObservable.of(mImageReader).map(imageReader -> {String mImageName = System.currentTimeMillis() + ".png";Log.e(TAG, "image name is : " + mImageName);Bitmap bitmap = null;//從imageReader中獲取到最新的ImageImage image = imageReader.acquireLatestImage();if (image == null) {} else {//將Image對象轉成bitmapint width = image.getWidth();int height = image.getHeight();//byteBuffer都保存在image.Plane中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);bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);//這里使用完要記得close.如果沒有close,當imageReader達到max_count上限時將會拋出異常image.close();}return bitmap == null ? new Object() : bitmap;});}

    這里需要注意的是,需要通過這個回調,每當屏幕發生變化,就會回調這個接口,可以得到最新的截圖。
    ImageReader::setOnImageAvailableListener

    2. 搭建Socket連接,將圖片的數據進行傳遞

    node 部分的代碼在 https://github.com/deepsadness/MediaProjectionDemo/tree/master/sockt

    因為我們的目標是在網頁內打開,所以需要和網頁進行通信。
    可以簡單的使用WebSocket進行雙方通向

    簡單示意圖Again.png

    ?

    通過Socket.iohttps://socket.io/ 就可以簡單的實現

    • Android端的代碼
      通過WebSocket將Bitmap的字節碼發送出去
    private fun sendBitmap(it: Bitmap) {val byteArrayOutputStream = ByteArrayOutputStream()it.compress(Bitmap.CompressFormat.JPEG, 60, byteArrayOutputStream)val byteArray = byteArrayOutputStream.toByteArray()SocketIoManager.getInstance().send(byteArray)}public void send(byte[] bitmapArray) {if (!mSocketReady) {return;}if (bitmapArray != null) {mSocket.emit("event", bitmapArray);}}
    • Node端的代碼
      簡單的SocketIo實現.代碼在 /sockt/io-server.js
    var io = require('socket.io')(); var clients = [] io.on('connection', function (client) {clients.push(client);console.log('connection!');client.emit('join', 'welcome to join!!')client.on('chat message', function (msg) {console.log("receive msg=" + msg);});client.on('event', function (msg) {// console.log("event", msg);console.log("event", "send image~~");//通過event事件出去clients.forEach(function (it) {it.emit('event', msg)})}); }); io.on('disconnect', function (client) { }) io.listen(9000);

    3. 如何將圖片顯示出來

    代碼在 /sockt/index.html中
    html中的src就可以直接對傳遞byte[]的進行解析。

    socket.on('image', function (msg) {var arrayBufferView = new Uint8Array(msg);var blob = new Blob([arrayBufferView], { type: "image/jpeg" });var urlCreator = window.URL || window.webkitURL;var imageUrl = urlCreator.createObjectURL(blob);var img = document.getElementById("screen");// var img = document.querySelector("#photo");img.src = imageUrl;

    4. 下一步

    下一步,就是使用 錄制的Api,來做錄屏直播了。

    投屏嘗試系列文章

    • Android PC投屏簡單嘗試- 自定義協議章(Socket+Bitmap)
    • Android PC投屏簡單嘗試(錄屏直播)2—硬解章(MediaCodec+RMTP)

    ?



    作者:deep_sadness
    鏈接:https://www.jianshu.com/p/ce37330365f2
    來源:簡書
    簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。

    總結

    以上是生活随笔為你收集整理的Android PC投屏简单尝试- 自定义协议章(Socket+Bitmap)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。