| 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 |