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.content.Context; | |
15 import android.hardware.display.DisplayManager; | |
16 import android.hardware.display.VirtualDisplay; | |
17 import android.media.projection.MediaProjection; | |
18 import android.util.Log; | |
19 import android.view.Surface; | |
20 | |
21 import java.util.ArrayList; | |
22 import java.util.List; | |
23 | |
24 /** | |
25 * An implementation of VideoCapturer to capture the screen content as a video s tream. | |
26 * Capturing is done by {@code MediaProjection} on a {@code SurfaceTexture}. We interact with this | |
27 * {@code SurfaceTexture} using a {@code SurfaceTextureHelper}. | |
28 * The {@code SurfaceTextureHelper} is created by the native code and passed to this capturer in | |
29 * {@code VideoCapturer.initialize()}. On receiving a new frame, this capturer p asses it | |
30 * as a texture to the native code via {@code CapturerObserver.onTextureFrameCap tured()}. This takes | |
31 * place on the HandlerThread of the given {@code SurfaceTextureHelper}. When do ne with each frame, | |
32 * the native code returns the buffer to the {@code SurfaceTextureHelper} to be used for new | |
33 * frames. At any time, at most one frame is being processed. | |
34 * | |
35 * Note that startCapture(), stopCapture(), and dispose() are called from native code. | |
sakal
2016/08/26 07:21:49
This is only the case using the old deprecated API
arsany
2016/08/30 00:52:15
Thanks, Sami.
I wasn't aware of API deprecation, w
sakal
2016/08/30 07:30:13
The only real documentation of the API change is t
arsany
2016/08/30 20:23:17
Acknowledged.
| |
36 * Normally, a Java application should interact with {@code }VideSource} API, wh ich indirectly calls | |
37 * these methods on the underlying {@code VideoCapturer}. | |
38 */ | |
39 @TargetApi(21) | |
40 public class ScreenCapturerAndroid implements | |
41 VideoCapturer, SurfaceTextureHelper.OnTextureFrameAvailableListener { | |
42 | |
43 private static final int DISPLAY_FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_P UBLIC | |
44 | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; | |
45 // DPI for VirtualDisplay, does not seem to matter for us. | |
46 private static final int VIRTUAL_DISPLAY_DPI = 400; | |
47 | |
48 private int width; | |
49 private int height; | |
50 private VirtualDisplay virtualDisplay; | |
51 private SurfaceTextureHelper surfaceTextureHelper; | |
52 private CapturerObserver capturerObserver; | |
53 private long numCapturedFrames = 0; | |
54 private MediaProjection mediaProjection; | |
55 private boolean isDisposed = false; | |
56 private boolean isCapturing = false; | |
57 | |
58 /** | |
59 * Constructs a new Screen Capturer. To avoid distortion, make sure width and height have the | |
60 * same aspect ratio of the captured screen. | |
61 * | |
62 * @param mediaProjection media projection to be used to capture screen | |
63 * @param width output video width | |
64 * @param width output video height | |
65 **/ | |
66 public ScreenCapturerAndroid(MediaProjection mediaProjection, int width, int h eight) { | |
67 this.mediaProjection = mediaProjection; | |
68 this.width = width; | |
69 this.height = height; | |
70 } | |
71 | |
72 /** | |
73 * Sets the media projection used by this capturer. | |
74 * This method is useful when you need to pause the capture, and resume again, and make Android | |
75 * remove the platform cast icon while the video stream is paused (end users c an be | |
76 * confused/suspicious if the app notifies them that screen cast has stopped w hile Android | |
77 * still shows the cast icon because the app is still holding the media projec tion token). | |
78 * | |
79 * Call {@code VideoSource.stop()} before calling this method and | |
80 * {@code VideoSource.restart()} after that. | |
81 */ | |
82 public void updateMediaProjection(MediaProjection mediaProjection) { | |
83 if (isCapturing) { | |
84 throw new RuntimeException("updateMediaProjection can be called only" | |
85 + " when capturer is stopped"); | |
86 } | |
87 this.mediaProjection = mediaProjection; | |
88 } | |
89 | |
90 private void checkNotDisposed() { | |
91 if (isDisposed) { | |
92 throw new RuntimeException("capturer is disposed."); | |
93 } | |
94 } | |
95 | |
96 @Override | |
97 public synchronized void initialize( | |
98 final SurfaceTextureHelper surfaceTextureHelper, | |
99 final Context ignored_applicationContext, | |
100 final VideoCapturer.CapturerObserver capturerObserver) { | |
101 checkNotDisposed(); | |
102 | |
103 if (capturerObserver == null) { | |
104 throw new RuntimeException("capturerObserver not set."); | |
105 } | |
106 this.capturerObserver = capturerObserver; | |
107 | |
108 if (surfaceTextureHelper == null) { | |
109 throw new RuntimeException("surfaceTextureHelper not set."); | |
110 } | |
111 this.surfaceTextureHelper = surfaceTextureHelper; | |
112 } | |
113 | |
114 @Override | |
115 public synchronized List<CameraEnumerationAndroid.CaptureFormat> getSupportedF ormats() { | |
116 List<CameraEnumerationAndroid.CaptureFormat> supportedFormats = new ArrayLis t<>(); | |
117 supportedFormats.add(new CameraEnumerationAndroid.CaptureFormat( | |
118 width, height, 1 /* minFrameRate */, 30 /* maxFrameRate */)); | |
sakal
2016/08/26 07:21:49
Maybe minFrameRate 0? maxFrameRate 30 also seems a
arsany
2016/08/30 00:52:15
Done. Changed to 0, 60. But I am actually not cert
sakal
2016/08/30 07:30:13
I didn't really say we should change it to 60. I w
arsany
2016/08/30 20:23:17
Removed this altogether.
| |
119 return supportedFormats; | |
120 } | |
121 | |
122 // Initially called by native code. This can also be called from native code w hen the | |
sakal
2016/08/26 07:21:49
Only the case in the old deprecated API.
arsany
2016/08/30 00:52:14
Acknowledged.
arsany
2016/08/30 20:23:17
Done.
| |
123 // enclosing VideoSource is "restarted" after being stopped. | |
124 @Override | |
125 public synchronized void startCapture( | |
126 final int ignored_width, | |
sakal
2016/08/26 07:21:49
I would prefer using these variables as the format
arsany
2016/08/30 00:52:15
Within the old API, getSupportedFormats() get call
sakal
2016/08/30 07:30:13
Hmm, I see. I would prefer you use the new API tho
magjed_webrtc
2016/08/30 10:50:54
Yes, please update to the new API in your client,
arsany
2016/08/30 20:23:17
Done.
| |
127 final int ignored_height, | |
128 final int ignored_framerate) { | |
129 checkNotDisposed(); | |
130 isCapturing = true; | |
131 | |
132 this.surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, he ight); | |
133 virtualDisplay = mediaProjection.createVirtualDisplay( | |
134 "WebRTC_ScreenCapture", width, height, VIRTUAL_DISPLAY_DPI, | |
135 DISPLAY_FLAGS, new Surface(surfaceTextureHelper.getSurfaceTexture()), | |
136 null /* callback */, null /* callback handler */); | |
137 | |
138 capturerObserver.onCapturerStarted(true); | |
139 surfaceTextureHelper.startListening(ScreenCapturerAndroid.this); | |
140 } | |
141 | |
142 // Called by native code to pause capturing. | |
sakal
2016/08/26 07:21:48
Only the case in the old deprecated API.
arsany
2016/08/30 00:52:14
Acknowledged.
arsany
2016/08/30 20:23:17
Done.
| |
143 @Override | |
144 public synchronized void stopCapture() { | |
145 checkNotDisposed(); | |
146 isCapturing = false; | |
147 ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() { | |
148 @Override | |
149 public void run() { | |
150 surfaceTextureHelper.stopListening(); | |
sakal
2016/08/26 07:21:49
You should call capturerObserver.onCapturerStopped
arsany
2016/08/30 00:52:15
Done.
| |
151 virtualDisplay.release(); | |
152 } | |
153 }); | |
154 } | |
155 | |
156 // Called from native code to dispose the capturer when the enclosing VideoSou rce is disposed. | |
sakal
2016/08/26 07:21:48
Only the case in the old API. In the new API, app
arsany
2016/08/30 00:52:14
Acknowledged.
arsany
2016/08/30 20:23:17
Done.
| |
157 // The native code calls stopCapture() before calling this method. | |
158 @Override | |
159 public synchronized void dispose() { | |
160 isDisposed = true; | |
161 } | |
162 | |
163 @Override | |
164 public void onOutputFormatRequest(final int width, final int height, final int framerate) { | |
165 checkNotDisposed(); | |
166 surfaceTextureHelper.getHandler().post(new Runnable() { | |
167 @Override | |
168 public void run() { | |
169 capturerObserver.onOutputFormatRequest(width, height, framerate); | |
170 } | |
171 }); | |
172 } | |
173 | |
174 /** | |
175 * Changes output video format. This method can be used to scale the output | |
176 * video, or to change orientation when the captured screen is rotated for exa mple. | |
177 * | |
178 * Call {@code VideoSource.stop()} | |
179 * before calling this method and {@code VideoSource.restart()} after that. | |
180 * | |
181 * @param width new output video width | |
182 * @param height new output video height | |
183 * @param ignored_framerate ignored | |
184 */ | |
185 @Override | |
186 public void changeCaptureFormat(int width, int height, int ignored_framerate) { | |
sakal
2016/08/26 07:21:49
I think this should match the behavior of camera i
arsany
2016/08/30 00:52:15
Done. Agreed. PTAL
| |
187 checkNotDisposed(); | |
188 if (isCapturing) { | |
189 throw new RuntimeException("changeCaptureFormat can be called only" + | |
190 " when capturer is stopped"); | |
191 } | |
192 this.width = width; | |
193 this.height = height; | |
194 } | |
195 | |
196 // This is called on the internal looper thread of {@Code SurfaceTextureHelper }. | |
197 @Override | |
198 public void onTextureFrameAvailable(int oesTextureId, float[] transformMatrix, long timestampNs) { | |
199 numCapturedFrames++; | |
200 capturerObserver.onTextureFrameCaptured(width, height, oesTextureId, transfo rmMatrix, | |
201 0 /* rotation */, timestampNs); | |
202 } | |
203 | |
204 @Override | |
205 public boolean isScreencast() { | |
206 return true; | |
207 } | |
208 | |
209 public long getNumCapturedFrames() { | |
210 return numCapturedFrames; | |
211 } | |
212 } | |
213 | |
OLD | NEW |