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

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: merge master 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.view.Surface;
22
23 import java.util.ArrayList;
24 import java.util.List;
25
26 /**
27 * An implementation of VideoCapturer to capture the screen content as a video s tream.
28 * Capturing is done by {@code MediaProjection} on a {@code SurfaceTexture}. We interact with this
29 * {@code SurfaceTexture} using a {@code SurfaceTextureHelper}.
30 * The {@code SurfaceTextureHelper} is created by the native code and passed to this capturer in
31 * {@code VideoCapturer.initialize()}. On receiving a new frame, this capturer p asses it
32 * as a texture to the native code via {@code CapturerObserver.onTextureFrameCap tured()}. This takes
33 * place on the HandlerThread of the given {@code SurfaceTextureHelper}. When do ne with each frame,
34 * the native code returns the buffer to the {@code SurfaceTextureHelper} to be used for new
35 * frames. At any time, at most one frame is being processed.
36 */
37 @TargetApi(21)
38 public class ScreenCapturerAndroid implements
39 VideoCapturer, SurfaceTextureHelper.OnTextureFrameAvailableListener {
40
41 private static final int DISPLAY_FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_P UBLIC
42 | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
43 // DPI for VirtualDisplay, does not seem to matter for us.
44 private static final int VIRTUAL_DISPLAY_DPI = 400;
45
46 private final Intent mediaProjectionPermissionResultData;
47 private final MediaProjection.Callback mediaProjectionCallback;
48
49 private int width;
50 private int height;
51 private VirtualDisplay virtualDisplay;
52 private SurfaceTextureHelper surfaceTextureHelper;
53 private CapturerObserver capturerObserver;
54 private long numCapturedFrames = 0;
55 private MediaProjection mediaProjection;
56 private boolean isDisposed = false;
57 private MediaProjectionManager mediaProjectionManager;
58
59 /**
60 * Constructs a new Screen Capturer.
61 *
62 * @param mediaProjectionPermissionResultData the result data of MediaProjecti on permission
63 * activity; the calling app must validate that result code is Activity.RE SULT_OK before
64 * calling this method.
65 * @param mediaProjectionCallback MediaProjection callback to implement applic ation specific
66 * logic in events such as when the user revokes a previously granted capt ure permission.
67 **/
68 public ScreenCapturerAndroid(
69 Intent mediaProjectionPermissionResultData,
70 MediaProjection.Callback mediaProjectionCallback) {
71 this.mediaProjectionPermissionResultData = mediaProjectionPermissionResultDa ta;
72 this.mediaProjectionCallback = mediaProjectionCallback;
73 }
74
75 private void checkNotDisposed() {
76 if (isDisposed) {
77 throw new RuntimeException("capturer is disposed.");
78 }
79 }
80
81 @Override
82 public synchronized void initialize(
83 final SurfaceTextureHelper surfaceTextureHelper,
84 final Context applicationContext,
85 final VideoCapturer.CapturerObserver capturerObserver) {
86 checkNotDisposed();
87
88 if (capturerObserver == null) {
89 throw new RuntimeException("capturerObserver not set.");
90 }
91 this.capturerObserver = capturerObserver;
92
93 if (surfaceTextureHelper == null) {
94 throw new RuntimeException("surfaceTextureHelper not set.");
95 }
96 this.surfaceTextureHelper = surfaceTextureHelper;
97
98 mediaProjectionManager = (MediaProjectionManager)
99 applicationContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
100 }
101
102 /**
103 * This method is not called by native code, neither by the java app. It can b e removed later
104 * once it is removed from the VideoCapturer interface.
105 */
106 @Override
107 public List<CameraEnumerationAndroid.CaptureFormat> getSupportedFormats() {
108 return null;
109 }
110
111 @Override
112 public synchronized void startCapture(final int width, final int height,
113 final int ignoredFramerate) {
114 checkNotDisposed();
115
116 this.width = width;
117 this.height = height;
118
119 mediaProjection = mediaProjectionManager.getMediaProjection(
120 Activity.RESULT_OK, mediaProjectionPermissionResultData);
121
122 // Let MediaProjection callback use the SurfaceTextureHelper thread.
123 mediaProjection.registerCallback(mediaProjectionCallback, surfaceTextureHelp er.getHandler());
124
125 createVirtualDisplay();
126 capturerObserver.onCapturerStarted(true);
127 surfaceTextureHelper.startListening(ScreenCapturerAndroid.this);
128 }
129
130 @Override
131 public synchronized void stopCapture() {
132 checkNotDisposed();
133 ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() {
134 @Override
135 public void run() {
136 surfaceTextureHelper.stopListening();
137 capturerObserver.onCapturerStopped();
138
139 if (virtualDisplay != null) {
140 virtualDisplay.release();
141 virtualDisplay = null;
142 }
143
144 if (mediaProjection != null) {
145 // Unregister the callback before stopping, otherwise the callback rec ursively
146 // calls this method.
147 mediaProjection.unregisterCallback(mediaProjectionCallback);
148 mediaProjection.stop();
149 mediaProjection = null;
150 }
151 }
152 });
153 }
154
155
156 @Override
157 public synchronized void dispose() {
158 isDisposed = true;
159 }
160
161 @Override
162 public synchronized void onOutputFormatRequest(
163 final int width, final int height, final int framerate) {
164 checkNotDisposed();
165 surfaceTextureHelper.getHandler().post(new Runnable() {
166 @Override
167 public void run() {
168 capturerObserver.onOutputFormatRequest(width, height, framerate);
169 }
170 });
171 }
172
173 /**
174 * Changes output video format. This method can be used to scale the output
175 * video, or to change orientation when the captured screen is rotated for exa mple.
176 *
177 * @param width new output video width
178 * @param height new output video height
179 * @param ignoredFramerate ignored
180 */
181 @Override
182 public synchronized void changeCaptureFormat(
183 final int width, final int height, final int ignoredFramerate) {
184 checkNotDisposed();
185
186 this.width = width;
187 this.height = height;
188
189 if (virtualDisplay == null) {
190 // Capturer is stopped, the virtual display will be created in startCaptue r().
191 return;
192 }
193
194 // Create a new virtual display on the surfaceTextureHelper thread to avoid interference
195 // with frame processing, which happens on the same thread (we serialize eve nts by running
196 // them on the same thread).
197 ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() {
198 @Override
199 public void run() {
200 virtualDisplay.release();
201 createVirtualDisplay();
202 }
203 });
204 }
205
206 private void createVirtualDisplay() {
207 surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, height) ;
208 virtualDisplay = mediaProjection.createVirtualDisplay(
209 "WebRTC_ScreenCapture", width, height, VIRTUAL_DISPLAY_DPI,
210 DISPLAY_FLAGS, new Surface(surfaceTextureHelper.getSurfaceTexture()),
211 null /* callback */, null /* callback handler */);
212 }
213
214 // This is called on the internal looper thread of {@Code SurfaceTextureHelper }.
215 @Override
216 public void onTextureFrameAvailable(int oesTextureId, float[] transformMatrix, long timestampNs) {
217 numCapturedFrames++;
218 capturerObserver.onTextureFrameCaptured(width, height, oesTextureId, transfo rmMatrix,
219 0 /* rotation */, timestampNs);
220 }
221
222 @Override
223 public boolean isScreencast() {
224 return true;
225 }
226
227 public long getNumCapturedFrames() {
228 return numCapturedFrames;
229 }
230 }
231
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698