#include "system/includes.h" #include "server/video_server.h" #include "event/key_event.h" #include "event/device_event.h" #include "video_rec.h" #include "video_system.h" #include "action.h" #include "app_config.h" #include "asm/debug.h" #include "asm/osd.h" #include "app_database.h" #include "storage_device.h" #ifdef CONFIG_UI_ENABLE #include "ui_demo.h" #endif #ifdef CONFIG_NET_ENABLE #include "net_video_rec.h" #endif #define AUDIO_VOLUME 100 #ifndef CONFIG_VIDEO0_ENABLE #undef VREC0_FBUF_SIZE #define VREC0_FBUF_SIZE 0 #endif #ifndef CONFIG_VIDEO1_ENABLE #undef VREC1_FBUF_SIZE #define VREC1_FBUF_SIZE 0 #endif #ifndef CONFIG_VIDEO2_ENABLE #undef VREC2_FBUF_SIZE #define VREC2_FBUF_SIZE 0 #endif #define LOCK_FILE_PERCENT 40 //0~100 #define NAME_FILE_BY_DATE 0 //使用RTC日期文件名 struct video_rec_hdl rec_handler; #define __this (&rec_handler) static const unsigned char video_osd_format_buf[] = "yyyy-nn-dd hh:mm:ss"; static const u16 rec_pix_w[] = {1280, 640}; static const u16 rec_pix_h[] = {720, 480}; static const char *rec_dir[][2] = { #ifdef CONFIG_EMR_DIR_ENABLE {CONFIG_REC_DIR_0, CONFIG_EMR_REC_DIR_0}, {CONFIG_REC_DIR_1, CONFIG_EMR_REC_DIR_1}, {CONFIG_REC_DIR_2, CONFIG_EMR_REC_DIR_2}, #else {CONFIG_REC_DIR_0, CONFIG_REC_DIR_0}, {CONFIG_REC_DIR_1, CONFIG_REC_DIR_1}, {CONFIG_REC_DIR_2, CONFIG_REC_DIR_2}, #endif }; static const char *rec_path[][2] = { #ifdef CONFIG_EMR_DIR_ENABLE {CONFIG_REC_PATH_0, CONFIG_EMR_REC_PATH_0}, {CONFIG_REC_PATH_1, CONFIG_EMR_REC_PATH_1}, {CONFIG_REC_PATH_2, CONFIG_EMR_REC_PATH_2}, #else {CONFIG_REC_PATH_0, CONFIG_REC_PATH_0}, {CONFIG_REC_PATH_1, CONFIG_REC_PATH_1}, {CONFIG_REC_PATH_2, CONFIG_REC_PATH_2}, #endif }; #ifdef CONFIG_VIDEO_720P #define CAP_IMG_SIZE (300*1024) #else #define CAP_IMG_SIZE (150*1024) #endif static int video_rec_start(); static int video_rec_stop(u8 close); static int video_rec_device_event_handler(struct device_event *event); static int video_rec_sd_in(); static int video_rec_sd_out(); static int video_rec_get_abr(u32 width); /* #if 1 */ #ifdef CONFIG_NET_ENABLE /******************************用于网络实时流*************************************/ /* extern char *video_rec_finish_get_name(FILE *fd, int index, u8 is_emf); //index:video0则0,video1则1,video2则2 */ /* extern int video_rec_finish_notify(char *path); */ /* extern int video_rec_delect_notify(FILE *fd, int id); */ /* extern int video_rec_err_notify(const char *method); */ /* extern int video_rec_state_notify(void); */ /* extern int video_rec_start_notify(void); */ /* extern int video_rec_all_stop_notify(void); */ /* extern int net_video_rec_event_notify(void); */ /* extern int net_video_rec_event_stop(void); */ /* extern int net_video_rec_event_start(void); */ /* extern char *get_net_video_rec0_video_buf(int *buf_size); */ /* extern char *get_net_video_rec1_video_buf(int *buf_size); */ /* extern char *get_net_video_rec2_video_buf(int *buf_size); */ /* extern char *get_net_video_rec_audio_buf(int *buf_size); */ /* extern char *get_net_video_isc_buf(int *buf_size); */ /* extern void net_video_server_init(void); */ /* extern int net_video_event_hander(void *e); */ #else static int video_rec_finish_notify(char *path) { return 0; } static int video_rec_delect_notify(FILE *fd, int id) { return 0; } static int video_rec_err_notify(const char *method) { return 0; } static int video_rec_state_notify(void) { return 0; } static int video_rec_start_notify(void) { return 0; } static int video_rec_all_stop_notify(void) { return 0; } static int net_video_rec_event_notify(void) { return 0; } static int net_video_rec_event_stop(void) { return 0; } static int net_video_rec_event_start(void) { return 0; } static char *get_net_video_rec0_video_buf(int *buf_size) { *buf_size = 0; return NULL; } static char *get_net_video_rec1_video_buf(int *buf_size) { *buf_size = 0; return NULL; } static char *get_net_video_rec2_video_buf(int *buf_size) { *buf_size = 0; return NULL; } static char *get_net_video_rec_audio_buf(int *buf_size) { *buf_size = 0; return 0; } static char *get_net_video_isc_buf(int *buf_size) { *buf_size = 0; return NULL; } static void net_video_server_init(void) { } static void net_video_event_hander(void *e) { } #endif #ifdef CONFIG_NET_ENABLE #if (defined CONFIG_NO_SDRAM_ENABLE) //开wifi,没有sdram 录像buff使用实时流buff #undef AUDIO_BUF_SIZE #undef VREC0_FBUF_SIZE #define AUDIO_BUF_SIZE NET_AUDIO_BUF_SIZE #define VREC0_FBUF_SIZE NET_VREC0_FBUF_SIZE #endif #endif int get_video_rec_status(void) { if (__this->state == VIDREC_STA_START) { return 1; } else { return 0; } } int video_rec_control_doing(void) { int err; if (__this->state == VIDREC_STA_START) { err = video_rec_stop(0); } else { err = video_rec_start(); } return err; } #ifdef CONFIG_NET_ENABLE void *get_video_rec_handler(void) { return (void *)&rec_handler; } int video_rec_get_fps() { #ifdef VIDEO_REC_FPS return VIDEO_REC_FPS; #else return 0; #endif } int video_rec_get_audio_sampel_rate(void) { #ifdef VIDEO_REC_AUDIO_SAMPLE_RATE return VIDEO_REC_AUDIO_SAMPLE_RATE; #else return 8000; #endif } int video_rec_control_start(void) { int err; err = video_rec_start(); return err; } int video_rec_device_event_action(struct device_event *event) { return video_rec_device_event_handler(event); } int video_rec_sd_in_notify(void) { return video_rec_sd_in(); } int video_rec_sd_out_notify(void) { return video_rec_sd_out(); } int video_rec_get_abr_from(u32 width) { return video_rec_get_abr(width); } #endif /******************************************************************/ static void video_rec_buf_alloc() { int buf_size[] = {VREC0_FBUF_SIZE, VREC1_FBUF_SIZE, VREC2_FBUF_SIZE}; int bfsize = 0; #if (defined CONFIG_NO_SDRAM_ENABLE) __this->audio_buf = get_net_video_rec_audio_buf(&bfsize);//获取公用BUFF #endif if ((!__this->audio_buf) && AUDIO_BUF_SIZE > 0) { __this->audio_buf = malloc(AUDIO_BUF_SIZE); if (!__this->audio_buf) { log_d(">>>>>>>>>> audiobuf alloc err>>>>>>\n"); return ; } } for (int i = 0; i < ARRAY_SIZE(buf_size); i++) { if (buf_size[i]) { #if (defined CONFIG_NO_SDRAM_ENABLE) switch (i) { case 0: __this->video_buf[i] = get_net_video_rec0_video_buf(&bfsize);//获取公用BUFF break; case 1: __this->video_buf[i] = get_net_video_rec1_video_buf(&bfsize);//获取公用BUFF break; case 2: __this->video_buf[i] = get_net_video_rec2_video_buf(&bfsize);//获取公用BUFF break; } #endif if (!__this->video_buf[i]) { __this->video_buf[i] = malloc(buf_size[i]); if (!__this->video_buf[i]) { log_d(">>>>>>>>>> videobuf alloc err>>>>>>\n"); } } } else { __this->video_buf[i] = NULL; } } } static int video_rec_destroy() { if (__this->state != VIDREC_STA_FORBIDDEN) { return -EFAULT; } for (int i = 0; i < CONFIG_VIDEO_REC_NUM; i++) { #if (defined CONFIG_NO_SDRAM_ENABLE) int bfsize = 0; u8 *video_buf = NULL; switch (i) { case 0: video_buf = get_net_video_rec0_video_buf(&bfsize);//获取公用BUFF break; case 1: video_buf = get_net_video_rec1_video_buf(&bfsize);//获取公用BUFF break; case 2: video_buf = get_net_video_rec2_video_buf(&bfsize);//获取公用BUFF break; } if (video_buf == __this->video_buf[i] && __this->video_buf[i]) { __this->video_buf[i] = NULL; } #endif if (__this->video_buf[i]) { free(__this->video_buf[i]); __this->video_buf[i] = NULL; } } #if (defined CONFIG_NO_SDRAM_ENABLE) int bfsize = 0; u8 *audio_buf = get_net_video_rec_audio_buf(&bfsize);//获取公用BUFF if (audio_buf == __this->audio_buf && __this->audio_buf) { __this->audio_buf = NULL; } #endif if (__this->audio_buf) { free(__this->audio_buf); __this->audio_buf = NULL; } if (__this->cap_buf) { free(__this->cap_buf); __this->cap_buf = NULL; } return 0; } static int __get_sys_time(struct sys_time *time) { void *fd = dev_open("rtc", NULL); if (fd) { dev_ioctl(fd, IOCTL_GET_SYS_TIME, (u32)time); dev_close(fd); return 0; } return -EINVAL; } static const char *rec_file_name(int format) { #if NAME_FILE_BY_DATE struct sys_time time; static char file_name[MAX_FILE_NAME_LEN]; if (__get_sys_time(&time) == 0) { if (format == VIDEO_FMT_AVI) { sprintf(file_name, "VID_%d%02d%02d_%02d%02d%02d.AVI", time.year, time.month, time.day, time.hour, time.min, time.sec); } else if (format == VIDEO_FMT_MOV) { sprintf(file_name, "VID_%d%02d%02d_%02d%02d%02d.MOV", time.year, time.month, time.day, time.hour, time.min, time.sec); } else if (format == VIDEO_FMT_MP4) { sprintf(file_name, "VID_%d%02d%02d_%02d%02d%02d.MP4", time.year, time.month, time.day, time.hour, time.min, time.sec); } return file_name; } #endif if (format == VIDEO_FMT_AVI) { return "VID_***.AVI"; } else if (format == VIDEO_FMT_MP4) { return "VID_***.MP4"; } else { return "VID_***.MOV"; } } /*码率控制,根据具体分辨率设置*/ static int video_rec_get_abr(u32 width) { /*视频码率kbps使用说明: 码率:一帧图片以K字节为单位大小 * 帧率 * 8,比如:一帧图片为30KB,帧率为20帧,则码率为30*20*8=4800 VGA图片大小说明:低等质量(小于20K),中等质量(20K-40K),高质量(大于40K,极限70K) 720P图片大小说明:低等质量(小于50K),中等质量(50k-100K),高质量(大于100K,极限150K) */ if (width <= 720) { /* return 8000; */ return 4000; } else if (width <= 1280) { return 8000; /* return 10000; */ } else if (width <= 1920) { return 14000; } else { return 18000; } } /* *根据录像不同的时间和分辨率,设置不同的录像文件大小 */ static u32 video_rec_get_fsize(u8 cycle_time, u16 vid_width, int format) { u32 fsize; if (cycle_time > 10 || cycle_time == 0) { cycle_time = 3; } fsize = video_rec_get_abr(vid_width) * cycle_time * 8250; if (format == VIDEO_FMT_AVI) { fsize = fsize + fsize / 4; } printf("fsize---->> %d \n\n", fsize); return fsize; } static int video_rec_cmp_fname(void *afile, void *bfile) { int alen, blen; char afname[MAX_FILE_NAME_LEN]; char bfname[MAX_FILE_NAME_LEN]; if ((afile == NULL) || (bfile == NULL)) { return 0; } printf("video_rec_cmp_fname: %p, %p\n", afile, bfile); alen = fget_name(afile, (u8 *)afname, MAX_FILE_NAME_LEN); if (alen <= 0) { log_e("fget_name: afile=%x\n", afile); return 0; } ASCII_ToUpper(afname, alen); blen = fget_name(bfile, (u8 *)bfname, MAX_FILE_NAME_LEN); if (blen <= 0) { log_e("fget_name: bfile=%x\n", bfile); return 0; } ASCII_ToUpper(bfname, blen); printf("afname: %s, bfname: %s\n", afname, bfname); if (alen == blen && !strcmp(afname, bfname)) { return 1; } return 0; } static void video_rec_fscan_release(int lock_dir) { printf("video_rec_fscan_release: %d\n", lock_dir); for (int i = 0; i < 3; i++) { if (__this->fscan[lock_dir][i]) { fscan_release(__this->fscan[lock_dir][i]); __this->fscan[lock_dir][i] = NULL; } } } static void video_rec_fscan_dir(int id, int lock_dir, const char *path) { const char *str; #ifdef CONFIG_EMR_DIR_ENABLE str = "-tMOVAVIMP4 -sn"; #else str = lock_dir ? "-tMOVAVIMP4 -sn -ar" : "-tMOVAVIMP4 -sn -a/r"; #endif if (__this->fscan[lock_dir][id]) { if (__this->old_file_number[lock_dir][id] == 0) { puts("--------delete_all_scan_file\n"); fscan_release(__this->fscan[lock_dir][id]); __this->fscan[lock_dir][id] = NULL; } } if (!__this->fscan[lock_dir][id]) { __this->fscan[lock_dir][id] = fscan(path, str, 0); if (!__this->fscan[lock_dir][id]) { __this->old_file_number[lock_dir][id] = 0; } else { __this->old_file_number[lock_dir][id] = __this->fscan[lock_dir][id]->file_number; } __this->file_number[lock_dir][id] = __this->old_file_number[lock_dir][id]; printf("fscan_dir: %d, file_number = %d\n", id, __this->file_number[lock_dir][id]); } } static FILE *video_rec_get_first_file(int id) { int max_index = -1; int max_file_number = 0; int persent = __this->lock_fsize * 100 / __this->total_size; int lock_dir = !!(persent > LOCK_FILE_PERCENT); log_d("lock_file_persent: %d, %d, size: %dMB\n", id, persent, __this->lock_fsize / 1024); #ifdef CONFIG_VIDEO0_ENABLE video_rec_fscan_dir(0, lock_dir, rec_path[0][lock_dir]); #endif for (int i = 0; i < 3; i++) { if (__this->fscan[lock_dir][i]) { if (max_file_number < __this->file_number[lock_dir][i]) { max_file_number = __this->file_number[lock_dir][i]; max_index = i; } } } if (max_index < 0) { return NULL; } if (max_index != id && id >= 0) { /* 查看优先删除的文件夹是否满足删除条件 */ if (__this->file_number[lock_dir][id] + 3 > __this->file_number[lock_dir][max_index]) { max_index = id; } } log_d("fselect file from dir %d, %d\n", lock_dir, max_index); if (__this->fscan[lock_dir][max_index]) { FILE *f = fselect(__this->fscan[lock_dir][max_index], FSEL_FIRST_FILE, 0); if (f) { if (lock_dir == 0) { if (video_rec_cmp_fname(__this->file[max_index], f)) { fclose(f); return NULL; } } else { __this->lock_fsize -= flen(f) / 1024; log_d("lock fsize - = %d\n", __this->lock_fsize); } __this->file_number[lock_dir][max_index]--; __this->old_file_number[lock_dir][max_index]--; if (__this->old_file_number[lock_dir][max_index] == 0) { video_rec_fscan_release(lock_dir); } } #ifdef CONFIG_NET_ENABLE video_rec_delect_notify(f, -1); #endif return f; } else { log_e("fscan[%d][%d] err", lock_dir, max_index); return NULL; } return NULL; } static void video_rec_rename_file(int id, FILE *file, int fsize, int format) { char file_name[32]; __this->new_file[id] = NULL; #ifndef CONFIG_JLFAT_ENABLE int err = fcheck(file); if (err) { puts("fcheck fail\n"); #ifdef CONFIG_NET_ENABLE video_rec_delect_notify(file, -1); #endif fdelete(file); return; } #endif int size = flen(file); int persent = (size / 1024) * 100 / (fsize / 1024); printf("rename file: persent=%d, %d,%d\n", persent, size >> 20, fsize >> 20); if (persent >= 90 && persent <= 110) { #ifdef CONFIG_JLFAT_ENABLE sprintf(file_name, "%s%s", rec_dir[id][0] + strlen(CONFIG_ROOT_PATH) - 1, rec_file_name(format)); #else sprintf(file_name, "%s%s", rec_dir[id][0], rec_file_name(format)); #endif printf("fmove: %d, %d, %s\n", id, format, file_name); #ifdef CONFIG_NET_ENABLE video_rec_delect_notify(file, -1); #endif int err = fmove(file, file_name, &__this->new_file[id], 1, strlen(file_name)); if (err == 0) { fseek(__this->new_file[id], fsize, SEEK_SET); fseek(__this->new_file[id], 0, SEEK_SET); return; } puts("fmove_file_faild\n"); } #ifdef CONFIG_NET_ENABLE video_rec_delect_notify(file, -1); #endif fdelete(file); } static int video_rec_create_file(int id, u32 fsize, int format, const char *path) { FILE *file; int try_cnt = 0; char file_path[64]; sprintf(file_path, "%s%s", path, rec_file_name(format)); printf("id:%d , fopen: %s, min space %dMB\n", id, file_path, fsize >> 20); do { file = fopen(file_path, "w+"); if (!file) { log_e("fopen faild\n"); break; } goto __exit;//在写第一帧时候再seek整个文件大小 if (fseek(file, fsize, SEEK_SET)) { goto __exit; } log_e("fseek faild\n"); fdelete(file); } while (++try_cnt < 2); return -EIO; __exit: fseek(file, 0, SEEK_SET); /*__this->new_file_size[id] = fsize;*/ __this->new_file_size[id] = 0; __this->new_file[id] = file; return 0; } static int video_rec_del_old_file() { int i; int err; FILE *file; int fsize[3] = {0, 0, 0}; u32 cur_space; u32 need_space = 0; int cyc_time = db_select("cyc"); int format[3] = { VIDEO0_REC_FORMAT, VIDEO1_REC_FORMAT, VIDEO2_REC_FORMAT}; printf("cyc_time : %d\n", cyc_time); #ifdef CONFIG_VIDEO0_ENABLE if (!__this->new_file[0]) { int res = db_select("res"); res = (res > 1 || res < 0) ? 1 : res; fsize[0] = video_rec_get_fsize(cyc_time, rec_pix_w[res], VIDEO0_REC_FORMAT); need_space += fsize[0]; } #endif err = fget_free_space(CONFIG_ROOT_PATH, &cur_space); if (err) { return err; } printf("space: %dMB, %dMB\n", cur_space / 1024, need_space / 1024 / 1024); if (cur_space >= 3 * (need_space / 1024)) { for (i = 0; i < 3; i++) { if (fsize[i] != 0) { err = video_rec_create_file(i, fsize[i], format[i], rec_path[i][0]); if (err) { printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__); printf("[msg]>>>>>>>>>>>err=%d", err); return err; } } } printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__); return 0; } printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__); while (1) { if (cur_space >= (need_space / 1024) * 3) { break; } file = video_rec_get_first_file(-1); if (!file) { return -ENOMEM; } fdelete(file); fget_free_space(CONFIG_ROOT_PATH, &cur_space); } printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__); for (i = 0; i < 3; i++) { if (fsize[i] != 0) { file = video_rec_get_first_file(i); if (file) { video_rec_rename_file(i, file, fsize[i], format[i]); } if (!__this->new_file[i]) { err = video_rec_create_file(i, fsize[i], format[i], rec_path[i][0]); if (err) { return err; } } } } return 0; } /* * 判断SD卡是否挂载成功和簇大小是否大于32K */ static int storage_device_available() { struct vfs_partition *part; if (storage_device_ready() == 0) { return false; } else { part = fget_partition(CONFIG_ROOT_PATH); printf("part_fs_attr: %x\n", part->fs_attr); #ifdef CONFIG_JLFAT_ENABLE if (part->clust_size < 32 * 1024 || (part->fs_attr & F_ATTR_RO)) { #else if (part->clust_size < 32 || (part->fs_attr & F_ATTR_RO)) { #endif return false; } __this->total_size = part->total_size; } return true; } static void video_rec_close_file(int dev_id) { if (!__this->file[dev_id]) { return; } #ifdef CONFIG_NET_ENABLE char is_emf = 0; char *path = video_rec_finish_get_name(__this->file[dev_id], dev_id, is_emf); #endif fclose(__this->file[dev_id]); __this->file[dev_id] = NULL; #ifdef CONFIG_NET_ENABLE if (path) { //必须关闭文件之后才能调用,否则在读取文件信息不全!!! video_rec_finish_notify(path); } #endif } static int video0_rec_stop(u8 close) { union video_req req; int err; log_d("video0_rec_stop\n"); if (__this->video_rec0) { req.rec.channel = 0; req.rec.state = VIDEO_STATE_STOP; err = server_request(__this->video_rec0, VIDEO_REQ_REC, &req); if (err != 0) { #ifdef CONFIG_NET_ENABLE video_rec_err_notify("VIDEO_REC_ERR"); #endif printf("\nstop rec err 0x%x\n", err); return VREC_ERR_V0_REQ_STOP; } } video_rec_close_file(0); if (close) { if (__this->video_rec0) { server_close(__this->video_rec0); __this->video_rec0 = NULL; } } return 0; } static int take_photo(void) { int err; #ifdef CONFIG_USR_VIDEO_ENABLE extern int user_video_rec0_open(void); extern int user_video_rec_take_photo(void); extern int user_video_rec0_close(void); err = user_video_rec0_open(); if (err) { return -EINVAL; } os_time_dly(5); user_video_rec_take_photo(); user_video_rec0_close(); return 0; #endif return -EINVAL;; } int video_rec_take_photo(void) { struct server *server = NULL; union video_req req = {0}; char *path; char buf[48]; char name_buf[20]; int err; if (__this->state == VIDREC_STA_START) { server = __this->video_rec0; if (!server) { printf("waring :video not open\n"); return -EINVAL; } req.icap.quality = VIDEO_HIG_Q; req.icap.buf_size = CAP_IMG_SIZE; req.icap.buf = malloc(CAP_IMG_SIZE); if (!req.icap.buf) { goto error; } req.rec.text_osd = NULL; req.rec.graph_osd = NULL; req.icap.text_label = NULL; req.icap.file_name = name_buf; req.icap.path = CAMERA0_CAP_PATH"IMG_****.jpg"; path = CAMERA0_CAP_PATH; err = server_request(server, VIDEO_REQ_IMAGE_CAPTURE, &req); if (err != 0) { puts("\n\n\ntake photo err\n\n\n"); goto error; } sprintf(buf, "%s%s", path, req.icap.file_name); printf("%s\n\n", buf); if (req.icap.buf) { free(req.icap.buf); } return 0; error: if (req.icap.buf) { free(req.icap.buf); printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__); } return -EINVAL; } else if (__this->state != VIDREC_STA_STARTING && __this->state != VIDREC_STA_STOPING) { sys_timeout_add_to_task("sys_timer", NULL, take_photo, 10); #if 0 #ifdef CONFIG_USR_VIDEO_ENABLE extern int user_video_rec0_open(void); extern int user_video_rec_take_photo(void); extern int user_video_rec0_close(void); err = user_video_rec0_open(); if (err) { return -EINVAL; } os_time_dly(5); user_video_rec_take_photo(); user_video_rec0_close(); return 0; #endif #endif } return -EINVAL; } /* *注意:循环录像的时候,虽然要重新传参,但是要和start传的参数保持一致!!! */ static int video0_rec_savefile() { union video_req req = {0}; int err; if (!__this->file[0]) { return -ENOENT; } int res = db_select("res"); res = (res > 1 || res < 0) ? 1 : res; int isc_size = 0; req.rec.isc_sbuf = get_net_video_isc_buf(&isc_size); req.rec.sbuf_size = isc_size; req.rec.channel = 0; req.rec.width = rec_pix_w[res]; req.rec.height = rec_pix_h[res]; req.rec.format = VIDEO0_REC_FORMAT; req.rec.state = VIDEO_STATE_SAVE_FILE; req.rec.file = __this->file[0]; #ifdef CONFIG_NET_ENABLE req.rec.audio.sample_rate = video_rec_get_audio_sampel_rate(); #else req.rec.audio.sample_rate = 8000; #endif req.rec.audio.channel = 1; req.rec.pkg_mute.aud_mute = !db_select("mic"); req.rec.rec_small_pic = 0; err = server_request(__this->video_rec0, VIDEO_REQ_REC, &req); if (err != 0) { log_e("rec0_save_file: err=%d\n", err); return err; } return 0; } static int video_rec_savefile(int dev_id) { int i; int err; int post_msg = 0; union video_req req; printf(">>>>>> video_rec_savefile : 0x%x \n\n", dev_id); if (__this->state != VIDREC_STA_START) { return 0; } if (__this->need_restart_rec) { log_d("need restart rec"); video_rec_stop(0); video_rec_start(); return 0; } log_d("\nvideo_rec_start_new_file: %d\n", dev_id); video_rec_close_file(dev_id); if (__this->new_file[dev_id] == NULL) { err = video_rec_del_old_file(); if (err) { goto __err; } post_msg = 1; } __this->file[dev_id] = __this->new_file[dev_id]; __this->new_file[dev_id] = NULL; #ifdef CONFIG_VIDEO0_ENABLE if (dev_id == 0) { err = video0_rec_savefile(); if (err) { goto __err; } } #endif __this->state = VIDREC_STA_START; #ifdef CONFIG_NET_ENABLE video_rec_state_notify(); #endif #ifdef CONFIG_UI_ENABLE os_taskq_post("ui_demo", 1, UI_EVENT_COMPLETE_RECORD); #endif printf("rec_savefile ok .....\n\n"); return 0; __err: #ifdef CONFIG_VIDEO0_ENABLE err = video0_rec_stop(0); if (err) { printf("\nsave wrong0 %x\n", err); } #endif __this->state = VIDREC_STA_STOP; printf("rec_savefile err .....\n\n"); return -EFAULT; } static void rec_dev_server_event_handler(void *priv, int argc, int *argv) { /* *该回调函数会在录像过程中,写卡出错被当前录像APP调用,例如录像过程中突然拔卡 */ switch (argv[0]) { case VIDEO_SERVER_UVM_ERR: log_e("APP_UVM_DEAL_ERR\n"); break; case VIDEO_SERVER_PKG_ERR: log_e("video_server_pkg_err\n"); if (__this->state == VIDREC_STA_START) { #ifdef CONFIG_NET_ENABLE video_rec_err_notify("VIDEO_REC_ERR"); net_video_rec_event_stop(); #endif video_rec_stop(0); #ifdef CONFIG_NET_ENABLE net_video_rec_event_start(); #endif } break; case VIDEO_SERVER_PKG_END: if (db_select("cyc") > 0) { video_rec_savefile((int)priv); } else { #ifdef CONFIG_NET_ENABLE net_video_rec_event_stop(); #endif video_rec_stop(0); #ifdef CONFIG_NET_ENABLE net_video_rec_event_start(); #endif } break; default : log_e("unknow rec server cmd %x , %x!\n", argv[0], (int)priv); break; } } #ifdef CONFIG_VIDEO0_ENABLE static int video0_rec_start() { int err; union video_req req = {0}; struct video_text_osd text_osd = {0}; struct video_graph_osd graph_osd; u16 max_one_line_strnum; puts("start_video_rec0\n"); if (!__this->video_rec0) { #ifdef CONFIG_UVC_VIDEO2_ENABLE if (uvc_host_online() < 0) { log_e("uvc no online\n"); return VREC_ERR_UVC_OFFLINE; } __this->video_rec0 = server_open("video_server", "video2.0"); #else __this->video_rec0 = server_open("video_server", "video0.0"); #endif if (!__this->video_rec0) { return VREC_ERR_V0_SERVER_OPEN; } server_register_event_handler(__this->video_rec0, (void *)0, rec_dev_server_event_handler); } /*db_update("res",1); db_update("cyc",1); */ int res = db_select("res"); res = (res > 1 || res < 0) ? 1 : res; int isc_size = 0; req.rec.isc_sbuf = get_net_video_isc_buf(&isc_size); req.rec.sbuf_size = isc_size; #ifdef CONFIG_UVC_VIDEO2_ENABLE req.rec.uvc_id = uvc_host_online(); #endif #ifdef CONFIG_VIDEO_REC_PPBUF_MODE req.rec.bfmode = VIDEO_PPBUF_MODE; #endif #ifdef CONFIG_VIDEO_SPEC_DOUBLE_REC_MODE req.rec.wl80_spec_mode = VIDEO_WL80_SPEC_DOUBLE_REC_MODE; #endif #ifdef CONFIG_UI_ENABLE req.rec.picture_mode = 1;//整帧图片模式 #endif req.rec.channel = 0; req.rec.camera_type = VIDEO_CAMERA_NORMAL; req.rec.width = rec_pix_w[res]; req.rec.height = rec_pix_h[res]; req.rec.format = VIDEO0_REC_FORMAT; req.rec.state = VIDEO_STATE_START; req.rec.file = __this->file[0]; #ifdef CONFIG_NET_ENABLE req.rec.fsize = __this->new_file_size[0]; #endif req.rec.quality = VIDEO_LOW_Q; req.rec.fps = 0 ; #ifdef CONFIG_NET_ENABLE req.rec.real_fps = video_rec_get_fps(); #else req.rec.real_fps = 0; #endif #ifdef CONFIG_NET_ENABLE req.rec.audio.sample_rate = video_rec_get_audio_sampel_rate(); #else req.rec.audio.sample_rate = 8000; #endif req.rec.audio.channel = 1; req.rec.audio.volume = AUDIO_VOLUME; req.rec.audio.buf = __this->audio_buf; req.rec.audio.buf_len = AUDIO_BUF_SIZE; req.rec.pkg_mute.aud_mute = !db_select("mic"); /* *码率,I帧和P帧比例,必须是偶数(当录MOV的时候才有效), *roio_xy :值表示宏块坐标, [6:0]左边x坐标 ,[14:8]右边x坐标,[22:16]上边y坐标,[30:24]下边y坐标,写0表示1个宏块有效 * roio_ratio : 区域比例系数 */ req.rec.abr_kbps = video_rec_get_abr(req.rec.width); req.rec.IP_interval = 0; /*感兴趣区域为下方 中间 2/6 * 4/6 区域,可以调整 感兴趣区域qp 为其他区域的 70% ,可以调整 */ req.rec.roi.roio_xy = (req.rec.height * 5 / 6 / 16) << 24 | (req.rec.height * 3 / 6 / 16) << 16 | (req.rec.width * 5 / 6 / 16) << 8 | (req.rec.width) * 1 / 6 / 16; req.rec.roi.roi1_xy = (req.rec.height * 11 / 12 / 16) << 24 | (req.rec.height * 4 / 12 / 16) << 16 | (req.rec.width * 11 / 12 / 16) << 8 | (req.rec.width) * 1 / 12 / 16; req.rec.roi.roi2_xy = 0; req.rec.roi.roi3_xy = (1 << 24) | (0 << 16) | ((req.rec.width / 16) << 8) | 0; req.rec.roi.roio_ratio = 256 * 70 / 100 ; req.rec.roi.roio_ratio1 = 256 * 90 / 100; req.rec.roi.roio_ratio2 = 0; req.rec.roi.roio_ratio3 = 256 * 80 / 100; #ifdef CONFIG_OSD_ENABLE text_osd.font_w = OSD_DEFAULT_WIDTH;//必须16对齐 text_osd.font_h = OSD_DEFAULT_HEIGHT;//必须16对齐 text_osd.text_format = video_osd_format_buf; /*text_osd.text_format = NULL;//关闭OSD时间水印*/ text_osd.x = (req.rec.width - text_osd.font_w * strlen(video_osd_format_buf) + 15) / 16 * 16; text_osd.y = (req.rec.height - text_osd.font_h + 15) / 16 * 16; text_osd.osd_yuv = 0xe20095; if (db_select("dat") > 0) { req.rec.text_osd = &text_osd; } #endif req.rec.cycle_time = db_select("cyc"); if (req.rec.cycle_time > 10 || req.rec.cycle_time <= 0) { req.rec.cycle_time = 3; } req.rec.buf = __this->video_buf[0]; req.rec.buf_len = VREC0_FBUF_SIZE; req.rec.rec_small_pic = 0; req.rec.cycle_time = req.rec.cycle_time * 60; printf(">>>>>>>video rec cyc time : %d min\n\n", req.rec.cycle_time / 60); err = server_request(__this->video_rec0, VIDEO_REQ_REC, &req); if (err != 0) { puts("\n\n\nstart rec err\n\n\n"); return VREC_ERR_V0_REQ_START; } __this->state = VIDREC_STA_START; return 0; } #endif static int video_rec_start() { int err; u32 clust; u8 cnt = 0; u8 state = __this->state; int buf_size[] = {VREC0_FBUF_SIZE, VREC1_FBUF_SIZE, VREC2_FBUF_SIZE}; __this->char_wait = 0; __this->need_restart_rec = 0; if (__this->state == VIDREC_STA_START) { return 0; } log_d("(((((( video_rec_start: in\n"); if (!storage_device_available()) { return 0; } /* * 申请录像所需要的音频和视频帧buf */ video_rec_buf_alloc(); /* * 判断SD卡空间,删除旧文件并创建新文件 */ redo: err = video_rec_del_old_file(); if (err) { cnt++; if (cnt < 3) { log_d("retry ... \n\n"); os_time_dly(50); goto redo; } log_e("start free space err\n"); return VREC_ERR_START_FREE_SPACE; } for (int i = 0; i < 3; i++) { __this->file[i] = __this->new_file[i]; __this->new_file[i] = NULL; } #ifdef CONFIG_VIDEO0_ENABLE err = video0_rec_start(); if (err) { video0_rec_stop(0); return err; } #endif __this->state = VIDREC_STA_START; log_d("video_rec_start: out )))))))\n"); return 0; } static void video0_rec_close() { if (__this->video_rec0) { server_close(__this->video_rec0); __this->video_rec0 = NULL; } } static int video_rec_storage_device_ready(void *p) { __this->sd_wait = 0; #ifdef CONFIG_ENABLE_VLIST FILE_LIST_INIT(1); #endif if ((int)p == 1) { #ifdef CONFIG_NET_ENABLE printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__); video_rec_start_notify();//先停止网络实时流再录像,录像完毕再通知APP #else video_rec_start(); #endif #if TCFG_DEMO_UI_RUN /* extern OS_SEM video_sem; */ /* os_sem_post(&video_sem); */ #endif } return 0; } static int video_rec_stop(u8 close) { int err; __this->need_restart_rec = 0; if (__this->state != VIDREC_STA_START) { return 0; } puts("\nvideo_rec_stop\n"); __this->state = VIDREC_STA_STOPING; #ifdef CONFIG_VIDEO0_ENABLE err = video0_rec_stop(close); if (err) { puts("\nstop0 err\n"); } #endif __this->state = VIDREC_STA_STOP; puts("video_rec_stop: exit\n"); return 0; } static int video_rec_close() { #ifdef CONFIG_VIDEO0_ENABLE video0_rec_close(); #endif return 0; } static int device_ready(void *priv) { #ifdef CONFIG_UVC_VIDEO2_ENABLE if (uvc_host_online() < 0 || !storage_device_ready()) { return false; } return true; #else return storage_device_ready(); #endif } static int video_rec_init() { int err = 0; __this->sd_wait = wait_completion(device_ready, video_rec_storage_device_ready, (void *)1, NULL); return err; } static int video_rec_uninit() { int err; if (__this->state == VIDREC_STA_START) { return -EFAULT; } if (__this->sd_wait) { wait_completion_del(__this->sd_wait); __this->sd_wait = 0; } if (__this->char_wait) { wait_completion_del(__this->char_wait); __this->char_wait = 0; } if (__this->state == VIDREC_STA_START) { err = video_rec_stop(1); } video_rec_close(); video_rec_fscan_release(0); video_rec_fscan_release(1); __this->state = VIDREC_STA_FORBIDDEN; __this->lan_det_setting = 0; return 0; } /* *录像的状态机,进入录像app后就是跑这里 */ static int video_rec_state_machine(struct application *app, enum app_state state, struct intent *it) { int err = 0; int len; switch (state) { case APP_STA_CREATE: log_d("\n >>>>>>> video_rec: create\n"); memset(__this, 0, sizeof(struct video_rec_hdl)); video_rec_buf_alloc(); __this->state = VIDREC_STA_IDLE; break; case APP_STA_START: if (!it) { break; } switch (it->action) { case ACTION_VIDEO_REC_MAIN: puts("ACTION_VIDEO_REC_MAIN\n"); video_rec_init(); net_video_server_init(); break; case ACTION_VIDEO_REC_SET_CONFIG: break; case ACTION_VIDEO_REC_CHANGE_STATUS: break; } break; case APP_STA_PAUSE: puts("--------app_rec: APP_STA_PAUSE\n"); video_rec_fscan_release(0); video_rec_fscan_release(1); break; case APP_STA_RESUME: puts("--------app_rec: APP_STA_RESUME\n"); break; case APP_STA_STOP: puts("--------app_rec: APP_STA_STOP\n"); if (__this->state == VIDREC_STA_START) { video_rec_stop(0); } #ifdef CONFIG_NET_ENABLE video_rec_all_stop_notify(); #endif if (video_rec_uninit()) { err = 1; break; } break; case APP_STA_DESTROY: puts("--------app_rec: APP_STA_DESTROY\n"); if (video_rec_destroy()) { err = 2; break; } f_free_cache(CONFIG_ROOT_PATH); log_d("<<<<<<< video_rec: destroy\n"); break; } return err; } /* *录像app的按键响应函数 */ static int video_rec_key_event_handler(struct key_event *key) { int err; switch (key->action) { case KEY_EVENT_CLICK: switch (key->value) { case KEY_OK: printf("video_rec_key_ok: %d\n", __this->state); break; case KEY_MENU: break; case KEY_MODE: #ifdef CONFIG_NET_ENABLE video_rec_control_doing(); #endif puts("rec key mode\n"); break; case KEY_UP: break; case KEY_DOWN: break; default: break; } break; default: break; } return false; } static int video_rec_sd_in() { #ifdef CONFIG_NET_ENABLE video_rec_sd_event_ctp_notify(1); #endif #ifdef CONFIG_NET_ENABLE net_video_rec_status_notify(); #endif __this->lock_fsize_count = 0; return 0; } static int video_rec_sd_out() { video_rec_fscan_release(0); video_rec_fscan_release(1); if (__this->sd_wait == 0) { __this->sd_wait = wait_completion(device_ready, video_rec_storage_device_ready, (void *)1, NULL); } #ifdef CONFIG_NET_ENABLE video_rec_sd_event_ctp_notify(0); #endif return 0; } /* *录像app的设备响应函数 */ static int video_rec_device_event_handler(struct device_event *event) { int err; struct intent it; u8 *tpye; static u8 uvc_offline = 0; if (!ASCII_StrCmp(event->arg, "usb_host*", 9)) { switch (event->event) { case DEVICE_EVENT_IN: tpye = (u8 *)event->value; if (strstr(tpye, "uvc")) { __this->uvc_id = tpye[3] - '0'; } printf("UVC or msd_storage online : %s, id=%d\n", tpye, __this->uvc_id); if (uvc_offline) { uvc_offline = 0; if (__this->sd_wait == 0) { __this->sd_wait = wait_completion(device_ready, video_rec_storage_device_ready, (void *)1, NULL); } } break; case DEVICE_EVENT_OUT: tpye = (u8 *)event->value; if (strstr(tpye, "uvc")) { __this->uvc_id = tpye[3] - '0'; } printf("UVC or msd_storage offline : %s, id=%d\n", tpye, __this->uvc_id); uvc_offline = 1; #if TCFG_DEMO_UI_RUN //关闭UVC实时流 /* user_net_video_rec_close(1); */ user_video_rec0_close(); //关闭jpeg解码YUV jpeg2yuv_close(); #endif if (__this->state == VIDREC_STA_START) { video_rec_stop(1); } #ifdef CONFIG_NET_ENABLE net_video_rec_event_stop(); #endif break; } } else if (!ASCII_StrCmp(event->arg, "sd*", 4)) { switch (event->event) { case DEVICE_EVENT_IN: video_rec_sd_in(); break; case DEVICE_EVENT_OUT: if (!fmount_exist(CONFIG_STORAGE_PATH)) { video_rec_sd_out(); } break; } } else if (!ASCII_StrCmp(event->arg, "sys_power", 7)) { switch (event->event) { case DEVICE_EVENT_POWER_CHARGER_IN: puts("\n\ncharger in\n\n"); break; case DEVICE_EVENT_POWER_CHARGER_OUT: puts("charger out\n"); break; case DEVICE_EVENT_POWER_PERCENT:;//电池电量 int battery_val = event->value; printf("battery_val = %d \n", battery_val); break; } } else if (!ASCII_StrCmp(event->arg, "parking", 7)) { switch (event->event) { case DEVICE_EVENT_IN: puts("parking on\n"); //parking on return true; case DEVICE_EVENT_OUT://parking off puts("parking off\n"); return true; } } else if (!strcmp(event->arg, "camera0_err")) { log_e("camera0_err\n"); if (__this->state == VIDREC_STA_START) { video_rec_stop(0); video_rec_start(); } #ifdef CONFIG_NET_ENABLE net_video_rec_event_start(); #endif } return false; } /*录像app的事件总入口*/ static int video_rec_event_handler(struct application *app, struct sys_event *event) { switch (event->type) { case SYS_KEY_EVENT: return video_rec_key_event_handler((struct key_event *)event->payload); case SYS_DEVICE_EVENT: return video_rec_device_event_handler((struct device_event *)event->payload); case SYS_NET_EVENT: net_video_event_hander((void *)event->payload); return true; default: return false; } } static const struct application_operation video_rec_ops = { .state_machine = video_rec_state_machine, .event_handler = video_rec_event_handler, }; REGISTER_APPLICATION(app_video_rec) = { .name = "video_rec", .ops = &video_rec_ops, .state = APP_STA_DESTROY, };