summaryrefslogtreecommitdiff
path: root/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
diff options
context:
space:
mode:
Diffstat (limited to 'modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java')
-rw-r--r--modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java78
1 files changed, 58 insertions, 20 deletions
diff --git a/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java b/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
index c8abdbe3..2f571d0c 100644
--- a/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
+++ b/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
@@ -11,20 +11,18 @@
package org.webrtc.videoengine;
import java.io.IOException;
-import java.util.Locale;
import java.util.concurrent.Exchanger;
-import java.util.concurrent.locks.ReentrantLock;
import android.content.Context;
import android.graphics.ImageFormat;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.graphics.SurfaceTexture;
-import android.graphics.YuvImage;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera;
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemClock;
import android.util.Log;
import android.view.OrientationEventListener;
import android.view.SurfaceHolder.Callback;
@@ -50,11 +48,15 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
private final Camera.CameraInfo info;
private final OrientationEventListener orientationListener;
private final long native_capturer; // |VideoCaptureAndroid*| in C++.
- private SurfaceTexture dummySurfaceTexture;
+ private SurfaceTexture cameraSurfaceTexture;
+ private int[] cameraGlTextures = null;
// Arbitrary queue depth. Higher number means more memory allocated & held,
// lower number means more sensitivity to processing time in the client (and
// potentially stalling the capturer if it runs out of buffers to write to).
private final int numCaptureBuffers = 3;
+ private double averageDurationMs;
+ private long lastCaptureTimeMs;
+ private int frameCount;
// Requests future capturers to send their frames to |localPreview| directly.
public static void setLocalPreview(SurfaceHolder localPreview) {
@@ -114,6 +116,8 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
private synchronized boolean startCapture(
final int width, final int height,
final int min_mfps, final int max_mfps) {
+ Log.d(TAG, "startCapture: " + width + "x" + height + "@" +
+ min_mfps + ":" + max_mfps);
if (cameraThread != null || cameraThreadHandler != null) {
throw new RuntimeException("Camera thread already started!");
}
@@ -121,7 +125,6 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
cameraThread = new CameraThread(handlerExchanger);
cameraThread.start();
cameraThreadHandler = exchange(handlerExchanger, null);
- orientationListener.enable();
final Exchanger<Boolean> result = new Exchanger<Boolean>();
cameraThreadHandler.post(new Runnable() {
@@ -129,14 +132,14 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
startCaptureOnCameraThread(width, height, min_mfps, max_mfps, result);
}
});
- return exchange(result, false); // |false| is a dummy value here.
+ boolean startResult = exchange(result, false); // |false| is a dummy value.
+ orientationListener.enable();
+ return startResult;
}
private void startCaptureOnCameraThread(
int width, int height, int min_mfps, int max_mfps,
Exchanger<Boolean> result) {
- Log.d(TAG, "startCapture: " + width + "x" + height + "@" +
- min_mfps + ":" + max_mfps);
Throwable error = null;
try {
camera = Camera.open(id);
@@ -150,13 +153,27 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
} else {
// No local renderer (we only care about onPreviewFrame() buffers, not a
// directly-displayed UI element). Camera won't capture without
- // setPreview{Texture,Display}, so we create a dummy SurfaceTexture and
- // hand it over to Camera, but never listen for frame-ready callbacks,
+ // setPreview{Texture,Display}, so we create a SurfaceTexture and hand
+ // it over to Camera, but never listen for frame-ready callbacks,
// and never call updateTexImage on it.
try {
- // "42" because http://goo.gl/KaEn8
- dummySurfaceTexture = new SurfaceTexture(42);
- camera.setPreviewTexture(dummySurfaceTexture);
+ cameraGlTextures = new int[1];
+ // Generate one texture pointer and bind it as an external texture.
+ GLES20.glGenTextures(1, cameraGlTextures, 0);
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ cameraGlTextures[0]);
+ GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
+ GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+
+ cameraSurfaceTexture = new SurfaceTexture(cameraGlTextures[0]);
+ cameraSurfaceTexture.setOnFrameAvailableListener(null);
+ camera.setPreviewTexture(cameraSurfaceTexture);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -178,6 +195,8 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
camera.addCallbackBuffer(new byte[bufSize]);
}
camera.setPreviewCallbackWithBuffer(this);
+ frameCount = 0;
+ averageDurationMs = 1000 / max_mfps;
camera.startPreview();
exchange(result, true);
return;
@@ -198,6 +217,8 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
// Called by native code. Returns true when camera is known to be stopped.
private synchronized boolean stopCapture() {
+ Log.d(TAG, "stopCapture");
+ orientationListener.disable();
final Exchanger<Boolean> result = new Exchanger<Boolean>();
cameraThreadHandler.post(new Runnable() {
@Override public void run() {
@@ -212,14 +233,12 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
}
cameraThreadHandler = null;
cameraThread = null;
- orientationListener.disable();
+ Log.d(TAG, "stopCapture done");
return status;
}
private void stopCaptureOnCameraThread(
Exchanger<Boolean> result) {
- Log.d(TAG, "stopCapture");
- Looper.myLooper().quit();
if (camera == null) {
throw new RuntimeException("Camera is already stopped!");
}
@@ -232,10 +251,16 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
camera.setPreviewDisplay(null);
} else {
camera.setPreviewTexture(null);
+ cameraSurfaceTexture = null;
+ if (cameraGlTextures != null) {
+ GLES20.glDeleteTextures(1, cameraGlTextures, 0);
+ cameraGlTextures = null;
+ }
}
camera.release();
camera = null;
exchange(result, true);
+ Looper.myLooper().quit();
return;
} catch (IOException e) {
error = e;
@@ -244,11 +269,12 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
}
Log.e(TAG, "Failed to stop camera", error);
exchange(result, false);
+ Looper.myLooper().quit();
return;
}
private native void ProvideCameraFrame(
- byte[] data, int length, long captureObject);
+ byte[] data, int length, long timeStamp, long captureObject);
// Called on cameraThread so must not "synchronized".
@Override
@@ -262,7 +288,19 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
if (camera != callbackCamera) {
throw new RuntimeException("Unexpected camera in callback!");
}
- ProvideCameraFrame(data, data.length, native_capturer);
+ frameCount++;
+ long captureTimeMs = SystemClock.elapsedRealtime();
+ if (frameCount > 1) {
+ double durationMs = captureTimeMs - lastCaptureTimeMs;
+ averageDurationMs = 0.9 * averageDurationMs + 0.1 * durationMs;
+ if ((frameCount % 30) == 0) {
+ Log.d(TAG, "Camera TS " + captureTimeMs +
+ ". Duration: " + (int)durationMs + " ms. FPS: " +
+ (int) (1000 / averageDurationMs + 0.5));
+ }
+ }
+ lastCaptureTimeMs = captureTimeMs;
+ ProvideCameraFrame(data, data.length, captureTimeMs, native_capturer);
camera.addCallbackBuffer(data);
}