It seems like there are 2 unrelated bugs here, one on the Nexus 4, the other on the Samsung S5. They both seem to manifest as the same issue, pictures taken in low light conditions with the flash on appear extremely dark, but have very different root causes.
Nexus 4
The Nexus 4 breaks when using continuous focus in conjunction with the flash. This seems like a relatively well known issue and the only solution seems to be to use FOCUS_MODE_AUTO
instead of FOCUS_MODE_CONTINUOUS_PICTURE
. The root cause seems to be related to taking the picture too early, before the flash gets a chance to fire.
As far as I know, the Nexus 4 is the only device that needs this kind of special casing (i.e. it reports supporting FOCUS_MODE_CONTINUOUS_PICTURE
but breaks horribly with it).
// dummy method, replace with wherever you setup camera params
public void onCameraOpened(Camera camera) {
Camera.Parameters params = camera.getParameters();
setFocusModeParameter(
params,
Build.MODEL.equals("Nexus 4")
? new String[] {
Camera.Parameters.FOCUS_MODE_AUTO
}
: new String[] {
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_AUTO,
}
);
camera.setParameters(params);
}
public static void setFocusModeParameter(Camera.Parameters params, String... preferences) {
List<String> supported_focus_modes = params.getSupportedFocusModes();
if (supported_focus_modes == null) {
return;
}
for (String pref : preferences) {
if (supported_focus_modes.contains(pref)) {
params.setFocusMode(pref);
return;
}
}
}
Samsung S5
Unlike the Nexus 4, the Samsung S5 seems to lag behind the flash which causes a dark picture. As far as I can tell, turning on the hidden zero shutter lag parameter (in the safe manner described in the robust section) seems to have no ill effects on the following devices: Nexus 4, Nexus 5, Samsung S3, Samsung S4, Samsung Galaxy Tab S (SM-T700).
Turn on Zero Shutter Lag
Try setting the following hidden camera parameter, which seems to solve the problem on my S5.
Camera.Parameters params = camera.getParameters();
params.set("zsl", "on");
camera.setParameters(params);
More Robust Solution
If the solution above works, I'm using a slightly more robust way to detect when the zsl
parameter is available:
// dummy method, replace with whatever sets up camera parameters
public void onCameraOpened(Camera camera) {
Camera.Parameters params = camera.getParameters();
setHiddenParameter(params, "zsl-values", "zsl", "on");
camera.setParameters(params);
}
public static void setHiddenParameter(Camera.Parameters params, String values_key, String key, String value) {
if (params.get(key) == null) {
return;
}
String possible_values_str = params.get(values_key);
if (possible_values_str == null) {
return;
}
String[] possible_values = possible_values_str.split(",");
for (String possible : possible_values) {
if (possible.equals(value)) {
params.set(key, value);
return;
}
}
}
Explanations
This part is only here to document the rabbit hole to find this parameter, hopefully someone that knows more than me can expand on this.
Symptoms:
- On the Samsung S5, taking pictures in extremely dark conditions with the flash set to
FLASH_MODE_ON
or FLASH_MODE_AUTO
leads to dark or completely black photos.
- This does not seem to happen on any other device I have tested (Nexus 4, Nexus 5, Samsung S3, Samsung S4)
- If I take pictures standing close to the objects (~3 ft) in a completely dark room, I get a extremely dark picture with only a few things visible.
- If I take pictures in front of an open space (>5 ft) in a completely dark room, I get a completely black picture.
The first thing I tried was messing with focus related settings, reasoning that the open space would cause the focus to take longer, thus messing with the timing of taking the picture with the flash. Neither FOCUS_MODE_AUTO
nor FOCUS_MODE_CONTINUOUS_PICTURE
seemed to help the situation.
I also tried locking the auto-exposure and auto-whitebalance adjustments before calling camera.takePicture(...)
to make sure those process weren't throwing the flash timing off, but that did not seem to help either.
It still felt like a timing issue though, so I started comparing the difference in parameters between the parameters my app was using vs. the native camera app.
Native Camera
12-10 15:49:08.659: W/QCameraParameters(265): [FW_DBG] setFirmwareMode: none
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] Requested preview size 1920 x 1080
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] dualrecording-hint : 0 m_FaceAE=1 Camera ID=0
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] Requested video size 1920 x 1080
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] Requested picture size 2048 x 1152
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] Requested FOV 62.000000
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] requested jpeg thumbnail size 512 x 288
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] set optimal jpeg thumbnail size 512 x 288
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] rotation val = 90
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] m_bNoDisplayMode = 0
12-10 15:49:08.659: W/QCameraParameters(265): setZslMode : m_nDualMode=0, mHdrMode=0, mTakeLowlight=0, m_bRecordingHint=0, mAutoLLS=0, m_nDualRecordingHint=0
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] ZSL = ON
12-10 15:49:08.659: I/QCameraParameters(265): [PARM_DBG] Requested FpsRange Values:(15000, 30000)
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] flash mode = on
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] AEC lock = false
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] AWB lock = false
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] mHdrMode 0 mTakeLowlight 0
12-10 15:49:08.659: E/QCameraParameters(265): SAMSUNG APPS HDR MODE
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] live snapshot size 2048 x 1152
12-10 15:49:08.659: E/QCameraParameters(265): [syscamera][setRthdrModes::2831][str::off][prev_str::off]
12-10 15:49:08.659: E/QCameraParameters(265): [syscamera][setPafModes::2863][str::on][prev_str::on]
12-10 15:49:08.659: E/QCameraParameters(265): [syscamera][setDrcModes::2891][str::on][prev_str::on]
12-10 15:49:08.659: W/QCameraParameters(265): updateParameters : X - mCameraId=0, final_rc=0, line=4465
12-10 15:49:08.659: W/QCameraParameters(265): [PARM_DBG] setNumOfSnapshot : nBurstNum = 1, nExpnum = 1
My App
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] Requested preview size 1920 x 1080
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] dualrecording-hint : 0 m_FaceAE=1 Camera ID=0
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] Requested video size 1920 x 1080
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] Requested picture size 2048 x 1152
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] Requested FOV 62.000000
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] requested jpeg thumbnail size 512 x 288
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] set optimal jpeg thumbnail size 512 x 288
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] m_bNoDisplayMode = 0
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] ZSL = off
12-10 15:48:33.109: I/QCameraParameters(265): [PARM_DBG] Requested FpsRange Values:(10000, 30000)
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] flash mode = on
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] AEC lock = false
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] AWB lock = false
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] mHdrMode 0 mTakeLowlight 0
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] live snapshot size 2048 x 1152
12-10 15:48:33.109: E/QCameraParameters(265): [syscamera][setPafModes::2863][str::off][prev_str::off]
12-10 15:48:33.109: E/QCameraParameters(265): [syscamera][setDrcModes::2891][str::off][prev_str::off]
12-10 15:48:33.109: W/QCameraParameters(265): updateParameters : X - mCameraId=0, final_rc=0, line=4465
12-10 15:48:33.109: W/QCameraParameters(265): [PARM_DBG] setNumOfSnapshot : nBurstNum = 1, nExpnum = 1
Native vs. My App
The AEC (auto exposure) and AWB (white balance) lines are the same, so that's consistent with what I've tried before. The one difference is the ZSL parameter, which I've never heard of before.
Googling for ZSL finds this SO answer:
To achieve zero shutter lag, the camera driver must maintain a small circular buffer pool containing full resolution frames. Images are captured at sensor rate and are sent to preview and to the circular buffer pool (either as raw Bayer or as processed/semi-processed YUV). When the use presses the shutter, the newest buffer in the circular pool is extracted, processed and compressed as JPEG. On older mobile phone cameras, the sensor is not able to capture full resolution frames at a high enough frame rate, and therefore ZSL cannot be implemented.
So it seems like the shutter lag causes a timing mismatch between when the flash fires and when the picture is captured. Turning on ZSL seems to eliminate the issue entirely. It should probably be on by default given that flash behaviour is broken without it, but I'm not going to hold my breath on that one.