hdr是Camera中比较常见的功能,展锐平台下hdr的实现是在 oem 下的 cmr_hdr.c 文件中,本篇我们来介绍hdr的流程。
先来看下一个比较关键的结构体,class_hdr ,后面会经常看到对这个结构体的操作
struct class_hdr {
struct ipm_common common;
struct hdr_frame_addr hdr_addr[HDR_CAP_NUM];
cmr_uint mem_size;
cmr_uint width;
cmr_uint height;
cmr_uint is_inited;
cmr_handle hdr_thread;
struct img_addr dst_addr;
ipm_callback reg_cb;
struct ipm_frame_in frame_in;
struct class_hdr_lib_context lib_cxt;
cmr_uint ev_effect_frame_interval;
void *dbg_info_addr;
int32_t dbg_info_size;
};
在往下是函数声明:
//hdr 算法库使用的整体流程
static cmr_int hdr_open(cmr_handle ipm_handle, struct ipm_open_in *in,struct ipm_open_out *out, cmr_handle *class_handle);
static cmr_int hdr_close(cmr_handle class_handle);
static cmr_int hdr_transfer_frame(cmr_handle class_handle,struct ipm_frame_in *in,struct ipm_frame_out *out);
//跑hdr算法的核心
static cmr_int req_hdr_do(cmr_handle class_handle, struct img_addr *dst_addr,struct img_size frame_size);
static cmr_int hdr_arithmetic(cmr_handle class_handle,struct img_addr *dst_addr, cmr_u32 width,cmr_u32 height);
//保存帧
static cmr_int hdr_save_frame(cmr_handle class_handle, struct ipm_frame_in *in);
static cmr_int req_hdr_save_frame(cmr_handle class_handle,struct ipm_frame_in *in);
//hdr封装的对算法库接口的调用
static cmr_int hdr_sprd_adapter_init(struct class_hdr *hdr_handle);
static cmr_int hdr_sprd_adapter_deinit(struct class_hdr *hdr_handle);
static cmr_int hdr_sprd_adapter_fast_stop(struct class_hdr *hdr_handle);
static cmr_int hdr_sprd_adapter_process(struct class_hdr *hdr_handle,sprd_hdr_cmd_t cmd, void *param);
关于 保存帧 的,我们在上一篇介绍 3dnr 的时候也提到了,3dnr是5帧数据送到算法中去跑的,本篇介绍的hdr是 3帧 数据跑的。跑算法前需要收集的数据帧数是算法库的要求,具体是在 cmr_common.h 中定义的。
#define CAP_4IN1_NUM 5
#define HDR_CAP_NUM 3
#define PRE_3DNR_NUM 2
#define CAP_3DNR_NUM 5
cmr_common.h 中还定义了许多libcamera下的宏和结构体,一般我会在当前算法的 .h 下面搜定义,搜不到就去cmr_common.h 中去搜,有机会可以在专门介绍下cmr_common.h中的那些关键定义。
好了,现在我们知道 hdr 是需要保存3帧数据的,从上面的函数声明开始看流程吧。
hdr_open
打开hdr
1,构建 struct class_hdr *hdr_handle = NULL;
class_hdr 结构体我们在开篇的时候就介绍了,关于hdr的数据都在这个结构体里面的了,所以在最开始open的时候就创建并且初始化了部分数据
hdr_handle = (struct class_hdr *)malloc(sizeof(struct class_hdr));
hdr_handle->common.ipm_cxt = (struct ipm_context_t *)ipm_handle;
hdr_handle->common.class_type = IPM_TYPE_HDR;
hdr_handle->common.receive_frame_count = 0;
hdr_handle->common.save_frame_count = 0;
hdr_handle->common.ops = &hdr_ops_tab_info;
hdr_handle->mem_size = size;
CMR_LOGD("hdr_handle->mem_size = 0x%lx", hdr_handle->mem_size);
hdr_handle->height = in->frame_size.height;
hdr_handle->width = in->frame_size.width;
hdr_handle->reg_cb = in->reg_cb;
2, hdr_thread_create
创建hdr的thred 消息处理对象
ret = cmr_thread_create(&class_handle->hdr_thread,
CAMERA_HDR_MSG_QUEUE_SIZE, hdr_thread_proc,
(void *)class_handle, "hdr");
3,hdr_sprd_adapter_init
将上述创建的 class_hdr 结构体传入算法接口中去初始化算法
ret = hdr_sprd_adapter_init(hdr_handle);
static cmr_int hdr_sprd_adapter_init(struct class_hdr *hdr_handle) {
//获取hdr的效果参数
sprd_hdr_init_param_t hdr_param;
ret = ipm_in->ipm_isp_ioctl(oem_handle, COM_ISP_GET_HDR_PARAM,
&isp_cmd_parm);
hdr_param.tuning_param = isp_cmd_parm.hdr_param.param_ptr;
hdr_param.tuning_param_size= isp_cmd_parm.hdr_param.param_size;
//将hdr效果参数传给hdr算法库去做init
hdr_handle->lib_cxt.lib_handle =
sprd_hdr_adpt_init(max_width, max_height, (void *)&hdr_param);
}
hdr_close
close的操作与open是相对的,open的时候打开或者创建了什么,close的时候就要去关闭、销毁什么
1,hdr_sprd_adapter_fast_stop(hdr_handle);
fast_stop 很好理解,就是停止hdr算法,其对应的算法接口如下:
ret = sprd_hdr_adpt_ctrl(hdr_handle->lib_cxt.lib_handle,SPRD_HDR_FAST_STOP_CMD, NULL);
2,hdr_thread_destroy(hdr_handle)
thread_destory也是常规的操作,销毁在hdr_open的时候创建的消息处理器
static cmr_int hdr_thread_destroy(struct class_hdr *class_handle) {
if (class_handle->is_inited) {
ret = cmr_thread_destroy(class_handle->hdr_thread);
class_handle->hdr_thread = 0;
class_handle->is_inited = 0;
}
}
3, hdr_sprd_adapter_deinit(hdr_handle);
这里也是直接调用算法接口的deinit
ret = sprd_hdr_adpt_deinit(hdr_handle->lib_cxt.lib_handle);
hdr_sprd_adapter_deinit 和 sprd_hdr_adpt_deinit ,名字很像,看着有点晕哈,可以这样理解,以hdr开头的是当前cmr_hdr定义的函数,以 sprd 开头的是算法库的统一接口,我们在上篇介绍 3dnr 的文章中也能看到 sprd 开头的函数,比如 sprd_mfnr_adpt_init。
hdr_transfer_frame
此函数会被调用3次,每次传入一帧数据,类似上面的 3dnr 中 threednr_transfer_frame 会被调用5次。
我们分3部分来讲:
- 打印帧数据的地址
- 保存帧数据
- 需要的3帧数据都拿到之后的流程
static cmr_int hdr_transfer_frame(cmr_handle class_handle,struct ipm_frame_in *in,struct ipm_frame_out *out){
addr = &in->dst_frame.addr_vir;
//1 打印帧数据的地址
CMR_LOGD("ipm_frame_in.private_data 0x%lx before (in->dst)addr 0x%lx (in->src)addr 0x%lx",
(cmr_int)in->private_data, addr->addr_y, (&in->src_frame.addr_vir)->addr_y);
//2 保存帧数据
ret = req_hdr_save_frame(class_handle, in);
//3 需要的3帧数据都拿到之后的流程
if (hdr_handle->common.save_frame_count ==
(HDR_CAP_NUM + hdr_handle->ev_effect_frame_interval)) {
CMR_LOGD("HDR enable = %d", hdr_enable);
cmr_bzero(&out->dst_frame, sizeof(struct img_frm));
sensor_ioctl = hdr_handle->common.ipm_cxt->init_in.ipm_sensor_ioctl;
isp_ioctl = hdr_handle->common.ipm_cxt->init_in.ipm_isp_ioctl;
oem_handle = hdr_handle->common.ipm_cxt->init_in.oem_handle;
hdr_handle->frame_in = *in;
ret = req_hdr_do(class_handle, addr, size); // 关键点*****
if (ret != CMR_CAMERA_SUCCESS) {
CMR_LOGE("req_hdr_do fail");
}
CMR_LOGE("after addr 0x%lx", addr->addr_y);
out->dst_frame = in->dst_frame;
out->private_data = in->private_data;
struct sensor_exp_info sensor_info;
sensor_id = hdr_handle->common.ipm_cxt->init_in.sensor_id;
get_sensor_info = hdr_handle->common.ipm_cxt->init_in.get_sensor_info;
get_sensor_info(oem_handle, sensor_id, &sensor_info);
if (CAM_IMG_FMT_BAYER_MIPI_RAW == sensor_info.image_format) {
isp_param1.cmd_value = hdr_enable;
ret = isp_ioctl(oem_handle, COM_ISP_SET_HDR, (void *)&isp_param1);
} else {
sn_param.cmd_value = OEM_EV_LEVEL_2;
ret =
sensor_ioctl(oem_handle, COM_SN_SET_HDR_EV, (void *)&sn_param);
}
if (ret) {
CMR_LOGE("HDR failed to set ev.");
}
}
}
1. 打印帧数据的地址
我们看到log显示3帧数的 in->src不同, in->dst 都是一样的,并且第一帧的src 与 dst 是相同的。
Line 99341: 06-06 12:31:33.356 471 3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010 before (in->dst)addr 0xe4e1e000 (in->src)addr 0xe4e1e000
Line 99507: 06-06 12:31:33.422 471 3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010 before (in->dst)addr 0xe4e1e000 (in->src)addr 0xe3b8c000
Line 99605: 06-06 12:31:33.467 471 3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010 before (in->dst)addr 0xe4e1e000 (in->src)addr 0xe28fa000
2. 保存帧数据
req_hdr_save_frame,以req开头的都是发送一个message,关键逻辑是在message的处理函数 hdr_save_frame中。
static cmr_int hdr_save_frame(cmr_handle class_handle,struct ipm_frame_in *in) {
hdr_handle->lib_cxt.ev[0] = in->ev[0];
hdr_handle->lib_cxt.ev[1] = in->ev[1];
hdr_handle->lib_cxt.hdr_callback = in->hdr_callback;
hdr_handle->lib_cxt.ae_exp_gain_info = in->ae_exp_gain_info;
if (hdr_handle->mem_size >= in->src_frame.buf_size &&
NULL != (void *)in->src_frame.addr_vir.addr_y) {
hdr_handle->hdr_addr[frame_sn].addr_phy.addr_y = in->src_frame.addr_phy.addr_y;
hdr_handle->hdr_addr[frame_sn].addr_phy.addr_u = in->src_frame.addr_phy.addr_u;
hdr_handle->hdr_addr[frame_sn].addr_phy.addr_v = in->src_frame.addr_phy.addr_v;
hdr_handle->hdr_addr[frame_sn].addr_vir.addr_y = in->src_frame.addr_vir.addr_y;
hdr_handle->hdr_addr[frame_sn].addr_vir.addr_u = in->src_frame.addr_vir.addr_u;
hdr_handle->hdr_addr[frame_sn].addr_vir.addr_v = in->src_frame.addr_vir.addr_v;
hdr_handle->hdr_addr[frame_sn].fd = in->src_frame.fd;
}
}
hdr_save_frame的主要逻辑是将参数in->src_frame的地址保存到 hdr_handle->hdr_addr 数组中。
3. 需要的3帧数据都拿到之后的流程
save的逻辑走完了,在回到 hdr_transfer_frame 中。有一个 if 判断
if (hdr_handle->common.save_frame_count == (HDR_CAP_NUM + hdr_handle->ev_effect_frame_interval))
HDR_CAP_NUM 宏定义 = 3
hdr_handle->ev_effect_frame_interval = 0
即当 save_frame_count == 3 ,该 if 条件满足。
if 满足会调用 req_hdr_do,又是一个req开头的函数,显然是有对应的实现逻辑,即 hdr_arithmetic
hdr_arithmetic 的流程:
3.1 取出保存的3帧数据的虚拟地址
static cmr_int hdr_arithmetic(cmr_handle class_handle,struct img_addr *dst_addr, cmr_u32 width,cmr_u32 height) {
//这是我们在 hdr_save_frame 中保存的3帧数据的虚拟地址
temp_addr0 = (cmr_u8 *)hdr_handle->hdr_addr[0].addr_vir.addr_y;
temp_addr1 = (cmr_u8 *)hdr_handle->hdr_addr[1].addr_vir.addr_y;
temp_addr2 = (cmr_u8 *)hdr_handle->hdr_addr[2].addr_vir.addr_y;
}
3.2 构建 sprd_hdr_param 参数
sprd_hdr_param 参数
取 hdr_handle->hdr_addr[0] 和 hdr_handle->hdr_addr[1] 的地址为 input 参数
取 hdr_handle->hdr_addr[0] 为 output 参数
if ((NULL != temp_addr0) && (NULL != temp_addr1) && (NULL != temp_addr2)) {
#ifndef CONFIG_SPRD_HDR_LIB_VERSION_2
ret = HDR_Function(temp_addr0, temp_addr2, temp_addr1, temp_addr0,
height, width, p_format);
#else
sprd_hdr_param.input[0].format = SPRD_CAMALG_IMG_NV21;
sprd_hdr_param.input[0].addr[0] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_y;
sprd_hdr_param.input[0].addr[1] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_u;
sprd_hdr_param.input[0].addr[2] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_v;
sprd_hdr_param.input[0].ion_fd = hdr_handle->hdr_addr[0].fd;
sprd_hdr_param.input[0].offset[0] = 0;
sprd_hdr_param.input[0].offset[1] = width * height;
sprd_hdr_param.input[0].width = width;
sprd_hdr_param.input[0].height = height;
sprd_hdr_param.input[0].stride = width;
sprd_hdr_param.input[0].size = width * height * 3 / 2;
sprd_hdr_param.ev[0] = hdr_handle->lib_cxt.ev[0];
sprd_hdr_param.detect_out = hdr_handle->lib_cxt.hdr_callback;
CMR_LOGI("sprd_hdr_param.callback %p", sprd_hdr_param.callback);
sprd_hdr_param.input[1].format = SPRD_CAMALG_IMG_NV21;
sprd_hdr_param.input[1].addr[0] = (void *)hdr_handle->hdr_addr[1].addr_vir.addr_y;
sprd_hdr_param.input[1].addr[1] = (void *)hdr_handle->hdr_addr[1].addr_vir.addr_u;
sprd_hdr_param.input[1].addr[2] = (void *)hdr_handle->hdr_addr[1].addr_vir.addr_v;
sprd_hdr_param.input[1].ion_fd = hdr_handle->hdr_addr[1].fd;
sprd_hdr_param.input[1].offset[0] = 0;
sprd_hdr_param.input[1].offset[1] = width * height;
sprd_hdr_param.input[1].width = width;
sprd_hdr_param.input[1].height = height;
sprd_hdr_param.input[1].stride = width;
sprd_hdr_param.input[1].size = width * height * 3 / 2;
sprd_hdr_param.ev[1] = hdr_handle->lib_cxt.ev[1];
sprd_hdr_param.output.format = SPRD_CAMALG_IMG_NV21;
sprd_hdr_param.output.addr[0] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_y;
sprd_hdr_param.output.addr[1] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_u;
sprd_hdr_param.output.addr[2] = (void *)hdr_handle->hdr_addr[0].addr_vir.addr_v;
sprd_hdr_param.output.ion_fd = hdr_handle->hdr_addr[0].fd;
sprd_hdr_param.output.offset[0] = 0;
sprd_hdr_param.output.offset[1] = width * height;
sprd_hdr_param.output.width = width;
sprd_hdr_param.output.height = height;
sprd_hdr_param.output.stride = width;
sprd_hdr_param.output.size = width * height * 3 / 2;
ret = hdr_sprd_adapter_process(hdr_handle, SPRD_HDR_PROCESS_CMD,
(void *)&sprd_hdr_param);
3.3 执行 hdr_sprd_adapter_process
case SPRD_HDR_PROCESS_CMD:
ret =
sprd_hdr_adpt_ctrl(hdr_handle->lib_cxt.lib_handle,
SPRD_HDR_PROCESS_CMD, (sprd_hdr_param_t *)param);
break;
3.4 获取hdr合成结果的 exit 信息
ret = sprd_hdr_adpt_ctrl(hdr_handle->lib_cxt.lib_handle, SPRD_HDR_GET_EXIF_INFO_CMD,
(void *)&get_exif_param);
hdr_arithmetic 的流程到这里就完成了,主要是取出我们之前保存的帧数据地址,给hdr算法去跑,算法跑完之后在去拿exif信息。
注:不知道大家有没有注意到,我们在hdr_save_frame中保存的是3帧数据,但是这里向算法里面送的时候只取了2帧数据,hdr_handle->hdr_addr[2]没有用到,不清楚平台这样设计的含义是什么。
hdr拍照的完整log 如下,与我们上述介绍的代码流程也是一致的:
open
Line 98694: 06-06 12:31:33.107 471 3159 D cmr_hdr : 174, hdr_open: in->frame_size.width = 4160,in->frame_size.height = 3120
Line 98695: 06-06 12:31:33.107 471 3159 D cmr_hdr : 184, hdr_open: hdr_handle->mem_size = 0x1291200
Line 98740: 06-06 12:31:33.134 471 3159 D cmr_hdr : 913, hdr_sprd_adapter_init: max width*height = [4160 * 3120]
Line 98749: 06-06 12:31:33.134 471 3159 E cmr_hdr : 920, hdr_sprd_adapter_init: failed to get isp hdr param 1
Line 98752: 06-06 12:31:33.134 471 3159 D cmr_hdr : 927, hdr_sprd_adapter_init: hdr_param 0x0,size 0
Line 98845: 06-06 12:31:33.141 471 3352 D cmr_hdr : 696, hdr_thread_proc: HDR pre_proc
Line 98846: 06-06 12:31:33.141 471 3352 D cmr_hdr : 438, hdr_frame_proc: HDR enable = 1 image_format = 48
第一帧
Line 99341: 06-06 12:31:33.356 471 3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010 before (in->dst)addr 0xe4e1e000 (in->src)addr 0xe4e1e000
Line 99342: 06-06 12:31:33.356 471 3352 D cmr_hdr : 700, hdr_thread_proc: HDR save frame
Line 99343: 06-06 12:31:33.356 471 3352 D cmr_hdr : 643, hdr_save_frame: ev: -0.800000, 0.800000, 0xea369278,0xea379870
Line 99344: 06-06 12:31:33.356 471 3352 D cmr_hdr : 654, hdr_save_frame: HDR frame_sn 0, y_addr 0xe4e1e000 save_frame_count 1 ev_effect_frame_interval 0
第二帧
Line 99507: 06-06 12:31:33.422 471 3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010 before (in->dst)addr 0xe4e1e000 (in->src)addr 0xe3b8c000
Line 99508: 06-06 12:31:33.423 471 3352 D cmr_hdr : 700, hdr_thread_proc: HDR save frame
Line 99509: 06-06 12:31:33.423 471 3352 D cmr_hdr : 643, hdr_save_frame: ev: -0.800000, 0.800000, 0xea369278,0xea379870
Line 99510: 06-06 12:31:33.423 471 3352 D cmr_hdr : 654, hdr_save_frame: HDR frame_sn 1, y_addr 0xe3b8c000 save_frame_count 2 ev_effect_frame_interval 0
第三帧
Line 99605: 06-06 12:31:33.467 471 3159 D cmr_hdr : 328, hdr_transfer_frame: ipm_frame_in.private_data 0xeed40010 before (in->dst)addr 0xe4e1e000 (in->src)addr 0xe28fa000
Line 99606: 06-06 12:31:33.467 471 3352 D cmr_hdr : 700, hdr_thread_proc: HDR save frame
Line 99607: 06-06 12:31:33.467 471 3352 D cmr_hdr : 643, hdr_save_frame: ev: -0.800000, 0.800000, 0xea369278,0xea379870
Line 99608: 06-06 12:31:33.467 471 3352 D cmr_hdr : 654, hdr_save_frame: HDR frame_sn 2, y_addr 0xe28fa000 save_frame_count 3 ev_effect_frame_interval 0
三帧数据拿到之后的处理
Line 99609: 06-06 12:31:33.467 471 3159 D cmr_hdr : 338, hdr_transfer_frame: HDR enable = 0
Line 99610: 06-06 12:31:33.468 471 3352 D cmr_hdr : 712, hdr_thread_proc: out private_data 0xeed40010
Line 99611: 06-06 12:31:33.468 471 3352 D cmr_hdr : 715, hdr_thread_proc: CMR_EVT_HDR_START addr 0xe4e1e000 4160 3120
Line 99612: 06-06 12:31:33.468 471 3159 E cmr_hdr : 348, hdr_transfer_frame: after addr 0xe4e1e000
Line 99613: 06-06 12:31:33.468 471 3352 D cmr_hdr : 716, hdr_thread_proc: HDR thread proc start
Line 99615: 06-06 12:31:33.468 471 3352 D cmr_hdr : 528, hdr_arithmetic: width 4160, height 3120 temp_addr0 0xe4e1e000 temp_addr1 0xe3b8c000 temp_addr2 0xe28fa000
Line 99616: 06-06 12:31:33.468 471 3352 I cmr_hdr : 549, hdr_arithmetic: sprd_hdr_param.callback 0x0
Line 102466: 06-06 12:31:35.357 471 3352 I cmr_hdr : 584, hdr_arithmetic: exif_info 0xf61c1180
Line 102468: 06-06 12:31:35.358 471 3352 I cmr_hdr : 592, hdr_arithmetic: exif_info size = 120
Line 102469: 06-06 12:31:35.358 471 3352 E cmr_hdr : 607, hdr_arithmetic: dst_addr->addr_y 0xe4e1e000 temp_addr0 0xe4e1e000
Line 102472: 06-06 12:31:35.358 471 3352 D cmr_hdr : 614, hdr_arithmetic: hdr done.
//hdr完成,close
Line 102473: 06-06 12:31:35.358 471 3352 D cmr_hdr : 732, hdr_thread_proc: HDR thread proc done
Line 102474: 06-06 12:31:35.358 471 3352 D cmr_hdr : 738, hdr_thread_proc: hdr_dbg_info_addr 0xf61c1180 hdr_dbg_info_size 120
Line 103027: 06-06 12:31:35.455 471 3124 D cmr_hdr : 231, hdr_close: E
关于hdr算法的流程就介绍到这里