I have used the Camera2 API to implement a background video recorder running as a service and recording from the front cam. For this I have created a new SurfaceView, set its size to 1x1 and moved it to the top left corner. My code is shown below. I'm using Android 5.1.
With the Camera API it worked very well but unfortunately the frame rate is only at 20 fps (and when I increase the exposure compensation it drops even more), although with the Open Camera app I have 30 fps also with increased exposure compensation. That is why I want to try Camera2 API (hoping to get higher fps). Unfortunately, I'm getting the following error:
MediaRecoder: setOutputFile called in an invalid state(2)
MediaRecorder: start called in an invalid state: 2
Here is my code:
public class RecorderServiceCamera2 extends Service implements SurfaceHolder.Callback {
private WindowManager windowManager;
private SurfaceView surfaceView;
private CameraDevice mCamera;
private MediaRecorder mediaRecorder = null;
private CaptureRequest mCaptureRequest;
private CameraCaptureSession mSession;
#Override
public void onCreate() {
// Create new SurfaceView, set its size to 1x1, move it to the top left corner and set this service as a callback
windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
surfaceView = new SurfaceView(this);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
1, 1,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT
);
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
windowManager.addView(surfaceView, layoutParams);
surfaceView.getHolder().addCallback(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
//.setSmallIcon(R.mipmap.app_icon)
.setContentTitle("Background Video Recorder")
.setContentText("")
.setContentIntent(pendingIntent).build();
startForeground(MainActivity.NOTIFICATION_ID_RECORDER_SERVICE, notification);
return Service.START_NOT_STICKY;
}
#Override
public void surfaceCreated(final SurfaceHolder surfaceHolder) {
mediaRecorder = new MediaRecorder();
try {
CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
String[] cameras = manager.getCameraIdList();
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[1]);
StreamConfigurationMap configs = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] sizes = configs.getOutputSizes(MediaCodec.class);
final Size sizeHigh = sizes[0];
manager.openCamera(cameras[1], new CameraDevice.StateCallback() {
#Override
public void onOpened(#NonNull CameraDevice camera) {
mCamera = camera;
mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setMaxFileSize(0);
mediaRecorder.setOrientationHint(0);
mediaRecorder.setOutputFile("test.mp4");
try { mediaRecorder.prepare(); } catch (Exception ignored) {}
List<Surface> list = new ArrayList<>();
list.add(surfaceHolder.getSurface());
try {
CaptureRequest.Builder captureRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
captureRequest.addTarget(surfaceHolder.getSurface());
mCaptureRequest = captureRequest.build();
} catch (CameraAccessException e) {
e.printStackTrace();
}
try {
mCamera.createCaptureSession(list, new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession session) {
mSession = session;
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession session) {
mSession = session;
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
mediaRecorder.start();
try {
mSession.setRepeatingRequest(mCaptureRequest,
new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureStarted(#NonNull CameraCaptureSession session, #NonNull CaptureRequest request, long timestamp, long frameNumber) {
super.onCaptureStarted(session, request, timestamp, frameNumber);
}
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session, #NonNull CaptureRequest request, #NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
}
#Override
public void onCaptureFailed(#NonNull CameraCaptureSession session, #NonNull CaptureRequest request, #NonNull CaptureFailure failure) {
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onDisconnected(#NonNull CameraDevice camera) {
}
#Override
public void onError(#NonNull CameraDevice camera, int error) {
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
// Stop recording and remove SurfaceView
#Override
public void onDestroy() {
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
mCamera.close();
mediaRecorder= null;
windowManager.removeView(surfaceView);
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {}
#Override
public IBinder onBind(Intent intent) { return null; }
}
You don't need to put up a SurfaceView with Camera2, or even with Camera1 (with the latter, just create a SurfaceTexture to get a Surface from, and never call updateTexImage; with the former, just don't include a preview Surface at all, it's not needed).
That said, you're trying to use the same Surface in both Camera2 and the MediaRecorder; that doesn't work. MediaRecorder doesn't need to draw to a Surface at all to record, you can just leave that part off. It's only there to allow MediaRecorder to be used independently of the camera API, where it manages a camera behind the scenes.
I suspect the call to MediaRecorder.prepare() is throwing an error that you're ignoring, about the preview Surface already being in use, which is why start doesn't later work.
And yes, as CommonsWare says, you likely will need a foreground service in Android P.
Related
I'm creating barcode reading application using google vision and it consists a flash on/off function also. i was able to implement flash using "CameraManager" like in the following code due to the "Camera" is now deprecated.but when camera screen is on, flash light is not working.when camera is freeze(when barcode is detected i'm stoping the camerasource), flash light is working. but i want to flash light on/off with out regarding the camera view is on or stop.i need this get done without using the deprecated APIs.can some one tell me how i can get solved this problem. thanks in advance.
private void setupBarcode(){
cameraPreview = (SurfaceView) findViewById(R.id.cameraPreview);
txtResult = findViewById(R.id.txtResult);
barcodeDetector = new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build();
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setAutoFocusEnabled(true)
.build();
//Add Event
cameraPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//Request permission
ActivityCompat.requestPermissions(ScanActivity.this,
new String[]{Manifest.permission.CAMERA}, RequestCameraPermissionID);
return;
}
try {
cameraSource.start(cameraPreview.getHolder());
txtResult.setText("");
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
cameraSource.stop();
}
});
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
#Override
public void release() {
}
#Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> qrcodes = detections.getDetectedItems();
if (qrcodes.size() != 0) {
txtResult.post(new Runnable() {
#Override
public void run() {
okButton.setEnabled(true);
txtResult.setText(qrcodes.valueAt(0).displayValue);
cameraSource.stop();
}
});
}
}
});
}
private void flashLightOn() {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
String cameraId = cameraManager.getCameraIdList()[0];
cameraManager.setTorchMode(cameraId, true);
} catch (CameraAccessException e) {
}
}
Use the open source 'CameraSource.java' file from goole vision library in your project.
Get file from here
And then use the following code:
public void switchFlashLight(boolean status) {
cameraSource.setFlashMode(status ? Camera.Parameters.FLASH_MODE_TORCH : Camera.Parameters.FLASH_MODE_OFF);
}
I want Camera preview and Camera Capture use Camera2 API.
this code is works, but there is one problem.
problem is When click the capture button, capture succeeds, camera preview stops.
if I press the capture button again, the camera preview is still paused.
if I do not press the capture button after running the app, camera preview is success work.
Error log when the capture button is pressed.
java.lang.IllegalArgumentException: submitRequestList:208: Request targets Surface that is not part of current capture session
at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:650)
at android.hardware.camera2.impl.ICameraDeviceUserWrapper.submitRequestList(ICameraDeviceUserWrapper.java:86)
at android.hardware.camera2.impl.CameraDeviceImpl.submitCaptureRequest(CameraDeviceImpl.java:935)
at android.hardware.camera2.impl.CameraDeviceImpl.setRepeatingRequest(CameraDeviceImpl.java:974)
at android.hardware.camera2.impl.CameraCaptureSessionImpl.setRepeatingRequest(CameraCaptureSessionImpl.java:243)
at com.bilal.androidthingscameralib.CameraHelper$4.onCaptureCompleted(CameraHelper.java:273)
at java.lang.reflect.Method.invoke(Native Method)
at android.hardware.camera2.dispatch.InvokeDispatcher.dispatch(InvokeDispatcher.java:39)
at android.hardware.camera2.dispatch.HandlerDispatcher$1.run(HandlerDispatcher.java:65)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: android.os.ServiceSpecificException: submitRequestList:208: Request targets Surface that is not part of current capture session (code 3)
at android.os.Parcel.readException(Parcel.java:2018)
at android.os.Parcel.readException(Parcel.java:1950)
at android.hardware.camera2.ICameraDeviceUser$Stub$Proxy.submitRequestList(ICameraDeviceUser.java:334)
at android.hardware.camera2.impl.ICameraDeviceUserWrapper.submitRequestList(ICameraDeviceUserWrapper.java:84)
at android.hardware.camera2.impl.CameraDeviceImpl.submitCaptureRequest(CameraDeviceImpl.java:935)
at android.hardware.camera2.impl.CameraDeviceImpl.setRepeatingRequest(CameraDeviceImpl.java:974)
at android.hardware.camera2.impl.CameraCaptureSessionImpl.setRepeatingRequest(CameraCaptureSessionImpl.java:243)
at com.bilal.androidthingscameralib.CameraHelper$4.onCaptureCompleted(CameraHelper.java:273)
at java.lang.reflect.Method.invoke(Native Method)
at android.hardware.camera2.dispatch.InvokeDispatcher.dispatch(InvokeDispatcher.java:39)
at android.hardware.camera2.dispatch.HandlerDispatcher$1.run(HandlerDispatcher.java:65)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
and my source (MainActivity.class);
private InitializeCamera mInitializeCamera;
TextureView mTextureView;
ImageView imgCaptureImage;
#Override
public void onCreate(Bundle savedInstanceState) {
Button capBtn = (Button) findViewById(R.id.capBtn); //capture button
#Override
public void onClick(View v) {
mInitializeCamera.captureImage();
}
}
//camera open texture
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mInitializeCamera = new InitializeCamera(getApplicationContext(), mOnPictureAvailableListener, mTextureView, 640, 480, 1);
}
...
};
//picture taken
private OnPictureAvailableListener mOnPictureAvailableListener =
new OnPictureAvailableListener() {
#Override
public void onPictureAvailable(byte[] imageBytes) {
onPictureTaken(imageBytes);
}
};
//get picture
private void onPictureTaken(final byte[] imageBytes) {
if (imageBytes != null) {
Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
imgCaptureImage.setImageBitmap(bitmap);
}
}
(InitializeCamera.class) :)
OnPictureAvailableListener mOnPictureAvailableListener;
private HandlerThread mCameraHandlerThread;
//Initialize camera library class
private CameraHelper mCameraHelper;
//Handler for running Camera Task in the background
private Handler mCameraHandler = new Handler();
public InitializeCamera(Context mContext, OnPictureAvailableListener mOnPictureAvailableListener, TextureView textureView, int imageHeight, int imageWidth, int maxSize) {
this.mOnPictureAvailableListener = mOnPictureAvailableListener;
//create new handler thread for camera operations.
mCameraHandlerThread = new HandlerThread("CameraBackground");
mCameraHandlerThread.start();
//Initialize Camera class.
mCameraHelper = CameraHelper.getInstance();
mCameraHelper.initializeCameraHelper(mContext, mCameraHandler, textureView, this, imageHeight, imageWidth, maxSize);
}
//capture image
public void captureImage() {
mCameraHelper.takePicture();
}
//get imagereader available
#Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireNextImage();
ByteBuffer imageBuf = image.getPlanes()[0].getBuffer();
final byte[] imageBytes = new byte[imageBuf.remaining()];
image.close();
//post image bytes data to main UI Thread for displaying it in image view
mCameraHandler.post(new Runnable() {
#Override
public void run()( {
mOnPictureAvailableListener.onPictureAvailable(imageBytes);
}
});
}
(CameraHelper.class) (thanks)
TextureView textureView;
private CameraDevice mCameraDevice;
private CameraCaptureSession mCaptureSession;
private ImageReader mImageReader;
Size imageDimension;
private CaptureRequest.Builder captureRequestBuilder;
//Lazy-loaded singleton, so only one instance of the camera is created.
private CameraHelper() {
}
private static class InstanceHolder {
private static CameraHelper mCamera = new CameraHelper();
}
public static CameraHelper getInstance() {
return InstanceHolder.mCamera;
}
//Initialize the camera device.
public void initializeCameraHelper(Context context, Handler backgroundHandler, TextureView textureView, ImageReader.OnImageAvailableListener imageAvailableListener, int imageWidth, int imageHeight, int maxImages) {
this.textureView = textureView;
//discover the camera instance
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
String[] camIds = {};
try {
camIds = manager.getCameraIdList();
if (camIds.length < 1) {
return;
}
String id = camIds[0];
CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
imageDimension = map.getOutputSizes(SurfaceTexture.class)[2];
//Initialize the image processor
mImageReader = ImageReader.newInstance(imageWidth, imageHeight, ImageFormat.JPEG, maxImages);
mImageReader.setOnImageAvailableListener(
imageAvailableListener, backgroundHandler);
//Open camera resource
manager.openCamera(id, mStateCallback, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* Callback handling device state changes
*/
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
#Override
public void onOpened(#NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Opened camera.");
mCameraDevice = cameraDevice;
createCameraPreview();
}
#Override
public void onDisconnected(#NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Camera disconnected, closing.");
closeCaptureSession();
cameraDevice.close();
}
#Override
public void onError(#NonNull CameraDevice cameraDevice, int i) {
Log.d(TAG, "Camera device error, closing.");
closeCaptureSession();
cameraDevice.close();
}
#Override
public void onClosed(#NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Closed camera, releasing");
mCameraDevice = null;
}
};
private void createCameraPreview() {
Log.d(TAG, "createCameraPreview --" + textureView);
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
Surface surface = new Surface(texture);
captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession session) {
if (mCameraDevice == null) {
return;
}
mCaptureSession = session;
updatePreview();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession session) {
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//ready preview screen
private void updatePreview() {
if (mCameraDevice == null) {
}
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
try {
mCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* Begin a still image capture
*/
public void takePicture() {
if (mCameraDevice == null) {
Log.w(TAG, "Cannot capture image. Camera not initialized.");
return;
}
// Here, we create a CameraCaptureSession for capturing still images.
try {
mCameraDevice.createCaptureSession(
Collections.singletonList(mImageReader.getSurface()),
mSessionCallback,
null);
} catch (CameraAccessException cae) {
Log.d(TAG, "access exception while preparing pic", cae);
}
}
/**
* Callback handling session state changes
*/
private CameraCaptureSession.StateCallback mSessionCallback =
new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (mCameraDevice == null) {
return;
}
// When the session is ready, we start capture.
mCaptureSession = cameraCaptureSession;
triggerImageCapture();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Log.w(TAG, "Failed to configure camera");
}
};
/**
* Execute a new capture request within the active session
*/
private void triggerImageCapture() {
try {
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
Log.d(TAG, "Capture request created.");
mCaptureSession.capture(captureBuilder.build(), mCaptureCallback, null);
} catch (CameraAccessException cae) {
Log.d(TAG, "camera capture exception");
}
}
/**
* Callback handling capture session events
*/
private final CameraCaptureSession.CaptureCallback mCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureProgressed(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull CaptureResult partialResult) {
Log.d(TAG, "Partial result");
}
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull TotalCaptureResult result) {
//session.close();
//mCaptureSession = null;
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
try {
mCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), mCaptureCallback, null); //OCCUR ERROR LOG
} catch (CameraAccessException e) {
e.printStackTrace();
}
Log.d(TAG, "CaptureSession closed");
}
};
Occur error log CameraHelper.class
Request targets Surface that is not part of current capture session
private final CameraCaptureSession.CaptureCallback mCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull TotalCaptureResult result) {
//session.close();
//mCaptureSession = null;
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
try {
mCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), mCaptureCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
Log.d(TAG, "CaptureSession closed");
}
};
this part
I think after capture, there seems to be a problem with mCaptureCallback
How to fix this problem?
if you know please advice for me..
thanks.
You're creating a new capture session in takePicture with just the one ImageReader Surface; after that, you can no longer use the TextureView Surface as a capture request target. You have to create yet another session to resume preview.
It's better to just add the JPEG ImageReader Surface to the initial capture session, and not have to be continually tearing down sessions to take pictures (which is quite slow). For regular preview, don't target the ImageReader in the repeating request, and then for still capture, just issue a capture request that targets the ImageReader.
See the camera2basic sample for examples of this.
I have been using TextureView for my camera preview until I cannot find a way to set its background color so I switched to SurfaceView but the preview is not working anymore. Everything else is working fine, except for the preview.
Here is my code when I used TextureView:
public void previewCamera(){
try {
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(previewSize.getWidth(),previewSize.getHeight());
Surface previewSurface = new Surface(surfaceTexture);
captureRequestBuilder = device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(previewSurface);
device.createCaptureSession(Arrays.asList(previewSurface), new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(CameraCaptureSession session) {
updatePreview(session);
}
#Override
public void onConfigureFailed(CameraCaptureSession session) {
Toast.makeText(getApplicationContext(),"unable to setup cam preview",Toast.LENGTH_SHORT).show();
}
},null);
} catch (CameraAccessException e) {
e.printStackTrace();
}}
..and here is my code using SurfaceView
public void previewCamera(){
try {
Surface previewSurface = surfaceView.getHolder().getSurface();
captureRequestBuilder = device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(previewSurface);
device.createCaptureSession(Arrays.asList(previewSurface), new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(CameraCaptureSession session) {
updatePreview(session);
}
...and I am adding this code just in case it has something to do with it
private void updatePreview(CameraCaptureSession session) {
previewSession = session;
if (null == device) {
return;
}
try {
handlerThread = new HandlerThread("CameraPreview");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
previewSession.setRepeatingRequest(captureRequestBuilder.build(), null, ((isRecording)? null:handler));
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Thanks
Have you tried setting a fixed size before retrieving the Surface object?
surfaceView.setFixedSize(previewSize.getWidth(),previewSize.getHeight());
You should not use Handler if you use SurfaceView to preview.Just like following:
manager.openCamera("0", mStateCallback, null);
By the way,the size of SurfaceView won't let the preview don't work,so you don't have to set a fix size.
In my app i capture a video using MediaRecorder.
But when i turn off the screen while the app is recording, the resulting video is black at that time. Is there a way to prevent this and make the camera continue to record? Here is my Code:
CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
mMediaRecorder = new MediaRecorder();
outputFile = new File(dir, "temp");
try {
String[] ids = manager.getCameraIdList();
CameraCharacteristics characteristics = manager.getCameraCharacteristics(ids[0]);
StreamConfigurationMap configs = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] sizes = configs.getOutputSizes(MediaCodec.class);
final Size sizeHigh = sizes[0];
manager.openCamera(ids[0], new CameraDevice.StateCallback() {
#Override
public void onOpened(CameraDevice camera) {
mCamera = camera;
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
mMediaRecorder.setVideoSize(sizeHigh.getWidth(), sizeHigh.getHeight());
mMediaRecorder.setMaxFileSize(0);
try {
mMediaRecorder.setOutputFile(outputFile.getAbsolutePath());
mMediaRecorder.prepare();
List<Surface> list = new ArrayList<>();
list.add(mMediaRecorder.getSurface());
final CaptureRequest.Builder captureRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
captureRequest.addTarget(mMediaRecorder.getSurface());
mCaptureRequest = captureRequest.build();
mCamera.createCaptureSession(list, new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(CameraCaptureSession session) {
mSession = session;
mMediaRecorder.start();
try {
mSession.setRepeatingRequest(mCaptureRequest,
new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) {
super.onCaptureStarted(session, request, timestamp, frameNumber);
if(frameNumber > 2*60*25) {
mSession.stopRepeating();
mMediaRecorder.stop();
mSession=null;
mCamera.close();
}
}
#Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onConfigureFailed(CameraCaptureSession session) {
mSession = session;
}
}, null);
} catch (Exception e) {
e.printStackTrace();
}
I'm building a camera app based on Camera2, but the picture I save is not matching the latest one I saw on my surface view. It seems that the Preview session works but when I ask for a capture, the new request stop the preview and capture the image. The surface view freeze on the latest pic and that create a gap between the time I press the shutter button (preview running and request capture) and the onCaptureCompleted from the capture request.
Here is the preview session
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Log.d(TAG, "here is the width of texture" + mPreviewSize.getWidth());
Log.d(TAG, "here is the height of texture" +mPreviewSize.getHeight());
Surface surface = new Surface(texture);
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
if (null == mCameraDevice) {
return;
}
mCaptureSession = cameraCaptureSession;
try {
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest,
mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onConfigureFailed(
#NonNull CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
the mCaptureCallback is defined as below :
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {
private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW:
break;
case STATE_CAPTURE:
mState = STATE_PREVIEW;
capturePicture();
break;
}
}
#Override
public void onCaptureProgressed(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull CaptureResult partialResult) {
process(partialResult);
}
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull TotalCaptureResult result) {
TotalCaptureResult iResult = result;
Log.d(TAG, "Frame on Completed: "+result.getFrameNumber());
process(result);
}
}
What's happening is that I repeating the preview and it works. the process is just used to keep it running and nothing happened until the mState is set to CAPTURE.
It's set to capture when we click on the shutter button. When I click on the button, I call:
private void takePicture(){
try {
mFile = ImageSaver.generateNewFileImage();
mState = STATE_CAPTURE;
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException e) {
Log.e(TAG,"Camera exception",e);
}
}
I call then CapturePicture as mState is in Capture as defined in the mCaptureCallback
private void capturePicture() {
mTakePictureRunnable = new Runnable() {
#Override
public void run() {
takePictureNow();
}
};
mBackgroundHandler.post(mTakePictureRunnable);
}
the takePicutreNow is defined
private void takePictureNow() {
Log.d(TAG, "Running captureStillPicture");
try {
if (null == mCameraDevice) {
return;
}
// This is the CaptureRequest.Builder that we use to take a picture.
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Log.d(TAG, "here is the width of texture" + mPreviewSize.getWidth());
Log.d(TAG, "here is the height of texture" + mPreviewSize.getHeight());
Surface surface = new Surface(texture);
captureBuilder.addTarget(surface);
// Orientation
int rotation = getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
//Location if needed
boolean Location_Saved = CameraSettings.Instance().getBoolean(CameraSettings.SAVE_LOCATION,
getResources().getBoolean(R.bool.action_camera_settings_dflt_location));
if(Location_Saved == true) {
captureBuilder.set(CaptureRequest.JPEG_GPS_LOCATION, mLocationManager.getCurrentLocation());
} else {
captureBuilder.set(CaptureRequest.JPEG_GPS_LOCATION, null);
}
CameraCaptureSession.CaptureCallback CaptureCallback
= new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureStarted(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull long timestamp,
#NonNull long framenumber) {
playShutterSound();
showShutterAnimation();
}
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull TotalCaptureResult result) {
Log.d(TAG, mFile.toString());
mState = STATE_PREVIEW;
}
};
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
mCaptureSession.stopRepeating();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
This process is working but I got a lag between the press and the imagesave and the latest pic I saw on the screen from the preview is not exactly the same saved if I move quickly.
It seems that the capture do not update the surface and the surface only show when we were in Preview
Any idea to see what I save ?
Thanks
The surface will freeze when you take an image, that might not be the image that gets saved. If you still want to show camera preview on the screen, you just have to restart the camera preview by calling the function you used to create preview at the first time.