I'm trying to create an activity that will record video and take pictures at the same time. I'm currently using the CameraX API for its simplicity and would like to stick with it instead of the Camera2 or anything else. For the layout, I have two buttons, one for pictures, another for video and a TextureView. My question is what is the best way of switching between the ImageCapture and VideoCapture objects?
I have to bind them to the lifecycle of the activity in order for them to work, but only one will work at a time. Otherwise, I get this problem. I was trying to make the buttons do it for me, but then that requires me to change what the buttons do after I switch the Capture objects. I was also thinking of using fragments, but I feel like there is an easier way. I would also prefer the buttons to be combined into one if possible.
private void startCamera() {
CameraX.unbindAll();
Rational aspectRatio = new Rational(textureView.getWidth(), textureView.getHeight());
Size screen = new Size(textureView.getWidth(), textureView.getHeight());
PreviewConfig pConfig = new PreviewConfig.Builder().setTargetAspectRatio(aspectRatio).setTargetResolution(screen).build();
Preview preview = new Preview(pConfig);
preview.setOnPreviewOutputUpdateListener(new Preview.OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(Preview.PreviewOutput output) {
ViewGroup parent = (ViewGroup) textureView.getParent();
parent.removeView(textureView);
parent.addView(textureView);
textureView.setSurfaceTexture(output.getSurfaceTexture());
updateTransform();
}
});
setupPictures(preview);
setupVideos(preview);
}
private void setupPictures(Preview preview) {
ImageCaptureConfig imageCaptureConfig = new ImageCaptureConfig.Builder().setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY).
setTargetRotation(getWindowManager().getDefaultDisplay().getRotation()).build();
final ImageCapture imgCap = new ImageCapture(imageCaptureConfig);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = new File("/storage/emulated/0/DCIM/Camera/"); //getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image;
try {
image = File.createTempFile(
imageFileName, //
".jpg", //
storageDir // directory
);
imgCap.takePicture(image, new ImageCapture.OnImageSavedListener() {
@Override
public void onImageSaved(@NonNull File file) {
String msg = "Pic capture at " + file.getAbsolutePath();
Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onError(@NonNull ImageCapture.UseCaseError useCaseError, @NonNull String message, @Nullable Throwable cause) {
String msg = "Pic Capture failed: " + message;
Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
if (cause != null) {
cause.printStackTrace();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
CameraX.bindToLifecycle(this, preview, imgCap); // I want this to be triggered by one of the
// two buttons and same with the other
// but buttons also need to take pic or vid
}
private void setupVideos(Preview preview) {
VideoCaptureConfig videoCaptureConfig = new VideoCaptureConfig.Builder().
setLensFacing(CameraX.LensFacing.BACK).
setTargetRotation(getWindowManager().getDefaultDisplay().getRotation()).build();
final boolean[] recording = new boolean[]{false};
@SuppressLint("RestrictedApi")
final VideoCapture vidCap = new VideoCapture(videoCaptureConfig);
CameraX.bindToLifecycle(this, preview, vidCap);
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@SuppressLint("RestrictedApi")
@Override
public void onClick(View v) {
if (!recording[0]) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "MP4_" + timeStamp + "_";
File storageDir = new File("/storage/emulated/0/DCIM/Camera/"); //getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File video;
try {
video = File.createTempFile(
imageFileName, /* prefix */
".mp4", /* suffix */
storageDir /* directory */
);
vidCap.startRecording(video, new VideoCapture.OnVideoSavedListener() {
@Override
public void onVideoSaved(File file) {
String msg = "Vid capture at " + file.getAbsolutePath();
Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onError(VideoCapture.UseCaseError useCaseError, String message, @Nullable Throwable cause) {
String msg = "Vid Capture failed: " + message;
Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
if (cause != null) {
cause.printStackTrace();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
recording[0] = true;
} else {
vidCap.stopRecording();
recording[0] = false;
}
}
});
CameraX.bindToLifecycle(this, preview, vidCap);
}
Again, my layout is just two buttons at the top and the TextureView. You will also need the audio, camera, and external storage write permission in both the manifest file then check for them within the activity.