I am using the Camera2 api to show a preview form the camera. I also want to implement a ImageReader to process the images. I have a Start preview function. When I call it the preview is just black. If I remove "mimageReader.getSurface()" from Arrays.asList() I am able to see the camera preview. How can I have a camera preview show up and also use ImageReader?
private void startPreview()
{
List<Surface> outputSurfaces = new ArrayList<>();
List surfaces = new ArrayList<>();
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface previewSurface = new Surface(surfaceTexture);
try {
mCaptureRequestBuilder = _cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequestBuilder.addTarget(previewSurface);
_cameraDevice.createCaptureSession(Arrays.asList(previewSurface,mimageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(CameraCaptureSession session) {
Log.d("", "onConfigured: startPreview");
try {
session.setRepeatingRequest( mCaptureRequestBuilder.build(),null,mthreadhandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onConfigureFailed(CameraCaptureSession session) {
Log.d("", "onConfigureFailed: startPreview");
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
You have to create a new instance of ImageReader, set its onImageAvailableListener(you can process the preview frames here) and add it to PreviewRequestBuilder. For example:
mImageReader = ImageReader.newInstance(
mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 2);
mImageReader.setOnImageAvailableListener(mOnGetPreviewListener, mBackgroundHandler);
mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
It may be because your function startPreview() is not called, it can happens when we don't get surfaceTexture values at time.
This is my startPreview method
protected void createCameraPreview() {
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
Surface surface = new Surface(texture);
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback(){
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
//The camera is already closed
if (null == cameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
cameraCaptureSessions = cameraCaptureSession;
updatePreview();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Then i call updatePreview function to restart the preview
protected void updatePreview() {
if(null == cameraDevice) {
Log.e(TAG, "updatePreview error, return");
}
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
try {
cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Related
I have tried to get each frame from camera using Camer2 API for image processing purposes but I get a FATAL EXCEPTION which says
FATAL EXCEPTION: main
Process: com.example.avoor.camera2api, PID: 2831
java.lang.IllegalStateException: Image is already closed
I successfully can open the camera and send it to TuxtureView for previewing but the app crashes when it comes to OnImageAvailableListener.
Here is the codes that are used:
protected void createCameraPreview() {
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
Surface surface = new Surface(texture);
ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 3);
//reader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
captureRequestBuilder.addTarget(reader.getSurface());
List<Surface> outputSurfaces = new ArrayList<>();
outputSurfaces.add(reader.getSurface());
outputSurfaces.add(surface);
///////////////////////////////////////////////////////////////////
ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
try {
image = reader.acquireLatestImage();
final byte[] bytes;
bytes = convertYUV420888ToNV21(image);
Log.d(TAG,"Height:"+String.valueOf(image.getHeight())+
" Width: "+String.valueOf(image.getWidth()));
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
imageView.setImageBitmap(bitmap);
}
}
});
}
catch (IllegalStateException e) {
Log.e(TAG, "Too many images queued for saving, dropping image for request: ");
return;
}
finally {
if (image != null) {
image.close();
}
}
}
};
reader.setOnImageAvailableListener(readerListener, mBackgroundHandler);
///////////////////////////////////////////////////////////////////
cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback(){
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
//The camera is already closed
if (null == cameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
cameraCaptureSessions = cameraCaptureSession;
updatePreview();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
}
}, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
and here:
protected void updatePreview() {
if(null == cameraDevice) {
Log.e(TAG, "updatePreview error, return");
}
captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
try {
cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
and I have started the BackgroundThread when the camera is opened
Your reader is likely getting garbage collected; you're not saving it anywhere, and it goes out of scope at the end of createCameraPreview.
You do get a Surface from it, but Surface is like a weak reference; it won't keep ImageReader from being collected.
I am new to camera2 api. I want to build an image processing framework on my Android phone.
Step1: use the Camera2 API to open a camera preview stream
Step2: feed the preview frame data to OpenCV for processing
Step3: display the processed result live on the screen
Currently, I have finished Step1 using ImageReader and C++ OpenCV code. However, I don't know how to do step3.
How do I display the processed image on the screen? (I want to display the normal image, and overlay an icon if I detect predefined object)
Here are some key codes:
protected void createCameraPreview() {
try {
SurfaceTexture texture = textureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
// Surface surface = new Surface(texture);
Surface mImageSurface = mImageReader.getSurface();
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
// captureRequestBuilder.addTarget(surface);
captureRequestBuilder.addTarget(mImageSurface);
cameraDevice.createCaptureSession(Arrays.asList(mImageSurface), new CameraCaptureSession.StateCallback(){
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
//The camera is already closed
if (null == cameraDevice) {
return;
}
cameraCaptureSessions = cameraCaptureSession;
updatePreview();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "Configuration change", Toast.LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
protected void updatePreview() {
if(null == cameraDevice) {
Log.e(TAG, "updatePreview error, return");
}
try {
cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
Log.e(TAG, "onImageAvailable: " + count++);
Image img = null;
img = reader.acquireNextImage();
try {
if (img == null) throw new NullPointerException("cannot be null");
ByteBuffer buffer = img.getPlanes()[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
int width = img.getWidth();
int height = img.getHeight();
// ****try to get captured img for display here (synchronized)
// ****try to process image for detecting the object here (asynchronized)
} catch (NullPointerException ex) {
ex.printStackTrace();
}finally {
Log.e(TAG, "in the finally! ------------");
if (img != null)
img.close();
}
}
};
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.
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.
guys who can give me an example how to use camera2 characteristics in android?
F.e. how to use CONTROL_EFFECT_MODE_SEPIA effect ?
Have tried to use characteristics.get(CameraCharacteristics.CONTROL_EFFECT_MODE_SEPIA), but it gave me an The method get(CameraCharacteristics.Key<T>) in the type CameraCharacteristics is not applicable for the arguments (int).
Thanks.
Neither first one nor second one didn't work for me. I am sure i am doing something wrong, so i want to show my code, and hope you can help me to find a solution.
#Override
public void openCamera() {
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
Log.e(TAG, "openCamera E");
try {
String cameraId = manager.getCameraIdList()[0];
characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mPreviewSize = map.getOutputSizes(SurfaceTexture.class)[0];
Log.e(TAG, "Preview size is: " + mPreviewSize.toString());
manager.openCamera(cameraId, mStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
Log.e(TAG, "openCamera X");
}
public void updatePreview() {
if(null == mCameraDevice) {
Log.e(TAG, "updatePreview error, return");
}
mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_OFF);
HandlerThread thread = new HandlerThread("CameraPreview");
thread.start();
backgroundHandler = new Handler(thread.getLooper());
try {
mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void startPreview() {
if(null == mCameraDevice || !myTexture.isAvailable() || null == mPreviewSize) {
Log.e(TAG, "startPreview fail, return");
}
SurfaceTexture texture = myTexture.getSurfaceTexture();
if(null == texture) {
Log.e(TAG,"texture is null, return");
return;
}
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface surface = new Surface(texture);
try {
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
} catch (CameraAccessException e) {
e.printStackTrace();
}
mPreviewBuilder.addTarget(surface);
try {
mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(CameraCaptureSession session) {
mPreviewSession = session;
updatePreview();
}
#Override
public void onConfigureFailed(CameraCaptureSession session) {
Log.e(TAG,"onConfiguration failed.");
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
#Override
public void onOpened(CameraDevice camera) {
Log.e(TAG, "onOpened");
mCameraDevice = camera;
startPreview();
}
#Override
public void onDisconnected(CameraDevice camera) {
Log.e(TAG, "onDisconnected");
}
#Override
public void onError(CameraDevice camera, int error) {
Log.e(TAG, "onError");
}
};
You need to set the effect mode in your CaptureRequest; CameraCharacteristics will tell you what effect modes are supported.
Roughly, use:
int[] supportedEffects = characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS);
int selectedEffect = supportedEffects[0]; // select effect you want here
requestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, selectedEffect);
captureSession.setRepeatingRequest(requestBuilder.build());
mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
mPreviewBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_NEGATIVE);
mCameraCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), mPreviewSessionCallback, mHandler);
exaple:https://github.com/pinguo-yuyidong/Camera2/blob/master/app/src/main/java/us/yydcdut/androidltest/listener/EffectItemClickListener.java