如何实现手动设置快门和增益?
如何避免因为不同的数据流在不同的地方设置增益和快门?

思路

拍照和预览设置增益可能会发生改变,如果用手动AE也可能出问题。最好的地方是他们共用的地方,可以想到的是sensor模块,sensor模块是和硬件最密切的,在这里可以进行寄存器的设置。我们找到不论何种模式都会进行设置的地方进行修改就行了。

过程

拿imx290举例,它的常用寄存器地址和函数是在imx290_lib.h中定义的。

static sensor_lib_t sensor_lib_ptr =
{
.sensor_slave_info =
{
.sensor_name = SENSOR_MODEL,
.slave_addr = 0x34,
.i2c_freq_mode = SENSOR_I2C_MODE_FAST,
.addr_type = CAMERA_I2C_WORD_ADDR,
.sensor_id_info =
{
.sensor_id_reg_addr = 0x301E, // a fake id register
.sensor_id = 0xB201, // a fixed value
}
...
}
...
}

这是对结构体的乱序赋值,既能初始化时赋值,也可以不考虑顺序。

.exposure_func_table =
{
.sensor_calculate_exposure = sensor_calculate_exposure,
.sensor_fill_exposure_array = sensor_fill_exposure_array,
},

仔细看其中,会发现这个结构体里还有函数方法。而这个函数就是具体不同型号中对寄存器的操作,就比如写入增益寄存器,可能存在有的有一个字节,有的需要两个字节。给一个相同的函数入口,其实就是实现了面向对象编程中的多态。

我们需要修改的是写入sensor的增益和快门,因此越接近底层越不容易出现问题,比如避免因为预览和拍照流的参数位置不同造成的影响。

sensor_fill_exposure_array函数是对曝光类寄存器进行操作,
由于sensor_fill_exposure_array是对曝光表的直接寄存器操作,因此可以说是最底层了,但是不同的sensor这个函数的内容也不一样,因此我们需要先找到调用它的函数。

我们可以看到sensor模块中的sensor_apply_exposuresensor_set_exposure函数不仅仅是在sensor aec的init中进行调用,还有双摄,从摄,手动AE,自动AE的设置,可以说它是所有数据流设置曝光增益流程的必经之处。

sensor_apply_exposure中会对aec的最大曝光行最大增益等进行判断,保证不会超过lib.h的限制之后,然后通过sensor_fill_exposure_array写入。不希望我们写入的值超过了sensor的设置,因此我们最好在apply函数之前,也就是sensor_set_exposure函数之后进行修改。

sensor_calculate_exposure函数中,将增益转换成了可以写入寄存器的整型,因此我们也不要动,在sensor_calculate_exposure之前进行修改,同时又尽可能的少,就选择在sensor_set_exposure函数里的sensor_calculate_exposure对曝光和增益进行修改。

让人吐血的是,这里已经有高通的设置了。

#ifdef ENABLE_MANUAL_EXPOSURE_UPDATE
if(updateExtGain(&ext_real_gain) == SENSOR_SUCCESS)
real_gain = ext_real_gain;

if(updateExtLinecount(&ext_linecount) == SENSOR_SUCCESS)
linecount = ext_linecount;
#endif
static int32_t updateExtGain(float *real_gain)
{
char ext_real_gain[PROPERTY_VALUE_MAX];

RETURN_ERROR_ON_NULL(real_gain);

property_get("persist.camera.sensor.gain", ext_real_gain, "0");
*real_gain = atoi(ext_real_gain);
if (*real_gain > 0) {
SERR("Updated gain: %f", *real_gain);
return SENSOR_SUCCESS;
}

return SENSOR_FAILURE;
}
static int32_t updateExtLinecount(uint32_t *linecount)
{
char ext_linecount[PROPERTY_VALUE_MAX];

RETURN_ERROR_ON_NULL(linecount);

property_get("persist.camera.sensor.linecount", ext_linecount, "0");
*linecount = atoi(ext_linecount);
if (*linecount > 0) {
SERR("Updated linecount: %d", *linecount);
return SENSOR_SUCCESS;
}

return SENSOR_FAILURE;
}

但是将ENABLE_MANUAL_EXPOSURE_UPDATE定义一下,去修改persist.camera.sensor.gainpersist.camera.sensor.linecount,没有发现图像有明显变化。

发现赋值过去的real_gain和linecount两个变量根本没有用到,没有进行设置,于是将

/* 修改前   
lib->sensor_lib_ptr->exposure_func_table.sensor_calculate_exposure(
exposure.real_gain, exposure.linecount, &exp_info, &exposure.hdr_exposure);
*/
lib->sensor_lib_ptr->exposure_func_table.sensor_calculate_exposure(
real_gain, linecount, &exp_info, &exposure.hdr_exposure);

对比660设备的代码,发现660代码进行了设置。

if (lib->sensor_lib_ptr->exposure_func_table.calc_exp_array_type ==
CALC_CUSTOM_IN_LIB &&
lib->sensor_lib_ptr->exposure_func_table.sensor_calculate_exposure) {
lib->sensor_lib_ptr->exposure_func_table.sensor_calculate_exposure(
real_gain, linecount, &exp_info, exposure.s_real_gain);
} else {
sensor_cmn_calc_exposure(lib->sensor_lib_ptr,
real_gain, linecount, &exp_info, exposure.s_real_gain);
}

这里应该是高通之前代码的错误,并没有像手册一样实现手动控制增益的功能。

功能叙述

persist.camera.sensor.gain用来设置手动增益,默认是整数,我改成了浮点数,方便去细调。如果不设置或者设置为0,则增益是自动

persist.camera.sensor.linecount用来设置手动快门,需要是整数,同样不进行设置或者设置为0,快门是自动。

注意: 8056平台不适用这套代码逻辑