Chromium Code Reviews| Index: webrtc/api/android/java/src/org/webrtc/ScreenCapturerAndroid.java |
| diff --git a/webrtc/api/android/java/src/org/webrtc/ScreenCapturerAndroid.java b/webrtc/api/android/java/src/org/webrtc/ScreenCapturerAndroid.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..cfcce1f14e0b8476404881f009d3d78fdd56b78d |
| --- /dev/null |
| +++ b/webrtc/api/android/java/src/org/webrtc/ScreenCapturerAndroid.java |
| @@ -0,0 +1,213 @@ |
| +/* |
| + * Copyright 2016 The WebRTC project authors. All Rights Reserved. |
| + * |
| + * Use of this source code is governed by a BSD-style license |
| + * that can be found in the LICENSE file in the root of the source |
| + * tree. An additional intellectual property rights grant can be found |
| + * in the file PATENTS. All contributing project authors may |
| + * be found in the AUTHORS file in the root of the source tree. |
| + */ |
| + |
| +package org.webrtc; |
| + |
| +import android.annotation.TargetApi; |
| +import android.content.Context; |
| +import android.hardware.display.DisplayManager; |
| +import android.hardware.display.VirtualDisplay; |
| +import android.media.projection.MediaProjection; |
| +import android.util.Log; |
| +import android.view.Surface; |
| + |
| +import java.util.ArrayList; |
| +import java.util.List; |
| + |
| +/** |
| + * An implementation of VideoCapturer to capture the screen content as a video stream. |
| + * Capturing is done by {@code MediaProjection} on a {@code SurfaceTexture}. We interact with this |
| + * {@code SurfaceTexture} using a {@code SurfaceTextureHelper}. |
| + * The {@code SurfaceTextureHelper} is created by the native code and passed to this capturer in |
| + * {@code VideoCapturer.initialize()}. On receiving a new frame, this capturer passes it |
| + * as a texture to the native code via {@code CapturerObserver.onTextureFrameCaptured()}. This takes |
| + * place on the HandlerThread of the given {@code SurfaceTextureHelper}. When done with each frame, |
| + * the native code returns the buffer to the {@code SurfaceTextureHelper} to be used for new |
| + * frames. At any time, at most one frame is being processed. |
| + * |
| + * 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.
|
| + * Normally, a Java application should interact with {@code }VideSource} API, which indirectly calls |
| + * these methods on the underlying {@code VideoCapturer}. |
| + */ |
| +@TargetApi(21) |
| +public class ScreenCapturerAndroid implements |
| + VideoCapturer, SurfaceTextureHelper.OnTextureFrameAvailableListener { |
| + |
| + private static final int DISPLAY_FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
| + | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; |
| + // DPI for VirtualDisplay, does not seem to matter for us. |
| + private static final int VIRTUAL_DISPLAY_DPI = 400; |
| + |
| + private int width; |
| + private int height; |
| + private VirtualDisplay virtualDisplay; |
| + private SurfaceTextureHelper surfaceTextureHelper; |
| + private CapturerObserver capturerObserver; |
| + private long numCapturedFrames = 0; |
| + private MediaProjection mediaProjection; |
| + private boolean isDisposed = false; |
| + private boolean isCapturing = false; |
| + |
| + /** |
| + * Constructs a new Screen Capturer. To avoid distortion, make sure width and height have the |
| + * same aspect ratio of the captured screen. |
| + * |
| + * @param mediaProjection media projection to be used to capture screen |
| + * @param width output video width |
| + * @param width output video height |
| + **/ |
| + public ScreenCapturerAndroid(MediaProjection mediaProjection, int width, int height) { |
| + this.mediaProjection = mediaProjection; |
| + this.width = width; |
| + this.height = height; |
| + } |
| + |
| + /** |
| + * Sets the media projection used by this capturer. |
| + * This method is useful when you need to pause the capture, and resume again, and make Android |
| + * remove the platform cast icon while the video stream is paused (end users can be |
| + * confused/suspicious if the app notifies them that screen cast has stopped while Android |
| + * still shows the cast icon because the app is still holding the media projection token). |
| + * |
| + * Call {@code VideoSource.stop()} before calling this method and |
| + * {@code VideoSource.restart()} after that. |
| + */ |
| + public void updateMediaProjection(MediaProjection mediaProjection) { |
| + if (isCapturing) { |
| + throw new RuntimeException("updateMediaProjection can be called only" |
| + + " when capturer is stopped"); |
| + } |
| + this.mediaProjection = mediaProjection; |
| + } |
| + |
| + private void checkNotDisposed() { |
| + if (isDisposed) { |
| + throw new RuntimeException("capturer is disposed."); |
| + } |
| + } |
| + |
| + @Override |
| + public synchronized void initialize( |
| + final SurfaceTextureHelper surfaceTextureHelper, |
| + final Context ignored_applicationContext, |
| + final VideoCapturer.CapturerObserver capturerObserver) { |
| + checkNotDisposed(); |
| + |
| + if (capturerObserver == null) { |
| + throw new RuntimeException("capturerObserver not set."); |
| + } |
| + this.capturerObserver = capturerObserver; |
| + |
| + if (surfaceTextureHelper == null) { |
| + throw new RuntimeException("surfaceTextureHelper not set."); |
| + } |
| + this.surfaceTextureHelper = surfaceTextureHelper; |
| + } |
| + |
| + @Override |
| + public synchronized List<CameraEnumerationAndroid.CaptureFormat> getSupportedFormats() { |
| + List<CameraEnumerationAndroid.CaptureFormat> supportedFormats = new ArrayList<>(); |
| + supportedFormats.add(new CameraEnumerationAndroid.CaptureFormat( |
| + 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.
|
| + return supportedFormats; |
| + } |
| + |
| + // Initially called by native code. This can also be called from native code when 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.
|
| + // enclosing VideoSource is "restarted" after being stopped. |
| + @Override |
| + public synchronized void startCapture( |
| + 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.
|
| + final int ignored_height, |
| + final int ignored_framerate) { |
| + checkNotDisposed(); |
| + isCapturing = true; |
| + |
| + this.surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, height); |
| + virtualDisplay = mediaProjection.createVirtualDisplay( |
| + "WebRTC_ScreenCapture", width, height, VIRTUAL_DISPLAY_DPI, |
| + DISPLAY_FLAGS, new Surface(surfaceTextureHelper.getSurfaceTexture()), |
| + null /* callback */, null /* callback handler */); |
| + |
| + capturerObserver.onCapturerStarted(true); |
| + surfaceTextureHelper.startListening(ScreenCapturerAndroid.this); |
| + } |
| + |
| + // 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.
|
| + @Override |
| + public synchronized void stopCapture() { |
| + checkNotDisposed(); |
| + isCapturing = false; |
| + ThreadUtils.invokeAtFrontUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() { |
| + @Override |
| + public void run() { |
| + surfaceTextureHelper.stopListening(); |
|
sakal
2016/08/26 07:21:49
You should call capturerObserver.onCapturerStopped
arsany
2016/08/30 00:52:15
Done.
|
| + virtualDisplay.release(); |
| + } |
| + }); |
| + } |
| + |
| + // Called from native code to dispose the capturer when the enclosing VideoSource 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.
|
| + // The native code calls stopCapture() before calling this method. |
| + @Override |
| + public synchronized void dispose() { |
| + isDisposed = true; |
| + } |
| + |
| + @Override |
| + public void onOutputFormatRequest(final int width, final int height, final int framerate) { |
| + checkNotDisposed(); |
| + surfaceTextureHelper.getHandler().post(new Runnable() { |
| + @Override |
| + public void run() { |
| + capturerObserver.onOutputFormatRequest(width, height, framerate); |
| + } |
| + }); |
| + } |
| + |
| + /** |
| + * Changes output video format. This method can be used to scale the output |
| + * video, or to change orientation when the captured screen is rotated for example. |
| + * |
| + * Call {@code VideoSource.stop()} |
| + * before calling this method and {@code VideoSource.restart()} after that. |
| + * |
| + * @param width new output video width |
| + * @param height new output video height |
| + * @param ignored_framerate ignored |
| + */ |
| + @Override |
| + 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
|
| + checkNotDisposed(); |
| + if (isCapturing) { |
| + throw new RuntimeException("changeCaptureFormat can be called only" + |
| + " when capturer is stopped"); |
| + } |
| + this.width = width; |
| + this.height = height; |
| + } |
| + |
| + // This is called on the internal looper thread of {@Code SurfaceTextureHelper}. |
| + @Override |
| + public void onTextureFrameAvailable(int oesTextureId, float[] transformMatrix, long timestampNs) { |
| + numCapturedFrames++; |
| + capturerObserver.onTextureFrameCaptured(width, height, oesTextureId, transformMatrix, |
| + 0 /* rotation */, timestampNs); |
| + } |
| + |
| + @Override |
| + public boolean isScreencast() { |
| + return true; |
| + } |
| + |
| + public long getNumCapturedFrames() { |
| + return numCapturedFrames; |
| + } |
| +} |
| + |