/* * Copyright (C) 2021-2022 Alibaba Group Holding Limited * Author: LuChongzhi ; Minxiong Tian * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include "csi_camera_dev_api.h" /* macros for share memory and semaphore */ #define SHMKEY (key_t)0x39126 #define SEMKEY (key_t)0x49126 #define IFLAGS (IPC_CREAT|IPC_EXCL) #define LOG_LEVEL 2 #define LOG_PREFIX "camera_demo1" #include #include #include #ifdef PLATFORM_SIMULATOR #include "apputilities.h" #endif #define PAGE_SIZE 1024 #define PAGE_ALIGN(addr) (void *)(((int)(addr)+PAGE_SIZE-1)&~( PAGE_SIZE-1)) /* data store in share-memory */ struct databuf { int d_buf[2]; }; static void dump_camera_meta(csi_frame_ex_s *frame, int idx); static void* save_camera_img(void *); static void* save_camera_stream(void *); static int set_properties(csi_cam_handle_t cam_handle, char *pArgs); static enum csi_pixel_fmt parse_format(char *fmt); // static void handle_dma_buf(csi_frame_s *framei, int cid); //extern int csi_camera_frame_unlock(csi_cam_handle_t cam_handle, csi_frame_s *frame); extern int csi_camera_put_frame(csi_frame_ex_s *frame); static void i_save(csi_frame_ex_s *framei, int cid, FILE *fp, int vmode); static void parse_fmt_res(int cindx); static void getseg(struct databuf* *pdata); static int getsem(); static void remove_s(); static void writer(struct databuf *buf, int index, int value); static void reader(struct databuf *buf, int index, int * rv); static int pr_error(char *mess); extern int csi_camera_set_pp_path_param(csi_cam_handle_t cam_handle, uint16_t line_num,uint16_t buf_mode); extern void *vi_plink_create(csi_camera_channel_cfg_s *chn_cfg); extern void vi_plink_release(void * plink); extern void display_camera_frame(void * plink, csi_frame_ex_s *frame); #define TEST_DEVICE_NAME "/dev/video0" #define CSI_CAMERA_TRUE 1 #define CSI_CAMERA_FALSE 0 #define chnMAX 8 #define fmtMAX 19 csi_cam_handle_t cam_handle; char *prog_name; int Soff = 0; static char device[CSI_CAMERA_NAME_MAX_LEN]; /* target video device like /dev/video0 */ static char path[128] = "./"; /* output path */ static int channelIdx[chnMAX]; /* channel information like 8 */ enum csi_pixel_fmt fenum[chnMAX]; int fid[chnMAX] = {0}; int tframes = 4; csi_camera_channel_id_e CAMERA_CHANNEL_ID[chnMAX]; /* File local Global array to store information which will be used when storig images */ static int PLANE[chnMAX] = {0}; static int PBIT[chnMAX] = {0}; static int PLNS[chnMAX][3] = {0}; static int LNN[chnMAX][3] = {0}; static int LSZ[chnMAX][3] = {0}; static int LNNV[chnMAX][3] = {0}; /* for rotation 90/270 */ static int LSZV[chnMAX][3] = {0}; /* for rotation 90/270 */ static int NOSTRD[chnMAX] = {0}; /* local global array for meta data verify */ static int cur_metaint[chnMAX] = {-1, -1, -1, -1}; static int pre_metaint[chnMAX] = {-1, -1, -1, -1}; static int cur_metasec[chnMAX] = {-1, -1, -1, -1}; static int pre_metasec[chnMAX] = {-1, -1, -1, -1}; static int cur_metausec[chnMAX] = {-1, -1, -1, -1}; static int pre_metausec[chnMAX] = {-1, -1, -1, -1}; static int shmid, semid; struct sembuf p1 = {0, -1, 0}, v1 = {0, 1, 0}; int meta_mode = -1; int flashMode = 0; int fNumInc[chnMAX] = {0}; int fCount = 1; int onoffMode = 0; int afterOnOff[chnMAX]; int onoffDrop = 4; int onoffFrame = 0; int ooNum[chnMAX] = {0}; int ofTop = 30; csi_frame_ex_s frame[chnMAX]; pthread_t tid[chnMAX]={0}; int hres[chnMAX], vres[chnMAX]; /* resolution information like 640x480 */ int idxArray[chnMAX][10]={0}; static char *dname; static int chNum = 0; char fmtArray[fmtMAX][50] = { "CSI_PIX_FMT_I420", "CSI_PIX_FMT_NV12", "CSI_PIX_FMT_BGR", "CSI_PIX_FMT_RAW_8BIT", "CSI_PIX_FMT_RAW_10BIT", "CSI_PIX_FMT_RAW_12BIT", "CSI_PIX_FMT_RAW_14BIT", "CSI_PIX_FMT_RAW_16BIT", "CSI_PIX_FMT_RGB_PLANAR_888", "CSI_PIX_FMT_RGB_INTEVLEAVED_888", "CSI_PIX_FMT_YUV_PLANAR_422", "CSI_PIX_FMT_YUV_PLANAR_420", "CSI_PIX_FMT_YUV_PLANAR_444", "CSI_PIX_FMT_YUV_SEMIPLANAR_422", "CSI_PIX_FMT_YUV_SEMIPLANAR_420", "CSI_PIX_FMT_YUV_SEMIPLANAR_444", "CSI_PIX_FMT_YUV_TEVLEAVED_422", "CSI_PIX_FMT_YUV_TEVLEAVED_420", "CSI_PIX_FMT_YUV_TEVLEAVED_444" }; void usage() { fprintf (stderr, "usage: %s [options]\n" "Options:\n" "\t-D dev target video device like /dev/video0\n" "\t-R resolution format is like 640x480, use 640x480/1092x1080 for multi channel output\n" "\t-F format like NV12, use NV12/NV12 to support multi channel output\n" "\t-C channel_index channel index defined in dts, index from 0, use 0/2 for multi channel output\n" "\t-M test mode 0: save as image; 1: save as stream file; 2: output to plink; 3. performace test\n" "\t-N number for frames 0: record forever; others: the recorded frame numbers, use 30/30 from multi channel output\n" "\t-n number of frames to save if not given, 4 is used\n" "\t-P Property id:type:value, use id:type:value/id:type:value for multi property setting\n" "\t-p output path store output file in the give path\n" "\t-S turn off stride mode if given, turn off stride mode\n" "\t-t stream turn on/off test if will turn off stream after several streams then turn on\n" "\t-s turn off sram mode if given, turn off sram mode between ISP/DSP and DSP/ISP-RY\n" "\t-m disable DW DMA-BUF if given, turn off dma-buf mode\n" "\t-f enable LED flash mode if given, enable LED flash mode for odd and even frame feature in lightA production board\n" "\t-i nubmer of process which needs to do IPC, use for multi-video and mutli-process test scenarios\n" "\t-y meta-data test 0 - meta dump all all elements; 1 - meta verify for CAMERA_NAME;\n" " 2 - meta verify for CHANNEL_ID; 3 - meta verify for FRAME_ID;\n" " 4 - meta verify for TIMESTAMP.\n" " 200 - meta verify for all elements\n" , prog_name ); } int main(int argc, char *argv[]) { bool running = 0; csi_camera_info_s camera_info; int opt; char *resDelim = "xX/"; int outMode = 0; int fNum[chnMAX] = {0}; char pArgs[500] = {'\0'}; /* store property args */ char *devDelim = "/"; char *sCur; int idx = 0; int ret = 0; csi_camera_event_type_e CAMERA_CHANNEL_EVENT_TYPE[chnMAX]; struct timeval init_time, cur_time; memset(&init_time, 0, sizeof(init_time)); memset(&cur_time, 0, sizeof(cur_time)); unsigned long video_open = 0; unsigned long channel_open = 0; unsigned long channel_start = 0; unsigned long first_frame = 0; unsigned long total_frame = 0; float fps = 0.0; int sramMode = 1; /* data for share-memory */ struct databuf *buf; int mi = 0; int srv1 = 0, srv2 = 0; prog_name = argv[0]; while ((opt = getopt(argc, argv, "t:D:R:F:C:M:hN:P:p:Smi:y:fsn:")) != EOF) { switch(opt) { case 'D': strcpy(device, optarg); strtok(optarg, devDelim); dname=strtok(NULL, devDelim); continue; case 'p': strcpy(path, optarg); continue; case 'S': Soff = 1; continue; case 't': onoffMode = 1; onoffFrame = atoi(optarg); continue; case 's': sramMode = 0; case 'f': flashMode = 1; continue; case 'P': strcpy(pArgs, optarg); continue; case 'R': idx = 0; sCur=strtok(optarg, resDelim); while (sCur != NULL){ if (idx % 2 == 0) hres[idx/2] = atoi(sCur); if (idx % 2 == 1) vres[idx/2] = atoi(sCur); sCur=strtok(NULL, resDelim); idx++; } continue; case 'M': outMode = atoi(optarg); continue; case 'i': mi = atoi(optarg); continue; case 'y': meta_mode = atoi(optarg); continue; case 'N': for(int j=0; j=2 */ if(mi >= 2){ semid = getsem(); getseg(&buf); printf("------IPC mode enable - finish-------\n"); } /* init the target channel ID and channel event type array */ for (int j=0; j 0 ) { int timeout = -1; // unit: ms, -1 means wait forever, or until error occurs csi_camera_get_event(event_handle, &event, timeout); printf("%s event.type = %d, event.id = %d\n", __func__, event.type, event.id); if(event.type == CSI_CAMERA_EVENT_TYPE_CAMERA){ switch (event.id) { case CSI_CAMERA_EVENT_ERROR: // do sth. printf("-------get CAMERA EVENT CSI_CAMERA_EVENT_ERROR!----------\n"); break; case CSI_CAMERA_EVENT_WARNING: printf("-------get CAMERA EVENT CSI_CAMERA_EVENT_WRN,RC: %s-------\n",event.bin); break; default: break; } } for (int j=0; j= 2){ writer(buf, 0, 1); } for (int j=0; j= 2){ while (1){ reader(buf, 0, &srv1); if (srv1 != mi){ sleep(2); } else{ /* update the second value in share-memory which is used to decide whether to remove share-memory */ writer(buf, 1, 1); reader(buf, 1, &srv2); if (srv2 == mi){ /* I am the last process to update the buf[1], remove the share-memory */ remove_s(); printf("------IPC mode: remove-------\n"); } break; } } } csi_camera_channel_stop(cam_handle, CAMERA_CHANNEL_ID[j]); } if ( flashMode == 1){ csi_camera_led_disable(cam_handle, LED_FLOODLIGHT_PROJECTION); } usleep (1000000); // 取消订阅某一个event, 也可以直接调用csi_camera_destory_event,结束所有的订阅 for (int j=0; jd_buf[index] += value; /* release semaphore */ semop(semid, &v1, 1); } /* function read to share-memory */ void reader(struct databuf *buf, int index, int *rv){ /* get semaphore before update share-memory */ semop(semid, &p1, 1); /* get share-memory value */ *rv = buf->d_buf[index]; /* release semaphore */ semop(semid, &v1, 1); } static void dump_camera_meta(csi_frame_ex_s *frame, int idx) { int i; //printf("%s\n", __func__); if (frame->frame_meta.type != CSI_META_TYPE_CAMERA) return; csi_camera_meta_s *meta_data = (csi_camera_meta_s *)frame->frame_meta.data; int meta_count = meta_data->count; csi_camera_meta_unit_s meta_unit; csi_camera_meta_id_e meta_id; if(meta_mode > 0){ meta_id = (1<<(meta_mode - 1)); csi_camera_frame_get_meta_unit( &meta_unit, meta_data, meta_id); } if(meta_mode == 1 || meta_mode == 2){ /* camera_name and channel_id - which should keep same during testing */ cur_metaint[idx] = meta_unit.int_value; if(pre_metaint[idx] == -1){ pre_metaint[idx] = cur_metaint[idx]; } else{ if(cur_metaint[idx] != pre_metaint[idx]){ pr_error("-----meta test fail: camera_id or channel_id changes during test--------"); } pre_metaint[idx] = cur_metaint[idx]; printf("-----meta test camera_name or channel_id:%d--------\n", cur_metaint[idx]); } } else if(meta_mode == 3){ /* frame id - which should increase from 0 */ if(cur_metaint[idx] == -1 && meta_unit.int_value != 1){ pr_error("-----meta test fail: first frame ID is not 1--------\n"); } cur_metaint[idx] = meta_unit.int_value; if(pre_metaint[idx] == -1){ pre_metaint[idx] = cur_metaint[idx]; } else{ if(cur_metaint[idx] != (pre_metaint[idx]+1)){ pr_error("-----meta test fail: frame_id is not continuous--------\n"); } printf("-----meta test: meta_id: %d, frame_id:%d, previous_id: %d--------\n", meta_unit.id, cur_metaint[idx], pre_metaint[idx]); pre_metaint[idx] = cur_metaint[idx]; } } else if(meta_mode == 4){ /* timestamps - which should be larger or equal to previous */ cur_metasec[idx] = meta_unit.time_value.tv_sec; cur_metausec[idx] = meta_unit.time_value.tv_usec; if(pre_metasec[idx] == -1){ pre_metasec[idx] = cur_metasec[idx]; pre_metausec[idx] = cur_metausec[idx]; } else{ if(cur_metasec[idx] <= pre_metasec[idx] && cur_metausec[idx] <= pre_metausec[idx]){ pr_error("-----meta test fail: timestamp decrease--------"); } printf("-----meta test time - second:%d--------\n", cur_metasec[idx]); printf("-----meta test time - usecond:%d--------\n", cur_metausec[idx]); pre_metasec[idx] = cur_metasec[idx]; pre_metausec[idx] = cur_metausec[idx]; } } else{ /* means meta_mode = 0 */ for (i = 0; i < meta_count; i++) { meta_id = (1<frame_data.stride[0] != LSZ[cid][0] && NOSTRD[cid] == 0){ //if(NOSTRD[cid] == 0){ smode = 1; printf("-------------In Stride mode, framei->img.strides[0]=%d---------------\n", framei->frame_data.stride[0]); } else{ printf("-------------No Stride, framei->img.strides[0]=%d---------------\n", framei->frame_data.stride[0]); } for (int p = 0; p < PLANE[cid]; p++){ /* loop for each planar */ //printf("-------------framei->img.usr_addr[%d]=0x%llx---------------\n", p, framei->img.usr_addr[p]); lindex = 0; //int size = 0; if(smode == 0){ /* no stride, write plane by plane*/ printf("-------------No Stride, length is = %d---------------\n", PLNS[cid][p]); //while(size < PLNS[cid][p]){ // size += fwrite(framei->img.usr_addr[p] + size, sizeof(char), PLNS[cid][p] - size, fp); // } fwrite(framei->frame_data.vir_addr[p], sizeof(char), PLNS[cid][p], fp); } else{ /* stride mode, write line by line */ for(int l = 0; l < LNN[cid][p]; l++){ if(framei->frame_data.stride[p] == 0){ printf("-------------Error: stride[%d] is 0---------------\n", p); lindex = LSZ[cid][p] * l; } else{ /* for each plane, the strides[x] is the same */ lindex = framei->frame_data.stride[0] * l; } fwrite(framei->frame_data.vir_addr[p] + lindex, sizeof(char), LSZ[cid][p], fp); } } } } else{ if(framei->frame_data.stride[0] != LSZV[cid][0] && NOSTRD[cid] == 0){ smode = 1; printf("-------------In Stride mode, framei->img.strides[0]=%d---------------\n", framei->frame_data.stride[0]); } else{ printf("-------------No Stride, framei->img.strides[0]=%d---------------\n", framei->frame_data.stride[0]); } for (int p = 0; p < PLANE[cid]; p++){ /* loop for each planar */ //printf("-------------framei->img.usr_addr[%d]=0x%llx---------------\n", p, framei->img.usr_addr[p]); lindex = 0; int size = 0; if(smode == 0){ /* no stride, write plane by plane*/ printf("-------------No Stride, length is = %d---------------\n", PLNS[cid][p]); while(size < PLNS[cid][p]){ size += fwrite(framei->frame_data.vir_addr[p] + size, sizeof(char), PLNS[cid][p] - size, fp); } } else{ /* stride mode, write line by line */ for(int l = 0; l < LNNV[cid][p]; l++){ if(framei->frame_data.stride[p] == 0){ printf("-------------Error: stride[%d] is 0---------------\n", p); lindex = LSZV[cid][p] * l; } else{ /* for each plane, the strides[x] is the same */ lindex = framei->frame_data.stride[0] * l; } fwrite(framei->frame_data.vir_addr[p] + lindex, sizeof(char), LSZV[cid][p], fp); } } } } } /* function: convert dma-buf file point to target address input1: framei input2: channel index */ // static void handle_dma_buf(csi_frame_ex_s *framei, int cid) // { // if (frame->img.type == CSI_IMG_TYPE_DMA_BUF) { // void *p[3] = {0}; // /* // The first plane which exists always. // All data for packed; Y data for planar or semi-planar // */ // void *phyaddr = vi_mem_import(framei->img.dmabuf[0].fds); // p[0] = vi_mem_map(phyaddr) + framei->img.dmabuf[0].offset; // framei->img.usr_addr[0] = p[0]; // /* // For other planes, in our solution, we only have plane[1] for // planar or sp; or no other plane for packed. // */ // for (int i=1; iimg.dmabuf[i].offset != NULL){ // p[i] = framei->img.dmabuf[i].offset + p[0]; // framei->img.usr_addr[i] = p[i]; // } // else{ // printf("-------------DMA ERROR, no usr_addr for plane=%d---------------\n", i); // } // } // vi_mem_release(phyaddr); // } // } /* function: save data as image input1: channel index */ static void* save_camera_img(void * idx) { int index = *(int*)(idx); free(idx); char fname[512]; char fidname[256]; FILE *fp; char fidc[128]; int fcount; int fidi = fid[index]; int cid = channelIdx[index]; int hresi = hres[index]; int vresi = vres[index]; enum csi_pixel_fmt fmt = fenum[index]; csi_frame_ex_s *framei = &frame[index]; int vmode = 0; if(framei->frame_info.width == vresi && framei->frame_info.height == hresi && vresi != hresi){ vmode = 1; vresi = framei->frame_info.height; hresi = framei->frame_info.width; } /* generate the image name */ fcount = fidi%tframes; sprintf(fname, "%s%s%s%s%d%s%d%s%d%s%d%s%d%s%s", path, "/img_", dname, "_", PLANE[index], "P_", cid, "_", hresi, "x", vresi, "_", fcount, ".", fmtArray[fmt]); /* dma-buf to address */ // handle_dma_buf(framei, index); if((fp = fopen(fname, "wb")) == NULL){ printf("Error: Can't open file\n"); return NULL; } /* data saving */ i_save(framei, index, fp, vmode); fclose(fp); /* frame ID saving */ sprintf(fidc, "%d\n", fidi); sprintf(fidname, "%s%s%s%s%d%s", path, "/fid_", dname, "_chn_", cid, ".txt"); if((fp = fopen(fidname, "a+")) == NULL){ printf("Error: Can't open file\n"); return NULL; } fwrite(fidc, sizeof(char), strlen(fidc), fp); fclose(fp); if(meta_mode >= 0){ dump_camera_meta(framei, index); } //csi_camera_frame_unlock(cam_handle, framei); csi_camera_put_frame(framei); // csi_frame_release(framei); if (onoffMode == 1 && fNumInc[index] % onoffFrame == 0){ afterOnOff[index] = onoffDrop; // stop stream, sleep, start stream csi_camera_channel_stop(cam_handle, CAMERA_CHANNEL_ID[index]); //sleep(1); csi_camera_channel_start(cam_handle, CAMERA_CHANNEL_ID[index]); } return NULL; } /* function: save data as stream input1: channel index */ static void* save_camera_stream(void *idx) { int index = *(int*)(idx); free(idx); FILE *fp; char fname[256]; int cid = channelIdx[index]; int hresi = hres[index]; int vresi = vres[index]; enum csi_pixel_fmt fmt = fenum[index]; csi_frame_ex_s *framei = &frame[index]; int vmode = 0; if(framei->frame_info.width == vresi && framei->frame_info.height == hresi && vresi != hresi){ vmode = 1; vresi = framei->frame_info.height; hresi = framei->frame_info.width; } /* generate stream name - for each time it is the same name */ sprintf(fname, "%s%s%s%s%d%s%d%s%d%s%d%s%s", path, "/str_", dname, "_", PLANE[index], "P_", cid, "_", hresi, "x", vresi, ".", fmtArray[fmt]); // handle_dma_buf(framei, index); if((fp = fopen(fname, "ab+")) == NULL){ printf("Error: Can't open file\n"); return NULL; } i_save(framei, index, fp, vmode); fclose(fp); if(meta_mode >= 0){ dump_camera_meta(framei, index); } //csi_camera_frame_unlock(cam_handle, framei); csi_camera_put_frame(framei); // csi_frame_release(framei); if (onoffMode == 1 && fNumInc[index] % onoffFrame == 0){ afterOnOff[index] = onoffDrop; // stop stream, sleep, start stream csi_camera_channel_stop(cam_handle, CAMERA_CHANNEL_ID[index]); //sleep(1); csi_camera_channel_start(cam_handle, CAMERA_CHANNEL_ID[index]); } return NULL; } // 同时配置多个参数 static int set_properties(csi_cam_handle_t cam_handle, char *pArgs) { csi_camera_properties_s properties; csi_camera_property_s property[30]; char *pDelim=":/"; int pid; int ptype; int pvalueInt; char *pvalueStr; char *pFirst; /* parse multi-properties which delimited by : or / 1. / is used to seprate multi properties 2. : is used to sperate pid:type:value for example: 1:2:1/2:2:1/3:1:90 */ pFirst = strtok(pArgs, pDelim); int i = 0; while(pFirst != NULL){ pid = atoi(pFirst); property[i].id = pid + CSI_CAMERA_PID_BASE; /* property type difinition: CSI_CAMERA_PROPERTY_TYPE_INTEGER = 1, CSI_CAMERA_PROPERTY_TYPE_BOOLEAN = 2, CSI_CAMERA_PROPERTY_TYPE_ENUM = 3, CSI_CAMERA_PROPERTY_TYPE_STRING = 7, CSI_CAMERA_PROPERTY_TYPE_BITMASK = 8, */ ptype = atoi(strtok(NULL, pDelim)); property[i].type = ptype; if (ptype == CSI_CAMERA_PROPERTY_TYPE_STRING) { pvalueStr = strtok(NULL, pDelim); strcpy(property[i].value.str_value, pvalueStr); } else { pvalueInt = atoi(strtok(NULL, pDelim)); property[i].value.int_value = pvalueInt; } i++; pFirst = strtok(NULL, pDelim); } /* set multi-properties(here the property number is i) by calling API just one time */ properties.count = i; properties.property = property; if (csi_camera_set_property(cam_handle, &properties) < 0) { printf("set_property fail!\n"); return -1; } printf("set_property ok!\n"); return 0; } static enum csi_pixel_fmt parse_format(char *fmt) { for(int i=0; i