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 13 matching lines...) Expand all Loading... |
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.graphics.SurfaceTexture; |
32 import android.hardware.Camera; | 32 import android.hardware.Camera; |
33 import android.hardware.Camera.PreviewCallback; | 33 import android.hardware.Camera.PreviewCallback; |
34 import android.opengl.EGLContext; | |
35 import android.opengl.GLES11Ext; | |
36 import android.opengl.GLES20; | |
37 import android.os.Handler; | 34 import android.os.Handler; |
38 import android.os.HandlerThread; | 35 import android.os.HandlerThread; |
39 import android.os.SystemClock; | 36 import android.os.SystemClock; |
40 import android.view.Surface; | 37 import android.view.Surface; |
41 import android.view.WindowManager; | 38 import android.view.WindowManager; |
42 | 39 |
43 import org.json.JSONException; | 40 import org.json.JSONException; |
44 import org.webrtc.CameraEnumerationAndroid.CaptureFormat; | 41 import org.webrtc.CameraEnumerationAndroid.CaptureFormat; |
45 import org.webrtc.Logging; | 42 import org.webrtc.Logging; |
46 | 43 |
47 import java.io.IOException; | 44 import java.io.IOException; |
48 import java.nio.ByteBuffer; | 45 import java.nio.ByteBuffer; |
49 import java.util.ArrayList; | 46 import java.util.ArrayList; |
50 import java.util.HashMap; | 47 import java.util.HashMap; |
51 import java.util.HashSet; | 48 import java.util.HashSet; |
52 import java.util.IdentityHashMap; | 49 import java.util.IdentityHashMap; |
53 import java.util.List; | 50 import java.util.List; |
54 import java.util.Map; | 51 import java.util.Map; |
55 import java.util.Set; | 52 import java.util.Set; |
56 import java.util.concurrent.CountDownLatch; | 53 import java.util.concurrent.CountDownLatch; |
57 import java.util.concurrent.TimeUnit; | 54 import java.util.concurrent.TimeUnit; |
58 | 55 |
| 56 import javax.microedition.khronos.egl.EGLContext; |
| 57 import javax.microedition.khronos.egl.EGL10; |
| 58 |
59 // Android specific implementation of VideoCapturer. | 59 // Android specific implementation of VideoCapturer. |
60 // An instance of this class can be created by an application using | 60 // An instance of this class can be created by an application using |
61 // VideoCapturerAndroid.create(); | 61 // VideoCapturerAndroid.create(); |
62 // This class extends VideoCapturer with a method to easily switch between the | 62 // This class extends VideoCapturer with a method to easily switch between the |
63 // front and back camera. It also provides methods for enumerating valid device | 63 // front and back camera. It also provides methods for enumerating valid device |
64 // names. | 64 // names. |
65 // | 65 // |
66 // Threading notes: this class is called from C++ code, Android Camera callbacks
, and possibly | 66 // Threading notes: this class is called from C++ code, Android Camera callbacks
, and possibly |
67 // arbitrary Java threads. All public entry points are thread safe, and delegate
the work to the | 67 // arbitrary Java threads. All public entry points are thread safe, and delegate
the work to the |
68 // camera thread. The internal *OnCameraThread() methods must check |camera| for
null to check if | 68 // camera thread. The internal *OnCameraThread() methods must check |camera| for
null to check if |
(...skipping 19 matching lines...) Expand all Loading... |
88 private int requestedHeight; | 88 private int requestedHeight; |
89 private int requestedFramerate; | 89 private int requestedFramerate; |
90 // The capture format will be the closest supported format to the requested fo
rmat. | 90 // The capture format will be the closest supported format to the requested fo
rmat. |
91 private CaptureFormat captureFormat; | 91 private CaptureFormat captureFormat; |
92 private final Object pendingCameraSwitchLock = new Object(); | 92 private final Object pendingCameraSwitchLock = new Object(); |
93 private volatile boolean pendingCameraSwitch; | 93 private volatile boolean pendingCameraSwitch; |
94 private CapturerObserver frameObserver = null; | 94 private CapturerObserver frameObserver = null; |
95 private final CameraEventsHandler eventsHandler; | 95 private final CameraEventsHandler eventsHandler; |
96 private boolean firstFrameReported; | 96 private boolean firstFrameReported; |
97 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. | |
106 private final SurfaceTextureHelper surfaceHelper; | 98 private final SurfaceTextureHelper surfaceHelper; |
107 // The camera API can output one old frame after the camera has been switched
or the resolution | 99 // The camera API can output one old frame after the camera has been switched
or the resolution |
108 // has been changed. This flag is used for dropping the first frame after came
ra restart. | 100 // has been changed. This flag is used for dropping the first frame after came
ra restart. |
109 private boolean dropNextFrame = false; | 101 private boolean dropNextFrame = false; |
110 | 102 |
111 // Camera error callback. | 103 // Camera error callback. |
112 private final Camera.ErrorCallback cameraErrorCallback = | 104 private final Camera.ErrorCallback cameraErrorCallback = |
113 new Camera.ErrorCallback() { | 105 new Camera.ErrorCallback() { |
114 @Override | 106 @Override |
115 public void onError(int error, Camera camera) { | 107 public void onError(int error, Camera camera) { |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 // Invoked on failure, e.g. camera is stopped or only one camera available. | 209 // Invoked on failure, e.g. camera is stopped or only one camera available. |
218 void onCameraSwitchError(String errorDescription); | 210 void onCameraSwitchError(String errorDescription); |
219 } | 211 } |
220 | 212 |
221 public static VideoCapturerAndroid create(String name, | 213 public static VideoCapturerAndroid create(String name, |
222 CameraEventsHandler eventsHandler) { | 214 CameraEventsHandler eventsHandler) { |
223 return VideoCapturerAndroid.create(name, eventsHandler, null); | 215 return VideoCapturerAndroid.create(name, eventsHandler, null); |
224 } | 216 } |
225 | 217 |
226 public static VideoCapturerAndroid create(String name, | 218 public static VideoCapturerAndroid create(String name, |
227 CameraEventsHandler eventsHandler, Object sharedEglContext) { | 219 CameraEventsHandler eventsHandler, EGLContext sharedEglContext) { |
228 final int cameraId = lookupDeviceName(name); | 220 final int cameraId = lookupDeviceName(name); |
229 if (cameraId == -1) { | 221 if (cameraId == -1) { |
230 return null; | 222 return null; |
231 } | 223 } |
232 | 224 |
233 final VideoCapturerAndroid capturer = new VideoCapturerAndroid(cameraId, eve
ntsHandler, | 225 final VideoCapturerAndroid capturer = new VideoCapturerAndroid(cameraId, eve
ntsHandler, |
234 sharedEglContext); | 226 sharedEglContext); |
235 capturer.setNativeCapturer(nativeCreateVideoCapturer(capturer)); | 227 capturer.setNativeCapturer(nativeCreateVideoCapturer(capturer)); |
236 return capturer; | 228 return capturer; |
237 } | 229 } |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 private String getSupportedFormatsAsJson() throws JSONException { | 322 private String getSupportedFormatsAsJson() throws JSONException { |
331 return CameraEnumerationAndroid.getSupportedFormatsAsJson(getCurrentCameraId
()); | 323 return CameraEnumerationAndroid.getSupportedFormatsAsJson(getCurrentCameraId
()); |
332 } | 324 } |
333 | 325 |
334 // Called from native VideoCapturer_nativeCreateVideoCapturer. | 326 // Called from native VideoCapturer_nativeCreateVideoCapturer. |
335 private VideoCapturerAndroid(int cameraId) { | 327 private VideoCapturerAndroid(int cameraId) { |
336 this(cameraId, null, null); | 328 this(cameraId, null, null); |
337 } | 329 } |
338 | 330 |
339 private VideoCapturerAndroid(int cameraId, CameraEventsHandler eventsHandler, | 331 private VideoCapturerAndroid(int cameraId, CameraEventsHandler eventsHandler, |
340 Object sharedContext) { | 332 EGLContext sharedContext) { |
341 Logging.d(TAG, "VideoCapturerAndroid"); | 333 Logging.d(TAG, "VideoCapturerAndroid"); |
342 this.id = cameraId; | 334 this.id = cameraId; |
343 this.eventsHandler = eventsHandler; | 335 this.eventsHandler = eventsHandler; |
344 cameraThread = new HandlerThread(TAG); | 336 cameraThread = new HandlerThread(TAG); |
345 cameraThread.start(); | 337 cameraThread.start(); |
346 cameraThreadHandler = new Handler(cameraThread.getLooper()); | 338 cameraThreadHandler = new Handler(cameraThread.getLooper()); |
347 videoBuffers = new FramePool(cameraThread); | 339 videoBuffers = new FramePool(cameraThread); |
348 if (sharedContext != null) { | 340 isCapturingToTexture = (sharedContext != null); |
349 surfaceHelper = SurfaceTextureHelper.create((EGLContext)sharedContext, cam
eraThreadHandler); | 341 surfaceHelper = SurfaceTextureHelper.create( |
| 342 isCapturingToTexture ? sharedContext : EGL10.EGL_NO_CONTEXT, cameraThrea
dHandler); |
| 343 if (isCapturingToTexture) { |
350 surfaceHelper.setListener(this); | 344 surfaceHelper.setListener(this); |
351 isCapturingToTexture = true; | |
352 } else { | |
353 surfaceHelper = null; | |
354 isCapturingToTexture = false; | |
355 } | 345 } |
356 } | 346 } |
357 | 347 |
358 private void checkIsOnCameraThread() { | 348 private void checkIsOnCameraThread() { |
359 if (Thread.currentThread() != cameraThread) { | 349 if (Thread.currentThread() != cameraThread) { |
360 throw new IllegalStateException("Wrong thread"); | 350 throw new IllegalStateException("Wrong thread"); |
361 } | 351 } |
362 } | 352 } |
363 | 353 |
364 // Returns the camera index for camera with name |deviceName|, or -1 if no suc
h camera can be | 354 // Returns the camera index for camera with name |deviceName|, or -1 if no suc
h camera can be |
(...skipping 25 matching lines...) Expand all Loading... |
390 @Override | 380 @Override |
391 public void run() { | 381 public void run() { |
392 if (camera != null) { | 382 if (camera != null) { |
393 throw new IllegalStateException("Release called while camera is runnin
g"); | 383 throw new IllegalStateException("Release called while camera is runnin
g"); |
394 } | 384 } |
395 if (cameraStatistics.pendingFramesCount() != 0) { | 385 if (cameraStatistics.pendingFramesCount() != 0) { |
396 throw new IllegalStateException("Release called with pending frames le
ft"); | 386 throw new IllegalStateException("Release called with pending frames le
ft"); |
397 } | 387 } |
398 } | 388 } |
399 }); | 389 }); |
400 if (isCapturingToTexture) { | 390 surfaceHelper.disconnect(); |
401 surfaceHelper.disconnect(); | |
402 } | |
403 cameraThread.quit(); | 391 cameraThread.quit(); |
404 ThreadUtils.joinUninterruptibly(cameraThread); | 392 ThreadUtils.joinUninterruptibly(cameraThread); |
405 cameraThread = null; | 393 cameraThread = null; |
406 } | 394 } |
407 | 395 |
408 // Used for testing purposes to check if release() has been called. | 396 // Used for testing purposes to check if release() has been called. |
409 public boolean isReleased() { | 397 public boolean isReleased() { |
410 return (cameraThread == null); | 398 return (cameraThread == null); |
411 } | 399 } |
412 | 400 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
448 Logging.d(TAG, "Opening camera " + id); | 436 Logging.d(TAG, "Opening camera " + id); |
449 firstFrameReported = false; | 437 firstFrameReported = false; |
450 if (eventsHandler != null) { | 438 if (eventsHandler != null) { |
451 eventsHandler.onCameraOpening(id); | 439 eventsHandler.onCameraOpening(id); |
452 } | 440 } |
453 camera = Camera.open(id); | 441 camera = Camera.open(id); |
454 info = new Camera.CameraInfo(); | 442 info = new Camera.CameraInfo(); |
455 Camera.getCameraInfo(id, info); | 443 Camera.getCameraInfo(id, info); |
456 } | 444 } |
457 try { | 445 try { |
458 if (isCapturingToTexture) { | 446 camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); |
459 camera.setPreviewTexture(surfaceHelper.getSurfaceTexture()); | |
460 } else { | |
461 cameraGlTexture = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL
_OES); | |
462 cameraSurfaceTexture = new SurfaceTexture(cameraGlTexture); | |
463 camera.setPreviewTexture(cameraSurfaceTexture); | |
464 } | |
465 } catch (IOException e) { | 447 } catch (IOException e) { |
466 Logging.e(TAG, "setPreviewTexture failed", error); | 448 Logging.e(TAG, "setPreviewTexture failed", error); |
467 throw new RuntimeException(e); | 449 throw new RuntimeException(e); |
468 } | 450 } |
469 | 451 |
470 Logging.d(TAG, "Camera orientation: " + info.orientation + | 452 Logging.d(TAG, "Camera orientation: " + info.orientation + |
471 " .Device orientation: " + getDeviceOrientation()); | 453 " .Device orientation: " + getDeviceOrientation()); |
472 camera.setErrorCallback(cameraErrorCallback); | 454 camera.setErrorCallback(cameraErrorCallback); |
473 startPreviewOnCameraThread(width, height, framerate); | 455 startPreviewOnCameraThread(width, height, framerate); |
474 frameObserver.onCapturerStarted(true); | 456 frameObserver.onCapturerStarted(true); |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
597 : " Pending buffers: " + cameraStatistics.pendingFramesTimeStamps
() + ".")); | 579 : " Pending buffers: " + cameraStatistics.pendingFramesTimeStamps
() + ".")); |
598 } | 580 } |
599 captureFormat = null; | 581 captureFormat = null; |
600 | 582 |
601 Logging.d(TAG, "Release camera."); | 583 Logging.d(TAG, "Release camera."); |
602 camera.release(); | 584 camera.release(); |
603 camera = null; | 585 camera = null; |
604 if (eventsHandler != null) { | 586 if (eventsHandler != null) { |
605 eventsHandler.onCameraClosed(); | 587 eventsHandler.onCameraClosed(); |
606 } | 588 } |
607 | |
608 if (cameraGlTexture != 0) { | |
609 GLES20.glDeleteTextures(1, new int[] {cameraGlTexture}, 0); | |
610 cameraGlTexture = 0; | |
611 } | |
612 if (cameraSurfaceTexture != null) { | |
613 cameraSurfaceTexture.release(); | |
614 } | |
615 } | 589 } |
616 | 590 |
617 private void switchCameraOnCameraThread() { | 591 private void switchCameraOnCameraThread() { |
618 checkIsOnCameraThread(); | 592 checkIsOnCameraThread(); |
619 Logging.d(TAG, "switchCameraOnCameraThread"); | 593 Logging.d(TAG, "switchCameraOnCameraThread"); |
620 stopCaptureOnCameraThread(); | 594 stopCaptureOnCameraThread(); |
621 synchronized (cameraIdLock) { | 595 synchronized (cameraIdLock) { |
622 id = (id + 1) % Camera.getNumberOfCameras(); | 596 id = (id + 1) % Camera.getNumberOfCameras(); |
623 } | 597 } |
624 dropNextFrame = true; | 598 dropNextFrame = true; |
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
913 private native void nativeOnByteBufferFrameCaptured(long nativeCapturer, | 887 private native void nativeOnByteBufferFrameCaptured(long nativeCapturer, |
914 byte[] data, int length, int width, int height, int rotation, long timeS
tamp); | 888 byte[] data, int length, int width, int height, int rotation, long timeS
tamp); |
915 private native void nativeOnTextureFrameCaptured(long nativeCapturer, int wi
dth, int height, | 889 private native void nativeOnTextureFrameCaptured(long nativeCapturer, int wi
dth, int height, |
916 int oesTextureId, float[] transformMatrix, long timestamp); | 890 int oesTextureId, float[] transformMatrix, long timestamp); |
917 private native void nativeOnOutputFormatRequest(long nativeCapturer, | 891 private native void nativeOnOutputFormatRequest(long nativeCapturer, |
918 int width, int height, int framerate); | 892 int width, int height, int framerate); |
919 } | 893 } |
920 | 894 |
921 private static native long nativeCreateVideoCapturer(VideoCapturerAndroid vide
oCapturer); | 895 private static native long nativeCreateVideoCapturer(VideoCapturerAndroid vide
oCapturer); |
922 } | 896 } |
OLD | NEW |