Android mediarecord: start failed: -38错误解决办法
- 最近在使用android MediaRecord类进行录像时,总在MediaRecord.start()就报错(start failed: -38),网上众说纷纭,但都没法解决我的情况,经过试验,发现麦克风有其他线程在使用,我想这就是这个错误的原因吧,于是把麦克风的使用先暂时注释后,再进行录像,竟然成功了。
- 总结,MediaRecord录像时请确保没其他线程在占用录像相关的资源,比如预览要关闭,麦克风没被占用等
- 最后,贴下代码记录一下:
package com.gosuncn.instant.camera2;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
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.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresPermission;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.util.Size;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.Gravity;
import android.view.Surface;
import android.view.TextureView;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.gosuncn.instant.util.CompareSizesByArea;
import com.gosuncn.instant.util.ImageUtil;
import com.gosuncn.instant.util.SizeSelector;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Semaphore;
import static com.gosuncn.instant.util.ImageUtil.COLOR_FormatI420;
public class GSCamera2Helper {
public static final int MEDIARECORD_STATUS_START_SUCCESS = 1;
public static final int MEDIARECORD_STATUS_START_FAILED = 2;
public static final int MEDIARECORD_STATUS_RECORDING = 3;
public static final int MEDIARECORD_STATUS_UNRECORDED = 4;
public static final int MEDIARECORD_STATUS_NOT_SUPPORT = 5;
public static final int MEDIARECORD_STATUS_STOP_SUCCESS = 6;
public static final int MEDIARECORD_STATUS_STOP_FAILED = 7;
@IntDef(flag = true, value = {MEDIARECORD_STATUS_START_SUCCESS, MEDIARECORD_STATUS_START_FAILED, MEDIARECORD_STATUS_RECORDING, MEDIARECORD_STATUS_UNRECORDED
, MEDIARECORD_STATUS_NOT_SUPPORT, MEDIARECORD_STATUS_STOP_SUCCESS, MEDIARECORD_STATUS_STOP_FAILED})
public @interface MediaRecordStatus {
}
private static final String TAG = "GSCamera2Helper";
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();
static {
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
static {
INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0);
}
private static final String[] VIDEO_PERMISSIONS = {
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
};
private Context mContext;
private GSCamera2Callback mCameraCallback;
private SparseArray<String> mCamerasList;
private String mCurrentCamera;
private Size mPreviewOutputSize;
private Size mMediaOutputSize;
private CameraManager mCameraManager;
private CameraDevice mCameraDevice;
private CameraCaptureSession mCameraCaptureSession;
private CameraCharacteristics mCameraCharacteristics;
private CaptureRequest.Builder mPreviewRequestBuilder;
private CaptureRequest.Builder mCaptureRequestBuilder;
private ImageReader mCaptureImageReader;
private ImageReader mPreviewImageReader;
private HandlerThread mBackgroundThread;
private Handler mBackgroundHandler;
boolean mSwappedDimensions;
private AutoFitTextureView mPreviewTextureView;
private SizeSelector mOutputSizeSelector;
boolean mFlashSupported = false;
private MediaRecorder mMediaRecorder;
private Integer mSensorOrientation;
private String mNextVideoAbsolutePath;
private String mMediaRecordStoreDirectory;
private boolean mIsRecordingVideo;
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
public GSCamera2Helper(Context context) {
this.mContext = context;
this.mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
}
public void setMediaRecordFileStoreDirectory(String directory) {
this.mMediaRecordStoreDirectory = directory;
}
public String getMediaRecordFileStoreDirectory() {
if (mMediaRecordStoreDirectory != null) {
return mMediaRecordStoreDirectory;
}
final File dir = mContext.getExternalFilesDir(null);
return dir == null ? "" : dir.getAbsolutePath();
}
private String genRecordFilePath() {
String videoSaveTime=System.currentTimeMillis()+"";
if (mMediaRecordStoreDirectory != null) {
return mMediaRecordStoreDirectory.endsWith("/")?
mMediaRecordStoreDirectory+videoSaveTime + ".mp4":
mMediaRecordStoreDirectory+File.separator+videoSaveTime + ".mp4";
}
final File dir = mContext.getExternalFilesDir(null);
if(dir==null){
return "";
}
String path=dir.getAbsolutePath();
if(!path.endsWith("/")){
path+=File.separator;
}
String savePath=path+ videoSaveTime + ".mp4";
Log.i(TAG, "genRecordFilePath: "+savePath);
return savePath;
}
public String getPreRecordFilepath() {
return mNextVideoAbsolutePath;
}
public void setmCameraCallback(GSCamera2Callback mCameraCallback) {
this.mCameraCallback = mCameraCallback;
}
public void setOutputSizeSelector(SizeSelector selector) {
this.mOutputSizeSelector = selector;
}
public boolean swappedDimensions() {
return mSwappedDimensions;
}
public SparseArray<String> getCameras() {
mCamerasList = new SparseArray<>();
try {
String[] camerasAvailable = mCameraManager.getCameraIdList();
CameraCharacteristics cam;
Integer characteristic;
for (String id : camerasAvailable) {
cam = mCameraManager.getCameraCharacteristics(id);
characteristic = cam.get(CameraCharacteristics.LENS_FACING);
if (characteristic != null) {
switch (characteristic) {
case CameraCharacteristics.LENS_FACING_FRONT://前置摄像头
mCamerasList.put(CameraCharacteristics.LENS_FACING_FRONT, id);
break;
case CameraCharacteristics.LENS_FACING_BACK://后置摄像头
mCamerasList.put(CameraCharacteristics.LENS_FACING_BACK, id);
break;
case CameraCharacteristics.LENS_FACING_EXTERNAL://其他
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mCamerasList.put(CameraCharacteristics.LENS_FACING_EXTERNAL, id);
}
break;
}
}
}
return mCamerasList;
} catch (CameraAccessException e) {
notifyError(e.getMessage());
return mCamerasList;
}
}
public void selectCamera(String id) {
if (mCamerasList == null) {
getCameras();
}
mCurrentCamera = mCamerasList.indexOfValue(id) < 0 ? null : id;
if (mCurrentCamera == null) {
notifyError("Camera id not found.");
return;
}
try {
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentCamera);
Boolean available = mCameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
mFlashSupported = available == null ? false : available;
StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
notifyError("Could not get configuration map.");
return;
}
mSensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
Log.i(TAG, "camera sensor orientation: " + mSensorOrientation);
int displayRotation = getDisplayRotation((Activity) mContext);
Toast.makeText(mContext, "camera sensor orientation:" + mSensorOrientation + ",display rotation=" + displayRotation, Toast.LENGTH_SHORT).show();
mSwappedDimensions = false;
Log.i(TAG, "displayRotation: " + displayRotation);
Log.i(TAG, "sensorOritentation: " + mSensorOrientation);
switch (displayRotation) {
case Surface.ROTATION_0:
case Surface.ROTATION_180:
if (mSensorOrientation == 90 || mSensorOrientation == 270) {
Log.i(TAG, "mSwappedDimensions set true !");
mSwappedDimensions = true;
}
break;
case Surface.ROTATION_90:
case Surface.ROTATION_270:
if (mSensorOrientation == 0 || mSensorOrientation == 180) {
Log.i(TAG, "swappedDimensions2 set true !");
mSwappedDimensions = true;
}
break;
default:
Log.e(TAG, "Display rotation is invalid: " + displayRotation);
}
int[] formats = map.getOutputFormats();
for (int format : formats) {
Log.i(TAG, "getOutputFormats(yuv_420_888:0x23,jpeg:0x100): " + format);
}
Size[] yuvOutputSizes = map.getOutputSizes(ImageFormat.YUV_420_888);
Size[] mediaOutputSizes = map.getOutputSizes(MediaRecorder.class);
Size[] previewOutputSizes = map.getOutputSizes(SurfaceTexture.class);
Size[] jpegOutputSizes = map.getOutputSizes(ImageFormat.JPEG);
List<com.gosuncn.instant.util.Size> previewSizes = new ArrayList<>();
List<com.gosuncn.instant.util.Size> mediaSizes = new ArrayList<>();
for (Size size : mediaOutputSizes) {
Log.i(TAG, "mediaOutputSizes: " + size.toString());
mediaSizes.add(new com.gosuncn.instant.util.Size(size.getWidth(), size.getHeight()));
}
for (Size size : previewOutputSizes) {
Log.i(TAG, "previewSizes: " + size.toString());
previewSizes.add(new com.gosuncn.instant.util.Size(size.getWidth(), size.getHeight()));
}
for (Size size : yuvOutputSizes) {
Log.i(TAG, "yuvOutputSizes: " + size.toString());
}
for (Size size : jpegOutputSizes) {
Log.i(TAG, "jpegOutputSizes: " + size.toString());
}
if (mOutputSizeSelector != null) {
com.gosuncn.instant.util.Size previewSize = mOutputSizeSelector.select(previewSizes).get(0);
com.gosuncn.instant.util.Size mediaSize = mOutputSizeSelector.select(mediaSizes).get(0);
mPreviewOutputSize = new Size(previewSize.getWidth(), previewSize.getHeight());
mMediaOutputSize = new Size(mediaSize.getWidth(), mediaSize.getHeight());
} else {
mPreviewOutputSize = Collections.min(Arrays.asList(yuvOutputSizes), new CompareSizesByArea());
mMediaOutputSize = Collections.min(Arrays.asList(mediaOutputSizes), new CompareSizesByArea());
}
Log.i(TAG, "selectCamera: previewsize=" + mPreviewOutputSize.toString() + "mediasize=" + mMediaOutputSize.toString());
mCaptureImageReader = ImageReader.newInstance(mPreviewOutputSize.getWidth(), mPreviewOutputSize.getHeight(), ImageFormat.JPEG, 1);
mPreviewImageReader = ImageReader.newInstance(mPreviewOutputSize.getWidth(), mPreviewOutputSize.getHeight(), ImageFormat.YUV_420_888, 2);
mCaptureImageReader.setOnImageAvailableListener(onCaptureImageAvailableListener, mBackgroundHandler);
mPreviewImageReader.setOnImageAvailableListener(onPreviewImageAvailableListener, mBackgroundHandler);
} catch (Exception e) {
notifyError(e.getMessage());
}
}
@SuppressLint("MissingPermission")
@RequiresPermission(allOf = {Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO})
public void openCamera(final AutoFitTextureView textureView) {
mPreviewTextureView = textureView;
mPreviewTextureView.setSurfaceTextureListener(surfaceTextureListener);
if (!hasPermissionsGranted(VIDEO_PERMISSIONS)) {
notifyError("You don't have the required permissions.");
return;
}
try {
startBackgroundThread();
mCameraManager.openCamera(mCurrentCamera, cameraDeviceStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
notifyError(e.getMessage());
}
}
public boolean closeCamera() {
stopBackgroundThread();
try {
if (null != mCameraCaptureSession) {
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mCaptureImageReader) {
mCaptureImageReader.close();
mCaptureImageReader = null;
}
if (null != mPreviewImageReader) {
mPreviewImageReader.close();
mPreviewImageReader = null;
}
} catch (Exception e) {
e.printStackTrace();
notifyError(e.getMessage());
return false;
}
return true;
}
public <T> void setCaptureSetting(CaptureRequest.Key<T> key, T value) {
if (mPreviewRequestBuilder != null && mCaptureRequestBuilder != null) {
mPreviewRequestBuilder.set(key, value);
mCaptureRequestBuilder.set(key, value);
}
}
public boolean captureImage() {
if (mIsRecordingVideo) {
notifyError("正在录制,无法抓拍");
return false;
}
mCaptureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION));
try {
mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), null, mBackgroundHandler);
return true;
} catch (CameraAccessException e) {
e.printStackTrace();
notifyError(e.getMessage());
}
return false;
}
public boolean setAutoFlash() {
if (!checkFlash()) {
return false;
}
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
updatePreview();
return true;
}
public boolean openFlash() {
if (!checkFlash()) {
return false;
}
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
updatePreview();
return true;
}
public boolean closeFlash() {
if (!checkFlash()) {
return false;
}
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
updatePreview();
return true;
}
public <T> T getCharacteristic(CameraCharacteristics.Key<T> key) {
if (mCameraCharacteristics != null) {
return mCameraCharacteristics.get(key);
}
return null;
}
public void startRecordingVideo() {
if (mIsRecordingVideo) {
notifyMediaRecordStatus(MEDIARECORD_STATUS_RECORDING, "已在录制中");
return;
}
if (null == mCameraDevice) {
notifyMediaRecordStatus(MEDIARECORD_STATUS_START_FAILED, "CameraDevice object is null.");
return;
}
if (!mPreviewTextureView.isAvailable()) {
notifyMediaRecordStatus(MEDIARECORD_STATUS_START_FAILED, "Preview TextureView is unavailable.");
return;
}
if (null == mMediaOutputSize) {
notifyMediaRecordStatus(MEDIARECORD_STATUS_START_FAILED, "MediaOutputSize is null.");
return;
}
try {
mIsRecordingVideo = false;
closePreviewSession();
setUpMediaRecorder();
SurfaceTexture texture = mPreviewTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mMediaOutputSize.getWidth(), mMediaOutputSize.getHeight());
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
List<Surface> surfaces = new ArrayList<>();
Surface previewSurface = new Surface(texture);
surfaces.add(previewSurface);
mPreviewRequestBuilder.addTarget(previewSurface);
Surface recorderSurface = mMediaRecorder.getSurface();
surfaces.add(recorderSurface);
mPreviewRequestBuilder.addTarget(recorderSurface);
mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
mCameraCaptureSession = session;
updatePreview();
mMediaRecorder.start();
mIsRecordingVideo = true;
notifyMediaRecordStatus(MEDIARECORD_STATUS_START_SUCCESS, "开始录制");
} catch (Exception e) {
e.printStackTrace();
notifyMediaRecordStatus(MEDIARECORD_STATUS_NOT_SUPPORT, "不支持此机型");
startPreview();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
notifyMediaRecordStatus(MEDIARECORD_STATUS_START_FAILED, "启动录制失败");
}
}, mBackgroundHandler);
} catch (Exception e) {
e.printStackTrace();
notifyMediaRecordStatus(MEDIARECORD_STATUS_START_FAILED, e.getMessage());
startPreview();
}
}
public boolean stopRecordingVideo() {
if (!mIsRecordingVideo) {
Log.e(TAG, "stopRecordingVideo: 未启动录制");
notifyMediaRecordStatus(MEDIARECORD_STATUS_UNRECORDED, "未启动录制 ");
return false;
}
try {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder=null;
mIsRecordingVideo = false;
startPreview();
notifyMediaRecordStatus(MEDIARECORD_STATUS_STOP_SUCCESS, "已停止录制");
} catch (Exception e) {
e.printStackTrace();
notifyMediaRecordStatus(MEDIARECORD_STATUS_STOP_FAILED, e.getMessage());
}
return true;
}
public void releaseRecording(){
}
private void startPreview() {
if (null == mCameraDevice || !mPreviewTextureView.isAvailable() || null == mPreviewOutputSize) {
return;
}
List<Surface> surfaces = new ArrayList<>();
try {
closePreviewSession();
SurfaceTexture texture = mPreviewTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewOutputSize.getWidth(), mPreviewOutputSize.getHeight());
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
Surface previewSurface = mPreviewImageReader.getSurface();
mPreviewRequestBuilder.addTarget(previewSurface);
surfaces.add(previewSurface);
Surface textureSurface = new Surface(texture);
mPreviewRequestBuilder.addTarget(textureSurface);
surfaces.add(textureSurface);
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
Surface captureSurface = mCaptureImageReader.getSurface();
mCaptureRequestBuilder.addTarget(captureSurface);
surfaces.add(captureSurface);
mCameraDevice.createCaptureSession(surfaces, cameraCaptureSessionStateCallback, mBackgroundHandler);
} catch (Exception e) {
e.printStackTrace();
notifyError("startPreview failed:" + e.getMessage());
}
}
private void stopPreview() {
if (mCameraCaptureSession == null) {
notifyError("mCameraCaptureSession is null.");
return;
}
try {
mCameraCaptureSession.stopRepeating();
} catch (CameraAccessException e) {
e.printStackTrace();
notifyError(e.getMessage());
}
}
private void restartPreview() {
stopPreview();
startPreview();
}
private void closePreviewSession() {
if (mCameraCaptureSession != null) {
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
}
private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
List<Size> bigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size option : choices) {
if (option.getHeight() == option.getWidth() * h / w &&
option.getWidth() >= width && option.getHeight() >= height) {
bigEnough.add(option);
}
}
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
private boolean hasPermissionsGranted(String[] permissions) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(mContext, permission)
!= PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
private void setUpMediaRecorder() throws IOException {
final Activity activity = (Activity) mContext;
if (null == activity) {
return;
}
if(mMediaRecorder == null){
mMediaRecorder = new MediaRecorder();
}
mMediaRecorder.reset();
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mNextVideoAbsolutePath = genRecordFilePath();
mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);
mMediaRecorder.setVideoEncodingBitRate(10000000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(mMediaOutputSize.getWidth(), mMediaOutputSize.getHeight());
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
int rotation = activity.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;
}
mMediaRecorder.prepare();
}
private void notifyMediaRecordStatus(@MediaRecordStatus int code, String message) {
if (mCameraCallback != null) {
mCameraCallback.onCameraMediaRecordStatusCallback(code, message);
}
}
private void setUpCaptureRequestBuilder(CaptureRequest.Builder builder) {
builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
}
private void updatePreview() {
if (null == mCameraDevice) {
return;
}
try {
setUpCaptureRequestBuilder(mPreviewRequestBuilder);
mCameraCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
notifyError("updatePreview failed:" + e.getMessage());
}
}
private void notifyError(String message) {
if (mCameraCallback != null) {
mCameraCallback.onCameraError(message);
}
}
private boolean checkFlash() {
if (!mFlashSupported) {
Log.e(TAG, "flash is not supported.");
notifyError("flash is not supported.");
return false;
}
if (mPreviewRequestBuilder == null) {
Log.e(TAG, "mCaptureRequestBuilder is null. ");
notifyError("mCaptureRequestBuilder is null.");
return false;
}
return true;
}
private int getDisplayRotation(Activity activity) {
return activity.getWindowManager().getDefaultDisplay().getRotation();
}
private void configureTransform(int viewWidth, int viewHeight) {
if (null == mPreviewTextureView || null == mPreviewOutputSize || null == mContext) {
return;
}
int rotation = getDisplayRotation((Activity) mContext);
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewOutputSize.getHeight(), mPreviewOutputSize.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 / mPreviewOutputSize.getHeight(),
(float) viewWidth / mPreviewOutputSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
}
mPreviewTextureView.setTransform(matrix);
}
@Deprecated
private void setupPreview_(TextureView textureView) {
Surface surface = new Surface(textureView.getSurfaceTexture());
List<Surface> surfaces = new ArrayList<>();
try {
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
Surface previewSurface = mPreviewImageReader.getSurface();
mPreviewRequestBuilder.addTarget(previewSurface);
surfaces.add(surface);
surfaces.add(previewSurface);
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
Surface captureSurface = mCaptureImageReader.getSurface();
mCaptureRequestBuilder.addTarget(captureSurface);
surfaces.add(captureSurface);
setCaptureSetting(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY);
mCameraDevice.createCaptureSession(surfaces, cameraCaptureSessionStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
notifyError(e.getMessage());
}
}
@Deprecated
private void setupPreview(AutoFitTextureView outputSurface) {
if (outputSurface.isAvailable()) {
Log.i(TAG, "setupPreview: size=" + outputSurface.getMeasuredWidth() + "*" + outputSurface.getMeasuredHeight());
setupPreview_(outputSurface);
}
}
private void setAspectRatioTextureView(TextureView textureView, int surfaceWidth, int surfaceHeight) {
int rotation = ((Activity) mContext).getWindowManager().getDefaultDisplay().getRotation();
int newWidth = surfaceWidth, newHeight = surfaceHeight;
switch (rotation) {
case Surface.ROTATION_0:
newWidth = surfaceWidth;
newHeight = (surfaceWidth * mPreviewOutputSize.getWidth() / mPreviewOutputSize.getHeight());
break;
case Surface.ROTATION_180:
newWidth = surfaceWidth;
newHeight = (surfaceWidth * mPreviewOutputSize.getWidth() / mPreviewOutputSize.getHeight());
break;
case Surface.ROTATION_90:
newWidth = surfaceHeight;
newHeight = (surfaceHeight * mPreviewOutputSize.getWidth() / mPreviewOutputSize.getHeight());
break;
case Surface.ROTATION_270:
newWidth = surfaceHeight;
newHeight = (surfaceHeight * mPreviewOutputSize.getWidth() / mPreviewOutputSize.getHeight());
break;
}
textureView.setLayoutParams(new FrameLayout.LayoutParams(newWidth, newHeight, Gravity.CENTER));
rotatePreview(textureView, rotation, newWidth, newHeight);
}
private int getCurrentOrientation() {
return mContext.getResources().getConfiguration().orientation;
}
private void rotatePreview(TextureView mTextureView, int rotation, int viewWidth, int viewHeight) {
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
mTextureView.setTransform(matrix);
}
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("GSCamera2Helper");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
private void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
notifyError(e.getMessage());
}
}
private CameraDevice.StateCallback cameraDeviceStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.i(TAG, "onOpened: " + camera.getId());
if (mCameraCallback != null) {
mCameraCallback.onCameraReady();
}
mCameraDevice = camera;
startPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
Log.i(TAG, "onDisconnected: " + camera.getId());
notifyError("CameraDevice.StateCallback.onDisconnected:opencamera failed.");
try {
mCameraDevice.close();
mCameraDevice = null;
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
Log.i(TAG, "onError: " + camera.getId() + ",error=" + error);
try {
mCameraDevice.close();
mCameraDevice = null;
} catch (Exception e) {
e.printStackTrace();
}
switch (error) {
case CameraDevice.StateCallback.ERROR_CAMERA_DEVICE:
notifyError("Camera device has encountered a fatal error.");
break;
case CameraDevice.StateCallback.ERROR_CAMERA_DISABLED:
notifyError("Camera device could not be opened due to a device policy.");
break;
case CameraDevice.StateCallback.ERROR_CAMERA_IN_USE:
notifyError("Camera device is in use already.");
break;
case CameraDevice.StateCallback.ERROR_CAMERA_SERVICE:
notifyError("Camera service has encountered a fatal error.");
break;
case CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE:
notifyError("Camera device could not be opened because there are too many other open camera devices.");
break;
}
}
};
private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.i(TAG, "onSurfaceTextureAvailable: w*h=" + width + "," + height);
startPreview();
}
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Log.i(TAG, "onSurfaceTextureSizeChanged: w*h=" + width + "," + height);
}
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Log.i(TAG, "onSurfaceTextureDestroyed:");
return false;
}
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Log.i(TAG, "onSurfaceTextureUpdated: ");
}
};
private CameraCaptureSession.StateCallback cameraCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
Log.i(TAG, "onConfigured: ");
mCameraCaptureSession = session;
updatePreview();
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.i(TAG, "onConfigureFailed: ");
notifyError("Could not configure capture session.");
}
};
private ImageReader.OnImageAvailableListener onCaptureImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Log.i(TAG, "onImageAvailable: imageformat(YUV_420_888:35,jpeg:256)=" + reader.getImageFormat());
if (mCameraCallback == null) {
return;
}
Image img = reader.acquireNextImage();
if (img == null) {
return;
}
int w = img.getWidth();
int h = img.getHeight();
Log.i(TAG, "onImageAvailable: w*h=" + w + "*" + h);
mCameraCallback.onCameraCapture(img);
img.close();
}
};
private ImageReader.OnImageAvailableListener onPreviewImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(final ImageReader reader) {
long s, e;
s = System.currentTimeMillis();
Image img = reader.acquireNextImage();
Log.i(TAG, "onPreviewImageAvailable: format=" + img.getFormat() + ",w*h=" + img.getWidth() + "*" + img.getHeight());
if (img == null) {
return;
}
if (mCameraCallback == null) {
img.close();
return;
}
int width = img.getWidth();
int height = img.getHeight();
byte[] data = ImageUtil.getDataFromImage(img, COLOR_FormatI420);
img.close();
long s1 = System.currentTimeMillis(), e1;
mCameraCallback.onCameraFrameProcessor(data, data.length, width, height, mSwappedDimensions);
e1 = System.currentTimeMillis();
Log.i(TAG, "onFrameProcessor take " + (e1 - s1) + " ms");
e = System.currentTimeMillis();
Log.i(TAG, "onImageAvailable take " + (e - s) + " ms");
}
};
}