I will explain my case.
I'm trying to do an application in which every 5 seconds will take an image, one without flash, and then after 5 seconds one with flash, and repeat this every time. So it will take one without flash, one with flash, one without flash, one with flash... infinitely.
The case is that with my code I can do this in some devices, but the same code won't work in others as I want. i.e:
BQ Aquaris X5 Plus : The no-flash image is correct, but the flash image will be just white.
BQ Aquaris E5 : Won't fire the flash.
How can this be possible, all devices in which I have tried are LEGACY hardware support level for Camera2 API.
This are some important methods in my code (I can't post all code due to char limit). I started from the Google Example:
This setAutoFlash does the mentioned above.
private void setAutoFlash(CaptureRequest.Builder requestBuilder) {
if (mFlashSupported) {
if(phototaken) {
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
}else{
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE);
}
}
}
This other one works in some devices and the bq aquaris e5 but doesn't fire the flash in the bq aquaris x5 plus.
private void setAutoFlash(CaptureRequest.Builder requestBuilder) {
if (mFlashSupported) {
if(phototaken) {
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
}else{
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
}
}
}
And my captureStillPicture
private void captureStillPicture() {
try {
final Activity activity = getActivity();
if (null == activity || 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());
// Use the same AE and AF modes as the preview.
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
setAutoFlash(captureBuilder);
// Orientation
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
CameraCaptureSession.CaptureCallback CaptureCallback
= new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull TotalCaptureResult result) {
showToast("Saved: " + mFile);
Log.d(TAG, mFile.toString());
unlockFocus();
}
};
mCaptureSession.stopRepeating();
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
phototaken = !phototaken;
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
The question is, what am i doing wrong so it doesn't work in all devices? Any help will be great.
There are two levels of control for the flash - manual, and controlled by the auto-exposure routine. You're currently mixing them together.
If you want to fire the flash manually, then you need to set AE_MODE to either AE_MODE_OFF or AE_MODE_ON; not any of the FLASH modes. Then, FLASH_MODE will control whether the flash will be in torch mode, off, or fire once for a given request.
Since you're always leave AE_MODE in one of the FLASH states, what you do to FLASH_MODE should not matter, barring a bug in some specific device.
If you want to guarantee flash firing in every other picture, you need to use AE_MODE_ON_ALWAYS_FLASH for the force-flash photos, and you need to use AE_MODE_ON for the no-flash phoots; don't touch FLASH_MODE.
Right now, with AUTO_FLASH, it's up to the device whether to fire a flash or not, so you'll see different behavior from different devices and lighting conditions - some will fire, some won't.
The other key thing you're not doing is running a precapture sequence; this is essential for flash pictures, because it allows the device to fire the preflash to determine correct flash power, focus, and white balance.
To run precapture, set the AE_MODE as desired, and then set AE_PRECAPTURE_TRIGGER to START for one request. This will transition AE_STATE to PRECAPTURE, and it'll stay there for some number of frames; once AE_STATE is no longer PRECAPTURE, you can issue the actual image capture request. Make sure you keep the AE_MODE consistent throughout this.
The sample app Camera2Basic implements the precapture sequence, so take a look there; it also has some optimizations that skip precapture in case the scene is not dark enough to need flash, but since you want to force-fire flash, that's not relevant to you.
Related
I'm working on a camera application with Android camera2. Too many black stripes appear in the image when the camera is turned on.
I think this is an FPS issue. When the photo is taken, these stripes disappear and a beautiful image is obtained. When I look at the fps supported by the camera with code: Range<Integer>[] ranges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);, I see that it is much higher than it should be. The only range value is [5000-60000]. And i could'nt set the FPS range by previewRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, getRange()); code, due to not supported fps range error. Here is my Camera session code:
private void createCameraSession() {
try {
previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
Surface previewSurface = new Surface(surfaceTexture);
previewRequestBuilder.addTarget(previewSurface);
mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
try {
mCameraCaptureSession = cameraCaptureSession;
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE, CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ);
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
previewRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, CameraMetadata.STATISTICS_FACE_DETECT_MODE_SIMPLE);
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
zoom.setZoom(previewRequestBuilder, zoomFactor);
CaptureRequest previewRequest = previewRequestBuilder.build();
Log.d("TAG", "preview build is done");
mCameraCaptureSession.setRepeatingRequest(previewRequest, null, mHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession session) {
}
}, mHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
I've tried many combination of AE and AWB modes but it wasn't work. The problem continues. Is there another way to set the target FPS range of the camera or the Surface? The api of my device Android 5.1, so Surface.setFrameRate() function doesn't work. Can anybody help me for the solution please?
First, Android 5.1 had a bug about reporting frame rate ranges - you'll need to divide the numbers from available target FPS range by 1000 for setting frame rate (so that'd be [5, 60] fps).
Second, this is very likely a problem with your lights. Most light sources flicker at some rate (60 or 50 Hz for old-fashioned lightbulbs or fluorescent lights. All sorts of hz between 60 and 200 for LED), and the camera autoexposure has to account for that or banding will happen.
Normally, antibanding algorithms will detect the flicker or banding and adjust exposure time to remove it. But your old device may not be able to handle modern LED light flicker.
Try the device outdoors and see if sunlight has banding to figure out if the lights are the problem.
Unfortunately, if lights are your problem, you may have trouble finding a way around this. You could tweak exposure compensation but that'll also make the image darker or brighter, and may still have banding. And I doubt your device supports manual control, and then you'd have to write your own autoexposure routine, which is complicated to do well.
I'm working on a project where I need to constantly take pictures with some defined resolution, quality and waiting period. The user is able to start/stop the capturing of pictures, the user can also run it with a preview or without a preview (as to do other things with his phone).
I have everything working fine most of the time for most devices but in some strange cases I see bad/blurred pictures. It is worth mentioning that the device is on the move.
Here is my current request:
private fun setRequestParams(builder: CaptureRequest.Builder) {
builder.set(CaptureRequest.BLACK_LEVEL_LOCK, false)
builder.set(CaptureRequest.CONTROL_AWB_LOCK, false)
builder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO)
builder.set(CaptureRequest.CONTROL_AE_LOCK, false)
builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
builder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE, CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_AUTO)
builder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0)
builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, 0)
builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO)
builder.set(CaptureRequest.COLOR_CORRECTION_MODE, CaptureRequest.CONTROL_MODE_AUTO)
builder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG)
builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
builder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,getRange())
builder.set(CaptureRequest.JPEG_QUALITY,pictureConfig.quality.toByte())
val capabilities = cameraCharacteristics?.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
val isManualFocusSupported : Boolean? = capabilities?.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)
if (isManualFocusSupported != null && isManualFocusSupported ) {
builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF)
builder.set(CaptureRequest.LENS_FOCUS_DISTANCE, 0.0f)
}
}
As I mentioned above, The user can either capture photos on foreground or background, for that I create 3 requests, I realized in some phones if there is no linked Surface the camera automatically closes, that's why I use the dummyView.
private val previewRequest: CaptureRequest by lazy {
captureSession!!.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
addTarget(preview!!)
setRequestParams(this)
}.build()
}
private val backgroundPreviewRequest : CaptureRequest by lazy {
captureSession!!.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
addTarget(dummyPreview!!)
}.build()
}
private val captureRequest: CaptureRequest by lazy {
captureSession!!.device.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply {
addTarget(imageReader!!.surface)
setRequestParams(this)
}.build()
}
When everything is ready I capture a photo :
captureSession?.setRepeatingRequest(previewRequest, null, cameraPreviewHandler)
captureSession?.capture(captureRequest,null, cameraHandler)
When the onImageAvailable is called I capture again. Sometimes I save the image, but if the capture is during the waiting period I just let it continue. Usually the waiting period is of 400ms.
My questions are:
My pictures are sometimes blurred and sometimes not (on the same capturing session) is there something to improve on the request ?
Phones of similar model (SM-A705FN) give inconsistent results. Same phone give partially blurred images when another phone gives good results. Is it possible that the camera's hardware was overused ?
In some phones the camera stops taking pictures without ever calling the camera error callback. Is there a way to know that my picture was lost somewhere ?
In some phones my code doesn't work at all for example Samsung A7 but it works on the Samsung A10 (which is less powerful)
PS: I was using repeatingRequest with almost identical request params but find out that even though the output was slightly higher (pictures were taken faster) the quality was poorer.
I am having a headache over the Camera API 1 for android. After reading all of the Internet content, I made some sample app that works OK. It creates a service, which then is used to operate with the camera in the background, so there is no preview or activity enabled. To achieve this I use a dummy SurfaceHolder, like this:
protected class MySurfaceHolder implements SurfaceHolder {
private final Surface surface;
private final SurfaceTexture surfaceTexture;
public MySurfaceHolder () {
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
if (textures.length > 0) {
this.surfaceTexture = new SurfaceTexture(textures[0]);
this.surface = new Surface(this.surfaceTexture);
} else {
this.surface = null;
this.surfaceTexture = null;
}
}
[...]
}
and then I use it like this
// simplified version of my code
try {
initializeCamera(); // open camera and set Camera.Parameters
camera.setPreviewDisplay(new MySurfaceHolder());
camera.startPreview();
camera.unlock();
initializeMediaRecorder(); // create MediaRecorder, set video/audio parameters
mediaRecorder.prepare();
mediaRecorder.start();
// wait until recording finish and exit
} finally {
stopRecording();
}
the Camera and MediaRecorder initialization methods are just like the documentation states they should be (and they work).
Everything works and operates as it should. Almost everything - sometimes, under unknown circumstances the MediaRecorder creates empty files, like 32kB containing only headers and info about the video - no frames. The longer I record like this, the bigger is the file (few kB every few seconds). After 1 minute, the file weights about 80kB. Funny thing is I know that the camera is working and capturing frames (I debugged it a little showing preview frames), but the frames are not written into the output file.
Also when it happens I am not able to record in FHD (1920x1080) - I get the "start failed" message - at this time camera is not capturing frames. The same thing could happen when I use wrong (not supported) video size. I suppose in this case the message is thrown at the mediaRecorder.start(); line, and stopRecording(); is invoked but I am not sure.
After some time or after unknown action the problem is suddenly gone (I don't know when, I don't know how). It happens for sure on Android 5.1, but may happen on other versions as well.
Could this bug be related to my custom surface code?
What could cause the MediaRecorder to not write frames into a file?
Why I am not able to record in FHD, but in the same time I am able to record in HD (1280x720)?
Is there any alternative for MediaRecorder, so I can avoid these bugs?
May it happen when another app is trying to get Camera object, thus distrupting current recording? If so, how to regain access to the Camera object (I apparently am not able to do this now on some devices).
EDIT:
I think I might have a clue. I am calling
camera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
// ... get current frame
}
camera.startPreview();
to get preview frame of current recording. It appears that the bug occurs when I am using this method to get preview frame (at random times). It seems flawed, because not all devices react to this thing properly (sometimes there is no preview frame...). Is there any other, better method of handling current preview frame without the real surface?
I need to speed-up capturing of camera2 API. I tried to build "android-Camera2Basic" project from google samples. For default capture request from example:
if (null == activity || 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());
// Use the same AE and AF modes as the preview.
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// Orientation
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
CameraCaptureSession.CaptureCallback CaptureCallback
= new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
showToast("Saved: " + mFile);
unlockFocus();
}
};
mCaptureSession.stopRepeating();
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
It takes 200-300ms from send request
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
And get result in
onImageAvailable(ImageReader reader)
Is it possible to reduce this time? I tried set different parameters for capture request, such as TEMPLATE_ZERO_SHUTTER_LAG, NOISE_REDUCTION_MODE_OFF, EDGE_MODE_OFF, etc. But it has no any effect.
If I try to capture burst, then all images, except first are comes very fast, no more then in 30-40ms. How can I reduce capturing time for first image?
replying to your comment, but making it into a proper answer:
If you check those slides from the Samsung dev. conference on slide #22 it shows the camera2 model. As you can see, there're several queues:
Pending Request queue
In flight capture queue
output image queue to the Surface showing the camera preview
and the callback to onCaptureComplete
that explains why the 1st capture is slow, but in burst mode the next images comes very fast. The requests and processing are queued and the 1st takes 300ms to arrive all the way back on the callback but the next one is already "right behind it".
If you're interested in the new API (and who wouldn't be, camera2 is amazing), you can also check the full video from the Samsung Dev. conference on YouTube. And the official docs. Lot's of good info on those.
I'm working on an Android app that uses the camera to preview and take pictures. I use FOCUS_MODE_CONTINUOUS_PICTURE with the galaxy S4 and find that the focusing works very well.
However, on the galaxy S5 the FOCUS_MODE_CONTINUOUS_PICTURE rarely ever finds the focus properly. The camera will zoom into focus, but then zoom back out of focus repeatedly.
Does anyone have an idea of why the FOCUS_MODE_CONTINUOUS_PICTURE works so poorly on the S5, or can anyone confirm whether or not they have the same issue?
I too have experienced these same issues.
The Galaxy S5, and possibly other devices, don't seem to have reliable behavior in continuous picture focus mode. This is very frustrating as a developer, when code works perfectly on most devices, but then along comes the S5 (a very popular device) and we look pretty bad.
After much head scratching, I think I have a solution (more of a workaround) that is working well.
set camera to FOCUS_MODE_CONTINUOUS_PICTURE
in gesture handler for taking a photo (e.g. button tap, touch event), switch camera to FOCUS_MODE_AUTO, then call Camera.autoFocus() in a deferred manner
this provides the nice continuous focus UI during photo preview, but takes the picture in reliable auto-focus mode.
Here is the code:
protected void onTakePicture()
{
// mCamera is the Camera object
// mAutoFocusCallback is a Camera.AutoFocusCallback handler
try
{
// determine current focus mode
Camera.Parameters params = mCamera.getParameters();
if (params.getFocusMode().equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
{
mCamera.cancelAutoFocus(); // cancels continuous focus
List<String> lModes = params.getSupportedFocusModes();
if (lModes != null)
{
if (lModes.contains(Camera.Parameters.FOCUS_MODE_AUTO))
{
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); // auto-focus mode if supported
mCamera.setParameters(params); // set parameters on device
}
}
// start an auto-focus after a slight (100ms) delay
new Handler().postDelayed(new Runnable() {
public void run()
{
mCamera.autoFocus(mAutoFocusCallback); // auto-focus now
}
}, 100);
return;
}
mCamera.autoFocus(mAutoFocusCallback); // do the focus, callback is mAutoFocusCallback
}
catch (Exception e)
{
Log.e("myApp", e.getMessage());
}
}
please give this a try and report back your results