1

My app shows a preview and video recording starts with a button press.

What I'm trying to achieve is to automatically turn on flashlight (torch mode) as soon as the video recording starts.

However I couldn't find a way to do so. On Camera2 API we can use FLASH_MODE_AUTO which will use the flashlight when capturing photo when the scene is dark, but that doesn't work for video recording. There's this FLASH_MODE_TORCH which I could use to turn on the flashlight just like I wanted, but there isn't a FLASH_MODE_TORCH_AUTO to automatically do so when the scene is dark..

There were some answers that uses Ambient light sensor (Sensor.TYPE_LIGHT) of the device to determine whether we are in a dark scene, however that uses the front ambient light sensor instead of the camera itself I think. This is not ideal as the ambient light can be low but the rear camera is able to adjust exposure level to achieve good enough image quality without using flash. So ideally if the camera says 'flash is required' then only the app activates FLASH_MODE_TORCH.

Since the app shows a preview the device already know whether flash is needed before the button press, is there a way to determine whether flash is required during preview?

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Bruce
  • 2,357
  • 5
  • 29
  • 50
  • Check this. Hope it might help. https://stackoverflow.com/a/47270509/8413748 – Eishon Jul 08 '21 at 08:06
  • Thanks but seems like that solution requires a preflash to be fired, which is more suitable for photo taking and not video recording? – Bruce Jul 08 '21 at 08:13

2 Answers2

0

Please try the below method where you need you can use it

below is for Camera API

public void switchFlashOnMode() {
        Camera.Parameters p = getCamera().getParameters();
        try {
            //p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
            p.setFlashMode(Parameters.FLASH_MODE_AUTO);
            getCamera().setParameters(p);
            getCamera().startPreview();
            isFlashTorch = true;
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void switchFlashOffMode() {
        Camera.Parameters p = getCamera().getParameters();
        try {
            p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
            getCamera().setParameters(p);
            Thread.sleep(200);
            getCamera().stopPreview();
            isFlashTorch = false;
        }catch (Exception e){
            e.printStackTrace();
        }
    }

below is for Camera2 API

 void switchFlashMode() {
        if (!flashSupport) return;

        try {
            if (isFlashTorch) {
                isFlashTorch = false;
                requestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
            } else {
                isFlashTorch = true;
                  //requestBuilder.set(CaptureRequest.FLASH_MODE,CameraMetadata.FLASH_MODE_TORCH);
                  requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            }    

            cameraCaptureSession.setRepeatingRequest(requestBuilder.build(), null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

hope it will help you

gpuser
  • 1,143
  • 1
  • 9
  • 6
  • Hi thanks for your answer, my understanding is CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH only trigger flashlight once when taking photo and is not the same as permanent on FLASH_MODE_TORCH? plus don't think it will work for video recording, unless I am mistaken – Bruce Jul 08 '21 at 13:55
  • Just tried it, it didn't work. FLASH_MODE_TORCH turns on flash for video recording but forces ON no matter the brightness of the scene, while CONTROL_AE_MODE_ON_AUTO_FLASH is Auto but it doesn't work for video recording :( – Bruce Jul 09 '21 at 03:28
0

Finally figured this out, gpuser's answer used the right flag but it is not complete - still need to code the callback and turn on the torchlight when needed.

I also found that for video recording, we still use the same Camera2 API init and configuration steps, just that some of the callbacks will be fired multiple times, so I added a flag to perform the flash detection only once.

1)After camera started capturing, run this code

performAutoTorchDetectionOnce = true; // set this flag first, will be used later
captureRequestBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // CONTROL_AE_MODE_ON_AUTO_FLASH is important here, to enable flash detection
captureSession.setRepeatingRequest(captureRequestBuilder.build(), captureCallback, null);

2)And this is my captureCallback implementation, change it depending on your needs. The gist of it is that eventually the camera capture will fall into one of the two states, CONTROL_AE_STATE_CONVERGED or CONTROL_AE_STATE_FLASH_REQUIRED. These two states mean that auto exposure algorithm has finished running, if it is converged means no flash is needed whereas flash_required will mean that we have to turn on flash. In the latter we will then need to manually turn on the flash in the next step.

private CameraCaptureSession.CaptureCallback captureCallback =
    new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
                                         long timestamp, long frameNumber) {
                super.onCaptureStarted(session, request, timestamp, frameNumber);
            }

            @Override
            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);

                Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                if (aeState != null) {
                    if (performAutoTorchDetectionOnce) {
                        if (aeState == CameraMetadata.CONTROL_AE_STATE_CONVERGED            // CONTROL_AE_STATE_CONVERGED means Auto-exposure has finished
                            || aeState == CameraMetadata.CONTROL_AE_STATE_FLASH_REQUIRED) { // CONTROL_AE_STATE_FLASH_REQUIRED means Auto-exposure has finished, but flash is required
                            performAutoTorchDetectionOnce = false;
                            enableTorch(aeState == CameraMetadata.CONTROL_AE_STATE_FLASH_REQUIRED);
                        }
                    }
                }
            }
    };

3)Here's the enableTorch implementation. I tried leaving CONTROL_AE_MODE as CONTROL_AE_MODE_ON_AUTO_FLASH but it didn't work, torch light does not turn on, so I have to change it to CONTROL_AE_MODE_ON.

public synchronized void enableTorch(boolean enable) {
        Timber.d("enableTorch(" + enable + ") called");
        try {
            if (isCaptureStarted()) {
                if (enable) {
                    captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
                } else {
                    captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
                }
                captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
                captureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
            }
        } catch (CameraAccessException e) {
            Timber.e(e, "enableTorch(" + enable + ") failed: ");
        }
    }
Bruce
  • 2,357
  • 5
  • 29
  • 50