OLD | NEW |
---|---|
(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 | |
OLD | NEW |