diff --git a/camera/MultiCameraApplication/AndroidManifest.xml b/camera/MultiCameraApplication/AndroidManifest.xml
index ef0784c..1516ff5 100644
--- a/camera/MultiCameraApplication/AndroidManifest.xml
+++ b/camera/MultiCameraApplication/AndroidManifest.xml
@@ -1,51 +1,60 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/BotmLeftCam.java b/camera/MultiCameraApplication/java/com/intel/multicamera/BotmLeftCam.java
index 6a05ae0..6230aa4 100644
--- a/camera/MultiCameraApplication/java/com/intel/multicamera/BotmLeftCam.java
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/BotmLeftCam.java
@@ -1,4 +1,5 @@
/*
+ * Copyright 2014 The Android Open Source Project
* Copyright (c) 2019 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +23,8 @@
import android.content.*;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
+import android.graphics.Matrix;
+import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.*;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -71,14 +74,15 @@ public class BotmLeftCam {
protected CameraCaptureSession cameraCaptureSessions;
protected CaptureRequest captureRequest;
protected CaptureRequest.Builder captureRequestBuilder;
- private Size imageDimension;
+ private Size imageDimension, previewSize;
private ImageReader imageReader;
private File file;
- private static final int REQUEST_CAMERA_PERMISSION = 200;
- private final int PERMISSIONS_REQUEST_SNAPSHOT = 3;
-
private Handler mBackgroundHandler;
private HandlerThread mBackgroundThread;
+ private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
+ private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
+ private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
+ private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private SharedPreferences settings;
/**
@@ -92,6 +96,11 @@ public class BotmLeftCam {
private ContentValues mCurrentVideoValues, mCurrentPictureValues;
byte[] jpegLength;
+ /**
+ * Orientation of the camera sensor
+ */
+ private int mSensorOrientation;
+
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
@@ -151,11 +160,12 @@ public void onClick(View view) {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// open your camera here
- openCamera();
+ openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Transform you image captured size according to the surface width and height
+ configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
@@ -165,34 +175,45 @@ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
};
- public void openCamera() {
+ public void openCamera(int width, int height) {
CameraManager manager = (CameraManager)mActivity.getSystemService(Context.CAMERA_SERVICE);
Log.e(TAG, "is camera open");
try {
+ if (!((manager.getCameraIdList().length >= 3) &&
+ (manager.getCameraIdList().length <= 4))) {
+ Log.e(TAG, "this camera is not connected ");
+ return;
+ }
cameraId = manager.getCameraIdList()[2];
Log.e(TAG, "is camera open ID" + cameraId);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map =
- characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) return;
- // Add permission for camera and let user grant the permission
- if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) !=
- PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(mActivity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
- PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) !=
- PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- mActivity,
- new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO,
- Manifest.permission.WRITE_EXTERNAL_STORAGE},
- REQUEST_CAMERA_PERMISSION);
- return;
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
+ String Key = SettingsActivity.SettingsFragment.getchangedPrefKey();
+
+ if (Key.compareTo("video_list") == 0) {
+ String videoQuality = settings.getString("video_list", "medium");
+
+ int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
+ Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
+
+ mProfile = CamcorderProfile.get(0, quality);
+ previewSize = new Size(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
+
+ configureTransform(width, height);
+ } else {
+ previewSize = SettingsActivity.SettingsFragment.sizeFromSettingString(
+ settings.getString("capture_list", "640x480"));
+ Log.d(TAG,
+ "Selected imageDimension" + previewSize.getWidth() + previewSize.getHeight());
+ configureTransform(width, height);
}
- mMediaRecorder = new MediaRecorder();
+ mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+ configureTransform(width, height);
startBackgroundThread();
manager.openCamera(cameraId, stateCallback, null);
@@ -213,25 +234,50 @@ public void onOpened(CameraDevice camera) {
}
@Override
public void onDisconnected(CameraDevice camera) {
- cameraDevice.close();
+ Log.e(TAG, "onDisconnected");
+ closeCamera();
}
@Override
public void onError(CameraDevice camera, int error) {
- cameraDevice.close();
- cameraDevice = null;
+ Log.e(TAG, "onError");
+ closeCamera();
}
};
+ private void configureTransform(int viewWidth, int viewHeight) {
+ if (null == textureView || null == previewSize) {
+ return;
+ }
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ Matrix matrix = new Matrix();
+ RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
+ Log.e(TAG, "configureTransform() viewWidth: " + viewWidth + " viewHeight: " + viewHeight);
+ RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
+ float centerX = viewRect.centerX();
+ float centerY = viewRect.centerY();
+ if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
+ bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
+ matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
+ float scale = Math.max((float)viewHeight / previewSize.getHeight(),
+ (float)viewWidth / previewSize.getWidth());
+ matrix.postScale(scale, scale, centerX, centerY);
+ matrix.postRotate(90 * (rotation - 2), centerX, centerY);
+ } else if (Surface.ROTATION_180 == rotation) {
+ matrix.postRotate(180, centerX, centerY);
+ }
+ textureView.setTransform(matrix);
+ }
+
protected void createCameraPreview() {
try {
closePreviewSession();
SurfaceTexture texture = textureView.getSurfaceTexture();
if (texture == null) return;
-
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
String Key = SettingsActivity.SettingsFragment.getchangedPrefKey();
imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString(
- settings.getString("capture_list", "640x480"));
+ settings.getString("capture_list", "640x480"));
String videoQuality = settings.getString("video_list", "medium");
int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
@@ -282,6 +328,7 @@ public void closeCamera() {
imageReader = null;
}
if (null != mMediaRecorder) {
+ mMediaRecorder.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
@@ -293,7 +340,7 @@ public void closeCamera() {
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
- mBackgroundThread = new HandlerThread("Camera-$cameraId");
+ mBackgroundThread = new HandlerThread("Camera-3");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
@@ -329,6 +376,20 @@ protected void updatePreview() {
}
}
+ /**
+ * Retrieves the JPEG orientation from the specified screen rotation.
+ *
+ * @param rotation The screen rotation.
+ * @return The JPEG orientation (one of 0, 90, 270, and 360)
+ */
+ private int getOrientation(int rotation) {
+ // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X)
+ // We have to take that into account and rotate JPEG properly.
+ // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS.
+ // For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
+ return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
+ }
+
protected void takePicture() {
if (null == cameraDevice) {
Log.e(TAG, "cameraDevice is null");
@@ -336,9 +397,11 @@ protected void takePicture() {
}
try {
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString(
settings.getString("capture_list", "640x480"));
-
+ Log.d(TAG, "Selected imageDimension" + imageDimension.getWidth() +
+ imageDimension.getHeight());
ImageReader reader = ImageReader.newInstance(
imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1);
List outputSurfaces = new ArrayList(2);
@@ -351,17 +414,7 @@ protected void takePicture() {
CameraMetadata.CONTROL_MODE_AUTO);
// Orientation
int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
- captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
-
- // Add permission for camera and let user grant the permission
- if (ActivityCompat.checkSelfPermission(mActivity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
- PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- mActivity, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
- PERMISSIONS_REQUEST_SNAPSHOT);
- return;
- }
+ captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_IMAGE);
if (fileDetails == null || fileDetails.length < 5) {
@@ -448,11 +501,18 @@ public void onConfigureFailed(CameraCaptureSession session) {}
/* Recording Start*/
private void startRecordingVideo() {
- if (null == cameraDevice || !textureView.isAvailable() || null == mProfile) {
+ if (null == cameraDevice || !textureView.isAvailable()) {
return;
}
try {
closePreviewSession();
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
+ String videoQuality = settings.getString("video_list", "medium");
+
+ int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
+ Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
+
+ mProfile = CamcorderProfile.get(0, quality);
setUpMediaRecorder();
SurfaceTexture texture = textureView.getSurfaceTexture();
if (texture == null) return;
@@ -507,9 +567,10 @@ private void setUpMediaRecorder() throws IOException {
if (null == mActivity) {
return;
}
+
+ mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
- mMediaRecorder.setProfile(mProfile);
String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_VIDEO);
if (fileDetails == null || fileDetails.length < 5) {
@@ -521,16 +582,33 @@ private void setUpMediaRecorder() throws IOException {
Utils.getContentValues(Utils.MEDIA_TYPE_VIDEO, fileDetails, mProfile.videoFrameWidth,
mProfile.videoFrameHeight);
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
/**
* set output file in media recorder
*/
mMediaRecorder.setOutputFile(mVideoFilename);
-
+ mMediaRecorder.setVideoEncodingBitRate(10000000);
+ mMediaRecorder.setVideoFrameRate(30);
+ mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ switch (mSensorOrientation) {
+ case SENSOR_ORIENTATION_DEFAULT_DEGREES:
+ mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
+ break;
+ case SENSOR_ORIENTATION_INVERSE_DEGREES:
+ mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
+ break;
+ }
try {
mMediaRecorder.prepare();
} catch (IOException ex) {
Log.e(TAG, "prepare failed for " + mVideoFilename, ex);
mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
throw new RuntimeException(ex);
}
}
@@ -549,6 +627,8 @@ private void stopRecordingVideo() {
// Stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
if (null != mActivity) {
Toast.makeText(mActivity, "Video saved: " + mVideoFilename, Toast.LENGTH_SHORT).show();
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/BotmRightCam.java b/camera/MultiCameraApplication/java/com/intel/multicamera/BotmRightCam.java
index 6fec868..e5ba66d 100644
--- a/camera/MultiCameraApplication/java/com/intel/multicamera/BotmRightCam.java
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/BotmRightCam.java
@@ -1,4 +1,5 @@
/*
+ * Copyright 2014 The Android Open Source Project
* Copyright (c) 2019 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +23,8 @@
import android.content.*;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
+import android.graphics.Matrix;
+import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.*;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -71,14 +74,15 @@ public class BotmRightCam {
protected CameraCaptureSession cameraCaptureSessions;
protected CaptureRequest captureRequest;
protected CaptureRequest.Builder captureRequestBuilder;
- private Size imageDimension;
+ private Size imageDimension, previewSize;
private ImageReader imageReader;
private File file;
- private static final int REQUEST_CAMERA_PERMISSION = 200;
- private final int PERMISSIONS_REQUEST_SNAPSHOT = 3;
-
private Handler mBackgroundHandler;
private HandlerThread mBackgroundThread;
+ private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
+ private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
+ private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
+ private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private SharedPreferences settings;
/**
@@ -92,6 +96,11 @@ public class BotmRightCam {
private ContentValues mCurrentVideoValues, mCurrentPictureValues;
byte[] jpegLength;
+ /**
+ * Orientation of the camera sensor
+ */
+ private int mSensorOrientation;
+
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
@@ -151,11 +160,12 @@ public void onClick(View view) {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// open your camera here
- openCamera();
+ openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Transform you image captured size according to the surface width and height
+ configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
@@ -165,34 +175,43 @@ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
};
- public void openCamera() {
+ public void openCamera(int width, int height) {
CameraManager manager = (CameraManager)mActivity.getSystemService(Context.CAMERA_SERVICE);
Log.e(TAG, "is camera open");
try {
+ if (manager.getCameraIdList().length != 4) {
+ Log.e(TAG, "this camera is not connected ");
+ return;
+ }
+
cameraId = manager.getCameraIdList()[3];
Log.e(TAG, "is camera open ID" + cameraId);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) return;
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
+ String Key = SettingsActivity.SettingsFragment.getchangedPrefKey();
- // Add permission for camera and let user grant the permission
- if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) !=
- PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(mActivity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
- PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) !=
- PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- mActivity,
- new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO,
- Manifest.permission.WRITE_EXTERNAL_STORAGE},
- REQUEST_CAMERA_PERMISSION);
- return;
- }
- mMediaRecorder = new MediaRecorder();
+ if (Key.compareTo("video_list") == 0) {
+ String videoQuality = settings.getString("video_list", "medium");
+ int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
+ Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
+
+ mProfile = CamcorderProfile.get(0, quality);
+ previewSize = new Size(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
+
+ configureTransform(width, height);
+ } else {
+ previewSize = SettingsActivity.SettingsFragment.sizeFromSettingString(
+ settings.getString("capture_list", "640x480"));
+ Log.d(TAG,
+ "Selected imageDimension" + previewSize.getWidth() + previewSize.getHeight());
+ configureTransform(width, height);
+ }
+ mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+ configureTransform(width, height);
startBackgroundThread();
manager.openCamera(cameraId, stateCallback, null);
@@ -218,20 +237,45 @@ public void onDisconnected(CameraDevice camera) {
}
@Override
public void onError(CameraDevice camera, int error) {
- cameraDevice.close();
- cameraDevice = null;
+ Log.e(TAG, "onError");
+ closeCamera();
}
};
+ private void configureTransform(int viewWidth, int viewHeight) {
+ if (null == textureView || null == previewSize) {
+ return;
+ }
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ Matrix matrix = new Matrix();
+ RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
+ Log.e(TAG, "configureTransform() viewWidth: " + viewWidth + " viewHeight: " + viewHeight);
+ RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
+ float centerX = viewRect.centerX();
+ float centerY = viewRect.centerY();
+ if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
+ bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
+ matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
+ float scale = Math.max((float)viewHeight / previewSize.getHeight(),
+ (float)viewWidth / previewSize.getWidth());
+ matrix.postScale(scale, scale, centerX, centerY);
+ matrix.postRotate(90 * (rotation - 2), centerX, centerY);
+ } else if (Surface.ROTATION_180 == rotation) {
+ matrix.postRotate(180, centerX, centerY);
+ }
+ textureView.setTransform(matrix);
+ }
+
protected void createCameraPreview() {
try {
closePreviewSession();
SurfaceTexture texture = textureView.getSurfaceTexture();
if (texture == null) return;
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
String Key = SettingsActivity.SettingsFragment.getchangedPrefKey();
imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString(
- settings.getString("capture_list", "640x480"));
+ settings.getString("capture_list", "640x480"));
String videoQuality = settings.getString("video_list", "medium");
int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
@@ -283,6 +327,7 @@ public void closeCamera() {
imageReader = null;
}
if (null != mMediaRecorder) {
+ mMediaRecorder.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
@@ -294,7 +339,7 @@ public void closeCamera() {
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
- mBackgroundThread = new HandlerThread("Camera-$cameraId");
+ mBackgroundThread = new HandlerThread("Camera-4");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
@@ -330,6 +375,20 @@ protected void updatePreview() {
}
}
+ /**
+ * Retrieves the JPEG orientation from the specified screen rotation.
+ *
+ * @param rotation The screen rotation.
+ * @return The JPEG orientation (one of 0, 90, 270, and 360)
+ */
+ private int getOrientation(int rotation) {
+ // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X)
+ // We have to take that into account and rotate JPEG properly.
+ // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS.
+ // For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
+ return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
+ }
+
protected void takePicture() {
if (null == cameraDevice) {
Log.e(TAG, "cameraDevice is null");
@@ -337,9 +396,11 @@ protected void takePicture() {
}
try {
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString(
settings.getString("capture_list", "640x480"));
-
+ Log.d(TAG, "Selected imageDimension" + imageDimension.getWidth() +
+ imageDimension.getHeight());
ImageReader reader = ImageReader.newInstance(
imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1);
List outputSurfaces = new ArrayList(2);
@@ -352,17 +413,7 @@ protected void takePicture() {
CameraMetadata.CONTROL_MODE_AUTO);
// Orientation
int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
- captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
-
- // Add permission for camera and let user grant the permission
- if (ActivityCompat.checkSelfPermission(mActivity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
- PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- mActivity, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
- PERMISSIONS_REQUEST_SNAPSHOT);
- return;
- }
+ captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_IMAGE);
if (fileDetails == null || fileDetails.length < 5) {
@@ -449,11 +500,18 @@ public void onConfigureFailed(CameraCaptureSession session) {}
/* Recording Start*/
private void startRecordingVideo() {
- if (null == cameraDevice || !textureView.isAvailable() || null == mProfile) {
+ if (null == cameraDevice || !textureView.isAvailable()) {
return;
}
try {
closePreviewSession();
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
+ String videoQuality = settings.getString("video_list", "medium");
+
+ int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
+ Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
+
+ mProfile = CamcorderProfile.get(0, quality);
setUpMediaRecorder();
SurfaceTexture texture = textureView.getSurfaceTexture();
if (texture == null) return;
@@ -508,9 +566,10 @@ private void setUpMediaRecorder() throws IOException {
if (null == mActivity) {
return;
}
+
+ mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
- mMediaRecorder.setProfile(mProfile);
String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_VIDEO);
if (fileDetails == null || fileDetails.length < 5) {
@@ -522,16 +581,33 @@ private void setUpMediaRecorder() throws IOException {
Utils.getContentValues(Utils.MEDIA_TYPE_VIDEO, fileDetails, mProfile.videoFrameWidth,
mProfile.videoFrameHeight);
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
/**
* set output file in media recorder
*/
mMediaRecorder.setOutputFile(mVideoFilename);
-
+ mMediaRecorder.setVideoEncodingBitRate(10000000);
+ mMediaRecorder.setVideoFrameRate(30);
+ mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ switch (mSensorOrientation) {
+ case SENSOR_ORIENTATION_DEFAULT_DEGREES:
+ mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
+ break;
+ case SENSOR_ORIENTATION_INVERSE_DEGREES:
+ mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
+ break;
+ }
try {
mMediaRecorder.prepare();
} catch (IOException ex) {
Log.e(TAG, "prepare failed for " + mVideoFilename, ex);
mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
throw new RuntimeException(ex);
}
}
@@ -550,6 +626,8 @@ private void stopRecordingVideo() {
// Stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
if (null != mActivity) {
Toast.makeText(mActivity, "Video saved: " + mVideoFilename, Toast.LENGTH_SHORT).show();
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/MainActivity.java b/camera/MultiCameraApplication/java/com/intel/multicamera/MainActivity.java
index 30a040b..0e1c474 100644
--- a/camera/MultiCameraApplication/java/com/intel/multicamera/MainActivity.java
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/MainActivity.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2012 The Android Open Source Project
* Copyright (c) 2019 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,6 +17,7 @@
package com.intel.multicamera;
+import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -34,6 +36,7 @@
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
+import androidx.core.app.ActivityCompat;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@@ -52,6 +55,8 @@ public class MainActivity extends AppCompatActivity {
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private FrameLayout frameView0, frameView1, frameView2, frameView3;
+ private boolean mHasCriticalPermissions = false;
+
TopLeftCam mTopLeftCam;
TopRightCam mTopRightCam;
BotmLeftCam mBotmLeftCam;
@@ -71,12 +76,19 @@ protected void onCreate(Bundle savedInstanceState) {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
+ checkPermissions();
+ if (!mHasCriticalPermissions) {
+ Log.v(TAG, "onCreate: Missing critical permissions.");
+ finish();
+ return;
+ }
+
setVisibilityFrameLayout();
}
public void Open_TopLeftCam() {
mTopLeftCam_textureView = findViewById(R.id.textureview0);
- assert mTopLeftCam_textureView != null;
+ if (mTopLeftCam_textureView == null) return;
mTopLeftCam_PictureButton = findViewById(R.id.Picture0);
mTopLeftCam_RecordButton = findViewById(R.id.Record0);
@@ -92,7 +104,7 @@ else if (isImageCaptureIntent())
public void Open_TopRightCam() {
mTopRightCam_textureView = findViewById(R.id.textureview1);
- assert mTopRightCam_textureView != null;
+ if (mTopRightCam_textureView == null) return;
mTopRightCam_PictureButton = findViewById(R.id.Picture1);
mTopRightCam_RecordButton = findViewById(R.id.Record1);
@@ -103,7 +115,7 @@ public void Open_TopRightCam() {
public void Open_BotmLeftCam() {
mBotmLeftCam_textureView = findViewById(R.id.textureview2);
- assert mTopRightCam_textureView != null;
+ if (mBotmLeftCam_textureView == null) return;
mBotmLeftCam_PictureButton = findViewById(R.id.Picture2);
mBotmLeftCam_RecordButton = findViewById(R.id.Record2);
@@ -114,7 +126,7 @@ public void Open_BotmLeftCam() {
public void Open_BotmRightCam() {
mBotmRightCam_textureView = findViewById(R.id.textureview3);
- assert mTopRightCam_textureView != null;
+ if (mBotmRightCam_textureView == null) return;
mBotmRightCam_PictureButton = findViewById(R.id.Picture3);
mBotmRightCam_RecordButton = findViewById(R.id.Record3);
@@ -200,26 +212,121 @@ private void manageTopLeftCam() {
frameView0.setVisibility(FrameLayout.VISIBLE);
} else if (mTopLeftCam_textureView == null) {
mTopLeftCam_textureView = findViewById(R.id.textureview0);
- assert mTopLeftCam_textureView != null;
+ if (mTopLeftCam_textureView == null) return;
}
if (mTopLeftCam_textureView.isAvailable()) {
frameView0.setVisibility(FrameLayout.VISIBLE);
- mTopLeftCam.openCamera();
+ mTopLeftCam.openCamera(mTopLeftCam_textureView.getWidth(),
+ mTopLeftCam_textureView.getHeight());
} else {
mTopLeftCam_textureView.setSurfaceTextureListener(mTopLeftCam.textureListener);
}
}
+ private void manageTopRightCam() {
+ if (mTopRightCam == null) {
+ Open_TopRightCam();
+ frameView1.setVisibility(FrameLayout.VISIBLE);
+
+ } else if (mTopRightCam_textureView == null) {
+ mTopRightCam_textureView = findViewById(R.id.textureview1);
+ if (mTopRightCam_textureView == null) return;
+ }
+
+ if (mTopRightCam_textureView.isAvailable()) {
+ frameView1.setVisibility(FrameLayout.VISIBLE);
+ mTopRightCam.openCamera(mTopRightCam_textureView.getWidth(),
+ mTopRightCam_textureView.getHeight());
+ } else {
+ mTopRightCam_textureView.setSurfaceTextureListener(mTopRightCam.textureListener);
+ }
+ }
+
+ private void manageBotmLeftCam() {
+ if (mBotmLeftCam == null) {
+ Open_BotmLeftCam();
+ frameView2.setVisibility(FrameLayout.VISIBLE);
+
+ } else if (mBotmLeftCam_textureView == null) {
+ mBotmLeftCam_textureView = findViewById(R.id.textureview2);
+ if (mBotmLeftCam_textureView == null) return;
+ }
+
+ if (mBotmLeftCam_textureView.isAvailable()) {
+ frameView2.setVisibility(FrameLayout.VISIBLE);
+ mBotmLeftCam.openCamera(mBotmLeftCam_textureView.getWidth(),
+ mBotmLeftCam_textureView.getHeight());
+ } else {
+ mBotmLeftCam_textureView.setSurfaceTextureListener(mBotmLeftCam.textureListener);
+ }
+ }
+
+ private void manageBotmRightCam() {
+ if (mBotmRightCam == null) {
+ Open_BotmRightCam();
+ frameView3.setVisibility(FrameLayout.VISIBLE);
+
+ } else if (mBotmRightCam_textureView == null) {
+ mBotmRightCam_textureView = findViewById(R.id.textureview3);
+ if (mBotmRightCam_textureView == null) return;
+ }
+
+ if (mBotmRightCam_textureView.isAvailable()) {
+ frameView3.setVisibility(FrameLayout.VISIBLE);
+ mBotmRightCam.openCamera(mBotmRightCam_textureView.getWidth(),
+ mBotmRightCam_textureView.getHeight());
+ } else {
+ mBotmRightCam_textureView.setSurfaceTextureListener(mBotmRightCam.textureListener);
+ }
+ }
+
+ /**
+ * Checks if any of the needed Android runtime permissions are missing.
+ * If they are, then launch the permissions activity under one of the following conditions:
+ * a) The permissions dialogs have not run yet. We will ask for permission only once.
+ * b) If the missing permissions are critical to the app running, we will display a fatal error
+ * dialog. Critical permissions are: camera, microphone and storage. The app cannot run without
+ * them. Non-critical permission is location.
+ */
+ private void checkPermissions() {
+ if (ActivityCompat.checkSelfPermission(getApplicationContext(),
+ Manifest.permission.CAMERA) ==
+ PackageManager.PERMISSION_GRANTED &&
+ ActivityCompat.checkSelfPermission(getApplicationContext(),
+ Manifest.permission.RECORD_AUDIO) ==
+ PackageManager.PERMISSION_GRANTED &&
+ ActivityCompat.checkSelfPermission(getApplicationContext(),
+ Manifest.permission.READ_EXTERNAL_STORAGE) ==
+ PackageManager.PERMISSION_GRANTED) {
+ mHasCriticalPermissions = true;
+ } else {
+ mHasCriticalPermissions = false;
+ }
+
+ if (!mHasCriticalPermissions) {
+ Intent intent = new Intent(this, PermissionsActivity.class);
+ startActivity(intent);
+ finish();
+ }
+ }
+
@Override
protected void onResume() {
super.onResume();
Log.e(TAG, "onResume");
+ checkPermissions();
+ if (!mHasCriticalPermissions) {
+ Log.v(TAG, "onResume: Missing critical permissions.");
+ finish();
+ return;
+ }
+
CameraManager manager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);
try {
numOfCameras = manager.getCameraIdList().length;
- Log.d(TAG, "Total Cameras: " + manager.getCameraIdList().length);
+ Log.d(TAG, "onResume Total Cameras: " + manager.getCameraIdList().length);
} catch (CameraAccessException e) {
e.printStackTrace();
}
@@ -227,17 +334,17 @@ protected void onResume() {
if (numOfCameras == 1) {
manageTopLeftCam();
} else if (numOfCameras == 2) {
- if (mTopLeftCam_textureView.isAvailable()) {
- mTopLeftCam.openCamera();
- } else {
- mTopLeftCam_textureView.setSurfaceTextureListener(mTopLeftCam.textureListener);
- }
-
- if (mTopRightCam_textureView.isAvailable()) {
- mTopRightCam.openCamera();
- } else {
- mTopRightCam_textureView.setSurfaceTextureListener(mTopRightCam.textureListener);
- }
+ manageTopLeftCam();
+ manageTopRightCam();
+ } else if (numOfCameras == 3) {
+ manageTopLeftCam();
+ manageBotmLeftCam();
+ manageTopRightCam();
+ } else if (numOfCameras == 4) {
+ manageTopLeftCam();
+ manageTopRightCam();
+ manageBotmLeftCam();
+ manageBotmRightCam();
} else {
Log.d(TAG, "onResume No CAMERA CONNECTED");
frameView0.setVisibility(FrameLayout.INVISIBLE);
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/PermissionsActivity.java b/camera/MultiCameraApplication/java/com/intel/multicamera/PermissionsActivity.java
new file mode 100644
index 0000000..0df96ac
--- /dev/null
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/PermissionsActivity.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.intel.multicamera;
+
+import android.Manifest;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.*;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Window;
+import android.view.WindowManager;
+import androidx.core.app.ActivityCompat;
+import androidx.preference.PreferenceManager;
+
+
+/**
+ * Activity that shows permissions request dialogs and handles lack of critical permissions.
+ */
+public class PermissionsActivity extends QuickActivity {
+ private String TAG = "PermissionsActivity";
+
+ private static int PERMISSION_REQUEST_CODE = 1;
+ private static int RESULT_CODE_OK = 1;
+ private static int RESULT_CODE_FAILED = 2;
+
+ private int mIndexPermissionRequestCamera;
+ private int mIndexPermissionRequestMicrophone;
+ private int mIndexPermissionRequestLocation;
+ private int mIndexPermissionRequestStorage;
+ private boolean mShouldRequestCameraPermission;
+ private boolean mShouldRequestMicrophonePermission;
+ private boolean mShouldRequestLocationPermission;
+ private boolean mShouldRequestStoragePermission;
+ private int mNumPermissionsToRequest;
+ private boolean mFlagHasCameraPermission;
+ private boolean mFlagHasMicrophonePermission;
+ private boolean mFlagHasStoragePermission;
+
+
+ /**
+ * Close activity when secure app passes lock screen or screen turns
+ * off.
+
+ private final BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.v(TAG, "received intent, finishing: " + intent.getAction());
+ finish();
+ }
+ };*/
+
+ @Override
+ protected void onCreateTasks(Bundle savedInstanceState) {
+ setContentView(R.layout.permissions);
+
+ // Filter for screen off so that we can finish permissions activity
+ // when screen is off.
+ //IntentFilter filter_screen_off = new IntentFilter(Intent.ACTION_SCREEN_OFF);
+ //registerReceiver(mShutdownReceiver, filter_screen_off);
+
+ Window win = getWindow();
+ win.clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
+
+ @Override
+ protected void onResumeTasks() {
+ mNumPermissionsToRequest = 0;
+ checkPermissions();
+ }
+
+ @Override
+ protected void onDestroyTasks() {
+ Log.v(TAG, "onDestroy: unregistering receivers");
+
+ //unregisterReceiver(mShutdownReceiver);
+ }
+
+ /**
+ * Package private conversion method to turn String storage format into
+ * booleans.
+ *
+ * @param value String to be converted to boolean
+ * @return boolean value of stored String
+ */
+ public boolean convertToBoolean(String value) {
+ boolean ret=false;
+
+ if (value.compareTo("false") == 0) {
+ ret = false;
+ Log.d(TAG,"####FALSE");
+
+ } else if (value.compareTo("true")==0) {
+ Log.d(TAG,"####TRUE");
+ ret =true;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Retrieve a setting's value as a String, manually specifiying
+ * a default value.
+ */
+ public String getString(String key, String defaultValue) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ try {
+ return preferences.getString(key, defaultValue);
+ } catch (ClassCastException e) {
+ Log.w(TAG, "existing preference with invalid type, removing and returning default", e);
+ preferences.edit().remove(key).apply();
+ return defaultValue;
+ }
+ }
+
+
+
+ private void checkPermissions() {
+ if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestCameraPermission = true;
+ } else {
+ mFlagHasCameraPermission = true;
+ }
+
+ if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.RECORD_AUDIO)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestMicrophonePermission = true;
+ } else {
+ mFlagHasMicrophonePermission = true;
+ }
+
+ if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+ mNumPermissionsToRequest++;
+ mShouldRequestStoragePermission = true;
+ } else {
+ mFlagHasStoragePermission = true;
+ }
+
+ if (mNumPermissionsToRequest != 0) {
+ if (!convertToBoolean(getString("pref_has_seen_permissions_dialogs", "false"))) {
+
+ buildPermissionsRequest();
+ } else {
+ // Permissions dialog has already been shown, or we're on
+ // lockscreen, and we're still missing permissions.
+ handlePermissionsFailure();
+ }
+ } else {
+ handlePermissionsSuccess();
+ }
+ }
+
+ private void buildPermissionsRequest() {
+ String[] permissionsToRequest = new String[mNumPermissionsToRequest];
+ int permissionsRequestIndex = 0;
+
+ if (mShouldRequestCameraPermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.CAMERA;
+ mIndexPermissionRequestCamera = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+ if (mShouldRequestMicrophonePermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.RECORD_AUDIO;
+ mIndexPermissionRequestMicrophone = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+ if (mShouldRequestStoragePermission) {
+ permissionsToRequest[permissionsRequestIndex] = Manifest.permission.WRITE_EXTERNAL_STORAGE;
+ mIndexPermissionRequestStorage = permissionsRequestIndex;
+ permissionsRequestIndex++;
+ }
+
+
+ Log.v(TAG, "requestPermissions count: " + permissionsToRequest.length);
+ requestPermissions(permissionsToRequest, PERMISSION_REQUEST_CODE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[], int[] grantResults) {
+ Log.v(TAG, "onPermissionsResult counts: " + permissions.length + ":" + grantResults.length);
+
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
+ settings.edit().putString("pref_has_seen_permissions_dialogs","true").apply();
+
+ if (mShouldRequestCameraPermission) {
+ if (grantResults.length > 0 && grantResults[mIndexPermissionRequestCamera] ==
+ PackageManager.PERMISSION_GRANTED) {
+ mFlagHasCameraPermission = true;
+ } else {
+ handlePermissionsFailure();
+ }
+ }
+ if (mShouldRequestMicrophonePermission) {
+ if (grantResults.length > 0 && grantResults[mIndexPermissionRequestMicrophone] ==
+ PackageManager.PERMISSION_GRANTED) {
+ mFlagHasMicrophonePermission = true;
+ } else {
+ handlePermissionsFailure();
+ }
+ }
+ if (mShouldRequestStoragePermission) {
+ if (grantResults.length > 0 && grantResults[mIndexPermissionRequestStorage] ==
+ PackageManager.PERMISSION_GRANTED) {
+ mFlagHasStoragePermission = true;
+ } else {
+ handlePermissionsFailure();
+ }
+ }
+
+ if (mFlagHasCameraPermission && mFlagHasMicrophonePermission && mFlagHasStoragePermission) {
+ handlePermissionsSuccess();
+ }else {
+ // Permissions dialog has already been shown
+ // and we're still missing permissions.
+ handlePermissionsFailure();
+ }
+ }
+
+ private void handlePermissionsSuccess() {
+ Intent intent = new Intent(this, MainActivity.class);
+ startActivity(intent);
+ finish();
+ }
+
+ private void handlePermissionsFailure() {
+ new AlertDialog.Builder(this).setTitle(getResources().getString(R.string.camera_error_title))
+ .setMessage(getResources().getString(R.string.error_permissions))
+ .setCancelable(false)
+ .setOnKeyListener(new Dialog.OnKeyListener() {
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ finish();
+ }
+ return true;
+ }
+ })
+ .setNegativeButton(getResources().getString(R.string.dialog_dismiss),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ })
+ .show();
+ }
+}
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/QuickActivity.java b/camera/MultiCameraApplication/java/com/intel/multicamera/QuickActivity.java
new file mode 100644
index 0000000..f13ffc2
--- /dev/null
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/QuickActivity.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.intel.multicamera;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+
+
+/**
+ * Workaround for lockscreen double-onResume() bug:
+ *
+ * We track 3 startup situations:
+ *
+ * - Normal startup -- e.g. from GEL.
+ * - Secure lock screen startup -- e.g. with a keycode.
+ * - Non-secure lock screen startup -- e.g. with just a swipe.
+ *
+ * The KeyguardManager service can be queried to determine which state we are in.
+ * If started from the lock screen, the activity may be quickly started,
+ * resumed, paused, stopped, and then started and resumed again. This is
+ * problematic for launch time from the lock screen because we typically open the
+ * camera in onResume() and close it in onPause(). These camera operations take
+ * a long time to complete. To workaround it, this class filters out
+ * high-frequency onResume()->onPause() sequences if the KeyguardManager
+ * indicates that we have started from the lock screen.
+ *
+ *
+ * Subclasses should override the appropriate on[Create|Start...]Tasks() method
+ * in place of the original.
+ *
+ *
+ * Sequences of onResume() followed quickly by onPause(), when the activity is
+ * started from a lockscreen will result in a quick no-op.
+ *
+ */
+public abstract class QuickActivity extends Activity {
+ private String TAG = "QuickActivity";
+
+ /** onResume tasks delay from secure lockscreen. */
+ private static final long ON_RESUME_DELAY_SECURE_MILLIS = 30;
+ /** onResume tasks delay from non-secure lockscreen. */
+ private static final long ON_RESUME_DELAY_NON_SECURE_MILLIS = 15;
+
+ /** A reference to the main handler on which to run lifecycle methods. */
+ private Handler mMainHandler;
+
+ /**
+ * True if onResume tasks have been skipped, and made false again once they
+ * are executed within the onResume() method or from a delayed Runnable.
+ */
+ private boolean mSkippedFirstOnResume = false;
+
+ /** When application execution started in SystemClock.elapsedRealtimeNanos(). */
+ protected long mExecutionStartNanoTime = 0;
+ /** Was this session started with onCreate(). */
+ protected boolean mStartupOnCreate = false;
+
+ /**
+ * A runnable for deferring tasks to be performed in onResume() if starting
+ * from the lockscreen.
+ */
+ private final Runnable mOnResumeTasks = new Runnable() {
+ @Override
+ public void run() {
+ if (mSkippedFirstOnResume) {
+ Log.v(TAG, "delayed Runnable --> onResumeTasks()");
+ // Doing the tasks, can set to false.
+ mSkippedFirstOnResume = false;
+ onResumeTasks();
+ }
+ }
+ };
+
+ @Override
+ protected final void onNewIntent(Intent intent) {
+ logLifecycle("onNewIntent", true);
+ Log.v(TAG, "Intent Action = " + intent.getAction());
+ setIntent(intent);
+ super.onNewIntent(intent);
+ onNewIntentTasks(intent);
+ logLifecycle("onNewIntent", false);
+ }
+
+ @Override
+ protected final void onCreate(Bundle bundle) {
+ mExecutionStartNanoTime = SystemClock.elapsedRealtimeNanos();
+ logLifecycle("onCreate", true);
+ mStartupOnCreate = true;
+ super.onCreate(bundle);
+ mMainHandler = new Handler(getMainLooper());
+ onCreateTasks(bundle);
+ logLifecycle("onCreate", false);
+ }
+
+ @Override
+ protected final void onStart() {
+ logLifecycle("onStart", true);
+ onStartTasks();
+ super.onStart();
+ logLifecycle("onStart", false);
+ }
+
+ @Override
+ protected final void onResume() {
+ logLifecycle("onResume", true);
+
+ // For lockscreen launch, there are two possible flows:
+ // 1. onPause() does not occur before mOnResumeTasks is executed:
+ // Runnable mOnResumeTasks sets mSkippedFirstOnResume to false
+ // 2. onPause() occurs within ON_RESUME_DELAY_*_MILLIS:
+ // a. Runnable mOnResumeTasks is removed
+ // b. onPauseTasks() is skipped, mSkippedFirstOnResume remains true
+ // c. next onResume() will immediately execute onResumeTasks()
+ // and set mSkippedFirstOnResume to false
+
+
+ mMainHandler.removeCallbacks(mOnResumeTasks);
+ if (mSkippedFirstOnResume == false) {
+
+ long delay = mSkippedFirstOnResume ? ON_RESUME_DELAY_SECURE_MILLIS :
+ ON_RESUME_DELAY_NON_SECURE_MILLIS;
+ // Skipping onResumeTasks; set to true.
+ mSkippedFirstOnResume = true;
+ Log.v(TAG, "onResume() --> postDelayed(mOnResumeTasks," + delay + ")");
+ mMainHandler.postDelayed(mOnResumeTasks, delay);
+ } else {
+ Log.v(TAG, "onResume --> onResumeTasks()");
+ // Doing the tasks, can set to false.
+ mSkippedFirstOnResume = false;
+ onResumeTasks();
+ }
+ super.onResume();
+ logLifecycle("onResume", false);
+ }
+
+ @Override
+ protected final void onPause() {
+ logLifecycle("onPause", true);
+ mMainHandler.removeCallbacks(mOnResumeTasks);
+ // Only run onPauseTasks if we have not skipped onResumeTasks in a
+ // first call to onResume. If we did skip onResumeTasks (note: we
+ // just killed any delayed Runnable), we also skip onPauseTasks to
+ // adhere to lifecycle state machine.
+ if (mSkippedFirstOnResume == false) {
+ Log.v(TAG, "onPause --> onPauseTasks()");
+ onPauseTasks();
+ }
+ super.onPause();
+ mStartupOnCreate = false;
+ logLifecycle("onPause", false);
+ }
+
+ @Override
+ protected final void onStop() {
+ if (isChangingConfigurations()) {
+ Log.v(TAG, "changing configurations");
+ }
+ logLifecycle("onStop", true);
+ onStopTasks();
+ super.onStop();
+ logLifecycle("onStop", false);
+ }
+
+ @Override
+ protected final void onRestart() {
+ logLifecycle("onRestart", true);
+ super.onRestart();
+ // TODO Support onRestartTasks() and handle the workaround for that too.
+ logLifecycle("onRestart", false);
+ }
+
+ @Override
+ protected final void onDestroy() {
+ logLifecycle("onDestroy", true);
+ onDestroyTasks();
+ super.onDestroy();
+ logLifecycle("onDestroy", false);
+ }
+
+ private void logLifecycle(String methodName, boolean start) {
+ String prefix = start ? "START" : "END";
+ Log.v(TAG, prefix + " " + methodName + ": Activity = " + toString());
+ }
+
+ /**
+ * Subclasses should override this in place of {@link Activity#onNewIntent}.
+ */
+ protected void onNewIntentTasks(Intent newIntent) {
+ }
+
+ /**
+ * Subclasses should override this in place of {@link Activity#onCreate}.
+ */
+ protected void onCreateTasks(Bundle savedInstanceState) {
+ }
+
+ /**
+ * Subclasses should override this in place of {@link Activity#onStart}.
+ */
+ protected void onStartTasks() {
+ }
+
+ /**
+ * Subclasses should override this in place of {@link Activity#onResume}.
+ */
+ protected void onResumeTasks() {
+ }
+
+ /**
+ * Subclasses should override this in place of {@link Activity#onPause}.
+ */
+ protected void onPauseTasks() {
+ }
+
+ /**
+ * Subclasses should override this in place of {@link Activity#onStop}.
+ */
+ protected void onStopTasks() {
+ }
+
+ /**
+ * Subclasses should override this in place of {@link Activity#onDestroy}.
+ */
+ protected void onDestroyTasks() {
+ }
+}
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/SettingsActivity.java b/camera/MultiCameraApplication/java/com/intel/multicamera/SettingsActivity.java
index 68f7231..879fcad 100644
--- a/camera/MultiCameraApplication/java/com/intel/multicamera/SettingsActivity.java
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/SettingsActivity.java
@@ -107,7 +107,7 @@ public static class SettingsFragment
public static SparseArray sCachedSelectedVideoQualities =
new SparseArray(3);
private static String mPrefChangedKey = null;
- static boolean isPrefChangedKeyChnaged = false;
+ static boolean isPrefChangedKeyChanged = false;
/** The selected {@link CamcorderProfile} qualities. */
public static class SelectedVideoQualities {
public int large = -1;
@@ -150,7 +150,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
mCamcorderProfileNames = getResources().getStringArray(R.array.camcorder_profile_names);
- isPrefChangedKeyChnaged = false;
+ isPrefChangedKeyChanged = false;
}
@Override
@@ -193,12 +193,11 @@ public void onPause() {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String Key) {
setSummary(findPreference(Key));
mPrefChangedKey = Key;
- isPrefChangedKeyChnaged = true;
+ isPrefChangedKeyChanged = true;
}
public static String getchangedPrefKey() {
- if (isPrefChangedKeyChnaged == true) {
- isPrefChangedKeyChnaged = false;
+ if (isPrefChangedKeyChanged == true) {
return mPrefChangedKey;
} else {
return DEFAULT_KEY;
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/TopLeftCam.java b/camera/MultiCameraApplication/java/com/intel/multicamera/TopLeftCam.java
index 3233405..7d44c31 100644
--- a/camera/MultiCameraApplication/java/com/intel/multicamera/TopLeftCam.java
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/TopLeftCam.java
@@ -1,4 +1,5 @@
/*
+ * Copyright 2014 The Android Open Source Project
* Copyright (c) 2019 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +23,8 @@
import android.content.*;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
+import android.graphics.Matrix;
+import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.*;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -71,14 +74,15 @@ public class TopLeftCam {
protected CameraCaptureSession cameraCaptureSessions;
protected CaptureRequest captureRequest;
protected CaptureRequest.Builder captureRequestBuilder;
- private Size imageDimension;
+ private Size imageDimension, previewSize;
private ImageReader imageReader;
private File file;
- private static final int REQUEST_CAMERA_PERMISSION = 200;
- private final int PERMISSIONS_REQUEST_SNAPSHOT = 3;
-
private Handler mBackgroundHandler;
private HandlerThread mBackgroundThread;
+ private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
+ private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
+ private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
+ private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private SharedPreferences settings;
/**
@@ -93,6 +97,10 @@ public class TopLeftCam {
byte[] jpegLength;
private boolean mIsVideoCaptureIntent, mIsImageCaptureIntent, mIsonDoneClicked;
+ /**
+ * Orientation of the camera sensor
+ */
+ private int mSensorOrientation;
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
@@ -201,11 +209,12 @@ public void onClick(View view) {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// open your camera here
- openCamera();
+ openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Transform you image captured size according to the surface width and height
+ configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
@@ -215,34 +224,46 @@ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
};
- public void openCamera() {
+ public void openCamera(int width, int height) {
CameraManager manager = (CameraManager)mActivity.getSystemService(Context.CAMERA_SERVICE);
Log.e(TAG, "is camera open");
try {
+ if (!((manager.getCameraIdList().length >= 1) &&
+ (manager.getCameraIdList().length <= 4))) {
+ Log.e(TAG, "this camera is not connected ");
+ return;
+ }
+
cameraId = manager.getCameraIdList()[0];
Log.e(TAG, "is camera open ID" + cameraId);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) return;
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
+ String Key = SettingsActivity.SettingsFragment.getchangedPrefKey();
- // Add permission for camera and let user grant the permission
- if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) !=
- PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(mActivity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
- PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) !=
- PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- mActivity,
- new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO,
- Manifest.permission.WRITE_EXTERNAL_STORAGE},
- REQUEST_CAMERA_PERMISSION);
- return;
+ if (Key.compareTo("video_list") == 0) {
+ String videoQuality = settings.getString("video_list", "medium");
+
+ int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
+ Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
+
+ mProfile = CamcorderProfile.get(0, quality);
+ previewSize = new Size(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
+
+ configureTransform(width, height);
+ } else {
+ previewSize = SettingsActivity.SettingsFragment.sizeFromSettingString(
+ settings.getString("capture_list", "640x480"));
+ Log.d(TAG,
+ "Selected imageDimension" + previewSize.getWidth() + previewSize.getHeight());
+ configureTransform(width, height);
}
- mMediaRecorder = new MediaRecorder();
+ mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+
+ configureTransform(width, height);
startBackgroundThread();
manager.openCamera(cameraId, stateCallback, null);
@@ -269,23 +290,47 @@ public void onDisconnected(CameraDevice camera) {
}
@Override
public void onError(CameraDevice camera, int error) {
- cameraDevice.close();
- cameraDevice = null;
+ Log.e(TAG, "onError");
+ closeCamera();
}
};
+ private void configureTransform(int viewWidth, int viewHeight) {
+ if (null == textureView || null == previewSize) {
+ return;
+ }
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ Matrix matrix = new Matrix();
+ RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
+ Log.e(TAG, "configureTransform() viewWidth: " + viewWidth + " viewHeight: " + viewHeight);
+ RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
+ float centerX = viewRect.centerX();
+ float centerY = viewRect.centerY();
+ if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
+ bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
+ matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
+ float scale = Math.max((float)viewHeight / previewSize.getHeight(),
+ (float)viewWidth / previewSize.getWidth());
+ matrix.postScale(scale, scale, centerX, centerY);
+ matrix.postRotate(90 * (rotation - 2), centerX, centerY);
+ } else if (Surface.ROTATION_180 == rotation) {
+ matrix.postRotate(180, centerX, centerY);
+ }
+ textureView.setTransform(matrix);
+ }
+
protected void createCameraPreview() {
try {
closePreviewSession();
SurfaceTexture texture = textureView.getSurfaceTexture();
if (texture == null) return;
int quality = -1;
-
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
String Key = SettingsActivity.SettingsFragment.getchangedPrefKey();
String videoQuality = settings.getString("video_list", "medium");
imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString(
- settings.getString("capture_list", "640x480"));
+ settings.getString("capture_list", "640x480"));
quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
@@ -336,6 +381,7 @@ public void closeCamera() {
imageReader = null;
}
if (null != mMediaRecorder) {
+ mMediaRecorder.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
@@ -347,7 +393,7 @@ public void closeCamera() {
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
- mBackgroundThread = new HandlerThread("Camera-$cameraId");
+ mBackgroundThread = new HandlerThread("Camera_0");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
@@ -383,6 +429,20 @@ protected void updatePreview() {
}
}
+ /**
+ * Retrieves the JPEG orientation from the specified screen rotation.
+ *
+ * @param rotation The screen rotation.
+ * @return The JPEG orientation (one of 0, 90, 270, and 360)
+ */
+ private int getOrientation(int rotation) {
+ // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X)
+ // We have to take that into account and rotate JPEG properly.
+ // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS.
+ // For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
+ return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
+ }
+
protected void takePicture() {
if (null == cameraDevice) {
Log.e(TAG, "cameraDevice is null");
@@ -390,11 +450,14 @@ protected void takePicture() {
}
try {
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString(
settings.getString("capture_list", "640x480"));
+ Log.d(TAG, "Selected imageDimension " + imageDimension.getWidth() +
+ imageDimension.getHeight());
ImageReader reader = ImageReader.newInstance(
- imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1);
+ imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1);
List outputSurfaces = new ArrayList(2);
outputSurfaces.add(reader.getSurface());
outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));
@@ -405,17 +468,7 @@ protected void takePicture() {
CameraMetadata.CONTROL_MODE_AUTO);
// Orientation
int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
- captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
-
- // Add permission for camera and let user grant the permission
- if (ActivityCompat.checkSelfPermission(mActivity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
- PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- mActivity, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
- PERMISSIONS_REQUEST_SNAPSHOT);
- return;
- }
+ captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_IMAGE);
if (fileDetails == null || fileDetails.length < 5) {
@@ -502,11 +555,19 @@ public void onConfigureFailed(CameraCaptureSession session) {}
/* Recording Start*/
private void startRecordingVideo() {
- if (null == cameraDevice || !textureView.isAvailable() || null == mProfile) {
+ if (null == cameraDevice || !textureView.isAvailable()) {
return;
}
try {
closePreviewSession();
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
+ String videoQuality = settings.getString("video_list", "medium");
+
+ int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
+ Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
+
+ mProfile = CamcorderProfile.get(0, quality);
+
setUpMediaRecorder();
SurfaceTexture texture = textureView.getSurfaceTexture();
if (texture == null) return;
@@ -561,10 +622,9 @@ private void setUpMediaRecorder() throws IOException {
if (null == mActivity) {
return;
}
-
+ mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
- mMediaRecorder.setProfile(mProfile);
String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_VIDEO);
if (fileDetails == null || fileDetails.length < 5) {
@@ -576,16 +636,33 @@ private void setUpMediaRecorder() throws IOException {
Utils.getContentValues(Utils.MEDIA_TYPE_VIDEO, fileDetails, mProfile.videoFrameWidth,
mProfile.videoFrameHeight);
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
/**
* set output file in media recorder
*/
mMediaRecorder.setOutputFile(mVideoFilename);
-
+ mMediaRecorder.setVideoEncodingBitRate(10000000);
+ mMediaRecorder.setVideoFrameRate(30);
+ mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ switch (mSensorOrientation) {
+ case SENSOR_ORIENTATION_DEFAULT_DEGREES:
+ mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
+ break;
+ case SENSOR_ORIENTATION_INVERSE_DEGREES:
+ mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
+ break;
+ }
try {
mMediaRecorder.prepare();
} catch (IOException ex) {
Log.e(TAG, "prepare failed for " + mVideoFilename, ex);
mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
throw new RuntimeException(ex);
}
}
@@ -604,6 +681,8 @@ private void stopRecordingVideo() {
// Stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
if (null != mActivity) {
Toast.makeText(mActivity, "Video saved: " + mVideoFilename, Toast.LENGTH_SHORT).show();
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/TopRightCam.java b/camera/MultiCameraApplication/java/com/intel/multicamera/TopRightCam.java
index e0f824a..de027f9 100644
--- a/camera/MultiCameraApplication/java/com/intel/multicamera/TopRightCam.java
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/TopRightCam.java
@@ -1,4 +1,5 @@
/*
+ * Copyright 2014 The Android Open Source Project
* Copyright (c) 2019 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +23,8 @@
import android.content.*;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
+import android.graphics.Matrix;
+import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.*;
import android.hardware.camera2.params.StreamConfigurationMap;
@@ -71,14 +74,15 @@ public class TopRightCam {
protected CameraCaptureSession cameraCaptureSessions;
protected CaptureRequest captureRequest;
protected CaptureRequest.Builder captureRequestBuilder;
- private Size imageDimension;
+ private Size imageDimension, previewSize;
private ImageReader imageReader;
private File file;
- private static final int REQUEST_CAMERA_PERMISSION = 200;
- private final int PERMISSIONS_REQUEST_SNAPSHOT = 3;
-
private Handler mBackgroundHandler;
private HandlerThread mBackgroundThread;
+ private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
+ private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
+ private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
+ private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private SharedPreferences settings;
/**
@@ -92,6 +96,11 @@ public class TopRightCam {
private ContentValues mCurrentVideoValues, mCurrentPictureValues;
byte[] jpegLength;
+ /**
+ * Orientation of the camera sensor
+ */
+ private int mSensorOrientation;
+
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
@@ -151,11 +160,12 @@ public void onClick(View view) {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// open your camera here
- openCamera();
+ openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Transform you image captured size according to the surface width and height
+ configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
@@ -165,35 +175,44 @@ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
};
- public void openCamera() {
+ public void openCamera(int width, int height) {
CameraManager manager = (CameraManager)mActivity.getSystemService(Context.CAMERA_SERVICE);
Log.e(TAG, "is camera open");
try {
+ if (!((manager.getCameraIdList().length >= 2) &&
+ (manager.getCameraIdList().length <= 4))) {
+ Log.e(TAG, "this camera is not connected ");
+ return;
+ }
cameraId = manager.getCameraIdList()[1];
Log.e(TAG, "is camera open ID" + cameraId);
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map =
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) return;
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
+ String Key = SettingsActivity.SettingsFragment.getchangedPrefKey();
- // Add permission for camera and let user grant the permission
- if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) !=
- PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(mActivity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
- PackageManager.PERMISSION_GRANTED &&
- ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) !=
- PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- mActivity,
- new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO,
- Manifest.permission.WRITE_EXTERNAL_STORAGE},
- REQUEST_CAMERA_PERMISSION);
- return;
- }
+ if (Key.compareTo("video_list") == 0) {
+ String videoQuality = settings.getString("video_list", "medium");
+
+ int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
+ Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
- mMediaRecorder = new MediaRecorder();
+ mProfile = CamcorderProfile.get(0, quality);
+ previewSize = new Size(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
+ configureTransform(width, height);
+ } else {
+ previewSize = SettingsActivity.SettingsFragment.sizeFromSettingString(
+ settings.getString("capture_list", "640x480"));
+ Log.d(TAG,
+ "Selected imageDimension" + previewSize.getWidth() + previewSize.getHeight());
+ configureTransform(width, height);
+ }
+
+ mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+ configureTransform(width, height);
startBackgroundThread();
manager.openCamera(cameraId, stateCallback, null);
@@ -219,21 +238,46 @@ public void onDisconnected(CameraDevice camera) {
}
@Override
public void onError(CameraDevice camera, int error) {
- cameraDevice.close();
- cameraDevice = null;
+ Log.e(TAG, "onError");
+ closeCamera();
}
};
+ private void configureTransform(int viewWidth, int viewHeight) {
+ if (null == textureView || null == previewSize) {
+ return;
+ }
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ Matrix matrix = new Matrix();
+ RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
+ Log.e(TAG, "configureTransform() viewWidth: " + viewWidth + " viewHeight: " + viewHeight);
+ RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
+ float centerX = viewRect.centerX();
+ float centerY = viewRect.centerY();
+ if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
+ bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
+ matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
+ float scale = Math.max((float)viewHeight / previewSize.getHeight(),
+ (float)viewWidth / previewSize.getWidth());
+ matrix.postScale(scale, scale, centerX, centerY);
+ matrix.postRotate(90 * (rotation - 2), centerX, centerY);
+ } else if (Surface.ROTATION_180 == rotation) {
+ matrix.postRotate(180, centerX, centerY);
+ }
+ textureView.setTransform(matrix);
+ }
+
protected void createCameraPreview() {
try {
closePreviewSession();
SurfaceTexture texture = textureView.getSurfaceTexture();
if (texture == null) return;
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
String Key = SettingsActivity.SettingsFragment.getchangedPrefKey();
imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString(
- settings.getString("capture_list", "640x480"));
+ settings.getString("capture_list", "640x480"));
String videoQuality = settings.getString("video_list", "medium");
int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
@@ -285,6 +329,7 @@ public void closeCamera() {
imageReader = null;
}
if (null != mMediaRecorder) {
+ mMediaRecorder.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
@@ -296,7 +341,7 @@ public void closeCamera() {
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
- mBackgroundThread = new HandlerThread("Camera-$cameraId");
+ mBackgroundThread = new HandlerThread("Camera_1");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
@@ -332,6 +377,20 @@ protected void updatePreview() {
}
}
+ /**
+ * Retrieves the JPEG orientation from the specified screen rotation.
+ *
+ * @param rotation The screen rotation.
+ * @return The JPEG orientation (one of 0, 90, 270, and 360)
+ */
+ private int getOrientation(int rotation) {
+ // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X)
+ // We have to take that into account and rotate JPEG properly.
+ // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS.
+ // For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
+ return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
+ }
+
protected void takePicture() {
if (null == cameraDevice) {
Log.e(TAG, "cameraDevice is null");
@@ -339,9 +398,11 @@ protected void takePicture() {
}
try {
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString(
settings.getString("capture_list", "640x480"));
-
+ Log.d(TAG, "Selected imageDimension: " + imageDimension.getWidth() +
+ imageDimension.getHeight());
ImageReader reader = ImageReader.newInstance(
imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1);
List outputSurfaces = new ArrayList(2);
@@ -354,17 +415,7 @@ protected void takePicture() {
CameraMetadata.CONTROL_MODE_AUTO);
// Orientation
int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
- captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
-
- // Add permission for camera and let user grant the permission
- if (ActivityCompat.checkSelfPermission(mActivity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
- PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(
- mActivity, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
- PERMISSIONS_REQUEST_SNAPSHOT);
- return;
- }
+ captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_IMAGE);
if (fileDetails == null || fileDetails.length < 5) {
@@ -451,11 +502,18 @@ public void onConfigureFailed(CameraCaptureSession session) {}
/* Recording Start*/
private void startRecordingVideo() {
- if (null == cameraDevice || !textureView.isAvailable() || null == mProfile) {
+ if (null == cameraDevice || !textureView.isAvailable()) {
return;
}
try {
closePreviewSession();
+ settings = PreferenceManager.getDefaultSharedPreferences(mActivity);
+ String videoQuality = settings.getString("video_list", "medium");
+
+ int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality);
+ Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
+
+ mProfile = CamcorderProfile.get(0, quality);
setUpMediaRecorder();
SurfaceTexture texture = textureView.getSurfaceTexture();
if (texture == null) return;
@@ -510,9 +568,10 @@ private void setUpMediaRecorder() throws IOException {
if (null == mActivity) {
return;
}
+
+ mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
- mMediaRecorder.setProfile(mProfile);
String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_VIDEO);
if (fileDetails == null || fileDetails.length < 5) {
@@ -524,16 +583,34 @@ private void setUpMediaRecorder() throws IOException {
Utils.getContentValues(Utils.MEDIA_TYPE_VIDEO, fileDetails, mProfile.videoFrameWidth,
mProfile.videoFrameHeight);
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
/**
* set output file in media recorder
*/
mMediaRecorder.setOutputFile(mVideoFilename);
+ mMediaRecorder.setVideoEncodingBitRate(10000000);
+ mMediaRecorder.setVideoFrameRate(30);
+ mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+
+ int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
+ switch (mSensorOrientation) {
+ case SENSOR_ORIENTATION_DEFAULT_DEGREES:
+ mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
+ break;
+ case SENSOR_ORIENTATION_INVERSE_DEGREES:
+ mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
+ break;
+ }
try {
mMediaRecorder.prepare();
} catch (IOException ex) {
Log.e(TAG, "prepare failed for " + mVideoFilename, ex);
mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
throw new RuntimeException(ex);
}
}
@@ -552,6 +629,8 @@ private void stopRecordingVideo() {
// Stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
if (null != mActivity) {
Toast.makeText(mActivity, "Video saved: " + mVideoFilename, Toast.LENGTH_SHORT).show();
diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/Utils.java b/camera/MultiCameraApplication/java/com/intel/multicamera/Utils.java
index ae9e6f1..aa91fc0 100644
--- a/camera/MultiCameraApplication/java/com/intel/multicamera/Utils.java
+++ b/camera/MultiCameraApplication/java/com/intel/multicamera/Utils.java
@@ -184,8 +184,10 @@ public static ContentValues getContentValues(int type, String fileDetails[], int
Long.valueOf(fileDetails[4]) / 1000);
contentValue.put(MediaStore.Video.Media.MIME_TYPE, fileDetails[2]);
contentValue.put(MediaStore.Video.Media.DATA, fileDetails[3]);
- contentValue.put(MediaStore.MediaColumns.WIDTH, width);
- contentValue.put(MediaStore.MediaColumns.HEIGHT, height);
+ contentValue.put(MediaStore.Video.Media.WIDTH, width);
+ contentValue.put(MediaStore.Video.Media.HEIGHT, height);
+ contentValue.put(MediaStore.Video.Media.RESOLUTION,
+ Integer.toString(width) + "x" + Integer.toString(height));
}
return contentValue;
}
diff --git a/camera/MultiCameraApplication/res/layout/permissions.xml b/camera/MultiCameraApplication/res/layout/permissions.xml
new file mode 100644
index 0000000..eec475b
--- /dev/null
+++ b/camera/MultiCameraApplication/res/layout/permissions.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/camera/MultiCameraApplication/res/values/strings.xml b/camera/MultiCameraApplication/res/values/strings.xml
index b5b36cd..b97c7ef 100644
--- a/camera/MultiCameraApplication/res/values/strings.xml
+++ b/camera/MultiCameraApplication/res/values/strings.xml
@@ -59,4 +59,14 @@
"'IMG'_yyyyMMdd_HHmmss"
+
+
+ Camera error
+
+
+ The app does not have critical permissions needed to run. Please check your permissions settings.
+
+
+ Dismiss
+