4

I based my code on the sample from Google Camera2-Basic and added flash always support. But It looks like the picture is taken right after the flash has occur. I (almost) always get non flashed picture, even though the flash is triggered.

The modified preview request builder :

mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);

And I also added this control mode to captureStillPicture()

captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);

and modified the process() switch with :

       case STATE_WAITING_PRECAPTURE: {
          // CONTROL_AE_STATE can be null on some devices
          Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
          Log.i(TAG, "aeState = " + aeState);
          if (aeState == null ||
              aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
              aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
              aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
            mState = STATE_WAITING_NON_PRECAPTURE;
          }
          break;
        }
gbero
  • 3,890
  • 1
  • 26
  • 30
  • which device did you try? – Alex Cohn Nov 24 '15 at 13:36
  • Thanks! I have never been impressed with the quality of camera2 implementation on Nexus5. – Alex Cohn Nov 24 '15 at 14:47
  • Well I think it's more a problem in my implementation but I can't figure out what :( – gbero Nov 24 '15 at 14:53
  • FWIW, your code doesn't quite line up with [Google's Camera2Basic code](https://github.com/googlesamples/android-Camera2Basic/blob/master/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java). `CONVERGED` is handled in `STATE_WAITING_LOCK` there. – CommonsWare Nov 26 '15 at 14:51
  • Yep I added that case because I want either flash on or off mode. Not flash_auto. And according to this post it was the way to go : http://stackoverflow.com/a/24664959/1532108 – gbero Nov 26 '15 at 14:55
  • @gbero: May I know if you were able to solve your problem? I am facing exact problem. Please help. – kunal18 Mar 03 '16 at 13:41

1 Answers1

0

I had this exact same problem, also on a Nexus 5. I worked around it by checking CaptureResult.FLASH_STATE and triggering a capture immediately if it is equal to CaptureResult.FLASH_STATE_FIRED. Basically, if the flash fires, then you need to do a capture right then and there, regardless of what state you are in, because it's only going to fire once. If the auto-focus/exposure etc hasn't properly converged at that point there's nothing you can do about it.

You can do a check at the start of onCaptureCompleted like this:

if(mFlashAvailable && mWaitingForFlash)
{
     Integer flashState = result.get(CaptureResult.FLASH_STATE);
     if(flashState != null && flashState==CaptureResult.FLASH_STATE_FIRED)
     {
         mWaitingForFlash = false;

         // do the capture...
         mState = STATE_PICTURE_TAKEN;
         captureStillPicture();

         return;  // don't call process()
     }
}

mFlashAvailable is set from the CameraCharacteristics when opening the camera like this:

mFlashAvailable = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);

mWaitingForFlash is just a flag you can set when you start the precapture, so that you don't capture more than one frame. This might not be necessary in your particular case however.

That will take care of when the flash fires before the state converges (i.e. the picture is after the flash), as you describe in your question. However you also need to handle the case when the flash is firing late (I've never seen this actually happen but just in case). You can do that by setting a flag like mExpectingFlash = true; when setting the CONTROL_AE_MODE to CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH, and not capturing the usual way if it is true (since you will capture when you detect the flash firing state instead). You can also set this flag when you get aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED if you are using CONTROL_AE_MODE_ON_AUTO_FLASH. As a safety net I have a timeout so I don't end up waiting for a flash that never comes in case CONTROL_AE_STATE_FLASH_REQUIRED is detected but the flash doesn't fire.

If you capture multiple frames, you can read the timestamp when you detect the flash firing, like this:

 long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);

and then cross check it against the timestamp of the image in onImageAvailable

 Image image = reader.acquireLatestImage();
 if (image != null)
 {
     long timestamp = image.getTimestamp();
     // ....
 }
samgak
  • 23,944
  • 4
  • 60
  • 82