OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.media; | |
6 | |
7 import android.app.Activity; | |
8 import android.app.Fragment; | |
9 import android.app.FragmentManager; | |
10 import android.app.FragmentTransaction; | |
11 import android.content.Context; | |
12 import android.content.Intent; | |
13 import android.graphics.PixelFormat; | |
14 import android.graphics.Point; | |
15 import android.hardware.display.DisplayManager; | |
16 import android.hardware.display.VirtualDisplay; | |
17 import android.media.Image; | |
18 import android.media.ImageReader; | |
19 import android.media.projection.MediaProjection; | |
20 import android.media.projection.MediaProjectionManager; | |
21 import android.os.Handler; | |
22 import android.os.HandlerThread; | |
23 import android.util.DisplayMetrics; | |
24 import android.view.Display; | |
25 import android.view.Surface; | |
26 | |
27 import org.chromium.base.BaseChromiumApplication; | |
28 import org.chromium.base.CalledByNative; | |
29 import org.chromium.base.JNINamespace; | |
30 import org.chromium.base.Log; | |
31 | |
32 import java.nio.ByteBuffer; | |
33 | |
34 /** | |
35 * This class implements Screen Capture using projection API, introduced in Andr oid | |
36 * API 21 (L Release). Capture takes place in the current Looper, while pixel | |
37 * download takes place in another thread used by ImageReader. | |
38 **/ | |
39 @JNINamespace("media") | |
40 public class ScreenCapture extends Fragment { | |
41 | |
42 // Internal class implementing the ImageReader listener. Gets pinged when a | |
43 // new frame is been captured and downloaded to memory-backed buffers. | |
44 private class CrImageReaderListener implements ImageReader.OnImageAvailableL istener { | |
45 @Override | |
46 public void onImageAvailable(ImageReader reader) { | |
47 Image image = null; | |
48 try { | |
49 image = reader.acquireLatestImage(); | |
50 if (image == null) return; | |
51 if (image.getFormat() != PixelFormat.RGBA_8888) { | |
52 Log.e(TAG, "Unexpected image format: " + image.getFormat() | |
53 + " or #planes: " + image.getPlanes().length); | |
54 return; | |
55 } | |
56 | |
57 Image.Plane[] planes = image.getPlanes(); | |
58 ByteBuffer buffer = (ByteBuffer) planes[0].getBuffer(); | |
59 buffer.get(mCapturedData); | |
60 | |
61 nativeOnFrameAvailable(mNativeScreenCapturerAndroid, | |
miu
2015/05/20 03:33:27
You need to pass extra values here:
1. image.getC
| |
62 mCapturedData, | |
63 mWidth, | |
64 mHeight); | |
65 | |
66 } catch (IllegalStateException ex) { | |
67 Log.e(TAG, "acquireLatestImage():" + ex); | |
68 return; | |
69 } finally { | |
70 if (image != null) { | |
71 image.close(); | |
72 } | |
73 } | |
74 } | |
75 }; | |
76 | |
77 private byte[] mCapturedData; | |
78 private final Context mContext; | |
79 | |
80 private MediaProjection mMediaProjection; | |
81 private MediaProjectionManager mMediaProjectionManager; | |
82 private VirtualDisplay mVirtualDisplay; | |
83 private static final int REQUEST_MEDIA_PROJECTION = 1; | |
84 | |
85 private Surface mSurface; | |
86 private ImageReader mImageReader = null; | |
87 private int mScreenDensity; | |
88 private int mWidth; | |
89 private int mHeight; | |
90 private int mResultCode; | |
91 private Intent mResultData; | |
92 // Native callback context variable. | |
93 private final long mNativeScreenCapturerAndroid; | |
94 | |
95 private static final String TAG = "ScreenCaptureAndroid"; | |
96 | |
97 ScreenCapture(Context context, long nativeScreenCapturerAndroid) { | |
98 mContext = context; | |
99 mNativeScreenCapturerAndroid = nativeScreenCapturerAndroid; | |
100 } | |
101 | |
102 @CalledByNative | |
103 public void start() { | |
104 Log.i(TAG, "start"); | |
105 try { | |
106 BaseChromiumApplication ba = (BaseChromiumApplication) mContext; | |
107 Activity activity = ba.getActivity(); | |
108 if (activity == null) { | |
109 Log.e(TAG, "activity is null"); | |
110 return; | |
111 } | |
112 | |
113 FragmentManager fragmentManager = activity.getFragmentManager(); | |
114 FragmentTransaction fragmentTransaction = fragmentManager.beginTrans action(); | |
115 fragmentTransaction.add(this, "screencapture"); | |
116 fragmentTransaction.commit(); | |
117 | |
118 DisplayMetrics metrics = new DisplayMetrics(); | |
119 Point size = new Point(); | |
120 Display display = activity.getWindowManager().getDefaultDisplay(); | |
121 display.getMetrics(metrics); | |
122 display.getSize(size); | |
123 mScreenDensity = metrics.densityDpi; | |
124 //mWidth = size.x; | |
miu
2015/05/20 03:33:27
Did you mean to fix these LOC rather than hard-cod
| |
125 //mHeight = size.y; | |
126 // Fixeme: If use size.x and size.y as the width and height, the scr een image | |
127 // will be disordered. | |
128 mWidth = 640; | |
129 mHeight = 480; | |
130 int expectedFrameSize = mWidth * mHeight * 4; | |
131 mCapturedData = new byte[expectedFrameSize]; | |
132 } catch (Exception e) { | |
133 Log.e(TAG, "ScreenCapture " + e); | |
134 return; | |
135 } | |
136 | |
137 mMediaProjectionManager = (MediaProjectionManager) | |
138 mContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE); | |
139 if (mMediaProjectionManager == null) { | |
140 Log.e(TAG, "mMediaProjectionManager is null"); | |
141 return; | |
142 } | |
143 | |
144 startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent (), | |
145 REQUEST_MEDIA_PROJECTION); | |
146 } | |
147 | |
148 @Override | |
149 public void onActivityResult(int requestCode, int resultCode, Intent data) { | |
150 if (requestCode == REQUEST_MEDIA_PROJECTION) { | |
151 int result; | |
152 if (resultCode != Activity.RESULT_OK) { | |
153 result = 0; | |
154 } else { | |
155 result = 1; | |
156 mResultCode = resultCode; | |
157 mResultData = data; | |
158 } | |
159 nativeOnActivityResult(mNativeScreenCapturerAndroid, result); | |
160 } | |
161 } | |
162 | |
163 // start screen capture. | |
164 @CalledByNative | |
165 public void startCapture() { | |
166 Log.i(TAG, "startCapture"); | |
167 mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCod e, mResultData); | |
168 if (mMediaProjection == null) { | |
169 Log.i(TAG, "mMediaProjection is null"); | |
170 return; | |
171 } | |
172 | |
173 mImageReader = ImageReader.newInstance(mWidth, mHeight, PixelFormat.RGBA _8888, 2); | |
miu
2015/05/20 03:33:27
Could you add a comment here about why "2" is a re
| |
174 mSurface = mImageReader.getSurface(); | |
175 HandlerThread thread = new HandlerThread("ScreenPreview"); | |
miu
2015/05/20 03:33:27
ScreenPreview?
| |
176 thread.start(); | |
177 final Handler backgroundHandler = new Handler(thread.getLooper()); | |
178 final CrImageReaderListener imageReaderListener = new CrImageReaderListe ner(); | |
179 mImageReader.setOnImageAvailableListener(imageReaderListener, | |
180 backgroundHandler); | |
181 | |
182 mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture", | |
183 mWidth, mHeight, mScreenDensity, | |
184 DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, | |
185 mSurface, null, null); | |
186 } | |
187 | |
188 // Stops screen capture. | |
189 @CalledByNative | |
190 public void stopCapture() { | |
191 Log.i(TAG, "stopCapture"); | |
192 if (mImageReader != null) mImageReader.close(); | |
193 | |
194 if (mVirtualDisplay != null) { | |
195 mVirtualDisplay.release(); | |
196 mVirtualDisplay = null; | |
197 } | |
198 if (mMediaProjection != null) { | |
199 mMediaProjection.stop(); | |
200 mMediaProjection = null; | |
201 } | |
202 } | |
203 | |
204 // Method for ScreenCapture implementations to call back native code. | |
205 public native void nativeOnFrameAvailable(long nativeScreenCapturerAndroid, | |
206 byte[] data, | |
207 int width, | |
208 int height); | |
209 | |
210 // Method for ScreenCapture implementations to call back native code. | |
211 public native void nativeOnActivityResult(long nativeScreenCapturerAndroid, | |
212 int result); | |
213 | |
214 } | |
OLD | NEW |