blob: a4c39a8c26dd748a83a7173935ec8d5580328549 [file] [log] [blame]
/*
* Copyright (c) 2011 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.videoengine;
import java.io.IOException;
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;
import org.webrtc.videoengine.CaptureCapabilityAndroid;
import org.webrtc.videoengine.VideoCaptureDeviceInfoAndroid.AndroidVideoCaptureDevice;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
public class VideoCaptureAndroid implements PreviewCallback, Callback {
private Camera camera;
private AndroidVideoCaptureDevice currentDevice = null;
public ReentrantLock previewBufferLock = new ReentrantLock();
private int PIXEL_FORMAT = ImageFormat.NV21;
PixelFormat pixelFormat = new PixelFormat();
// True when the C++ layer has ordered the camera to be started.
private boolean isRunning=false;
private final int numCaptureBuffers = 3;
private int expectedFrameSize = 0;
private int orientation = 0;
private int id = 0;
// C++ callback context variable.
private long context = 0;
private SurfaceHolder localPreview = null;
// True if this class owns the preview video buffers.
private boolean ownsBuffers = false;
// Set this to 2 for VERBOSE logging. 1 for DEBUG
private static int LOGLEVEL = 0;
private static boolean VERBOSE = LOGLEVEL > 2;
private static boolean DEBUG = LOGLEVEL > 1;
CaptureCapabilityAndroid currentCapability = null;
public static
void DeleteVideoCaptureAndroid(VideoCaptureAndroid captureAndroid) {
if(DEBUG) Log.d("*WEBRTC*", "DeleteVideoCaptureAndroid");
captureAndroid.StopCapture();
captureAndroid.camera.release();
captureAndroid.camera = null;
captureAndroid.context = 0;
if(DEBUG) Log.v("*WEBRTC*", "DeleteVideoCaptureAndroid ended");
}
public VideoCaptureAndroid(int in_id,
long in_context,
Camera in_camera,
AndroidVideoCaptureDevice in_device) {
id = in_id;
context = in_context;
camera = in_camera;
currentDevice = in_device;
}
public int StartCapture(int width, int height, int frameRate) {
if(DEBUG) Log.d("*WEBRTC*", "StartCapture width" + width +
" height " + height +" frame rate " + frameRate);
try {
if (camera == null) {
Log.e("*WEBRTC*",
String.format(Locale.US,"Camera not initialized %d",id));
return -1;
}
currentCapability = new CaptureCapabilityAndroid();
currentCapability.width = width;
currentCapability.height = height;
currentCapability.maxFPS = frameRate;
PixelFormat.getPixelFormatInfo(PIXEL_FORMAT, pixelFormat);
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(currentCapability.width,
currentCapability.height);
parameters.setPreviewFormat(PIXEL_FORMAT );
parameters.setPreviewFrameRate(currentCapability.maxFPS);
camera.setParameters(parameters);
// Get the local preview SurfaceHolder from the static render class
localPreview = ViERenderer.GetLocalRenderer();
if(localPreview != null) {
localPreview.addCallback(this);
}
int bufSize = width * height * pixelFormat.bitsPerPixel / 8;
if(android.os.Build.VERSION.SDK_INT >= 7) {
// According to Doc addCallbackBuffer belongs to API level 8.
// But it seems like it works on Android 2.1 as well.
// At least SE X10 and Milestone
byte[] buffer = null;
for (int i = 0; i < numCaptureBuffers; i++) {
buffer = new byte[bufSize];
camera.addCallbackBuffer(buffer);
}
camera.setPreviewCallbackWithBuffer(this);
ownsBuffers = true;
}
else {
camera.setPreviewCallback(this);
}
camera.startPreview();
previewBufferLock.lock();
expectedFrameSize = bufSize;
isRunning = true;
previewBufferLock.unlock();
}
catch (Exception ex) {
Log.e("*WEBRTC*", "Failed to start camera");
return -1;
}
return 0;
}
public int StopCapture() {
if(DEBUG) Log.d("*WEBRTC*", "StopCapture");
try {
previewBufferLock.lock();
isRunning = false;
previewBufferLock.unlock();
camera.stopPreview();
if(android.os.Build.VERSION.SDK_INT > 7) {
camera.setPreviewCallbackWithBuffer(null);
}
else {
camera.setPreviewCallback(null);
}
}
catch (Exception ex) {
Log.e("*WEBRTC*", "Failed to stop camera");
return -1;
}
if(DEBUG) {
Log.d("*WEBRTC*", "StopCapture ended");
}
return 0;
}
native void ProvideCameraFrame(byte[] data,int length, long captureObject);
public void onPreviewFrame(byte[] data, Camera camera) {
previewBufferLock.lock();
if(VERBOSE) {
Log.v("*WEBRTC*",
String.format(Locale.US, "preview frame length %d context %x",
data.length, context));
}
if(isRunning) {
// If StartCapture has been called but not StopCapture
// Call the C++ layer with the captured frame
if (data.length == expectedFrameSize) {
ProvideCameraFrame(data, expectedFrameSize, context);
if (VERBOSE) {
Log.v("*WEBRTC*", String.format(Locale.US, "frame delivered"));
}
if(ownsBuffers) {
// Give the video buffer to the camera service again.
camera.addCallbackBuffer(data);
}
}
}
previewBufferLock.unlock();
}
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
try {
if(camera != null) {
camera.setPreviewDisplay(localPreview);
}
} catch (IOException e) {
Log.e("*WEBRTC*",
String.format(Locale.US,
"Failed to set Local preview. " + e.getMessage()));
}
}
// Sets the rotation of the preview render window.
// Does not affect the captured video image.
public void SetPreviewRotation(int rotation) {
if(camera != null) {
previewBufferLock.lock();
final boolean running = isRunning;
int width = 0;
int height = 0;
int framerate = 0;
if(running) {
width = currentCapability.width;
height = currentCapability.height;
framerate = currentCapability.maxFPS;
StopCapture();
}
int resultRotation = 0;
if(currentDevice.frontCameraType ==
VideoCaptureDeviceInfoAndroid.FrontFacingCameraType.Android23) {
// this is a 2.3 or later front facing camera.
// SetDisplayOrientation will flip the image horizontally
// before doing the rotation.
resultRotation=(360-rotation) % 360; // compensate the mirror
}
else {
// Back facing or 2.2 or previous front camera
resultRotation=rotation;
}
if(android.os.Build.VERSION.SDK_INT>7) {
camera.setDisplayOrientation(resultRotation);
}
else {
// Android 2.1 and previous
// This rotation unfortunately does not seems to work.
// http://code.google.com/p/android/issues/detail?id=1193
Camera.Parameters parameters = camera.getParameters();
parameters.setRotation(resultRotation);
camera.setParameters(parameters);
}
if(running) {
StartCapture(width, height, framerate);
}
previewBufferLock.unlock();
}
}
public void surfaceCreated(SurfaceHolder holder) {
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
}