[Android] Updates for camera

-rotate camera frames to follow display orientation
-release camera resources when a game is closed
-release camera resources when the activity is paused
This commit is contained in:
Florin9doi 2019-08-22 15:41:01 +03:00
parent 088901324d
commit e1164cfe4f
3 changed files with 133 additions and 32 deletions

View File

@ -116,7 +116,6 @@ static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) {
}
static int sceUsbCamPollReadVideoFrameEnd() {
INFO_LOG(HLE, "UNIMPL sceUsbCamPollReadVideoFrameEnd: %d", nextVideoFrame);
return nextVideoFrame;
}
@ -186,7 +185,12 @@ void Register_sceUsbCam()
void Camera::pushCameraImage(long long length, unsigned char* image) {
std::lock_guard<std::mutex> lock(videoBufferMutex);
videoBufferLength = length;
memset (videoBuffer, 0, sizeof(videoBuffer));
memcpy (videoBuffer, image, length);
if (length > sizeof(videoBuffer)) {
videoBufferLength = 0;
ERROR_LOG(HLE, "pushCameraImage: length error");
} else {
videoBufferLength = length;
memcpy (videoBuffer, image, length);
}
}

View File

@ -7,7 +7,12 @@ import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.os.SystemClock;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
@ -15,67 +20,140 @@ import java.util.List;
@TargetApi(23)
@SuppressWarnings("deprecation")
class CameraHelper {
private static final String TAG = "CameraHelper";
private static final String TAG = CameraHelper.class.getSimpleName();
private Display mDisplay;
private Camera mCamera = null;
private int previewWidth = 0;
private int previewHeight = 0;
private long lastTime = 0;
private boolean mIsCameraRunning = false;
private int mCameraOrientation = 0;
private Camera.Size mPreviewSize = null;
private long mLastFrameTime = 0;
private SurfaceTexture mSurfaceTexture;
private int getCameraRotation() {
int displayRotation = mDisplay.getRotation();
int displayDegrees = 0;
switch (displayRotation) {
case Surface.ROTATION_0: displayDegrees = 0; break;
case Surface.ROTATION_90: displayDegrees = 90; break;
case Surface.ROTATION_180: displayDegrees = 180; break;
case Surface.ROTATION_270: displayDegrees = 270; break;
}
return (mCameraOrientation - displayDegrees + 360) % 360;
}
static byte[] rotateNV21(final byte[] input, final int inWidth, final int inHeight,
final int outWidth, final int outHeight, final int rotation) {
final int inFrameSize = inWidth * inHeight;
final int outFrameSize = outWidth * outHeight;
final byte[] output = new byte[outFrameSize + outFrameSize/2];
if (rotation == 0 || rotation == 180) {
final int crop_left = (inWidth - outWidth) / 2;
final int crop_top = (inHeight - outHeight) / 2;
for (int j = 0; j < outHeight; j++) {
final int yInCol = (crop_top + j) * inWidth + crop_left;
final int uvInCol = inFrameSize + ((crop_top + j) >> 1) * inWidth + crop_left;
final int jOut = rotation == 180 ? outHeight - j - 1 : j;
final int yOutCol = jOut * outWidth;
final int uvOutCol = outFrameSize + (jOut >> 1) * outWidth;
for (int i = 0; i < outWidth; i++) {
final int yIn = yInCol + i;
final int uIn = uvInCol + (i & ~1);
final int vIn = uIn + 1;
final int iOut = rotation == 180 ? outWidth - i - 1 : i;
final int yOut = yOutCol + iOut;
final int uOut = uvOutCol + (iOut & ~1);
final int vOut = uOut + 1;
output[yOut] = input[yIn];
output[uOut] = input[uIn];
output[vOut] = input[vIn];
}
}
} else if (rotation == 90 || rotation == 270) {
int crop_left = (inWidth - outHeight) / 2;
int crop_top = (inHeight - outWidth) / 2;
for (int j = 0; j < outWidth; j++) {
final int yInCol = (crop_top + j) * inWidth + crop_left;
final int uvInCol = inFrameSize + ((crop_top + j) >> 1) * inWidth + crop_left;
final int iOut = rotation == 90 ? outWidth - j - 1 : j;
for (int i = 0; i < outHeight; i++) {
final int yIn = yInCol + i;
final int uIn = uvInCol + (i & ~1);
final int vIn = uIn + 1;
final int jOut = rotation == 270 ? outHeight - i - 1 : i;
final int yOut = jOut * outWidth + iOut;
final int uOut = outFrameSize + (jOut >> 1) * outWidth + (iOut & ~1);
final int vOut = uOut + 1;
output[yOut] = input[yIn];
output[uOut] = input[uIn];
output[vOut] = input[vIn];
}
}
}
return output;
}
private Camera.PreviewCallback mPreviewCallback = new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] previewData, Camera camera) {
// throttle at 100 ms
long currentTime = System.currentTimeMillis();
if (currentTime - lastTime < 100) {
// throttle at 66 ms
long currentTime = SystemClock.elapsedRealtime();
if (currentTime - mLastFrameTime < 66) {
return;
}
lastTime = currentTime;
YuvImage yuvImage = new YuvImage(previewData, ImageFormat.NV21, previewWidth, previewHeight, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mLastFrameTime = currentTime;
// the expected values arrives in sceUsbCamSetupVideo
int targetW = 480;
int targetH = 272;
// crop to expected size and convert to Jpeg
Rect rect = new Rect(
(previewWidth - targetW) / 2,
(previewHeight - targetH) / 2,
previewWidth - (previewWidth - targetW) / 2,
previewHeight - (previewHeight - targetH) / 2
);
yuvImage.compressToJpeg(rect, 80, baos);
int cameraRotation = getCameraRotation();
byte[] newPreviewData = rotateNV21(previewData, mPreviewSize.width, mPreviewSize.height,
targetW, targetH, cameraRotation);
YuvImage yuvImage = new YuvImage(newPreviewData, ImageFormat.NV21, targetW, targetH, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// convert to Jpeg
Rect crop = new Rect(0, 0, targetW, targetH);
yuvImage.compressToJpeg(crop, 80, baos);
NativeApp.pushCameraImage(baos.toByteArray());
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
@SuppressWarnings("unused")
CameraHelper(Context context) {
CameraHelper(final Context context) {
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSurfaceTexture = new SurfaceTexture(10);
}
void startCamera() {
Log.d(TAG, "startCamera");
try {
Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
Camera.getCameraInfo(0, info);
mCameraOrientation = info.orientation;
mCamera = Camera.open();
Camera.Parameters param = mCamera.getParameters();
// Set preview size
List<Camera.Size> previewSizes = param.getSupportedPreviewSizes();
previewWidth = previewSizes.get(0).width;
previewHeight = previewSizes.get(0).height;
mPreviewSize = previewSizes.get(0);
for (int i = 0; i < previewSizes.size(); i++) {
Log.d(TAG, "getSupportedPreviewSizes[" + i + "]: " + previewSizes.get(i).height + " " + previewSizes.get(i).width);
if (previewSizes.get(i).width <= 640 && previewSizes.get(i).height <= 480) {
previewWidth = previewSizes.get(i).width;
previewHeight = previewSizes.get(i).height;
mPreviewSize = previewSizes.get(i);
break;
}
}
Log.d(TAG, "setPreviewSize(" + previewWidth + ", " + previewHeight + ")");
param.setPreviewSize(previewWidth, previewHeight);
Log.d(TAG, "setPreviewSize(" + mPreviewSize.width + ", " + mPreviewSize.height + ")");
param.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
// Set preview FPS
int[] fps;
@ -94,18 +172,31 @@ class CameraHelper {
mCamera.setPreviewTexture(mSurfaceTexture);
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.startPreview();
mIsCameraRunning = true;
} catch (IOException e) {
Log.e(TAG, "Cannot start camera: " + e.toString());
}
}
void stopCamera() {
Log.d(TAG, "stopCamera");
if (mCamera != null) {
void pause() {
if (mIsCameraRunning && mCamera != null) {
Log.d(TAG, "pause");
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
void resume() {
if (mIsCameraRunning) {
Log.d(TAG, "resume");
startCamera();
}
}
void stopCamera() {
pause();
mIsCameraRunning = false;
}
}

View File

@ -740,6 +740,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
Log.e(TAG, "mGLSurfaceView really shouldn't be null in onPause");
}
}
mCameraHelper.pause();
Log.i(TAG, "onPause completed");
}
@ -779,6 +780,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
mSurfaceView.onResume();
}
}
mCameraHelper.resume();
gainAudioFocus(this.audioManager, this.audioFocusChangeListener);
NativeApp.resume();
@ -1335,6 +1337,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
// Only keep the screen bright ingame.
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
} else if (command.equals("event")) {
if (params.equals("exitgame")) {
mCameraHelper.stopCamera();
}
}
return false;
}