#include "basic_include.h" #include "fatfs/osal_file.h" #include "lib/heap/av_heap.h" #include "lib/heap/av_psram_heap.h" #include "lib/multimedia/msi.h" #include "stream_define.h" #include "video_app/file_common_api.h" #include "loop_record_moudle/loop_record_moudle.h" void *avimuxer_init2(void *fp, uint32_t max_size, int w, int h, int frate, int gop, int h265, int sampnum); uint32_t avimuxer_video2(void *ctx, unsigned char *buf, int len, int key, unsigned pts, uint8_t insert); uint32_t avimuxer_audio2(void *ctx, unsigned char *buf, int len, int key, unsigned pts); void avimuxer_sync(void *ctx); void avimuxer_exit2(void *ctx); // 结构体申请空间函数 #define STREAM_MALLOC av_psram_malloc #define STREAM_FREE av_psram_free #define STREAM_ZALLOC av_psram_zalloc // 结构体申请空间函数 #define STREAM_LIBC_MALLOC os_malloc #define STREAM_LIBC_FREE os_free #define STREAM_LIBC_ZALLOC os_zalloc #define AVI_MAX_SINGLE_SIZE (50 * 1024 * 1024) // 单个AVI文件大小 #define AVI_LOOP_REMAIN_CAP (256) // 循环录像剩余空间控制 #define AVI_DIR "0:/AVI" #define AVI_FILE_NAME ".AVI" enum { MSI_AVI_STOP = BIT(0), MSI_AVI_THREAD_DEAD = BIT(1), }; struct avi_encode_msi_s { struct msi *msi; struct os_event evt; void *loop; // 循环录像的句柄,定时检查是否足够空间以及是否需要删除文件 uint16_t filter_type; uint8_t rec_time; }; static void *creat_avi_file(void **loop, char *file_name) { void *fp = NULL; void *node = NULL; char *dir_path; char file_path[64]; char sub_path[32]; uint32_t sd_cap = 0; int res = 0; uint8_t changeflag = 0; uint32_t file_size = 0; res = osal_fatfsfree("0:", NULL, &sd_cap); os_printf(KERN_INFO "sd_cap: %d\n", sd_cap); if (res != 0 || sd_cap == 0) { goto creat_avi_file_end; } get_node: if (sd_cap < AVI_LOOP_REMAIN_CAP) { if (!*loop) { *loop = get_file_list(AVI_DIR, AVI_FILE_NAME); } if (*loop) { node = get_file_node(*loop); if (!node) { free_file_list(*loop); *loop = get_file_list(AVI_DIR, AVI_FILE_NAME); if (!*loop) { _os_printf("%s %d\r\n", __FUNCTION__, __LINE__); goto creat_avi_file_end; } node = get_file_node(*loop); } while (!node) { dir_path = get_file_dir(*loop); res = osal_unlink_dir(dir_path, 0); if (res != FR_OK) { _os_printf("unlink dir %s err, res: %d\r\n", dir_path, res); res = osal_unlink_dir(dir_path, 1); if (res != FR_OK) { _os_printf("%s %d, force unlink dir %s err, res: %d\r\n", __FUNCTION__, __LINE__, dir_path, res); goto creat_avi_file_end; } _os_printf("force unlink dir %s\r\n", dir_path); } _os_printf("unlink dir %s\r\n", dir_path); free_file_list(*loop); *loop = get_file_list(AVI_DIR, AVI_FILE_NAME); if (!*loop) { _os_printf("%s %d\r\n", __FUNCTION__, __LINE__); goto creat_avi_file_end; } node = get_file_node(*loop); if (node) { break; } } file_size = get_file_size(node); if (file_size < AVI_MAX_SINGLE_SIZE) { char path[64]; char *name = get_file_name(node); dir_path = get_file_dir(*loop); os_sprintf(path, "%s/%s", dir_path, name); res = osal_unlink(path); if (res == FR_OK) { _os_printf("unlink file %s, filesize: %d\r\n", path, file_size); } else { _os_printf("%s %d\tunlink file %s fail, res: %d\r\n", __FUNCTION__, __LINE__, path, res); } free_file_node(node); node = NULL; goto get_node; } changeflag = 1; } else { _os_printf("%s %d\r\n", __FUNCTION__, __LINE__); goto creat_avi_file_end; } } if (get_extension_file_name(AVI_DIR, sub_path, file_name, AVI_FILE_NAME)) { _os_printf("%s %d\tget_avi_file_name fail\r\n", __FUNCTION__, __LINE__); goto creat_avi_file_end; } os_sprintf(file_path, "%s/%s", sub_path, file_name); os_printf(KERN_INFO "avi file_path: %s\r\n", file_path); void *sub_dir = osal_opendir(sub_path); if (!sub_dir) { res = osal_fmkdir(sub_path); if (res != FR_OK) { if (res == FR_DENIED) { char path[64]; char *name = get_file_name(node); dir_path = get_file_dir(*loop); os_sprintf(path, "%s/%s", dir_path, name); res = osal_unlink(path); if (res != FR_OK) { _os_printf("%s %d\tunlink file %s fail, res: %d\r\n", __FUNCTION__, __LINE__, path, res); goto creat_avi_file_end; } _os_printf("unlink file %s\r\n", path); free_file_node(node); node = NULL; res = osal_fmkdir(sub_path); if (res == FR_OK) { goto get_node; } else { _os_printf("%s %d\tmkdir %s fail, res: %d\r\n", __FUNCTION__, __LINE__, sub_path, res); goto creat_avi_file_end; } } _os_printf("mkdir %s err, create rec dir\r\n", sub_path); void *rec_dir = osal_opendir(AVI_DIR); if (!rec_dir) { res = osal_fmkdir(AVI_DIR); if (res != FR_OK) { _os_printf("%s %d\tmkdir %s fail, res: %d\r\n", __FUNCTION__, __LINE__, AVI_DIR, res); goto creat_avi_file_end; } else { res = osal_fmkdir(sub_path); if (res != FR_OK) { _os_printf("%s %d\tmkdir %s fail, res: %d\r\n", __FUNCTION__, __LINE__, sub_path, res); goto creat_avi_file_end; } } } else { osal_closedir(rec_dir); _os_printf("%s %d\tmkdir %s fail, res: %d\r\n", __FUNCTION__, __LINE__, sub_path, res); goto creat_avi_file_end; } } _os_printf("mkdir %s\r\n", sub_path); } else { osal_closedir(sub_dir); } if (changeflag) { char old_filepath[64]; char *name = get_file_name(node); dir_path = get_file_dir(*loop); os_sprintf(old_filepath, "%s/%s", dir_path, name); res = osal_rename(old_filepath, file_path); if (res != FR_OK) { _os_printf("rename file %s err, res: %d\r\n", old_filepath, res); if (sd_cap < AVI_LOOP_REMAIN_CAP) { _os_printf("%s %d\tsd_cap: %d\r\n", __FUNCTION__, __LINE__, sd_cap); free_file_node(node); node = NULL; goto get_node; } } else { _os_printf("rename file %s to %s\r\n", old_filepath, file_path); } #if 0 char thumb_path[64]; gen_thumb_path(name, thumb_path, sizeof(thumb_path)); res = osal_unlink(thumb_path); if (res != FR_OK) { _os_printf("unlink thumb_path %s err, res: %d\r\n", thumb_path, res); } else { _os_printf("unlink thumb_path: %s\r\n", thumb_path); } #endif free_file_node(node); node = NULL; char *min_file = get_min_file(*loop); if (min_file && (os_strcmp(file_name, min_file) < 0)) { free_file_list(*loop); *loop = NULL; _os_printf("%s %d\tfree_file_list\r\n", __FUNCTION__, __LINE__); } } fp = osal_fopen(file_path, "wb+"); creat_avi_file_end: if (node) { free_file_node(node); node = NULL; } return fp; } static int avi_encode_running(struct msi *msi, uint32_t save_time) { int ret = 0; uint32_t res = 0; uint32_t AVI_status = 0; uint32_t write_start_time = 0; uint32_t sys_start_time = os_jiffies(); uint32_t count_fps = 0; uint32_t audio_fps = 0; uint32_t last_syn_time = os_jiffies(); uint32_t already_save_time = 0; uint32_t fbtime = 0; struct avi_encode_msi_s *avi_encode = (struct avi_encode_msi_s *) msi->priv; struct framebuff *fb = NULL; uint32_t fps = 25; uint32_t fps_time = 1000 / fps; char filename[64]; void *fp = creat_avi_file(&avi_encode->loop, filename); void *ctx = avimuxer_init2(fp, AVI_MAX_SINGLE_SIZE, 1280, 720, fps, 0, 0, 0); os_printf(KERN_INFO"fp:%X\tctx:%X\n", fp, ctx); if (fp && ctx) { ret = 0; } while (fp) { os_event_wait(&avi_encode->evt, MSI_AVI_STOP, &AVI_status, OS_EVENT_WMODE_OR, 0); // 结束写卡 if (AVI_status & MSI_AVI_STOP) { ret = 1; os_printf("%s:%d", __FUNCTION__, __LINE__); goto avi_encode_thread_end; } fb = msi_get_fb(msi, 0); if (fb && fb->mtype == F_JPG) { if (write_start_time == 0) { write_start_time = fb->time; } count_fps++; _os_printf(KERN_INFO "O"); if ((fb->time - write_start_time) / fps_time > count_fps) { uint32_t insert_num = ((fb->time - write_start_time) / fps_time) - count_fps; for (int i = 0; i < insert_num; i++) { res |= avimuxer_video2(ctx, fb->data, fb->len, 1, 40, 1); count_fps++; } } res |= avimuxer_video2(ctx, fb->data, fb->len, 1, 40, 0); fbtime = fb->time; msi_delete_fb(NULL, fb); fb = NULL; if (res || fbtime - write_start_time >= save_time) { goto avi_encode_thread_end; } } // 音频添加 else if (write_start_time && fb && fb->mtype == F_AUDIO) { _os_printf(KERN_INFO "A"); audio_fps++; res |= avimuxer_audio2(ctx, fb->data, fb->len, 0, 0); msi_delete_fb(NULL, fb); fb = NULL; if (res) { goto avi_encode_thread_end; } } else if (fb) { msi_delete_fb(NULL, fb); fb = NULL; } else { os_sleep_ms(1); } // 如果系统时间超过了30s依然没有保存完成,就直接退出 if (os_jiffies() - sys_start_time >= save_time + 30 * 1000) { goto avi_encode_thread_end; } if (os_jiffies() - last_syn_time > 1000) { // avimuxer_sync(ctx); last_syn_time = os_jiffies(); already_save_time++; os_printf(KERN_INFO"sync:%d %d %d\n", already_save_time, count_fps,audio_fps); } os_sleep_ms(1); } avi_encode_thread_end: os_printf(KERN_EMERG "%s:%d\tres:%d\n", __FUNCTION__, __LINE__, res); if (fb) { msi_delete_fb(NULL, fb); } if (ctx) { avimuxer_exit2(ctx); } if (fp) { osal_fclose(fp); fp = NULL; } // os_printf("save time:%d\tv_count:%d\n",(uint32_t)os_jiffies(),v_count); os_printf("avi encode end\n"); return ret; } // 测试文件保存,保存一分钟吧,一直录卡,直到关闭msi static void avi_encode_thread(void *d) { int ret = 0; struct msi *msi = (struct msi *) d; struct avi_encode_msi_s *avi_encode = (struct avi_encode_msi_s *) msi->priv; msi_get(msi); while (msi) { msi->enable = 1; ret = avi_encode_running(msi, avi_encode->rec_time * 1000); msi->enable = 0; os_printf(KERN_EMERG "%s:%d ret:%d\tend\n", __FUNCTION__, __LINE__, ret); if (ret) { break; } } os_printf("%s:%d\tret:%d\n", __FUNCTION__, __LINE__, ret); if (avi_encode->loop) { free_file_list(avi_encode->loop); avi_encode->loop = NULL; } struct framebuff *fb; while (1) { fb = msi_get_fb(msi, 0); if (fb) { msi_delete_fb(NULL, fb); } else { break; } } os_event_set(&avi_encode->evt, MSI_AVI_THREAD_DEAD, NULL); msi_put(msi); os_printf("%s:%d end\n", __FUNCTION__, __LINE__); } static int32_t avi_encode_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2) { int32_t ret = RET_OK; struct avi_encode_msi_s *avi_encode = (struct avi_encode_msi_s *) msi->priv; switch (cmd_id) { case MSI_CMD_POST_DESTROY: os_event_wait(&avi_encode->evt, MSI_AVI_THREAD_DEAD, NULL, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, -1); os_event_del(&avi_encode->evt); STREAM_LIBC_FREE(avi_encode); break; case MSI_CMD_PRE_DESTROY: os_event_set(&avi_encode->evt, MSI_AVI_STOP, NULL); break; case MSI_CMD_TRANS_FB: { struct framebuff *fb = (struct framebuff *) param1; if (fb->mtype == F_JPG && avi_encode->filter_type != (uint16_t) ~0) { ret = RET_ERR; if (avi_encode->filter_type == fb->stype) { ret = RET_OK; } } } break; case MSI_CMD_GET_RUNNING: { uint32_t rflags = 0; os_event_wait(&avi_encode->evt, MSI_AVI_THREAD_DEAD | MSI_AVI_STOP, &rflags, OS_EVENT_WMODE_OR, 0); if (param1) { *(uint32_t *) param1 = (rflags & (MSI_AVI_THREAD_DEAD | MSI_AVI_STOP)) ? 0 : 1; } } break; } return ret; } struct msi *avi_encode_msi_init(const char *avi_msi_name, uint16_t filter_type, uint8_t rec_time) { struct avi_encode_msi_s *avi_encode = NULL; struct msi *msi = msi_new(avi_msi_name, 64, NULL); if (msi && !msi->priv) { avi_encode = (struct avi_encode_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct avi_encode_msi_s)); ASSERT(avi_encode); avi_encode->filter_type = filter_type; avi_encode->rec_time = rec_time; msi->priv = avi_encode; os_event_init(&avi_encode->evt); avi_encode->msi = msi; msi->action = avi_encode_msi_action; msi->enable = 1; } else { // 不要重复打开,同一个名称需要等待上一次写入完成后并且关闭后才可以重新打开 // 因为这里new了一次,所以要destroy一次(new和destroy要配对,实际msi的destroy是在另一个地方) if (msi) { msi_destroy(msi); msi = NULL; goto avi_encode_msi_init_end; } } void *avi_hdl = os_task_create("avi_encode", avi_encode_thread, msi, OS_TASK_PRIORITY_NORMAL, 0, NULL, 2048); os_printf("avi_hdl:%X\n", avi_hdl); if (!avi_hdl && avi_encode) { os_event_set(&avi_encode->evt, MSI_AVI_THREAD_DEAD, NULL); } avi_encode_msi_init_end: return msi; }