截取SurfaceView的截图

时间:2014-08-01 18:15:27

标签: android camera screenshot surfaceview

正在开发一款简单的相机应用。我有代码截取整个活动的截图并将其写入Sd卡。问题是Surfaceview返回黑屏。

我想知道如何独立拍摄surfaceview的截图。 这是获取整个活动屏幕截图的代码。

    findViewById(R.id.screen).setOnClickListener(new OnClickListener() {
      @Override
     public void onClick(View v) {
     final RelativeLayout layout = (RelativeLayout) findViewById(R.id.RelativeLayout1);
      layout.setVisibility(RelativeLayout.GONE);
       Bitmap bitmap = takeScreenshot();
        Toast.makeText(getApplicationContext(),"Please Wait",         Toast.LENGTH_LONG).show();
       saveBitmap(bitmap);
   }
});

}



      public Bitmap takeScreenshot() {
       View rootView = findViewById(android.R.id.content).getRootView();
       rootView.setDrawingCacheEnabled(true);
       return rootView.getDrawingCache();
    }


           public void saveBitmap(Bitmap bitmap) {
         final MediaPlayer cheer = MediaPlayer.create(PicShot.this, R.raw.shutter);
       cheer.start();
        Random generator = new Random();
      int n = 10000;
       n = generator.nextInt(n);
        String fname = "Image-"+ n +".png";
      final RelativeLayout layout = (RelativeLayout)     findViewById(R.id.RelativeLayout1);
     File imagePath = new File(Environment.getExternalStorageDirectory() + "/" + fname);
     FileOutputStream fos;
      try {
        fos = new FileOutputStream(imagePath);
        bitmap.compress(CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
        layout.setVisibility(RelativeLayout.VISIBLE);
        Intent share = new Intent(Intent.ACTION_SEND);
        share.setType("image/*");
        Uri uri = Uri.fromFile(imagePath);
        share.putExtra(Intent.EXTRA_STREAM,uri);
        startActivity(Intent.createChooser(share, "Share Image"));
    } catch (FileNotFoundException e) {
        Log.e("GREC", e.getMessage(), e);
    } catch (IOException e) {
        Log.e("GREC", e.getMessage(), e);
    }
}

6 个答案:

答案 0 :(得分:12)

SurfaceView的曲面独立于绘制View元素的曲面。因此,捕获View内容将不包括SurfaceView。

您需要单独捕获SurfaceView内容并执行自己的合成步骤。捕获的最简单方法可能是重新渲染内容,但使用屏幕外位图作为目标而不是表面。如果您使用GLES渲染到屏幕外的pbuffer,则可以在交换缓冲区之前使用glReadPixels()

更新: Grafika's“来自相机的纹理”活动演示了使用OpenGL ES处理来自相机的实时视频。 EglSurfaceBase#saveFrame()显示了如何将GLES渲染捕获到位图。

更新:另请参阅this answer,其中提供了更多背景信息。

答案 1 :(得分:2)

如果您的情况允许,使用TextureView代替SurfaceView将使此问题更容易解决。它有一个方法getBitmap(),可以在Bitmap上返回当前帧的TextureView

答案 2 :(得分:2)

只需复制并粘贴过去的代码

  

注意:仅适用于API级别> = 24

private void takePhoto() {

        // Create a bitmap the size of the scene view.
        final Bitmap bitmap = Bitmap.createBitmap(surfaceView.getWidth(), surfaceView.getHeight(),
                Bitmap.Config.ARGB_8888);



        // Create a handler thread to offload the processing of the image.
        final HandlerThread handlerThread = new HandlerThread("PixelCopier");
        handlerThread.start();
        // Make the request to copy.
        PixelCopy.request(holder.videoView, bitmap, (copyResult) -> {
            if (copyResult == PixelCopy.SUCCESS) {
                Log.e(TAG,bitmap.toString());
                String name = String.valueOf(System.currentTimeMillis() + ".jpg");
                imageFile = ScreenshotUtils.store(bitmap,name);

            } else {
                Toast toast = Toast.makeText(getViewActivity(),
                        "Failed to copyPixels: " + copyResult, Toast.LENGTH_LONG);
                toast.show();
            }
            handlerThread.quitSafely();
        }, new Handler(handlerThread.getLooper()));
    }

答案 3 :(得分:0)

我与ExoPlayer有关的情况,需要获取当前帧的位图。

使用API​​> =24。

private val copyFrameHandler = Handler()

fun getFrameBitmap(callback: FrameBitmapCallback) {
    when(val view = videoSurfaceView) {
        is TextureView -> callback.onResult(view.bitmap)
        is SurfaceView -> {
            val bitmap = Bitmap.createBitmap(
                    videoSurfaceView.getWidth(),
                    videoSurfaceView.getHeight(),
                    Bitmap.Config.ARGB_8888
            )

            copyFrameHandler.removeCallbacksAndMessages(null)

            PixelCopy.request(view, bitmap, { copyResult: Int ->
                if (copyResult == PixelCopy.SUCCESS) {
                    callback.onResult(bitmap)
                } else {
                    callback.onResult(null)
                }
            }, copyFrameHandler)
        }
        else -> callback.onResult(null)
    }
}

fun onDestroy() {
    copyFrameHandler.removeCallbacksAndMessages(null)
}

interface FrameBitmapCallback {
    fun onResult(bitmap: Bitmap?)
}

答案 4 :(得分:0)

这就是我的方法。将此方法放在一些Util类中

@model trial.Models.RetrieveDemoEntities
@{
    ViewBag.Title = "Index";
}

<h2>Retrieving Data From Database</h2>

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>

    <style type="text/css">
        table, td, th {
            border: 1px solid green;
        }

        th {
            background-color: Pink;
            color: white;
        }
    </style>
</head>
<body>
    <table style="margin-left: 25%; margin-top: 10px; border: 2px solid LightGray;">
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Date_of_birth</th>
            <th>Blood Pressure</th>
        </tr>

        @foreach (var item in Model.patient_infoes)
        {
            <tr>
                <td>@item.ID</td>
                <td>@item.Name</td>
                <td>@item.Date_of_Birth</td>
                <td>@Blood_pressure</td>
            </tr>
        }
    </table>
</body>
</html>

用法

/**
 * Pixel copy to copy SurfaceView/VideoView into BitMap
 */
 fun usePixelCopy(videoView: SurfaceView,  callback: (Bitmap?) -> Unit) {
    val bitmap: Bitmap = Bitmap.createBitmap(
        videoView.width,
        videoView.height,
        Bitmap.Config.ARGB_8888
    );
    try {
    // Create a handler thread to offload the processing of the image.
    val handlerThread = HandlerThread("PixelCopier");
    handlerThread.start();
    PixelCopy.request(
        videoView, bitmap,
        PixelCopy.OnPixelCopyFinishedListener { copyResult ->
            if (copyResult == PixelCopy.SUCCESS) {
                callback(bitmap)
            }
            handlerThread.quitSafely();
        },
        Handler(handlerThread.looper)
    )
    } catch (e: IllegalArgumentException) {
        callback(null)
        // PixelCopy may throw IllegalArgumentException, make sure to handle it
        e.printStackTrace()
    }
}

注意:视频视图是SurfaceView的子类,因此该方法也可以获取视频视图的屏幕截图

答案 5 :(得分:0)

对于那些正在寻找API <= 24的解决方案的人来说,这就是我的解决方法。当用户单击捕获按钮时,关闭预览captureSession并创建一个新的captureSession用于静止图像捕获。

  mCaptureSession.stopRepeating();
  mCaptureSession.abortCaptures();
  mCaptureSession.close();
  mCaptureSession = null;

        mPreviewReader = ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(), ImageFormat.JPEG, MAX_IMAGES);
        mPreviewReader.setOnImageAvailableListener(mImageAvailableListener, mBackgroundHandler);

        mCameraDevice.createCaptureSession(
                Arrays.asList(mPreviewReader.getSurface()),
                //Arrays.asList(mSurface),
                new CameraCaptureSession.StateCallback() {
                    @Override
                    public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {

                        mCaptureSession = cameraCaptureSession;

                        try {
                            final CaptureRequest.Builder captureBuilder =
                                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                            captureBuilder.addTarget(mPreviewReader.getSurface());
                            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
                            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(mOrientation));
                            Log.d(TAG, "Capture request created.");
                            mCaptureSession.capture(captureBuilder.build(), mCaptureCallback, mBackgroundHandler);

                        } catch (CameraAccessException cae) {

                            Log.d(TAG, cae.toString());
                        }
                    }

                    @Override
                    public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {

                    }
                },
                mBackgroundHandler
        );

然后在onImageAvailableListener上,您可以使用imageReader.acquireNextImage()获取图像以获取仍然捕获的图像。