mirror of
https://github.com/libretro/ppsspp.git
synced 2024-11-23 16:19:44 +00:00
[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:
parent
088901324d
commit
e1164cfe4f
@ -116,7 +116,6 @@ static int sceUsbCamReadVideoFrame(u32 bufAddr, u32 size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int sceUsbCamPollReadVideoFrameEnd() {
|
static int sceUsbCamPollReadVideoFrameEnd() {
|
||||||
INFO_LOG(HLE, "UNIMPL sceUsbCamPollReadVideoFrameEnd: %d", nextVideoFrame);
|
|
||||||
return nextVideoFrame;
|
return nextVideoFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +185,12 @@ void Register_sceUsbCam()
|
|||||||
|
|
||||||
void Camera::pushCameraImage(long long length, unsigned char* image) {
|
void Camera::pushCameraImage(long long length, unsigned char* image) {
|
||||||
std::lock_guard<std::mutex> lock(videoBufferMutex);
|
std::lock_guard<std::mutex> lock(videoBufferMutex);
|
||||||
videoBufferLength = length;
|
|
||||||
memset (videoBuffer, 0, sizeof(videoBuffer));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,12 @@ import android.graphics.Rect;
|
|||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.graphics.YuvImage;
|
import android.graphics.YuvImage;
|
||||||
import android.hardware.Camera;
|
import android.hardware.Camera;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -15,67 +20,140 @@ import java.util.List;
|
|||||||
@TargetApi(23)
|
@TargetApi(23)
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
class CameraHelper {
|
class CameraHelper {
|
||||||
private static final String TAG = "CameraHelper";
|
private static final String TAG = CameraHelper.class.getSimpleName();
|
||||||
|
private Display mDisplay;
|
||||||
private Camera mCamera = null;
|
private Camera mCamera = null;
|
||||||
private int previewWidth = 0;
|
private boolean mIsCameraRunning = false;
|
||||||
private int previewHeight = 0;
|
private int mCameraOrientation = 0;
|
||||||
private long lastTime = 0;
|
private Camera.Size mPreviewSize = null;
|
||||||
|
private long mLastFrameTime = 0;
|
||||||
private SurfaceTexture mSurfaceTexture;
|
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() {
|
private Camera.PreviewCallback mPreviewCallback = new Camera.PreviewCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPreviewFrame(byte[] previewData, Camera camera) {
|
public void onPreviewFrame(byte[] previewData, Camera camera) {
|
||||||
// throttle at 100 ms
|
// throttle at 66 ms
|
||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = SystemClock.elapsedRealtime();
|
||||||
if (currentTime - lastTime < 100) {
|
if (currentTime - mLastFrameTime < 66) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastTime = currentTime;
|
mLastFrameTime = currentTime;
|
||||||
|
|
||||||
YuvImage yuvImage = new YuvImage(previewData, ImageFormat.NV21, previewWidth, previewHeight, null);
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
// the expected values arrives in sceUsbCamSetupVideo
|
// the expected values arrives in sceUsbCamSetupVideo
|
||||||
int targetW = 480;
|
int targetW = 480;
|
||||||
int targetH = 272;
|
int targetH = 272;
|
||||||
|
|
||||||
// crop to expected size and convert to Jpeg
|
int cameraRotation = getCameraRotation();
|
||||||
Rect rect = new Rect(
|
byte[] newPreviewData = rotateNV21(previewData, mPreviewSize.width, mPreviewSize.height,
|
||||||
(previewWidth - targetW) / 2,
|
targetW, targetH, cameraRotation);
|
||||||
(previewHeight - targetH) / 2,
|
YuvImage yuvImage = new YuvImage(newPreviewData, ImageFormat.NV21, targetW, targetH, null);
|
||||||
previewWidth - (previewWidth - targetW) / 2,
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
previewHeight - (previewHeight - targetH) / 2
|
|
||||||
);
|
// convert to Jpeg
|
||||||
yuvImage.compressToJpeg(rect, 80, baos);
|
Rect crop = new Rect(0, 0, targetW, targetH);
|
||||||
|
yuvImage.compressToJpeg(crop, 80, baos);
|
||||||
NativeApp.pushCameraImage(baos.toByteArray());
|
NativeApp.pushCameraImage(baos.toByteArray());
|
||||||
|
try {
|
||||||
|
baos.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
CameraHelper(Context context) {
|
CameraHelper(final Context context) {
|
||||||
|
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||||
mSurfaceTexture = new SurfaceTexture(10);
|
mSurfaceTexture = new SurfaceTexture(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void startCamera() {
|
void startCamera() {
|
||||||
Log.d(TAG, "startCamera");
|
Log.d(TAG, "startCamera");
|
||||||
try {
|
try {
|
||||||
|
Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
|
||||||
|
Camera.getCameraInfo(0, info);
|
||||||
|
mCameraOrientation = info.orientation;
|
||||||
|
|
||||||
mCamera = Camera.open();
|
mCamera = Camera.open();
|
||||||
Camera.Parameters param = mCamera.getParameters();
|
Camera.Parameters param = mCamera.getParameters();
|
||||||
|
|
||||||
// Set preview size
|
// Set preview size
|
||||||
List<Camera.Size> previewSizes = param.getSupportedPreviewSizes();
|
List<Camera.Size> previewSizes = param.getSupportedPreviewSizes();
|
||||||
previewWidth = previewSizes.get(0).width;
|
mPreviewSize = previewSizes.get(0);
|
||||||
previewHeight = previewSizes.get(0).height;
|
|
||||||
for (int i = 0; i < previewSizes.size(); i++) {
|
for (int i = 0; i < previewSizes.size(); i++) {
|
||||||
Log.d(TAG, "getSupportedPreviewSizes[" + i + "]: " + previewSizes.get(i).height + " " + previewSizes.get(i).width);
|
Log.d(TAG, "getSupportedPreviewSizes[" + i + "]: " + previewSizes.get(i).height + " " + previewSizes.get(i).width);
|
||||||
if (previewSizes.get(i).width <= 640 && previewSizes.get(i).height <= 480) {
|
if (previewSizes.get(i).width <= 640 && previewSizes.get(i).height <= 480) {
|
||||||
previewWidth = previewSizes.get(i).width;
|
mPreviewSize = previewSizes.get(i);
|
||||||
previewHeight = previewSizes.get(i).height;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.d(TAG, "setPreviewSize(" + previewWidth + ", " + previewHeight + ")");
|
Log.d(TAG, "setPreviewSize(" + mPreviewSize.width + ", " + mPreviewSize.height + ")");
|
||||||
param.setPreviewSize(previewWidth, previewHeight);
|
param.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
|
||||||
|
|
||||||
// Set preview FPS
|
// Set preview FPS
|
||||||
int[] fps;
|
int[] fps;
|
||||||
@ -94,18 +172,31 @@ class CameraHelper {
|
|||||||
mCamera.setPreviewTexture(mSurfaceTexture);
|
mCamera.setPreviewTexture(mSurfaceTexture);
|
||||||
mCamera.setPreviewCallback(mPreviewCallback);
|
mCamera.setPreviewCallback(mPreviewCallback);
|
||||||
mCamera.startPreview();
|
mCamera.startPreview();
|
||||||
|
mIsCameraRunning = true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "Cannot start camera: " + e.toString());
|
Log.e(TAG, "Cannot start camera: " + e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopCamera() {
|
void pause() {
|
||||||
Log.d(TAG, "stopCamera");
|
if (mIsCameraRunning && mCamera != null) {
|
||||||
if (mCamera != null) {
|
Log.d(TAG, "pause");
|
||||||
mCamera.setPreviewCallback(null);
|
mCamera.setPreviewCallback(null);
|
||||||
mCamera.stopPreview();
|
mCamera.stopPreview();
|
||||||
mCamera.release();
|
mCamera.release();
|
||||||
mCamera = null;
|
mCamera = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resume() {
|
||||||
|
if (mIsCameraRunning) {
|
||||||
|
Log.d(TAG, "resume");
|
||||||
|
startCamera();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopCamera() {
|
||||||
|
pause();
|
||||||
|
mIsCameraRunning = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -740,6 +740,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
|
|||||||
Log.e(TAG, "mGLSurfaceView really shouldn't be null in onPause");
|
Log.e(TAG, "mGLSurfaceView really shouldn't be null in onPause");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mCameraHelper.pause();
|
||||||
Log.i(TAG, "onPause completed");
|
Log.i(TAG, "onPause completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,6 +780,7 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
|
|||||||
mSurfaceView.onResume();
|
mSurfaceView.onResume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mCameraHelper.resume();
|
||||||
|
|
||||||
gainAudioFocus(this.audioManager, this.audioFocusChangeListener);
|
gainAudioFocus(this.audioManager, this.audioFocusChangeListener);
|
||||||
NativeApp.resume();
|
NativeApp.resume();
|
||||||
@ -1335,6 +1337,10 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
|
|||||||
// Only keep the screen bright ingame.
|
// Only keep the screen bright ingame.
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
}
|
}
|
||||||
|
} else if (command.equals("event")) {
|
||||||
|
if (params.equals("exitgame")) {
|
||||||
|
mCameraHelper.stopCamera();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user