OLD | NEW |
1 /* | 1 /* |
2 * libjingle | 2 * libjingle |
3 * Copyright 2015 Google Inc. | 3 * Copyright 2015 Google Inc. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
(...skipping 10 matching lines...) Expand all Loading... |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 */ | 26 */ |
27 | 27 |
28 package org.webrtc; | 28 package org.webrtc; |
29 | 29 |
30 import android.content.Context; | 30 import android.content.Context; |
| 31 import android.graphics.SurfaceTexture; |
31 import android.hardware.Camera; | 32 import android.hardware.Camera; |
32 import android.hardware.Camera.PreviewCallback; | 33 import android.hardware.Camera.PreviewCallback; |
33 import android.opengl.EGL14; | 34 import android.opengl.EGL14; |
34 import android.opengl.EGLContext; | 35 import android.opengl.EGLContext; |
| 36 import android.opengl.GLES11Ext; |
| 37 import android.opengl.GLES20; |
35 import android.os.Handler; | 38 import android.os.Handler; |
36 import android.os.HandlerThread; | 39 import android.os.HandlerThread; |
37 import android.os.SystemClock; | 40 import android.os.SystemClock; |
38 import android.text.StaticLayout; | |
39 import android.view.Surface; | 41 import android.view.Surface; |
40 import android.view.WindowManager; | 42 import android.view.WindowManager; |
41 | 43 |
42 import org.json.JSONException; | 44 import org.json.JSONException; |
43 import org.webrtc.CameraEnumerationAndroid.CaptureFormat; | 45 import org.webrtc.CameraEnumerationAndroid.CaptureFormat; |
44 import org.webrtc.Logging; | 46 import org.webrtc.Logging; |
45 | 47 |
46 import java.io.IOException; | 48 import java.io.IOException; |
47 import java.nio.ByteBuffer; | 49 import java.nio.ByteBuffer; |
48 import java.util.ArrayList; | 50 import java.util.ArrayList; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
86 private int requestedWidth; | 88 private int requestedWidth; |
87 private int requestedHeight; | 89 private int requestedHeight; |
88 private int requestedFramerate; | 90 private int requestedFramerate; |
89 // The capture format will be the closest supported format to the requested fo
rmat. | 91 // The capture format will be the closest supported format to the requested fo
rmat. |
90 private CaptureFormat captureFormat; | 92 private CaptureFormat captureFormat; |
91 private final Object pendingCameraSwitchLock = new Object(); | 93 private final Object pendingCameraSwitchLock = new Object(); |
92 private volatile boolean pendingCameraSwitch; | 94 private volatile boolean pendingCameraSwitch; |
93 private CapturerObserver frameObserver = null; | 95 private CapturerObserver frameObserver = null; |
94 private final CameraErrorHandler errorHandler; | 96 private final CameraErrorHandler errorHandler; |
95 private final boolean isCapturingToTexture; | 97 private final boolean isCapturingToTexture; |
| 98 // |cameraGlTexture| is used with setPreviewTexture if the capturer is capturi
ng to |
| 99 // ByteBuffers. |
| 100 private int cameraGlTexture; |
| 101 // |cameraSurfaceTexture| is used with setPreviewTexture if the capturer is ca
pturing to |
| 102 // ByteBuffers. Must be a member, see issue webrtc:5021. |
| 103 private SurfaceTexture cameraSurfaceTexture; |
| 104 //|surfaceHelper| is used if the capturer is capturing to a texture. Capturing
to textures require |
| 105 // API level 17. |
96 private final SurfaceTextureHelper surfaceHelper; | 106 private final SurfaceTextureHelper surfaceHelper; |
97 // The camera API can output one old frame after the camera has been switched
or the resolution | 107 // The camera API can output one old frame after the camera has been switched
or the resolution |
98 // has been changed. This flag is used for dropping the first frame after came
ra restart. | 108 // has been changed. This flag is used for dropping the first frame after came
ra restart. |
99 private boolean dropNextFrame = false; | 109 private boolean dropNextFrame = false; |
100 | 110 |
101 // Camera error callback. | 111 // Camera error callback. |
102 private final Camera.ErrorCallback cameraErrorCallback = | 112 private final Camera.ErrorCallback cameraErrorCallback = |
103 new Camera.ErrorCallback() { | 113 new Camera.ErrorCallback() { |
104 @Override | 114 @Override |
105 public void onError(int error, Camera camera) { | 115 public void onError(int error, Camera camera) { |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 // Invoked on failure, e.g. camera is stopped or only one camera available. | 208 // Invoked on failure, e.g. camera is stopped or only one camera available. |
199 void onCameraSwitchError(String errorDescription); | 209 void onCameraSwitchError(String errorDescription); |
200 } | 210 } |
201 | 211 |
202 public static VideoCapturerAndroid create(String name, | 212 public static VideoCapturerAndroid create(String name, |
203 CameraErrorHandler errorHandler) { | 213 CameraErrorHandler errorHandler) { |
204 return VideoCapturerAndroid.create(name, errorHandler, null); | 214 return VideoCapturerAndroid.create(name, errorHandler, null); |
205 } | 215 } |
206 | 216 |
207 public static VideoCapturerAndroid create(String name, | 217 public static VideoCapturerAndroid create(String name, |
208 CameraErrorHandler errorHandler, EGLContext sharedContext) { | 218 CameraErrorHandler errorHandler, Object sharedEglContext) { |
209 final int cameraId = lookupDeviceName(name); | 219 final int cameraId = lookupDeviceName(name); |
210 if (cameraId == -1) { | 220 if (cameraId == -1) { |
211 return null; | 221 return null; |
212 } | 222 } |
213 | 223 |
214 final VideoCapturerAndroid capturer = new VideoCapturerAndroid(cameraId, err
orHandler, | 224 final VideoCapturerAndroid capturer = new VideoCapturerAndroid(cameraId, err
orHandler, |
215 sharedContext); | 225 sharedEglContext); |
216 capturer.setNativeCapturer(nativeCreateVideoCapturer(capturer)); | 226 capturer.setNativeCapturer(nativeCreateVideoCapturer(capturer)); |
217 return capturer; | 227 return capturer; |
218 } | 228 } |
219 | 229 |
220 // Switch camera to the next valid camera id. This can only be called while | 230 // Switch camera to the next valid camera id. This can only be called while |
221 // the camera is running. | 231 // the camera is running. |
222 public void switchCamera(final CameraSwitchHandler handler) { | 232 public void switchCamera(final CameraSwitchHandler handler) { |
223 if (Camera.getNumberOfCameras() < 2) { | 233 if (Camera.getNumberOfCameras() < 2) { |
224 if (handler != null) { | 234 if (handler != null) { |
225 handler.onCameraSwitchError("No camera to switch to."); | 235 handler.onCameraSwitchError("No camera to switch to."); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
299 private String getSupportedFormatsAsJson() throws JSONException { | 309 private String getSupportedFormatsAsJson() throws JSONException { |
300 return CameraEnumerationAndroid.getSupportedFormatsAsJson(getCurrentCameraId
()); | 310 return CameraEnumerationAndroid.getSupportedFormatsAsJson(getCurrentCameraId
()); |
301 } | 311 } |
302 | 312 |
303 // Called from native VideoCapturer_nativeCreateVideoCapturer. | 313 // Called from native VideoCapturer_nativeCreateVideoCapturer. |
304 private VideoCapturerAndroid(int cameraId) { | 314 private VideoCapturerAndroid(int cameraId) { |
305 this(cameraId, null, null); | 315 this(cameraId, null, null); |
306 } | 316 } |
307 | 317 |
308 private VideoCapturerAndroid(int cameraId, CameraErrorHandler errorHandler, | 318 private VideoCapturerAndroid(int cameraId, CameraErrorHandler errorHandler, |
309 EGLContext sharedContext) { | 319 Object sharedContext) { |
310 Logging.d(TAG, "VideoCapturerAndroid"); | 320 Logging.d(TAG, "VideoCapturerAndroid"); |
311 this.id = cameraId; | 321 this.id = cameraId; |
312 this.errorHandler = errorHandler; | 322 this.errorHandler = errorHandler; |
313 cameraThread = new HandlerThread(TAG); | 323 cameraThread = new HandlerThread(TAG); |
314 cameraThread.start(); | 324 cameraThread.start(); |
315 cameraThreadHandler = new Handler(cameraThread.getLooper()); | 325 cameraThreadHandler = new Handler(cameraThread.getLooper()); |
316 videoBuffers = new FramePool(cameraThread); | 326 videoBuffers = new FramePool(cameraThread); |
317 surfaceHelper = SurfaceTextureHelper.create( | |
318 sharedContext == null ? EGL14.EGL_NO_CONTEXT : sharedContext, cameraThre
adHandler); | |
319 if (sharedContext != null) { | 327 if (sharedContext != null) { |
| 328 surfaceHelper = SurfaceTextureHelper.create((EGLContext)sharedContext, cam
eraThreadHandler); |
320 surfaceHelper.setListener(this); | 329 surfaceHelper.setListener(this); |
| 330 isCapturingToTexture = true; |
| 331 } else { |
| 332 surfaceHelper = null; |
| 333 isCapturingToTexture = false; |
321 } | 334 } |
322 isCapturingToTexture = sharedContext != null; | |
323 } | 335 } |
324 | 336 |
325 private void checkIsOnCameraThread() { | 337 private void checkIsOnCameraThread() { |
326 if (Thread.currentThread() != cameraThread) { | 338 if (Thread.currentThread() != cameraThread) { |
327 throw new IllegalStateException("Wrong thread"); | 339 throw new IllegalStateException("Wrong thread"); |
328 } | 340 } |
329 } | 341 } |
330 | 342 |
331 // Returns the camera index for camera with name |deviceName|, or -1 if no suc
h camera can be | 343 // Returns the camera index for camera with name |deviceName|, or -1 if no suc
h camera can be |
332 // found. If |deviceName| is empty, the first available device is used. | 344 // found. If |deviceName| is empty, the first available device is used. |
(...skipping 13 matching lines...) Expand all Loading... |
346 return -1; | 358 return -1; |
347 } | 359 } |
348 | 360 |
349 // Called by native code to quit the camera thread. This needs to be done manu
ally, otherwise the | 361 // Called by native code to quit the camera thread. This needs to be done manu
ally, otherwise the |
350 // thread and handler will not be garbage collected. | 362 // thread and handler will not be garbage collected. |
351 private void release() { | 363 private void release() { |
352 Logging.d(TAG, "release"); | 364 Logging.d(TAG, "release"); |
353 if (isReleased()) { | 365 if (isReleased()) { |
354 throw new IllegalStateException("Already released"); | 366 throw new IllegalStateException("Already released"); |
355 } | 367 } |
356 cameraThreadHandler.post(new Runnable() { | 368 ThreadUtils.invokeUninterruptibly(cameraThreadHandler, new Runnable() { |
357 @Override | 369 @Override |
358 public void run() { | 370 public void run() { |
359 if (camera != null) { | 371 if (camera != null) { |
360 throw new IllegalStateException("Release called while camera is runnin
g"); | 372 throw new IllegalStateException("Release called while camera is runnin
g"); |
361 } | 373 } |
362 if (cameraStatistics.pendingFramesCount() != 0) { | 374 if (cameraStatistics.pendingFramesCount() != 0) { |
363 throw new IllegalStateException("Release called with pending frames le
ft"); | 375 throw new IllegalStateException("Release called with pending frames le
ft"); |
364 } | 376 } |
365 } | 377 } |
366 }); | 378 }); |
367 surfaceHelper.disconnect(); | 379 if (isCapturingToTexture) { |
368 | 380 surfaceHelper.disconnect(); |
369 cameraThread.quitSafely(); | 381 } |
| 382 cameraThread.quit(); |
370 ThreadUtils.joinUninterruptibly(cameraThread); | 383 ThreadUtils.joinUninterruptibly(cameraThread); |
371 cameraThread = null; | 384 cameraThread = null; |
372 } | 385 } |
373 | 386 |
374 // Used for testing purposes to check if release() has been called. | 387 // Used for testing purposes to check if release() has been called. |
375 public boolean isReleased() { | 388 public boolean isReleased() { |
376 return (cameraThread == null); | 389 return (cameraThread == null); |
377 } | 390 } |
378 | 391 |
379 // Called by native code. | 392 // Called by native code. |
(...skipping 30 matching lines...) Expand all Loading... |
410 this.applicationContext = applicationContext; | 423 this.applicationContext = applicationContext; |
411 this.frameObserver = frameObserver; | 424 this.frameObserver = frameObserver; |
412 try { | 425 try { |
413 synchronized (cameraIdLock) { | 426 synchronized (cameraIdLock) { |
414 Logging.d(TAG, "Opening camera " + id); | 427 Logging.d(TAG, "Opening camera " + id); |
415 camera = Camera.open(id); | 428 camera = Camera.open(id); |
416 info = new Camera.CameraInfo(); | 429 info = new Camera.CameraInfo(); |
417 Camera.getCameraInfo(id, info); | 430 Camera.getCameraInfo(id, info); |
418 } | 431 } |
419 try { | 432 try { |
420 camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); | 433 if (isCapturingToTexture) { |
| 434 camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); |
| 435 } else { |
| 436 cameraGlTexture = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL
_OES); |
| 437 cameraSurfaceTexture = new SurfaceTexture(cameraGlTexture); |
| 438 camera.setPreviewTexture(cameraSurfaceTexture); |
| 439 } |
421 } catch (IOException e) { | 440 } catch (IOException e) { |
422 Logging.e(TAG, "setPreviewTexture failed", error); | 441 Logging.e(TAG, "setPreviewTexture failed", error); |
423 throw new RuntimeException(e); | 442 throw new RuntimeException(e); |
424 } | 443 } |
425 | 444 |
426 Logging.d(TAG, "Camera orientation: " + info.orientation + | 445 Logging.d(TAG, "Camera orientation: " + info.orientation + |
427 " .Device orientation: " + getDeviceOrientation()); | 446 " .Device orientation: " + getDeviceOrientation()); |
428 camera.setErrorCallback(cameraErrorCallback); | 447 camera.setErrorCallback(cameraErrorCallback); |
429 startPreviewOnCameraThread(width, height, framerate); | 448 startPreviewOnCameraThread(width, height, framerate); |
430 frameObserver.onCapturerStarted(true); | 449 frameObserver.onCapturerStarted(true); |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
550 Logging.d(TAG, "stopReturnBuffersToCamera called." | 569 Logging.d(TAG, "stopReturnBuffersToCamera called." |
551 + (cameraStatistics.pendingFramesCount() == 0? | 570 + (cameraStatistics.pendingFramesCount() == 0? |
552 " All buffers have been returned." | 571 " All buffers have been returned." |
553 : " Pending buffers: " + cameraStatistics.pendingFramesTimeStamps
() + ".")); | 572 : " Pending buffers: " + cameraStatistics.pendingFramesTimeStamps
() + ".")); |
554 } | 573 } |
555 captureFormat = null; | 574 captureFormat = null; |
556 | 575 |
557 Logging.d(TAG, "Release camera."); | 576 Logging.d(TAG, "Release camera."); |
558 camera.release(); | 577 camera.release(); |
559 camera = null; | 578 camera = null; |
| 579 |
| 580 if (cameraGlTexture != 0) { |
| 581 GLES20.glDeleteTextures(1, new int[] {cameraGlTexture}, 0); |
| 582 cameraGlTexture = 0; |
| 583 } |
| 584 if (cameraSurfaceTexture != null) { |
| 585 cameraSurfaceTexture.release(); |
| 586 } |
560 } | 587 } |
561 | 588 |
562 private void switchCameraOnCameraThread() { | 589 private void switchCameraOnCameraThread() { |
563 checkIsOnCameraThread(); | 590 checkIsOnCameraThread(); |
564 Logging.d(TAG, "switchCameraOnCameraThread"); | 591 Logging.d(TAG, "switchCameraOnCameraThread"); |
565 stopCaptureOnCameraThread(); | 592 stopCaptureOnCameraThread(); |
566 synchronized (cameraIdLock) { | 593 synchronized (cameraIdLock) { |
567 id = (id + 1) % Camera.getNumberOfCameras(); | 594 id = (id + 1) % Camera.getNumberOfCameras(); |
568 } | 595 } |
569 dropNextFrame = true; | 596 dropNextFrame = true; |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
854 private native void nativeOnByteBufferFrameCaptured(long nativeCapturer, | 881 private native void nativeOnByteBufferFrameCaptured(long nativeCapturer, |
855 byte[] data, int length, int width, int height, int rotation, long timeS
tamp); | 882 byte[] data, int length, int width, int height, int rotation, long timeS
tamp); |
856 private native void nativeOnTextureFrameCaptured(long nativeCapturer, int wi
dth, int height, | 883 private native void nativeOnTextureFrameCaptured(long nativeCapturer, int wi
dth, int height, |
857 int oesTextureId, float[] transformMatrix, long timestamp); | 884 int oesTextureId, float[] transformMatrix, long timestamp); |
858 private native void nativeOnOutputFormatRequest(long nativeCapturer, | 885 private native void nativeOnOutputFormatRequest(long nativeCapturer, |
859 int width, int height, int framerate); | 886 int width, int height, int framerate); |
860 } | 887 } |
861 | 888 |
862 private static native long nativeCreateVideoCapturer(VideoCapturerAndroid vide
oCapturer); | 889 private static native long nativeCreateVideoCapturer(VideoCapturerAndroid vide
oCapturer); |
863 } | 890 } |
OLD | NEW |