#include "basic_include.h" #include "lib/multimedia/msi.h" #include "stream_define.h" #include "lib/heap/av_heap.h" #include "lib/heap/av_psram_heap.h" #include "lib/heap/av_heap.h" #include "lib/heap/av_psram_heap.h" #include "jpg_concat_msi.h" #include "user_work/user_work.h" extern uint8_t get_vpp_w_h(uint16_t *w, uint16_t *h); extern uint8_t get_vpp1_w_h(uint16_t *w, uint16_t *h); // 这里是默认值,最好在project_config.h那里配置 #ifndef JPG_NODE_COUNT #define JPG_NODE_COUNT 10 #endif // 最大支持处理200K的图片 #define MAX_JPG_SIZE (200 * 1024) // data申请空间函数 #define STREAM_MALLOC av_psram_malloc #define STREAM_FREE av_psram_free #define STREAM_ZALLOC av_psram_zalloc // 结构体申请空间函数 #ifdef MORE_SRAM #define STREAM_LIBC_MALLOC av_psram_malloc #define STREAM_LIBC_FREE av_psram_free #define STREAM_LIBC_ZALLOC av_psram_zalloc #else #define STREAM_LIBC_MALLOC av_malloc #define STREAM_LIBC_FREE av_free #define STREAM_LIBC_ZALLOC av_zalloc #endif #define MAX_JPG_CONCAT_RECV (30) extern struct msi *hardware_jpg_msi(uint8_t which_jpg, uint8_t src_from, uint16_t jpg_node_len, uint16_t jpg_node_count); int32_t jpg_concat_msg_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2) { int32_t ret = RET_OK; struct jpg_concat_msi_s *jpg_concat_msg = (struct jpg_concat_msi_s *) msi->priv; switch (cmd_id) { case MSI_CMD_POST_DESTROY: { os_work_cancle2(&jpg_concat_msg->work, 1); fbpool_destroy(&jpg_concat_msg->tx_pool); STREAM_LIBC_FREE(jpg_concat_msg); } break; // 需要关闭,则将关联的msi关闭 case MSI_CMD_PRE_DESTROY: { os_work_cancle2(&jpg_concat_msg->work, 1); if (jpg_concat_msg->jpg_msi) { msi_destroy(jpg_concat_msg->jpg_msi); jpg_concat_msg->jpg_msi = NULL; } } break; case MSI_CMD_GET_RUNNING: { uint32_t running = 0; if (jpg_concat_msg->jpg_msi) { msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_GET_RUNNING, (uint32_t) &running, 0); } *(uint32_t *) param1 = running; } break; case MSI_CMD_JPEG_CONCAT: { uint32_t cmd_self = (uint32_t) param1; uint32_t arg = param2; switch (cmd_self) { case MSI_JPEG_OPEN: { // 如果jpg_msi已经启动,则报错,需要应用层检查是否停止 if (jpg_concat_msg->jpg_msi) { ret = 1; } else { jpg_concat_msg->from = arg; } } break; // 配置JPEG的参数?(这样就是固定size了,如果希望编码不同size,如何解决呢,注册形式?注册两个分辨率,然后由中断去切换?) // 暂时没有实现 // 发命令配置jpg编码的参数 case MSI_JPEG_MSG: { jpg_concat_msg->w = arg >> 16; jpg_concat_msg->h = arg & 0xffff; // msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_MSG, arg); } break; // 启动mjpeg case MSI_JPEG_START: { if (arg) { if (!jpg_concat_msg->jpg_msi) { jpg_concat_msg->jpg_msi = hardware_jpg_msi(jpg_concat_msg->which_jpg, jpg_concat_msg->from, 16 * 1024, jpg_concat_msg->jpg_node_count); if (jpg_concat_msg->jpg_msi) { msi_add_output(jpg_concat_msg->jpg_msi, NULL, msi->name); } } if (jpg_concat_msg->jpg_msi) { msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_SET_DATATAG, jpg_concat_msg->datatag, 0); msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_FROM, jpg_concat_msg->from); msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_SET_GEN420_TYPE, jpg_concat_msg->gen420_type); msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_SET_SCALE1_TYPE, jpg_concat_msg->scale1_type); uint16_t jpg_w, jpg_h; if (jpg_concat_msg->from == VPP_DATA0) { get_vpp_w_h(&jpg_w, &jpg_h); } else if (jpg_concat_msg->from == VPP_DATA1) { get_vpp1_w_h(&jpg_w, &jpg_h); } else { jpg_w = jpg_concat_msg->w; jpg_h = jpg_concat_msg->h; } // 其他的来源,则使用配置参数 msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_SET_TIME, jpg_concat_msg->set_time); // 判断jpg_w?如果超过某些size,就通知jpg的内存池,增加部分内存? msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_MSG, jpg_w << 16 | jpg_h); if (jpg_concat_msg->scale1_flag) { msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_SET_SCALE1_FLAG, 1); } //是否转发取决于启动参数 jpg_concat_msg->force_node = (arg&0x02)?1:0; msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_START, 0); } else { ret = RET_ERR; } } else { if (jpg_concat_msg->jpg_msi) { // scale1的自动模式,所以需要发送命令,让它关闭scale1 if (jpg_concat_msg->scale1_flag) { jpg_concat_msg->scale1_flag = 0; } msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_STOP, 0); if (jpg_concat_msg->auto_free) { // 删除jpg_msi,主要为了释放空间 msi_destroy(jpg_concat_msg->jpg_msi); jpg_concat_msg->jpg_msi = NULL; } } } } break; // 配置mjpeg的源头 case MSI_JPEG_FROM: { jpg_concat_msg->from = arg; } break; case MSI_SET_GEN420_TYPE: { jpg_concat_msg->gen420_type = arg; break; } // 设置scale1为自动模式 case MSI_SET_SCALE1_AUTO_FLAG: { jpg_concat_msg->scale1_flag = arg; } break; case MSI_SET_SCALE1_TYPE: { jpg_concat_msg->scale1_type = arg; break; } case MSI_JPEG_NODE_COUNT: { jpg_concat_msg->jpg_node_count = arg; break; } case MSI_GET_JPEG_MSI: { struct msi **jpg_msi = (struct msi **) param2; // 返回jpg_msi,主要为了外部获取(比如gen420,因为硬件之间耦合,所以这里做了中间层去获取,可能是jpg0可能是jpg1) if (jpg_concat_msg->jpg_msi && jpg_msi) { *jpg_msi = jpg_concat_msg->jpg_msi; } else { ret = RET_ERR; } } break; case MSI_SET_TIME: { jpg_concat_msg->set_time = arg; } break; default: break; } } break; // 接收到数据,唤醒workqueue,去组图,理论有图都要重新组,然后通过output的时候过滤类型,这里也可以先过滤部分类型 case MSI_CMD_TRANS_FB: { struct framebuff *fb = (struct framebuff *) param1; if (jpg_concat_msg->filter_type) { ret = RET_ERR; // 轮询类型是否有一致 uint16_t *each = jpg_concat_msg->filter_type; while (*each) { // 如果一致,返回OK if (*each == fb->stype) { ret = RET_OK; break; } each++; } } // 判断图片大小是否超过一定size,如果是,就不支持拼接(主要是空间问题) // 除了类型外,其他都是克隆发出去,其他模块处理要注意 // data是没有用的,因为是节点的形式 if (ret == RET_OK) { struct jpg_node_s *jpg_priv = (struct jpg_node_s *) fb->priv; if (jpg_priv) { if (jpg_concat_msg->force_node || jpg_priv->jpg_len > MAX_JPG_SIZE) { struct framebuff *send_fb = fb_clone(fb, fb->mtype << 8 | fb->stype, msi); send_fb->len = jpg_priv->jpg_len; send_fb->data = NULL; msi_output_fb(msi, send_fb); ret = RET_OK + 1; } } } if (ret == RET_OK) { os_run_work(&jpg_concat_msg->work); } } break; case MSI_CMD_FREE_FB: { struct framebuff *fb = (struct framebuff *) param1; if (fb->mtype == F_JPG) { if (fb->data) { STREAM_FREE(fb->data); fb->data = NULL; } if (fb->priv) { STREAM_LIBC_FREE(fb->priv); fb->priv = NULL; } fbpool_put(&jpg_concat_msg->tx_pool, fb); // 不需要内核去释放fb ret = RET_OK + 1; } // 那么应该是克隆的,由msi内核管理 else if (fb->clone) { } // 异常情况 else { os_printf(KERN_ERR "%s:%d free fb type err:%d\n", __FUNCTION__, __LINE__, fb->mtype); } } break; case MSI_CMD_SET_DATATAG: { jpg_concat_msg->datatag = param1; } break; default: break; } return ret; } static int32 jpg_concat_msi_work(struct os_work *work) { struct jpg_concat_msi_s *jpg_concat_msg = (struct jpg_concat_msi_s *) work; struct framebuff *fb; struct framebuff *send_fb; uint32_t jpg_len; uint8_t *jpg_psram_space; // 接收图片数据看看 fb = msi_get_fb(jpg_concat_msg->msi, 0); if (fb) { struct jpg_node_s *jpg_priv = (struct jpg_node_s *) fb->priv; if (jpg_priv) { jpg_len = jpg_priv->jpg_len; jpg_psram_space = (uint8_t *) STREAM_MALLOC(jpg_len); if (jpg_psram_space) { sys_dcache_invalid_range((uint32_t *) jpg_psram_space, jpg_len); send_fb = fbpool_get(&jpg_concat_msg->tx_pool, 0, jpg_concat_msg->msi); if (!send_fb) { _os_printf("="); STREAM_FREE(jpg_psram_space); } else { uint32_t offset = 0; uint32_t remain_len = jpg_len; uint32_t cp_len = 0; struct framebuff *tmp_fb = fb; // 开始拷贝数据 while (tmp_fb && remain_len) { if (remain_len > tmp_fb->len) { cp_len = tmp_fb->len; } else { cp_len = remain_len; } // os_printf("cp_len:%X\n",cp_len); hw_memcpy_no_cache(jpg_psram_space + offset, tmp_fb->data, cp_len); offset += cp_len; remain_len -= cp_len; tmp_fb = tmp_fb->next; } send_fb->data = jpg_psram_space; send_fb->len = jpg_len; send_fb->mtype = F_JPG; send_fb->stype = fb->stype; send_fb->time = fb->time; send_fb->datatag = fb->datatag; send_fb->srcID = fb->srcID; _os_printf("V"); struct jpg_node_s *jpg_msg = (struct jpg_node_s *) STREAM_LIBC_MALLOC(sizeof(struct jpg_node_s)); if (jpg_msg) { memcpy(jpg_msg, jpg_priv, sizeof(struct jpg_node_s)); send_fb->priv = (void *) jpg_msg; msi_output_fb(jpg_concat_msg->msi, send_fb); } // 申请不到内存,则不发送?正常不应该申请不到空间 else { os_printf("%s:%d malloc jpg_node_s fail\n", __FUNCTION__, __LINE__); msi_delete_fb(jpg_concat_msg->msi, send_fb); } } } } else { os_printf("%s:%d find jpg msg fail\n", __FUNCTION__, __LINE__); } // 将数据拷贝,然后发送出去 msi_delete_fb(NULL, fb); } return 0; } // 这里还需要做一个事情,就是如果用了VPP_DATA0,应该自动识别分辨率,而不是通过外部传进来 // 暂时自动识别还没有做 struct msi *jpg_concat_msi_init_start(uint32_t jpgid, uint16_t w, uint16_t h, uint16_t *filter_type, uint8_t src_from, uint8_t run) { ASSERT((jpgid == 0) || (jpgid == 1)); const char *msi_name = NULL; uint8_t which_jpg = jpgid; if (jpgid == 0) { msi_name = RS_JPG_CONCAT; } else if (jpgid == 1) { msi_name = RS_JPG_CONCAT1; } uint8_t isnew; struct msi *msi = msi_new(msi_name, MAX_JPG_CONCAT_RECV, &isnew); ASSERT(msi); struct jpg_concat_msi_s *jpg_concat_msg = (struct jpg_concat_msi_s *) msi->priv; if (msi && !jpg_concat_msg) { jpg_concat_msg = (struct jpg_concat_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct jpg_concat_msi_s)); ASSERT(jpg_concat_msg); msi->priv = (void *) jpg_concat_msg; msi->enable = 1; jpg_concat_msg->which_jpg = which_jpg; // 默认使用jpg0 jpg_concat_msg->msi = msi; jpg_concat_msg->msi->action = jpg_concat_msg_msi_action; jpg_concat_msg->msi->priv = (void *) jpg_concat_msg; jpg_concat_msg->msi->enable = 1; jpg_concat_msg->filter_type = filter_type; jpg_concat_msg->from = src_from; jpg_concat_msg->force_node = 0; #ifndef FAST_JPG jpg_concat_msg->auto_free = 1; #else jpg_concat_msg->auto_free = 0; #endif jpg_concat_msg->jpg_node_count = JPG_NODE_COUNT; fbpool_init(&jpg_concat_msg->tx_pool, MAX_JPG_CONCAT_RECV); jpg_concat_msg->w = w; jpg_concat_msg->h = h; // 启动workqueue OS_WORK_INIT(&jpg_concat_msg->work, jpg_concat_msi_work, 0); msi_do_cmd(msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_OPEN, src_from); // 关闭jpg msi_do_cmd(msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0); // 修改jpg参数 msi_do_cmd(msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_MSG, w << 16 | h); // 启动mjpg if (run) { msi_do_cmd(msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 1); } } return msi; }