Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: media/base/android/java/src/org/chromium/media/ScreenCapture.java

Issue 1917023003: ScreenCapture for Android phase1, part I (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address review comments Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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.annotation.TargetApi;
8 import android.app.Activity;
9 import android.app.Fragment;
10 import android.app.FragmentManager;
11 import android.app.FragmentTransaction;
12 import android.content.Context;
13 import android.content.Intent;
14 import android.graphics.PixelFormat;
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.Build;
22 import android.os.Handler;
23 import android.os.HandlerThread;
24 import android.util.DisplayMetrics;
25 import android.view.Display;
26 import android.view.Surface;
27
28 import org.chromium.base.ApplicationStatus;
29 import org.chromium.base.Log;
30 import org.chromium.base.annotations.CalledByNative;
31 import org.chromium.base.annotations.JNINamespace;
32
33 import java.nio.ByteBuffer;
34
35 /**
36 * This class implements Screen Capture using projection API, introduced in Andr oid
37 * API 21 (L Release). Capture takes place in the current Looper, while pixel
38 * download takes place in another thread used by ImageReader.
39 **/
40 @JNINamespace("media")
41 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
42 public class ScreenCapture extends Fragment {
43 // Internal class implementing the ImageReader listener. Gets pinged when a
44 // new frame is been captured and downloaded to memory-backed buffers.
45 private class CrImageReaderListener implements ImageReader.OnImageAvailableL istener {
46 @Override
47 public void onImageAvailable(ImageReader reader) {
48 synchronized (mCaptureStateLock) {
49 if (mCaptureState != CaptureState.STARTED) {
50 Log.e(TAG, "Get captured frame in unexpected state.");
51 return;
52 }
53 }
54
55 Image image = null;
56 try {
57 image = reader.acquireLatestImage();
58 if (image == null) return;
59 if (image.getFormat() != PixelFormat.RGBA_8888) {
60 Log.e(TAG, "Unexpected image format: " + image.getFormat() + " or #planes: "
61 + image.getPlanes().length);
62 return;
63 }
64
65 Image.Plane[] planes = image.getPlanes();
66 ByteBuffer buffer = (ByteBuffer) planes[0].getBuffer();
67 buffer.get(mCapturedData);
miu 2016/05/10 00:29:00 This is copying the image data into a separate byt
braveyao 2016/05/18 00:41:46 Done.
68
69 nativeOnFrameAvailable(mNativeScreenCaptureMachineAndroid, mCapt uredData,
70 image.getCropRect().width(), image.getCropRect().height( ),
miu 2016/05/10 00:29:00 More information needs to be passed to the native
braveyao 2016/05/18 00:41:46 Yes you're right. I meet a case that stride is dif
71 image.getTimestamp());
72 } catch (IllegalStateException ex) {
73 Log.e(TAG, "acquireLatestImage():" + ex);
74 return;
75 } finally {
76 if (image != null) image.close();
77 }
78 }
79 }
80
81 private class MediaProjectionCallback extends MediaProjection.Callback {
82 @Override
83 public void onStop() {
84 changeCaptureStateAndNotify(CaptureState.STOPPED);
85 mMediaProjection = null;
86 if (mVirtualDisplay == null) return;
87 mVirtualDisplay.release();
88 mVirtualDisplay = null;
89 }
90 }
91
92 private byte[] mCapturedData;
93 private final Context mContext;
94
95 private MediaProjection mMediaProjection;
96 private MediaProjectionManager mMediaProjectionManager;
97 private VirtualDisplay mVirtualDisplay;
98 private static final int REQUEST_MEDIA_PROJECTION = 1;
99
100 private Surface mSurface;
101 private ImageReader mImageReader = null;
102 private int mScreenDensity;
103 private int mWidth;
104 private int mHeight;
105 private int mResultCode;
106 private Intent mResultData;
107
108 // Native callback context variable.
109 private final long mNativeScreenCaptureMachineAndroid;
110
111 private static enum CaptureState { ATTACHED, ALLOWED, STARTED, STOPPING, STO PPED }
112 private CaptureState mCaptureState = CaptureState.STOPPED;
113 private final Object mCaptureStateLock = new Object();
114
115 private static final String TAG = "ScreenCaptureMachine";
116
117 ScreenCapture(Context context, long nativeScreenCaptureMachineAndroid) {
118 mContext = context;
119 mNativeScreenCaptureMachineAndroid = nativeScreenCaptureMachineAndroid;
120
121 Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
122 if (activity == null) {
123 Log.e(TAG, "activity is null");
124 return;
125 }
126
127 try {
128 FragmentManager fragmentManager = activity.getFragmentManager();
129 FragmentTransaction fragmentTransaction = fragmentManager.beginTrans action();
130 fragmentTransaction.add(this, "screencapture");
131 fragmentTransaction.commit();
132 } catch (Exception e) {
133 Log.e(TAG, "ScreenCaptureExcaption " + e);
134 }
135
136 try {
137 DisplayMetrics metrics = new DisplayMetrics();
138 Display display = activity.getWindowManager().getDefaultDisplay();
139 display.getMetrics(metrics);
140 mScreenDensity = metrics.densityDpi;
141 } catch (Exception e) {
142 Log.e(TAG, "ScreenCaptureExcaption " + e);
143 }
144 }
145
146 // Factory methods.
147 @CalledByNative
148 static ScreenCapture createScreenCaptureMachine(
149 Context context, long nativeScreenCaptureMachineAndroid) {
150 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
151 return new ScreenCapture(context, nativeScreenCaptureMachineAndroid) ;
152 }
153 return null;
154 }
155
156 @Override
157 public void onAttach(Context context) {
158 super.onAttach(context);
159 Log.d(TAG, "onAttach");
160 changeCaptureStateAndNotify(CaptureState.ATTACHED);
161 }
162
163 // This method was deprecated in API level 23 by onAttach(Context).
164 // TODO(braveyao): remove this method after the minSdkVersion of chromium is 23.
165 @SuppressWarnings("deprecation")
166 @Override
167 public void onAttach(Activity activity) {
168 super.onAttach(activity);
169 Log.d(TAG, "onAttach");
170 changeCaptureStateAndNotify(CaptureState.ATTACHED);
171 }
172
173 @Override
174 public void onDetach() {
175 super.onDetach();
176 Log.d(TAG, "onDetach");
177 stopCapture();
178 }
179
180 @CalledByNative
181 public boolean startPrompt(int width, int height) {
182 Log.d(TAG, "startPrompt");
183 synchronized (mCaptureStateLock) {
184 while (mCaptureState != CaptureState.ATTACHED) {
185 try {
186 mCaptureStateLock.wait();
187 } catch (InterruptedException ex) {
188 Log.e(TAG, "ScreenCaptureException: " + ex);
189 }
190 }
191 }
192
193 try {
194 mWidth = width;
195 mHeight = height;
196 int expectedFrameSize = mWidth * mHeight * 4;
197 mCapturedData = new byte[expectedFrameSize];
198
199 mMediaProjectionManager = (MediaProjectionManager) mContext.getSyste mService(
200 Context.MEDIA_PROJECTION_SERVICE);
201 if (mMediaProjectionManager == null) {
202 Log.e(TAG, "mMediaProjectionManager is null");
203 return false;
204 }
205
206 startActivityForResult(
207 mMediaProjectionManager.createScreenCaptureIntent(), REQUEST _MEDIA_PROJECTION);
208 } catch (Exception e) {
209 Log.e(TAG, "ScreenCaptureException " + e);
210 return false;
211 }
212 return true;
213 }
214
215 @Override
216 public void onActivityResult(int requestCode, int resultCode, Intent data) {
217 if (requestCode == REQUEST_MEDIA_PROJECTION) {
218 boolean result;
219 if (resultCode != Activity.RESULT_OK) {
220 result = false;
221 } else {
222 result = true;
223 mResultCode = resultCode;
224 mResultData = data;
225 changeCaptureStateAndNotify(CaptureState.ALLOWED);
226 }
227 nativeOnActivityResult(mNativeScreenCaptureMachineAndroid, result);
mcasas 2016/05/04 19:56:48 s/result/resultCode == Activity.RESULT_OK/ ?
braveyao 2016/05/18 00:41:46 Done.
228 }
229 }
230
231 @CalledByNative
232 public void startCapture() {
233 Log.d(TAG, "startCapture");
234 synchronized (mCaptureStateLock) {
235 if (mCaptureState != CaptureState.ALLOWED) {
236 Log.e(TAG, "startCapture() invoked without user permission.");
237 return;
238 }
239 }
240 mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCod e, mResultData);
241 if (mMediaProjection == null) {
242 Log.e(TAG, "mMediaProjection is null");
243 return;
244 }
245 mMediaProjection.registerCallback(new MediaProjectionCallback(), null);
246
247 final int maxImages = 2;
248 mImageReader = ImageReader.newInstance(mWidth, mHeight, PixelFormat.RGBA _8888, maxImages);
249 mSurface = mImageReader.getSurface();
250 HandlerThread thread = new HandlerThread("Screen");
251 thread.start();
252 final Handler backgroundHandler = new Handler(thread.getLooper());
253 final CrImageReaderListener imageReaderListener = new CrImageReaderListe ner();
254 mImageReader.setOnImageAvailableListener(imageReaderListener, background Handler);
255
256 mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture", mWidth, mHeight,
257 mScreenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mSurface, null,
258 null);
259
260 changeCaptureStateAndNotify(CaptureState.STARTED);
261 }
262
263 @CalledByNative
264 public void stopCapture() {
265 Log.d(TAG, "stopCapture");
266 synchronized (mCaptureStateLock) {
267 if (mMediaProjection != null && mCaptureState == CaptureState.STARTE D) {
268 mMediaProjection.stop();
269 changeCaptureStateAndNotify(CaptureState.STOPPING);
270 }
271
272 while (mCaptureState != CaptureState.STOPPED) {
273 try {
274 mCaptureStateLock.wait();
275 } catch (InterruptedException ex) {
276 Log.e(TAG, "ScreenCaptureEvent: " + ex);
277 }
278 }
279 }
280 }
281
282 private void changeCaptureStateAndNotify(CaptureState state) {
283 synchronized (mCaptureStateLock) {
284 mCaptureState = state;
285 mCaptureStateLock.notifyAll();
286 }
287 }
288
289 // Method for ScreenCapture implementations to call back native code.
290 private native void nativeOnFrameAvailable(long nativeScreenCaptureMachineAn droid, byte[] data,
291 int cropWidth, int cropHeight, long timestamp);
292
293 // Method for ScreenCapture implementations to call back native code.
294 private native void nativeOnActivityResult(
295 long nativeScreenCaptureMachineAndroid, boolean result);
296 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698