#ifdef SUPPORT_MS_EXTENSIONS #pragma bss_seg(".usb.data.bss") #pragma data_seg(".usb.data") #pragma code_seg(".usb.text") #pragma const_seg(".usb.text.const") #pragma str_literal_override(".usb.text.const") #endif #include "system/includes.h" /* #include "server/usb_server.h" */ #include "app_config.h" /* #include "action.h" */ /* #include "storage_device.h" */ /* #include "user_isp_cfg.h" */ /* #include "server/video_server.h" */ #include "uvc_device.h" #include "usb_config.h" #include "host_uvc.h" #include "video_ioctl.h" #include "video.h" #include "videobuf.h" #include "sdk_config.h" #include "debug.h" /* #define LOG_TAG_CONST USB */ #define LOG_TAG "[USB]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE /* #define LOG_DUMP_ENABLE */ #define LOG_CLI_ENABLE #define TIME_UVC_DEBUG 1//uvc时间debug #define USER_UVC_DEBUG 0//用于debug uvc一帧数据是否有问题的debug。主要是加上crc和写SD卡做校验 #define LOG_SEM_ENABLE 0// 信号量debug #if LOG_SEM_ENABLE #define sem_putchar(x) putchar(x) #define sem_printf printf #define sem_put_buf(x,len) put_buf(x,len) #else #define sem_putchar(...) #define sem_printf(...) #define sem_put_buf(...) #endif // LOG_SEM_ENABLE #define JLUVC_JPEG_SIZE_SET_ENABLE 0 //是否限制jpeg大小 #define UVC_JPEG_BUF_SIZE (30 * 1024) //限制jpeg 大小为30KB,(need enable) extern void jpeg_dec_flush(void); u8 *jluvc_jpeg_buf = NULL; int jluvc_jpeg_buf_len = 0; static u8 jluvc_start = 0; static OS_SEM jluvc_sem; struct uvc_cam_jpeg_t { volatile u8 streamon; u16 cur_width; u16 cur_height; void (*ui_refresh_cb)(void); }; struct uvc_cam_jpeg_t _uvc_cam_jpeg = {0}; #define __this (&_uvc_cam_jpeg) static int find_reso(u16 ref_width, u16 ref_height, struct uvc_capability *uvc_cap) { u8 find_width = 0; u8 find_height = 0; int w_index = 0; int h_index = 0; int i; while (h_index < uvc_cap->reso_num) { for (i = w_index; i < uvc_cap->reso_num; i++) { if (ref_width == uvc_cap->reso[i].width) { find_width = 1; w_index = i; } } for (i = h_index; i < uvc_cap->reso_num; i++) { if (ref_height == uvc_cap->reso[i].height) { find_height = 1; h_index = i; break; } } if (find_width && find_height) { if (uvc_cap->reso[h_index].width == ref_width) { return h_index; } else if (uvc_cap->reso[w_index].height == ref_height) { return w_index; } else { w_index++; h_index++; continue; } } else if (find_height) { return h_index; } else if (find_width) { return w_index; } else { return 0; } } return 0; } /** * @brief 设置JLUVC运行状态 * * 设置JLUVC的运行状态,0表示未运行,1表示运行中 * * @param status 要设置的运行状态 */ void jlset_uvc_status(u8 status) { log_debug("set_uvc_status: %d\n", status); jluvc_start = status; } /** * @brief 获取JLUVC运行状态 * * 返回JLUVC当前的运行状态,0表示未运行,1表示运行中 * * @return u8 JLUVC当前的运行状态 */ u8 jlget_uvc_status(void) { return jluvc_start; } /** * @brief 创建JLUVC信号量 * * 初始化并创建JLUVC使用的信号量资源 */ void jluvc_sem_create(void) { sem_printf("-----%s>>\n", __func__); os_sem_create(&jluvc_sem, 0); } /** * @brief 等待JLUVC信号量 * * 阻塞当前线程,直到JLUVC信号量的计数大于0。 * 当信号量计数大于0时,线程被唤醒并继续执行。 */ void jluvc_sem_pend(void) { sem_printf("-----%s>\n", __func__); os_sem_pend(&jluvc_sem, 0); } /** * @brief 释放JLUVC信号量 * * 通过增加信号量计数来释放信号量,允许等待该信号量的线程继续执行 */ void jluvc_sem_post(void) { sem_printf("-----%s>>\n", __func__); os_sem_post(&jluvc_sem); } /** * @brief 删除JLUVC信号量 * * 释放并删除JLUVC使用的信号量资源 */ void jluvc_sem_del(void) { sem_printf("-----%s>>\n", __func__); os_sem_del(&jluvc_sem, 0); } /** * @brief 初始化JLUVC缓冲区 * * 分配指定大小的内存块用于JLUVC操作 * * @param size 要分配的内存大小(字节) * @return 成功时返回指向分配内存的指针,失败时返回NULL */ void *jluvc_buf_init(u16 size) { log_debug("-----%s>>\n", __func__); void *ptr = NULL; if (ptr == NULL) { ptr = malloc(size); } return ptr; } /** * @brief 释放JLUVC缓冲区 * * 释放之前分配的JPEG缓冲区内存,并将缓冲区指针置为NULL */ void jluvc_buf_deinit(void) { log_debug("-----%s>>\n", __func__); if (jluvc_jpeg_buf) { free(jluvc_jpeg_buf); jluvc_jpeg_buf = NULL; } } /** * @brief 刷新JLUVC界面显示 * * 该函数用于向UI任务发送刷新消息,触发UVC JPEG测试界面的刷新操作。 * 通过向"ui"任务发送Q_CALLBACK类型消息来实现界面更新。 * * @note 该函数通过任务间通信机制实现界面刷新,不直接操作UI组件 */ static void jluvc_ui_reflush(void) { if (__this && __this->ui_refresh_cb) { __this->ui_refresh_cb(); } } int jluvc_set_refresh_cb(void (*cb)(void)) { if (!__this) { log_error("%s not find\n", __func__); return -1; } __this->ui_refresh_cb = cb; return 0; } /** * @brief UVC JPEG接收任务处理函数 * 该函数负责初始化UVC设备,接收JPEG格式的视频流数据,并进行处理。 * 主要功能包括:设备初始化、视频流控制、JPEG数据接收和处理等。 * @param arg 任务参数指针,未使用 * @return 无返回值 * @note 该任务为循环执行的任务,在系统退出时会进行资源释放 */ static void uvc_jpeg_recv_task(void *arg) { void *fd = NULL; struct uvc_reqbufs breq = {0}; struct video_buffer b = {0}; b.timeout = 50; b.noblock = 0; struct uvc_capability uvc_cap; int err = -1; int msg[32]; u32 i = 0; if (dev_online("uvc")) { const struct video_platform_data *video_data = NULL; struct uvc_platform_data *uvc_info = NULL; struct video_var_param_info info; struct video_format video_f; char video_name[6] = {0}; sprintf(video_name, "video%d", 0); extern void *device_platform_data_get(const char *name); video_data = device_platform_data_get(video_name); if (video_data && video_data->data) { for (int i = 0; i < video_data->num; i++) { if (video_data->data[i].tag == VIDEO_TAG_UVC) { uvc_info = video_data->data[i].data; break; } } } video_f.uvc_id = 0; info.f = &video_f; fd = dev_open("uvc", (void *)&info); //指定uvc设备0 if (fd) { dev_ioctl(fd, UVCIOC_QUERYCAP, (unsigned int)&uvc_cap); for (int i = 0; i < uvc_cap.reso_num; i++) { log_debug("uvc support [%d] %d x %d", i, uvc_cap.reso[i].width, uvc_cap.reso[i].height); } u8 fmt = uvc_cap.fmt; int reso_index = find_reso(uvc_info->width, uvc_info->height, &uvc_cap); u16 width = uvc_cap.reso[reso_index].width; u16 height = uvc_cap.reso[reso_index].height; log_debug("fmt :%d reso : %d, ref size : %d x %d, size : %d x %d.\n", fmt, reso_index, uvc_info->width, uvc_info->height, width, height); dev_ioctl(fd, UVCIOC_SET_CAP_SIZE, (unsigned int)(reso_index + 1)); breq.buf = NULL; /* breq.size = UVC_JPEG_BUF_SIZE; */ breq.size = uvc_info->mem_size;; /* breq.size = 300*1024;; */ err = dev_ioctl(fd, UVCIOC_REQBUFS, (unsigned int)&breq); if (!err) { dev_ioctl(fd, UVCIOC_STREAM_ON, 0); } else { err = -1; puts("uvc0 video open err1--------------------\n"); } } } jluvc_sem_create();//create sem u8 usr_wdt_cnt = 0;//定时喂狗 while (1) { os_taskq_accept(ARRAY_SIZE(msg), msg); /* os_taskq_pend(NULL, msg, ARRAY_SIZE(msg)); */ if (msg[0] == Q_MSG) { if (msg[1] == 0xaa) { if (os_task_del_req(OS_TASK_SELF) == OS_TASK_DEL_REQ) { //准备退出线程 if (fd) { dev_ioctl(fd, UVCIOC_STREAM_OFF, 0); dev_close(fd); } os_task_del_res(OS_TASK_SELF); } } } if (usr_wdt_cnt++ >= 20) { usr_wdt_cnt = 0; wdt_clear(); } //从uvc 缓存中请求一帧图像 if (fd) { err = dev_ioctl(fd, UVCIOC_DQBUF, (unsigned int)&b); } if (err || b.len <= 0) { //请求失败 os_time_dly(1); continue; } //请求成功 //TODO putchar('R'); log_debug("[msg]>>>>>>>>>>>b.len=%d", b.len); #if TIME_UVC_DEBUG static u32 last_time = 0; static u32 curr_time = 0; curr_time = jiffies_msec(); printf("[UVC]T = %d ms\n", curr_time - last_time); #endif //copy current stream to ui #if JLUVC_JPEG_SIZE_SET_ENABLE if (b.len > UVC_JPEG_BUF_SIZE) { continue; } /* if (!jluvc_jpeg_buf && (b.len <= UVC_JPEG_BUF_SIZE)) { //具体要不要限制jpeg的buff大小在这里限制 */ /* jluvc_jpeg_buf = jluvc_buf_init(b.len); */ /* } */ #else /* if (!jluvc_jpeg_buf) { */ /* jluvc_jpeg_buf = jluvc_buf_init(b.len); */ /* } */ #endif //endif JLUVC_JPEG_SIZE_SET_ENABLE /* if (jluvc_jpeg_buf [> && (b.len < UVC_JPEG_BUF_SIZE)<]) { //UI刷新数据 */ if (1 /* && (b.len < UVC_JPEG_BUF_SIZE)*/) { //UI刷新数据 jlset_uvc_status(1); #if USER_UVC_DEBUG extern u16 CRC16(const void *ptr, u32 len); void *pp = (void *)b.baddr; int len = b.len; FILE *fp; s8 path[64] = {0}; u16 wr_crc = CRC16((u8 *)pp, len); u16 rd_crc = 0; sprintf(path, "storage/sd0/C/a%d.jpg", i); fp = fopen(path, "wb+"); if (fp) { printf("@@@path=%s\n", path); fwrite(pp, len, 1, fp); i++; fclose(fp); } /* free(pp); */ void *rp = fopen(path, "rb"); if (rp) { printf("@@@path=%s\n", path); void *rd_buf = malloc(len); fread(rd_buf, len, 1, rp); rd_crc = CRC16((u8 *)rd_buf, len); fclose(rp); free(rd_buf); rd_buf = NULL; } printf("wr_crc=%d, rd_crc=%d\n", wr_crc, rd_crc); #endif//endif /* memset(jluvc_jpeg_buf, 0, b.len); */ /* memcpy(jluvc_jpeg_buf, (u8 *)b.baddr, b.len); */ /* jluvc_jpeg_buf_len = b.len; */ #if TCFG_UI_ENABLE int jljpeg_stream_src_data_copy(u8 * buf, int size); if (!jljpeg_stream_src_data_copy((u8 *)b.baddr, b.len)) { jluvc_ui_reflush(); } #endif /* jluvc_sem_post(); */ } #if TIME_UVC_DEBUG last_time = curr_time; #endif // put_buf((u8 *)b.baddr+ b.len -512 ,512); /* if (f) { */ /* fwrite(f, b.baddr, b.len); */ /* } */ #if UVC_JPG_DATA_WRITE2SD /// fwrite 一帧数据到SD卡中。 void *pp = b.baddr; int len = b.len; FILE *fp; u8 path[64] = {0}; sprintf(path, "storage/sd0/C/avid_%d.jpg", i); fp = fopen(path, "wb+"); if (fp) { printf("@@@path=%s\n", path); fwrite(pp, len, 1, fp); i++; fclose(fp); } free(pp); #endif //endif UVC_JPG_DATA_WRITE2SD dev_ioctl(fd, UVCIOC_QBUF, (unsigned int)&b); } } int video_uvc_jpeg_stream_open(u16 width, u16 height) { log_debug("video_uvc_jpeg_stream_open\n"); if (__this->streamon) { return -1; } memset(__this, 0x00, sizeof(struct uvc_cam_jpeg_t)); __this->cur_width = width; __this->cur_height = height; __this->streamon = 1; return os_task_create(uvc_jpeg_recv_task, NULL, 20, 1024, 32, "uvc_jpeg_recv"); } int video_uvc_jpeg_stream_close() { while (OS_TASK_NOT_EXIST != os_task_del_req((const char *)"uvc_jpeg_recv")) { os_taskq_post_msg((const char *)"uvc_jpeg_recv", 1, 0xaa); os_time_dly(1); } jluvc_sem_del(); jluvc_buf_deinit(); __this->streamon = 0; return 0; }