Index: media/base/android/java/src/org/chromium/media/ScreenCapture.java |
diff --git a/media/base/android/java/src/org/chromium/media/ScreenCapture.java b/media/base/android/java/src/org/chromium/media/ScreenCapture.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8174f729c82529c1a76ff4d1d1c3b046358100ef |
--- /dev/null |
+++ b/media/base/android/java/src/org/chromium/media/ScreenCapture.java |
@@ -0,0 +1,234 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.media; |
+ |
+import android.app.Activity; |
+import android.app.Fragment; |
+import android.app.FragmentManager; |
+import android.app.FragmentTransaction; |
+import android.content.Context; |
+import android.content.Intent; |
+import android.graphics.PixelFormat; |
+import android.hardware.display.DisplayManager; |
+import android.hardware.display.VirtualDisplay; |
+import android.media.Image; |
+import android.media.ImageReader; |
+//import android.media.projection.MediaProjection.Callback; |
whywhat
2015/08/17 13:58:46
nit: Uncomment or remove?
|
+import android.media.projection.MediaProjection; |
+import android.media.projection.MediaProjectionManager; |
+import android.os.Handler; |
+import android.os.HandlerThread; |
+import android.util.DisplayMetrics; |
+import android.view.Display; |
+import android.view.Surface; |
+ |
+import org.chromium.base.BaseChromiumApplication; |
+import org.chromium.base.CalledByNative; |
+import org.chromium.base.JNINamespace; |
+import org.chromium.base.Log; |
+ |
+import java.nio.ByteBuffer; |
+ |
+/** |
+ * This class implements Screen Capture using projection API, introduced in Android |
+ * API 21 (L Release). Capture takes place in the current Looper, while pixel |
+ * download takes place in another thread used by ImageReader. |
+ **/ |
+@JNINamespace("media") |
+public class ScreenCapture extends Fragment { |
+ |
+ // Internal class implementing the ImageReader listener. Gets pinged when a |
+ // new frame is been captured and downloaded to memory-backed buffers. |
+ private class CrImageReaderListener implements ImageReader.OnImageAvailableListener { |
+ @Override |
+ public void onImageAvailable(ImageReader reader) { |
+ Image image = null; |
+ try { |
+ image = reader.acquireLatestImage(); |
+ if (image == null) return; |
+ if (image.getFormat() != PixelFormat.RGBA_8888) { |
+ Log.e(TAG, "Unexpected image format: " + image.getFormat() |
+ + " or #planes: " + image.getPlanes().length); |
+ return; |
+ } |
+ |
+ Image.Plane[] planes = image.getPlanes(); |
+ ByteBuffer buffer = (ByteBuffer) planes[0].getBuffer(); |
+ buffer.get(mCapturedData); |
+ |
+ nativeOnFrameAvailable(mNativeScreenCaptureMachineAndroid, |
+ mCapturedData, |
+ image.getCropRect().width(), |
+ image.getCropRect().height(), |
+ image.getTimestamp()); |
+ |
+ } catch (IllegalStateException ex) { |
+ Log.e(TAG, "acquireLatestImage():" + ex); |
+ return; |
+ } finally { |
+ if (image != null) { |
whywhat
2015/08/17 13:58:46
nit: a one liner
|
+ image.close(); |
+ } |
+ } |
+ } |
+ }; |
+ |
+ private class MediaProjectionCallback extends MediaProjection.Callback { |
+ @Override |
+ public void onStop() { |
+ mMediaProjection = null; |
+ if (mVirtualDisplay == null) { |
whywhat
2015/08/17 13:58:46
nit: one liner
|
+ return; |
+ } |
+ mVirtualDisplay.release(); |
+ mVirtualDisplay = null; |
+ } |
+ } |
+ |
+ private byte[] mCapturedData; |
+ private final Context mContext; |
+ |
+ private MediaProjection mMediaProjection; |
+ private MediaProjectionManager mMediaProjectionManager; |
+ private VirtualDisplay mVirtualDisplay; |
+ private static final int REQUEST_MEDIA_PROJECTION = 1; |
+ |
+ private Surface mSurface; |
+ private ImageReader mImageReader = null; |
+ private int mScreenDensity; |
+ private int mWidth; |
+ private int mHeight; |
+ private int mResultCode; |
+ private Intent mResultData; |
+ |
+ // Native callback context variable. |
+ private final long mNativeScreenCaptureMachineAndroid; |
+ |
+ private static final String TAG = "ScreenCaptureMachine"; |
+ |
+ ScreenCapture(Context context, long nativeScreenCaptureMachineAndroid) { |
+ mContext = context; |
+ mNativeScreenCaptureMachineAndroid = nativeScreenCaptureMachineAndroid; |
+ try { |
+ BaseChromiumApplication ba = (BaseChromiumApplication) mContext; |
+ Activity activity = ba.getActivity(); |
+ if (activity == null) { |
+ Log.e(TAG, "activity is null"); |
+ return; |
+ } |
+ |
+ FragmentManager fragmentManager = activity.getFragmentManager(); |
+ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); |
+ fragmentTransaction.add(this, "screencapture"); |
+ fragmentTransaction.commit(); |
+ |
+ DisplayMetrics metrics = new DisplayMetrics(); |
+ Display display = activity.getWindowManager().getDefaultDisplay(); |
+ display.getMetrics(metrics); |
+ mScreenDensity = metrics.densityDpi; |
+ } catch (Exception e) { |
+ Log.e(TAG, "ScreenCaptureExcaption " + e); |
+ } |
+ } |
+ |
+ @Override |
+ public void onDetach() { |
+ super.onDetach(); |
+ Log.i(TAG, "onDetach"); |
+ stopCapture(); |
+ } |
+ |
+ @CalledByNative |
+ public boolean startPrompt(int width, int height) { |
+ Log.i(TAG, "startPrompt"); |
+ try { |
+ if (!isAdded()) { |
+ Log.e(TAG, "the fragment hasn't been added to an activity"); |
+ return false; |
+ } |
+ mWidth = width; |
+ mHeight = height; |
+ int expectedFrameSize = mWidth * mHeight * 4; |
+ mCapturedData = new byte[expectedFrameSize]; |
+ |
+ mMediaProjectionManager = (MediaProjectionManager) |
+ mContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE); |
+ if (mMediaProjectionManager == null) { |
+ Log.e(TAG, "mMediaProjectionManager is null"); |
+ return false; |
+ } |
+ |
+ startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), |
+ REQUEST_MEDIA_PROJECTION); |
+ } catch (Exception e) { |
+ Log.e(TAG, "ScreenCaptureExcaption " + e); |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ @Override |
+ public void onActivityResult(int requestCode, int resultCode, Intent data) { |
+ if (requestCode == REQUEST_MEDIA_PROJECTION) { |
+ int result; |
+ if (resultCode != Activity.RESULT_OK) { |
+ result = 0; |
+ } else { |
+ result = 1; |
+ mResultCode = resultCode; |
+ mResultData = data; |
+ } |
+ nativeOnActivityResult(mNativeScreenCaptureMachineAndroid, result); |
+ } |
+ } |
+ |
+ // start screen capture. |
whywhat
2015/08/17 13:58:46
nit: follow Javadoc style for public/protected cla
|
+ @CalledByNative |
+ public void startCapture() { |
+ Log.i(TAG, "startCapture"); |
whywhat
2015/08/17 13:58:46
nit: s/Log.i/Log.d?
|
+ mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData); |
+ if (mMediaProjection == null) { |
+ Log.i(TAG, "mMediaProjection is null"); |
+ return; |
+ } |
+ mMediaProjection.registerCallback(new MediaProjectionCallback(), null); |
+ |
+ final int maxImages = 2; |
+ mImageReader = ImageReader.newInstance(mWidth, mHeight, PixelFormat.RGBA_8888, maxImages); |
+ mSurface = mImageReader.getSurface(); |
+ HandlerThread thread = new HandlerThread("Screen"); |
+ thread.start(); |
+ final Handler backgroundHandler = new Handler(thread.getLooper()); |
+ final CrImageReaderListener imageReaderListener = new CrImageReaderListener(); |
+ mImageReader.setOnImageAvailableListener(imageReaderListener, |
+ backgroundHandler); |
+ |
+ mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture", |
+ mWidth, mHeight, mScreenDensity, |
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, |
+ mSurface, null, null); |
+ } |
+ |
+ // Stops screen capture. |
+ @CalledByNative |
+ public void stopCapture() { |
+ Log.i(TAG, "stopCapture"); |
whywhat
2015/08/17 13:58:46
ditto
|
+ if (mMediaProjection != null) { |
+ mMediaProjection.stop(); |
whywhat
2015/08/17 13:58:46
nit: fits on one line
|
+ } |
+ } |
+ |
+ // Method for ScreenCapture implementations to call back native code. |
+ public native void nativeOnFrameAvailable(long nativeScreenCaptureMachineAndroid, |
+ byte[] data, |
+ int cropWidth, |
+ int cropHeight, |
+ long timestamp); |
+ |
+ // Method for ScreenCapture implementations to call back native code. |
+ public native void nativeOnActivityResult(long nativeScreenCaptureMachineAndroid, |
whywhat
2015/08/17 13:58:46
Do these or @CalledByNative methdos have to be pub
|
+ int result); |
+ |
+} |