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

Side by Side Diff: webrtc/api/android/java/src/org/webrtc/ScreenCapturerAndroid.java

Issue 2276593003: Android Screen Capturer. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Manage MediaProjection inside ScreenCapturerAndroid and allow calling changeFormat() whether captur… Created 4 years, 3 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 /*
2 * Copyright 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 package org.webrtc;
12
13 import android.annotation.TargetApi;
14 import android.app.Activity;
15 import android.content.Context;
16 import android.content.Intent;
17 import android.hardware.display.DisplayManager;
18 import android.hardware.display.VirtualDisplay;
19 import android.media.projection.MediaProjection;
20 import android.media.projection.MediaProjectionManager;
21 import android.util.Log;
22 import android.view.Surface;
23
24 import java.util.ArrayList;
25 import java.util.List;
26
27 /**
28 * An implementation of VideoCapturer to capture the screen content as a video s tream.
29 * Capturing is done by {@code MediaProjection} on a {@code SurfaceTexture}. We interact with this
30 * {@code SurfaceTexture} using a {@code SurfaceTextureHelper}.
31 * The {@code SurfaceTextureHelper} is created by the native code and passed to this capturer in
32 * {@code VideoCapturer.initialize()}. On receiving a new frame, this capturer p asses it
33 * as a texture to the native code via {@code CapturerObserver.onTextureFrameCap tured()}. This takes
34 * place on the HandlerThread of the given {@code SurfaceTextureHelper}. When do ne with each frame,
35 * the native code returns the buffer to the {@code SurfaceTextureHelper} to be used for new
36 * frames. At any time, at most one frame is being processed.
37 *
38 * Note that startCapture(), stopCapture(), and dispose() are called from native code.
39 * Normally, a Java application should interact with {@code }VideSource} API, wh ich indirectly calls
40 * these methods on the underlying {@code VideoCapturer}.
41 */
42 @TargetApi(21)
43 public class ScreenCapturerAndroid implements
44 VideoCapturer, SurfaceTextureHelper.OnTextureFrameAvailableListener {
45
46 private static final int DISPLAY_FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_P UBLIC
47 | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
48 // DPI for VirtualDisplay, does not seem to matter for us.
49 private static final int VIRTUAL_DISPLAY_DPI = 400;
50
51 private final Intent mediaProjectionPermissionResultData;
52 private final MediaProjection.Callback mediaProjectionCallback;
53
54 private int width;
55 private int height;
56 private VirtualDisplay virtualDisplay;
57 private SurfaceTextureHelper surfaceTextureHelper;
58 private CapturerObserver capturerObserver;
59 private long numCapturedFrames = 0;
60 private MediaProjection mediaProjection;
61 private boolean isDisposed = false;
62 private MediaProjectionManager mediaProjectionManager;
63
64 /**
65 * Constructs a new Screen Capturer. To avoid distortion, make sure width and height have the
66 * same aspect ratio of the captured screen.
67 *
68 * @param width output video width
69 * @param width output video height
70 * @param mediaProjectionPermissionResultData the result data of MediaProjecti on permission
71 * activity; the calling app must validate that result code is Activity.RE SULT_OK before
72 * calling this method.
73 * @param mediaProjectionCallback MediaProjection callback to implement applic ation specific
74 * logic in events such as when the user revokes a previously granted capt ure permission.
75 **/
76 public ScreenCapturerAndroid(int width, int height,
magjed_webrtc 2016/08/30 10:50:54 You should be able to remove width and height as a
arsany 2016/08/30 20:23:18 Done.
77 Intent mediaProjectionPermissionResultData,
78 MediaProjection.Callback mediaProjectionCallback) {
79 this.width = width;
80 this.height = height;
81 this.mediaProjectionPermissionResultData = mediaProjectionPermissionResultDa ta;
82 this.mediaProjectionCallback = mediaProjectionCallback;
83 }
84
85 private void checkNotDisposed() {
86 if (isDisposed) {
87 throw new RuntimeException("capturer is disposed.");
88 }
89 }
90
91 @Override
92 public synchronized void initialize(
93 final SurfaceTextureHelper surfaceTextureHelper,
94 final Context applicationContext,
95 final VideoCapturer.CapturerObserver capturerObserver) {
96 checkNotDisposed();
97
98 if (capturerObserver == null) {
99 throw new RuntimeException("capturerObserver not set.");
100 }
101 this.capturerObserver = capturerObserver;
102
103 if (surfaceTextureHelper == null) {
104 throw new RuntimeException("surfaceTextureHelper not set.");
105 }
106 this.surfaceTextureHelper = surfaceTextureHelper;
107
108 mediaProjectionManager = (MediaProjectionManager)
109 applicationContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
110 }
111
112 @Override
113 public synchronized List<CameraEnumerationAndroid.CaptureFormat> getSupportedF ormats() {
114 List<CameraEnumerationAndroid.CaptureFormat> supportedFormats = new ArrayLis t<>();
115 supportedFormats.add(new CameraEnumerationAndroid.CaptureFormat(
116 width, height, 0 /* minFrameRate */, 60 /* maxFrameRate */));
117 return supportedFormats;
118 }
119
120 // Initially called by native code. This can also be called from native code w hen the
121 // enclosing VideoSource is "restarted" after being stopped.
122 @Override
123 public synchronized void startCapture(
124 final int ignored_width,
125 final int ignored_height,
126 final int ignored_framerate) {
127 checkNotDisposed();
128
129 mediaProjection = mediaProjectionManager.getMediaProjection(
130 Activity.RESULT_OK, mediaProjectionPermissionResultData);
131
132 // Let MediaProjection callback use the SurfaceTextureHelper thread.
133 ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() {
134 @Override
135 public void run() {
136 mediaProjection.registerCallback(mediaProjectionCallback, null);
magjed_webrtc 2016/08/30 10:50:54 Instead of invoking, you can just pass the handler
arsany 2016/08/30 20:23:17 Done.
137 }
138 });
139
140 createVirtualDisplay();
141 capturerObserver.onCapturerStarted(true);
142 surfaceTextureHelper.startListening(ScreenCapturerAndroid.this);
143 }
144
145 // Called by native code to pause capturing.
146 @Override
147 public synchronized void stopCapture() {
148 checkNotDisposed();
149 ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() {
150 @Override
151 public void run() {
152 surfaceTextureHelper.stopListening();
153 capturerObserver.onCapturerStopped();
154
155 if (virtualDisplay != null) {
156 virtualDisplay.release();
157 virtualDisplay = null;
158 }
159
160 if (mediaProjection != null) {
161 // Unregister the callback before stopping, otherwise the callback rec ursively
162 // calls this method.
163 mediaProjection.unregisterCallback(mediaProjectionCallback);
164 mediaProjection.stop();
165 mediaProjection = null;
166 }
167 }
168 });
169 }
170
171 // Called from native code to dispose the capturer when the enclosing VideoSou rce is disposed.
172 // The native code calls stopCapture() before calling this method.
173 @Override
174 public synchronized void dispose() {
175 isDisposed = true;
176 }
177
178 @Override
179 public synchronized void onOutputFormatRequest(
180 final int width, final int height, final int framerate) {
181 checkNotDisposed();
182 surfaceTextureHelper.getHandler().post(new Runnable() {
183 @Override
184 public void run() {
185 capturerObserver.onOutputFormatRequest(width, height, framerate);
186 }
187 });
188 }
189
190 /**
191 * Changes output video format. This method can be used to scale the output
192 * video, or to change orientation when the captured screen is rotated for exa mple.
193 *
194 * @param width new output video width
195 * @param height new output video height
196 * @param ignored_framerate ignored
197 */
198 @Override
199 public synchronized void changeCaptureFormat(
200 final int width, final int height, final int ignored_framerate) {
201 checkNotDisposed();
202
203 this.width = width;
204 this.height = height;
205
206 if (virtualDisplay == null) {
207 // Capturer is stopped, the virtual display will be created in startCaptue r().
208 return;
209 }
210
211 // Create a new virtual display on the surfaceTextureHelper thread to avoid interference
212 // with frame processing, which happens on the same thread (we serialize eve nts by running
213 // them on the same thread).
214 ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() {
215 @Override
216 public void run() {
217 virtualDisplay.release();
218 createVirtualDisplay();
219 }
220 });
221 }
222
223 private void createVirtualDisplay() {
224 surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, height) ;
225 virtualDisplay = mediaProjection.createVirtualDisplay(
226 "WebRTC_ScreenCapture", width, height, VIRTUAL_DISPLAY_DPI,
227 DISPLAY_FLAGS, new Surface(surfaceTextureHelper.getSurfaceTexture()),
228 null /* callback */, null /* callback handler */);
229 }
230
231 // This is called on the internal looper thread of {@Code SurfaceTextureHelper }.
232 @Override
233 public void onTextureFrameAvailable(int oesTextureId, float[] transformMatrix, long timestampNs) {
234 numCapturedFrames++;
235 capturerObserver.onTextureFrameCaptured(width, height, oesTextureId, transfo rmMatrix,
236 0 /* rotation */, timestampNs);
237 }
238
239 @Override
240 public boolean isScreencast() {
241 return true;
242 }
243
244 public long getNumCapturedFrames() {
245 return numCapturedFrames;
246 }
247 }
248
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698