#include "app_config.h" #include "cpu.h" #include "fs/fs.h" #include "app_config.h" #include "board_config.h" #include "generic/circular_buf.h" #include "app_config.h" #include "asm/audio_adc.h" #include "generic/circular_buf.h" #include "media/audio_decoder.h" #include "media/mixer.h" #include "generic/log.h" #include "app_config.h" #include "system/timer.h" #include "device/device.h" #include "key_event_deal.h" #include "fs.h" #include "res/resfile.h" #include "jlui/ui.h" #include "jlui_app/ui_style.h" #include "jlui_app/ui_api.h" #include "jlui_app/res_config.h" #include "jlui_app/ui_resource.h" #include "jlui_app/ui_sys_param.h" #include "timer.h" // 定时器 #include "ui/lcd/lcd_drive.h" // lcd 驱动 #include "res_config.h" // res 文件管理 #include #include "ascii.h" #include "res/font_ascii.h" #include "res/mem_var.h" #include "res/zz.h" #include "res/jpeg_decoder.h" #include "dev_manager.h" #include "fs/fs.h" #include "watch_syscfg_manage.h" #include "ui_core.h" #include "control.h" #include "dbi.h" // 推屏模块 #include "jlgpu_math.h" // matrix #include "jlgpu_driver.h" // gpu 驱动 #include "gpu_port.h" // GPU 接口,依赖于 jlgpu_driver.h #include "gpu_task.h" #include "ui_resource.h" // ui资源管理 #include "jlui_effect/jlui_effect.h" //特效相关 #include "ui_expand/buffer_manager.h" // buffer管理 #include "ui_expand/ui_expand.h" // 部分宏定义 #include "font/font_all.h" #include "font/font_textout.h" #include "ui_draw/ui_circle.h" #include "ui_animation.h" #include "font/language_list.h" #include "ui_measure.h" #include "jljpeg_decode.h" #include "gpu_draw.h" #include "ui_page_switch.h" #include "ui_draw/ui_basic.h" #include "ui_text.h" #include "jlui_app/ui_style.h" #include "res/resfile.h" #include "media/includes.h" #include "audio_config.h" #include "gpio.h" #include "app_task.h" #include "bt.h" #define LOG_TAG_CONST AVI_VIDEO #define LOG_TAG "[AVI_VIDEO]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE #define LOG_CLI_ENABLE #include "debug.h" #include "avi_video.h" #include "avi_audio_player.h" #if TCFG_VIDEO_DIAL_ENABLE #define AVI_TASK_NAME "avi_task" #define NEW_PARSE_RIFF_FILE 1 // 该宏打开使用新的文件解析流程,与旧流程主要区别在于兼容packres工具打包后的avi资源解析 #define DEFAULT_DWSUGGESTEDBUFFER_SIZE 20*1024 #define PCM_CBUF_SIZE (20*1024) /* 根据负载可调 320B*3帧约等于1KB */ struct avi_fs_info { FILE *fp; u32 offset; u32 len; int width; int height; }; cbuffer_t avi_pcm_cbuf; static u8 avi_play_mode = AVI_PLAY_LOOP; static MV_DRAW *avi_player = NULL; static PLAY_VIDEO_PATH_MANAGE *video_path_mange = NULL; #define __this (avi_player) /* static void *gpu_task_head; */ static void *avi_taskp; OS_SEM avi_msg_sem; static const char *avi_files[] = { "storage/UI_FAT/C/Btest.avi", // 可以继续添加更多文件 }; static u16 task_switch_flag; u16 app_video_task_switch_flag_get() { return task_switch_flag; } u16 app_video_task_switch_flag_set(u16 flag) { task_switch_flag = flag; return task_switch_flag; } /* static u8 __this->param.is_audio_mute = 1; */ #define AVI_FILE_COUNT (sizeof(avi_files) / sizeof(avi_files[0])) static void parse_list_chunk(FILE *file, long list_end, ChunkCallback callback, void *user_param, long file_size, const char **parent_hierarchy, int parent_level); typedef void (*DisplayMJpegFunc)(void *player, FILE *file, int data, int chunk_size, int width, int height); typedef void (*PlayPCMFunc)(void *player, FILE *file, int data, int chunk_size); /* --------------- */ /* extern define */ /* --------------- */ extern struct ui_load_info ui_load_info_table[]; extern UI_RESFILE *res_file; extern UI_RESFILE *str_file; extern void *jlui_malloc(int size, u32 ram_type, u32 module_type); extern void *jlui_zalloc(int size); extern void jlui_free(void *buf, u32 ram_type, u32 module_type); extern void jlui_malloc_ram_info_clr(u8 clr); extern void ui_module_ram_dump(); extern u32 psram_get_used_block(void); extern void avi_resume(void *avip); extern int avi_exec(void *avip, DisplayMJpegFunc jpgcb, PlayPCMFunc pcmcb); extern void avi_close(void *avip); extern void *avi_open(const char *path, int en_buf, int redrawid); extern u8 *avi_get_mjpegbuf(void *avip); extern int avi_get_mjpegbuflen(void *avip); extern int jpeg_module_opj_init(void *priv); extern char *watch_avi_get_related_path(u8 cur_watch); extern int watch_get_style(); extern uint32_t lv_tick_get(void); extern void audio_app_volume_set(u8 state, s16 volume, u8 fade); extern const u8 norflash_ext_sfc_en; // 给gpu判断互斥 extern void norflash_espi_mutex_enter(); extern void norflash_espi_mutex_exit(); extern u32 ps_ram_size[]; extern int avi_get_width(void *avip); extern int avi_get_height(void *avip); extern int avi_fluh(void *priv); extern void malloc_list_update_rets(void *buf, int rets); void avi_stop(void *avip); void avi_pause(void *avip); void animig_close(void *ap); /* void *animig_open(char *name, AVI_PARAM param, int arg); */ static void *avi_zalloc(size_t size) { u32 rets; __asm__ volatile("%0 = rets":"=r"(rets)); void *p = NULL; #if TCFG_PSRAM_DEV_ENABLE p = malloc_psram(size); memset(p, 0, size); #else p = zalloc(size); #endif malloc_list_update_rets(p, rets); return p; } static void *avi_malloc(size_t size) { u32 rets; __asm__ volatile("%0 = rets":"=r"(rets)); void *p = NULL; #if TCFG_PSRAM_DEV_ENABLE p = malloc_psram(size); #else p = malloc(size); #endif malloc_list_update_rets(p, rets); return p; } static void avi_free(void *pv) { #if TCFG_PSRAM_DEV_ENABLE free_psram(pv); #else free(pv); #endif } // 添加AI voice播放头文件引用 //#include "ai_rx_player.h" // PCM cbuf资源(对齐ai链路命名) static void pcm_cbuf_init(void) { if (!__this->avi_pcm_buf) { __this->avi_pcm_buf = avi_malloc(PCM_CBUF_SIZE); } ASSERT(__this->avi_pcm_buf, "pcm cbuf avi_malloc fail\n"); cbuf_init(&avi_pcm_cbuf, __this->avi_pcm_buf, PCM_CBUF_SIZE); __this->avi_audio_start = 1; log_info("\n[pcm_cbuf_init]mem_status:\n"); mem_stats(); } static void pcm_cbuf_deinit(void) { __this->avi_audio_start = 0; if (__this->avi_pcm_buf) { cbuf_clear(&avi_pcm_cbuf); avi_free(__this->avi_pcm_buf); __this->avi_pcm_buf = NULL; } log_info("\n[pcm_cbuf_deinit]mem_status:\n"); mem_stats(); } __attribute__((weak)) u8 get_avi_audio_status() { if (__this == NULL) { return 0; } return __this->avi_audio_start; } static void avi_switch_timeunlock(void *p) { if (__this == NULL) { return ; } __this->avi_is_switching = 0; wdt_clear(); } static void avi_switch_timelock(void) { __this->avi_is_switching = 1; u16 lock_timer = 0; lock_timer = sys_timeout_add(NULL, avi_switch_timeunlock, 200); } // 设置文件路径 void set_avi_index(int index) { __this->avi_index = index; } const char *set_avi_play_file(void) { if (__this == NULL || __this->avi_index > AVI_FILE_COUNT - 1 || __this->avi_index < 0) { log_error("\n\navi index overflow or __this is null,show default avi\n\n"); return avi_files[0]; } return avi_files[__this->avi_index]; } void avi_set_avi_playtimer_id(u16 timer_id) { if (__this == NULL) { log_error("%s __this is NULL", __func__); return ; } __this->avi_playtimer = timer_id; } u16 avi_get_avi_playtimer_id() { if (__this == NULL) { log_error("%s __this is NULL", __func__); return 0; } return __this->avi_playtimer; } // 新的avi停止接口,确保avi完全停止以及释放资源 void avi_play_shutdown(void) { u32 rets; __asm__ volatile("%0 = rets":"=r"(rets)); log_info("\n\n avi_play_shutdown rets=%x\n\n", rets); #if TCFG_APP_VIDEO_EN if (app_get_current_mode_name() == APP_MODE_VIDEO) { app_mode_stack_clr(APP_MODE_VIDEO); app_task_switch_back(); } #endif if (__this == NULL) { return ; } if (__this->avi_playtimer) { sys_timeout_del(__this->avi_playtimer); __this->avi_playtimer = 0; } jlgpu_scheduler_wait_sync(); if (avi_player) { animig_close(avi_player); avi_player = NULL; } log_info("\n\n[avi_play_shutdown]mem_status:\n"); mem_stats(); } void video_manage_init() { video_path_mange = avi_zalloc(sizeof(PLAY_VIDEO_PATH_MANAGE)); } void set_avi_play_mode(u8 mode) { avi_play_mode = mode; avi_play_shutdown(); } // 播放下一个文件 void avi_play_next(void) { AVI_PARAM param; if (__this != NULL) { memcpy(¶m, &(__this->param), sizeof(AVI_PARAM));//拷贝当前配置 } u8 is_dial = __this->param.is_dial; u8 *video_path = NULL; if (is_dial == 0) { video_path = (u8 *)param.fname; } else { video_path = (u8 *)watch_avi_get_related_path(watch_get_style()); } if (video_path == NULL) { log_error("%s path is NULL"); return ; } if (!__this->avi_is_switching) { avi_switch_timelock(); wdt_clear(); avi_play_shutdown(); avi_player = animig_open((char *)video_path, param, 3); // 缓存3帧可以达到流畅播放30FPS /* avi_player = (MV_DRAW *) animig_open((char *)set_avi_play_file(), 0, 3); // 缓存3帧可以达到流畅播放30FPS */ __this->avi_playtimer = 0; // UI_HIDE_CURR_WINDOW(); // UI_SHOW_WINDOW(AVI_WINDOW); // UI_SHOW_WINDOW(STYLE_DIAL_ID(WATCH)); } } void avi_play_loop(void) { AVI_PARAM param; if (__this != NULL) { memcpy(¶m, &(__this->param), sizeof(AVI_PARAM));//拷贝当前配置 } u8 is_dial = __this->param.is_dial; u8 *video_path = NULL; if (is_dial == 0) { video_path = (u8 *)param.fname; } else { video_path = (u8 *)watch_avi_get_related_path(watch_get_style()); } if (video_path == NULL) { log_error("%s path is NULL"); return ; } if (!__this->avi_is_switching) { avi_switch_timelock(); wdt_clear(); avi_play_shutdown(); avi_player = animig_open((char *)video_path, param, 3); // 缓存3帧可以达到流畅播放30FPS /* avi_player = (MV_DRAW *) animig_open((char *)set_avi_play_file(), 0, 3); // 缓存3帧可以达到流畅播放30FPS */ __this->avi_playtimer = 0; // UI_HIDE_CURR_WINDOW(); // UI_SHOW_WINDOW(AVI_WINDOW); // UI_SHOW_WINDOW(STYLE_DIAL_ID(WATCH)); } } // 播放上一个文件 void avi_play_prev(void) { AVI_PARAM param; if (__this != NULL) { memcpy(¶m, &(__this->param), sizeof(AVI_PARAM));//拷贝当前配置 } u8 is_dial = __this->param.is_dial; u8 *video_path = NULL; if (is_dial == 0) { video_path = (u8 *)param.fname; } else { video_path = (u8 *)watch_avi_get_related_path(watch_get_style()); } if (video_path == NULL) { log_error("%s path is NULL"); return ; } if (!__this->avi_is_switching) { avi_switch_timelock(); wdt_clear(); avi_play_shutdown(); avi_player = animig_open((char *)video_path, param, 3); // 缓存3帧可以达到流畅播放30FPS /* avi_player = (MV_DRAW *) animig_open((char *)set_avi_play_file(), 0, 3); // 缓存3帧可以达到流畅播放30FPS */ __this->avi_playtimer = 0; // UI_HIDE_CURR_WINDOW(); // UI_SHOW_WINDOW(AVI_WINDOW); // UI_SHOW_WINDOW(STYLE_DIAL_ID(WATCH)); } } static size_t avi_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { int len = fread(ptr, size, nmemb, stream); // log_info("pos %d len %d retlen %d", ftell(stream), size*nmemb, len); len = len / size; return len; } #define rewind(fp) fseek(fp, 0, SEEK_SET) char parse_riff_file(FILE *file, ChunkCallback callback, void *user_param, int curr_pos) { char *head = ""; /* 获取文件总大小 */ // fseek(file, 0, SEEK_END); log_info("\n\n\nparse_riff_file: initial file position: %u\n", ftell(file)); fseek(file, 0, SEEK_SET); __this->file_offset = ftell(file); log_info("\n\n\nparse_riff_file: after seek to 0, position: %u\n", ftell(file)); long file_size = flen(file); // ftell(file); // flen fseek(file, 0, SEEK_SET); log_info("flen %ld", file_size); fseek(file, curr_pos, SEEK_SET); char chunk_id[5] = {0}; u32 chunk_size; char format[5] = {0}; /* 读取RIFF头 */ if (avi_fread(chunk_id, 1, 4, file) != 4) { log_error("Failed to read chunk ID"); return -AVI_READ_CHUNK_ID_FAIL; } chunk_id[4] = '\0'; if (avi_fread(&chunk_size, 4, 1, file) != 1) { log_error("Failed to read chunk size"); return -AVI_READ_CHUNK_SIZE_FAIL; } log_info("\n\n\n\n\nRIFF chunk_size: %d\n\n\n\n\n", chunk_size); /* 校验块大小合法性 */ long current_pos = ftell(file) - __this->file_offset; log_info("\n\n\n\n[%s]current_pos: %d, chunk_size: %d, file_size: %d\n\n\n\n", __func__, (int)current_pos, chunk_size, (int)file_size); if (current_pos + chunk_size > file_size) { log_info("校验块大小合法性 %d %d %d", (int)current_pos, chunk_size, (int)file_size); log_error("RIFF chunk size exceeds file size"); return -AVI_FILE_LEN_ERR; } /* 处理子块 */ long riff_data_end = ftell(file) + chunk_size - __this->file_offset; log_info("riff_data_end %d %d", (int)riff_data_end, chunk_size); fseek(file, curr_pos, SEEK_SET); while ((ftell(file) - __this->file_offset) < riff_data_end) { wdt_clear(); char sub_chunk_id[5] = {0}; u32 sub_chunk_size; /* 读取子块头 */ if (avi_fread(sub_chunk_id, 1, 4, file) != 4) { log_error("Failed to read sub chunk ID"); return -AVI_READ_SUB_CHUNK_ID_FAIL; } sub_chunk_id[4] = '\0'; if (avi_fread(&sub_chunk_size, 4, 1, file) != 1) { log_error("Failed to read sub chunk size"); return -AVI_READ_SUB_CHUNK_ID_FAIL; } /* 校验子块大小 */ current_pos = ftell(file) - __this->file_offset; if (current_pos + sub_chunk_size > file_size) { log_info("校验子块大小 %d %d %d", (int)current_pos, sub_chunk_size, (int)file_size); log_error("Sub chunk exceeds file size"); return -AVI_SUB_CHUNK_LEN_ERR; } off_t sub_start = ftell(file) - 8 - __this->file_offset; // 计算子块起始位置 /* 处理LIST块 */ if (strcmp(sub_chunk_id, "LIST") == 0 || strcmp(sub_chunk_id, "RIFF") == 0) { off_t list_start = sub_start; // LIST块起始位置 char list_type[5] = {0}; if (avi_fread(list_type, 1, 4, file) != 4) { log_error("Failed to read LIST type"); return -AVI_LIST_TYPE_ERR; } list_type[4] = '\0'; int jump = 0; /* 构建LIST块层级信息 */ const char *list_hierarchy[] = {head, format, sub_chunk_id, list_type}; /* 调用回调时传递list_type */ if (callback) callback(file, list_start, sub_chunk_id, sub_chunk_size, list_type, list_hierarchy, 4, &jump, user_param); if (jump) { // fseek(file, sub_chunk_size - 4, SEEK_CUR); fseek(file, current_pos + sub_chunk_size, SEEK_SET); } else { long list_end = ftell(file) - __this->file_offset + sub_chunk_size - 4; parse_list_chunk(file, list_end, callback, user_param, file_size, list_hierarchy, 4); fseek(file, current_pos + sub_chunk_size, SEEK_SET); // add } } else { /* 构建子块层级信息 */ const char *sub_hierarchy[] = {head, format, sub_chunk_id}; /* 处理子块时调用回调 */ if (callback) callback(file, sub_start, sub_chunk_id, sub_chunk_size, NULL, sub_hierarchy, 3, NULL, user_param); fseek(file, current_pos + sub_chunk_size, SEEK_SET); } /* 处理对齐填充 */ if (sub_chunk_size % 2 != 0) { fseek(file, 1, SEEK_CUR); } } return 0; } /* LIST块解析实现 */ static void parse_list_chunk(FILE *file, long list_end, ChunkCallback callback, void *user_param, long file_size, const char **parent_hierarchy, int parent_level) { while (ftell(file) - __this->file_offset < list_end) { wdt_clear(); off_t chunk_start = ftell(file) - __this->file_offset; // 记录块起始位置 char sub_chunk_id[5] = {0}; u32 sub_chunk_size; /* 读取子块头 */ if (avi_fread(sub_chunk_id, 1, 4, file) != 4) { log_error("Failed to read sub chunk ID"); return; } sub_chunk_id[4] = '\0'; if (avi_fread(&sub_chunk_size, 4, 1, file) != 1) { log_error("Failed to read sub chunk size"); return; } /* 校验子块大小 */ long current_pos = ftell(file) - __this->file_offset; if (current_pos + sub_chunk_size > file_size) { log_error("Sub chunk exceeds file size"); return; } /* 构建层级信息 */ const char **current_hierarchy = avi_malloc((parent_level + 1) * sizeof(const char *)); memcpy(current_hierarchy, parent_hierarchy, parent_level * sizeof(const char *)); current_hierarchy[parent_level] = sub_chunk_id; /* 处理嵌套LIST块 */ if (strcmp(sub_chunk_id, "LIST") == 0) { char list_type[5] = {0}; if (avi_fread(list_type, 1, 4, file) != 4) { log_error("Failed to read nested LIST type"); avi_free(current_hierarchy); return; } list_type[4] = '\0'; int jump = 0; off_t nested_start = chunk_start; // log_info("nested_start %d", nested_start); /* 构建嵌套层级信息 */ const char **nested_hierarchy = avi_malloc((parent_level + 2) * sizeof(const char *)); memcpy(nested_hierarchy, current_hierarchy, (parent_level + 1) * sizeof(const char *)); nested_hierarchy[parent_level + 1] = list_type; callback(file, nested_start, sub_chunk_id, sub_chunk_size, list_type, nested_hierarchy, parent_level + 2, &jump, user_param); if (jump) { // fseek(file, sub_chunk_size - 4, SEEK_CUR); fseek(file, current_pos + sub_chunk_size, SEEK_SET); } else { long nested_end = ftell(file) - __this->file_offset + sub_chunk_size - 4; parse_list_chunk(file, nested_end, callback, user_param, file_size, nested_hierarchy, parent_level + 2); fseek(file, current_pos + sub_chunk_size, SEEK_SET); } avi_free(nested_hierarchy); } else { /* 调用回调 */ // log_info("chunk_start %d", chunk_start); if (callback) callback(file, chunk_start, sub_chunk_id, sub_chunk_size, NULL, current_hierarchy, parent_level + 1, NULL, user_param); // fseek(file, sub_chunk_size, SEEK_CUR); fseek(file, current_pos + sub_chunk_size, SEEK_SET); } /* 处理对齐填充 */ if (sub_chunk_size % 2 != 0) { fseek(file, 1, SEEK_CUR); } avi_free(current_hierarchy); } } // 打印流头信息(增强版) void print_stream_header(const StreamHeader *h) { const char *stream_type = "Unknown"; if (strncmp(h->fccType, "vids", 4) == 0) { stream_type = "Video Stream"; } else if (strncmp(h->fccType, "auds", 4) == 0) { stream_type = "Audio Stream"; } // 常见FourCC编解码器识别 const char *codec_name = "Unknown"; char fourcc[5] = {0}; memcpy(fourcc, h->fccHandler, 4); if (strcmp(fourcc, "MJPG") == 0) { codec_name = "Motion-JPEG"; } else if (strcmp(fourcc, "MP43") == 0) { codec_name = "MPEG-4 V3"; } else if (strcmp(fourcc, "DIVX") == 0) { codec_name = "DivX"; } else if (strcmp(fourcc, "H264") == 0) { codec_name = "H.264"; } else if (strcmp(fourcc, "AAC") == 0) { codec_name = "AAC Audio"; } else if (strcmp(fourcc, "mp3") == 0) { codec_name = "MPEG Layer-3"; } wdt_clear(); log_info("[strh] Stream Header @ 0x%08X\n", (uint32_t)h); log_info(" Stream Type: %s (%s)\n", stream_type, fourcc); log_info(" Codec: %s\n", codec_name); log_info(" Time Base: %u/%u (%.2f fps)\n", h->dwScale, h->dwRate, (h->dwScale > 0) ? (float)h->dwRate / h->dwScale : 0); log_info(" Stream Length: %u samples\n", h->dwLength); log_info(" Sample Size: %u bytes\n", h->dwSampleSize); log_info(" Buffer Size: %u bytes\n", h->dwSuggestedBufferSize); log_info(" Quality: %u/10000\n", h->dwQuality); log_info(" Flags: %s%s%s\n", (h->dwFlags & 0x0001) ? "AVISF_DISABLED " : "", (h->dwFlags & 0x0002) ? "AVISF_VIDEO_PALCHANGES " : "", (h->dwFlags & 0x0004) ? "AVISF_KNOWN_FLAGS " : ""); log_info("--------------------------------\n"); wdt_clear(); } // 打印视频格式(增强版) void print_video_format(const VideoFormat *f) { const char *compression = "Unknown"; char fourcc[5] = {0}; memcpy(fourcc, &f->biCompression, 4); if (strcmp(fourcc, "MJPG") == 0) { compression = "Motion-JPEG"; } else if (strcmp(fourcc, "DIB ") == 0) { compression = "Uncompressed RGB"; } else if (strcmp(fourcc, "MP4V") == 0) { compression = "MPEG-4 Video"; } else if (strcmp(fourcc, "H264") == 0) { compression = "H.264/AVC"; } log_info("[strf] Video Format @ 0x%08X\n", (uint32_t)f); log_info(" Dimensions: %ux%u pixels\n", f->biWidth, f->biHeight); log_info(" Compression: %s (%s)\n", fourcc, compression); log_info(" Color Planes: %u\n", f->biPlanes); log_info(" Bit Depth: %u bits/pixel\n", f->biBitCount); log_info(" Frame Size: %u bytes\n", f->biSizeImage); log_info(" Aspect Ratio: %.2f:1\n", (f->biWidth > 0 && f->biHeight > 0) ? (float)f->biWidth / (float)f->biHeight : 0); log_info(" Data Rate: %.2f MB/s\n", (f->biSizeImage > 0) ? (float)f->biSizeImage * 30.0f / (1024 * 1024) : 0); // 假设30fps log_info("--------------------------------\n"); } // 打印音频格式(增强版) void print_audio_format(const AudioFormat *f) { const char *format = "Unknown"; switch (f->wFormatTag) { case 0x0001: format = "PCM"; break; case 0x0055: format = "MP3"; break; case 0x0050: format = "MPEG2"; break; case 0x000F: format = "IMA ADPCM"; break; case 0x0161: format = "WMAv2"; break; case 0x0160: format = "WMAv1"; break; case 0x0162: format = "WMA Pro"; break; } log_info("[strf] Audio Format @ 0x%08X\n", (uint32_t)f); log_info(" Format: %s (0x%04X)\n", format, f->wFormatTag); log_info(" Channels: %u (%s)\n", f->nChannels, f->nChannels == 1 ? "Mono" : f->nChannels == 2 ? "Stereo" : "Multi-channel"); log_info(" Sample Rate: %u Hz\n", f->nSamplesPerSec); log_info(" Bit Depth: %u bits\n", f->wBitsPerSample); log_info(" Bitrate: %u kbps\n", (f->nAvgBytesPerSec * 8) / 1000); log_info(" Block Align: %u bytes\n", f->nBlockAlign); log_info(" Audio Duration: %.2f sec (estimated)\n", (f->nSamplesPerSec > 0) ? (float)f->nSamplesPerSec / f->nSamplesPerSec : 0); // 需要实际帧数计算 log_info("--------------------------------\n"); } static void parse_callback(FILE *file, off_t offset, const char *chunk_id, uint32_t size, const char *list_name, const char **hierarchy, int level, int *jump, void *user_param) { AVIPlayer *p = (AVIPlayer *)user_param; // 解析AVI主头 if (!strcmp(chunk_id, "avih") /* && level == 4 && !strcmp(hierarchy[3], "hdrl")*/ ) { fseek(file, offset + 8, SEEK_SET); AVIH_Header avih; avi_fread(&avih, sizeof(avih), 1, file); p->total_frames = avih.dwTotalFrames; p->frame_rate = 1000000 / avih.dwMicroSecPerFrame; p->total_duration = (uint64_t)avih.dwTotalFrames * (uint64_t)avih.dwMicroSecPerFrame; p->width = (int)avih.dwWidth; p->height = (int)avih.dwHeight; log_info("%d %d", p->width, p->height); log_info("%u %u", avih.dwWidth, avih.dwHeight); wdt_clear(); log_info("info %u %d %ld", p->total_frames, p->frame_rate, (long)p->total_duration); log_info("AVI 头信息:\n"); log_info("每帧时长(微秒): %u\n", avih.dwMicroSecPerFrame); log_info("最大字节/秒: %u\n", avih.dwMaxBytesPerSec); log_info("数据填充粒度: %u\n", avih.dwPaddingGranularity); log_info("全局标志: %08X\n", avih.dwFlags); // 十六进制显示更直观 log_info("总帧数: %u\n", avih.dwTotalFrames); log_info("初始帧数: %u\n", avih.dwInitialFrames); log_info("流数量: %u\n", avih.dwStreams); log_info("建议缓冲区大小: %u\n", avih.dwSuggestedBufferSize); log_info("视频宽度: %u 像素\n", avih.dwWidth); log_info("视频高度: %u 像素\n", avih.dwHeight); log_info("保留字段: [%u][%u][%u][%u]\n", // 显示保留字段内容 avih.dwReserved[0], avih.dwReserved[1], avih.dwReserved[2], avih.dwReserved[3]); wdt_clear(); p->avih_header = avih; } // 流头 else if (!strcmp(chunk_id, "strh")) { StreamHeader stream_header = {0}; fseek(file, offset + 8, SEEK_SET); avi_fread(&stream_header, 1, sizeof(stream_header), file); print_stream_header(&stream_header); if (!strncmp(stream_header.fccType, "vids", 4)) { p->video_stream_header = stream_header; p->stream_type = STREAM_VIDEO; } else if (!strncmp(stream_header.fccType, "auds", 4)) { p->audio_stream_header = stream_header; p->stream_type = STREAM_AUDIO; } else { p->stream_type = STREAM_NULL; } } // 流格式 else if (!strcmp(chunk_id, "strf")) { fseek(file, offset + 8, SEEK_SET); if (p->stream_type == STREAM_VIDEO) { VideoFormat vf = {0}; avi_fread(&vf, 1, sizeof(vf), file); print_video_format(&vf); p->video_format = vf; } else if (p->stream_type == STREAM_AUDIO) { AudioFormat af = {0}; avi_fread(&af, 1, sizeof(af), file); print_audio_format(&af); p->audio_format = af; } } // 记录movi列表位置 else if (!strcmp(chunk_id, "LIST") && list_name && !strcmp(list_name, "movi")) { p->movi_data_start = offset + 8; // LIST头(12字节)后开始 p->movi_data_len = size; log_info("movi %d", (int)p->movi_data_start); *jump = 1; // 跳过movi节省遍历时间 } // 计算总帧数 // 记录idx1块信息 else if (!strcmp(chunk_id, "idx1")) { p->idx1_offset = offset + 8; // 跳过chunk_id和size p->idx1_size = size; log_info("idx1 %d %d", (int)p->idx1_offset, (int)p->idx1_size); } } void *avi_open(const char *path, int en_buf, int redrawid) { // myavi_fread_cleanup(); log_info("%s %d %s", __func__, __LINE__, path); FILE *fp = fopen(path, "r"); AVIPlayer *player = avi_zalloc(sizeof(AVIPlayer)); wdt_clear(); log_info("%s %d fp %p", __func__, __LINE__, fp); mem_stats(); if (!fp) { log_error("%s open file fail "); goto __err; } log_info("%s %d", __func__, __LINE__); mem_stats(); player->file = fp; player->redrawid = redrawid; log_info("%s %d", __func__, __LINE__); mem_stats(); // 解析文件结构 log_info("\n\n\nBefore parse_riff_file: file position: %u\n", ftell(fp)); char parse_ret = parse_riff_file(fp, parse_callback, player, 0); if (parse_ret != 0) { log_error("%s check file fail =%d", __func__, parse_ret); goto __err; } log_info("%s %d", __func__, __LINE__); mem_stats(); rewind(fp); if (player->avih_header.dwTotalFrames == 0) { log_error("%s frames info no get "); goto __err; } void *usr_mmu = strstr(path, "virfat_flash"); if (usr_mmu) { player->view_file_info = avi_zalloc(sizeof(struct flash_file_info)); ASSERT(player->view_file_info); ui_res_flash_info_get(player->view_file_info, (char *)path, "res", true); player->cache.cnt = en_buf; player->cache.block_len = 0; player->cache.m = avi_zalloc(player->cache.cnt * sizeof(mjpegdata)); player->cache.m[0].time = lv_tick_get(); player->cache.act_idx = player->cache.pre_idx = 0; } else if (en_buf) { if (player->video_stream_header.dwSuggestedBufferSize == 0) { player->video_stream_header.dwSuggestedBufferSize = DEFAULT_DWSUGGESTEDBUFFER_SIZE; } int len = (player->video_stream_header.dwSuggestedBufferSize + 3) & ~3; log_info("%d", len); player->cache.cnt = en_buf; player->cache.block_len = len; player->cache.data = avi_zalloc(player->cache.cnt * player->cache.block_len); ASSERT(player->cache.data); player->cache.m = avi_zalloc(player->cache.cnt * sizeof(mjpegdata)); for (int i = 0; i < player->cache.cnt; i++) { player->cache.m[i].data = player->cache.data + i * player->cache.block_len; } extern uint32_t lv_tick_get(void); player->cache.m[0].time = lv_tick_get(); player->cache.act_idx = player->cache.pre_idx = 0; } else { ASSERT(0, "非norflash资源需要使能缓存"); } log_info("%s %d", __func__, __LINE__); mem_stats(); // os_time_dly(100); AVIPlayer *p = player; // // p->audio_format.wFormatTag = 0; // if(p->audio_format.wFormatTag == 0x0001) { // log_info("%s %d", __func__, __LINE__ ); // mem_stats(); // app_audio_state_switch(APP_AUDIO_STATE_MUSIC, app_audio_volume_max_query(AppVol_MUSIC), NULL); // 音量状态设置 // audio_dac_set_volume(&dac_hdl, app_audio_volume_max_query(AppVol_MUSIC)/100); // dac 音量设置 // audio_dac_set_sample_rate(&dac_hdl, p->audio_format.nSamplesPerSec * p->audio_format.nChannels); // 采样率设置 // log_info(">>> rate %d",p->audio_format.nSamplesPerSec * p->audio_format.nChannels); // audio_dac_start(&dac_hdl); // dac 启动 // audio_dac_channel_start(NULL); // JL_APA->APA_VL0 = 0x080F080F;//0x080F080F;//0x40004000; // audio_dac_set_volume(&dac_hdl, 1); // } os_mutex_create(&p->mutex); return player; __err: printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__); void avi_close(void *avip); avi_close(player); return NULL; } void avi_close(void *avip) { // myavi_fread_cleanup(); AVIPlayer *p = avip; if (p) { avi_stop(p); // avi_fs.offset = 0; if (p->timerid) { sys_timer_del(p->timerid); p->timerid = 0; } if (p->cache.m) { for (int i = 0; i < p->cache.cnt; i++) { if (p->cache.m[i].opj) { jljpeg_decode_release(p->cache.m[i].opj, 1); jpeg_free(p->cache.m[i].opj->device); jpeg_free(p->cache.m[i].opj); p->cache.m[i].opj = NULL; } } } if (p->cache.m) { avi_free(p->cache.m); p->cache.m = NULL; } if (p->cache.data) { free_psram(p->cache.data); p->cache.data = NULL; } // if(p->mjpegbuf){ // avi_free(p->mjpegbuf); // p->mjpegbuf = NULL; // } fclose(p->file); if (p->view_file_info) { ui_res_flash_info_free(p->view_file_info, "res"); avi_free(p->view_file_info); p->view_file_info = NULL; } if (p) { avi_free(p); p = NULL; } } } static off_t find_frame_offset(AVIPlayer *p, int frame_num) { // 每个索引条目16字节: ckid(4)+flags(4)+offset(4)+size(4) off_t entry_pos = p->idx1_offset + frame_num * 16; fseek(p->file, entry_pos, SEEK_SET); struct { char ckid[4]; uint32_t flags; uint32_t offset; uint32_t size; } entry; avi_fread(&entry, sizeof(entry), 1, p->file); return p->movi_data_start + entry.offset; } // 定位时间 void avi_seek(void *avip, float sec) { AVIPlayer *p = (AVIPlayer *)avip; p->lv_pos = 0; int frame_num = sec * p->frame_rate; if (frame_num >= p->total_frames) { frame_num = p->total_frames - 1; } p->current_pos = find_frame_offset(p, frame_num); } // 定位帧数 void avi_seek_frame(void *avip, int frame_num) { AVIPlayer *p = (AVIPlayer *)avip; p->lv_pos = 0; if (frame_num >= p->total_frames) { frame_num = p->total_frames - 1; } p->current_pos = find_frame_offset(p, frame_num); } u8 get_avi_pause_status() { if (__this == NULL || __this->pause == 1) { return true; } return false; } void avi_pause() { /* AVIPlayer *p = (AVIPlayer *)avip; */ AVIPlayer *p = (AVIPlayer *)__this->st; if (__this == NULL || __this->pause == 1 || p == NULL) { return; } p->ui_work = 2; avi_player->pause = 1; os_taskq_del_type("ui", Q_CALLBACK); if (p->audio_format.wFormatTag == 0x0001 && 0) { audio_dac_stop(&dac_hdl); audio_dac_close(&dac_hdl); } p->is_playing = 0; } static void avi_timer(void *priv); void avi_pcm_open(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; // 初始化循环缓冲区和互斥锁 if (p->audio_format.wFormatTag == 0) { return ; } pcm_cbuf_init(); // 创建AI voice播放参数 struct avi_audio_player_param param = {0}; param.coding_type = AUDIO_CODING_PCM; param.channel_mode = (p->audio_format.nChannels == 1) ? AUDIO_CH_L : AUDIO_CH_MIX; param.sample_rate = p->audio_format.nSamplesPerSec; param.bit_rate = p->audio_format.nSamplesPerSec * p->audio_format.nChannels * p->audio_format.wBitsPerSample; param.type = AVI_SERVICE_VOICE; // 帧长度 该参数底层库未使用 故注释 // param.frame_dms = 8; // p->total_duration / p->total_frames; log_info("[AVI_PCM] 音频参数: 采样率=%u, 声道=%d, 位深=%d, 帧长度=%dms\n", p->audio_format.nSamplesPerSec, p->audio_format.nChannels, p->audio_format.wBitsPerSample, param.frame_dms); // 打开AI voice播放 int ret = avi_audio_player_open(NULL, 0, ¶m); if (ret != 0) { log_info("[AVI_PCM] AI voice播放打开失败: %d\n", ret); } else { log_info("[AVI_PCM] AI voice播放打开成功\n"); } /* audio_app_volume_set(APP_AUDIO_STATE_MUSIC, 2, 1); */ } void avi_pcm_close(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; if (p->audio_format.wFormatTag == 0) { return ; } log_info("%s %d ", __func__, __LINE__); pcm_cbuf_deinit(); avi_audio_player_close(0); } void avi_resume() { /* AVIPlayer *p = (AVIPlayer *)avip; */ AVIPlayer *p = (AVIPlayer *)__this->st; printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__); /* log_info("dwMicroSecPerFrame %u", p->avih_header.dwMicroSecPerFrame); */ if (avi_player == NULL || avi_player->pause == 0) { return; } int argv[3] = {0}; int retry = 3; argv[0] = (int)avi_timer; argv[1] = 1; argv[2] = (int)p; /* int ret = os_taskq_post_type("ui", Q_CALLBACK, 3, argv); */ avi_player->pause = 0; /* avi_pcm_open(p); */ if (p->audio_format.wFormatTag == 0x0001 && 0) { log_info("%s %d", __func__, __LINE__); mem_stats(); app_audio_state_switch(APP_AUDIO_STATE_MUSIC, app_audio_volume_max_query(AppVol_MUSIC), NULL); // 音量状态设置 audio_dac_set_volume(&dac_hdl, app_audio_volume_max_query(AppVol_MUSIC) / 100); // dac 音量设置 audio_dac_set_sample_rate(&dac_hdl, p->audio_format.nSamplesPerSec * p->audio_format.nChannels); // 采样率设置 log_info(">>> rate %u", p->audio_format.nSamplesPerSec * p->audio_format.nChannels); audio_dac_start(&dac_hdl); // dac 启动 audio_dac_channel_start(NULL); JL_APA->APA_VL0 = 0x40004000; // 0x080F080F;//0x40004000; audio_dac_set_volume(&dac_hdl, 1); } p->is_playing = 1; p->ui_work = 0; } void avi_stop(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; avi_pause(avip); p->is_playing = 0; p->current_pos = 0; } typedef void (*DisplayMJpegFunc)(void *player, FILE *file, int data, int chunk_size, int width, int height); typedef void (*PlayPCMFunc)(void *player, FILE *file, int data, int chunk_size); void avi_resume(void *avip); int avi_exec(void *avip, DisplayMJpegFunc jpgcb, PlayPCMFunc pcmcb) { AVIPlayer *p = (AVIPlayer *)avip; int argv[2] = {0}; int ret; /* log_info("%s %d is_playing %d,", __func__, __LINE__, p->is_playing); */ if (!p) { return -1; } if (p->current_pos == 0) { avi_seek(avip, 0.0); } u8 is_vid = 0; while (is_vid == 0) { // 到达movi数据末尾时停止播放 if (p->current_pos >= p->movi_data_start + p->movi_data_len) { switch (avi_play_mode) { case AVI_PLAY_LOOP: // 默认模式,单视频循环 /* p->current_pos = 0; */ /* avi_seek(avip, 0.0); */ argv[0] = (int)avi_play_loop; argv[1] = 0; ret = os_taskq_post_type("ui", Q_CALLBACK, 2, argv); log_info("\n\n[AVI_PLAY_LOOP]%s %d is_playing %d\n\n", __func__, __LINE__, p->is_playing); return -1; break; case AVI_PLAY_ONCE: // 单视频播放一次(如有需要) /* argv[0] = (int)avi_play_next; */ /* argv[1] = 0; */ /* ret = os_taskq_post_type("ui", Q_CALLBACK, 2, argv); */ log_info("\n\n[AVI_PLAY_SINGLE]%s %d is_playing %d\n\n", __func__, __LINE__, p->is_playing); return -1; break; case AVI_PLAY_SEQUENCE: // 按顺序播放视频 argv[0] = (int)avi_play_next; argv[1] = 0; ret = os_taskq_post_type("ui", Q_CALLBACK, 2, argv); log_info("\n\n[AVI_PLAY_SEQUENCE]%s %d is_playing %d\n\n", __func__, __LINE__, p->is_playing); return -1; break; default: break; } } fseek(p->file, p->current_pos, SEEK_SET); char ckid[5] = {0}; uint32_t chunk_size; // 读取当前chunk头信息 if (avi_fread(ckid, 4, 1, p->file) != 1 || avi_fread(&chunk_size, 4, 1, p->file) != 1) { log_error("Failed to read chunk header"); p->current_pos = 0; return -1; } // 处理视频帧 if (strncmp(ckid, "00dc", 4) == 0) { // log_info("00dc"); // jpgcb(p, p->file, p->lv_pos, p->lv_size, p->width, p->height); jpgcb(p, p->file, p->current_pos + 8, chunk_size, p->width, p->height); p->lv_pos = p->current_pos + 8; p->lv_size = chunk_size; is_vid = 1; } // 处理音频帧 else if (strncmp(ckid, "01wb", 4) == 0) { // log_info("01wb"); pcmcb(p, p->file, p->current_pos + 8, chunk_size); } // 跳过未知chunk类型 else { log_error("Skipping unknown chunk: %s\n", ckid); put_buf((u8 *)ckid, 5); p->current_pos = 0; } if (avi_player->pause == 1) { break; } // 更新位置指针(chunk数据大小 + 头8字节) p->current_pos += chunk_size + 8; // 处理填充字节(奇数长度对齐) if (chunk_size % 2 != 0) { p->current_pos++; } if (is_vid == 1) { break; } } return 0; } int avi_get_width(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; return p->width; } int avi_get_height(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; return p->height; } void *avi_get_fp(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; return p->file; } int avi_get_now_len(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; return p->now_len; } int avi_get_now_offset(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; return p->now_offset; } // u8* avi_get_mjpegbuf(void* avip){AVIPlayer* p = (AVIPlayer*)avip;return p->mjpegbuf;} int avi_get_mjpegbuflen(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; return p->video_stream_header.dwSuggestedBufferSize; } int avi_get_redrawid(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; return p->redrawid; } float avi_get_total_sec(void *avip) { AVIPlayer *p = (AVIPlayer *)avip; return (float)p->total_duration / 1000000.0; } void *avi_get_view_file_info(void *priv) { AVIPlayer *p = (AVIPlayer *)priv; return (p->view_file_info); } /* GPU task id 产生 */ #define GPU_TASK_ID(page, id, index) ((page << 26) | (0 << 24) | (id << 8) | index) /* ------------------------------------------------------------------------------------*/ /** * @brief JLGPU_TASK_INHERIT_PARENT 继承父控件的GPU参数 * * 注意:这里可能会修改task_param的draw参数,应放到draw参数赋值之后 */ /* ------------------------------------------------------------------------------------*/ #define JLGPU_TASK_INHERIT_PARENT(tran) \ { \ if (dc->elm && dc->elm->parent) \ { \ log_debug("%s(), get parent info!:%x\n", __func__, dc->elm->id); \ struct element *elm = dc->elm->parent; \ struct element *par = elm->parent; \ if ((par) && (ui_id2type(par->id) == CTRL_TYPE_GRID)) \ { \ elm = par; \ } \ u8 is_overstepping_parent; \ pJLGPUTaskUnit_t task_parent = jlgpu_get_task_by_id(dc->gpu_task_head, JLGPU_ID_NONE, elm->id); \ if (task_parent) \ { \ is_overstepping_parent = false; \ struct element *elm = ui_core_get_element_by_id(task_parent->info.element_id); \ if (elm) \ { \ ui_core_get_element_rect_relative_screen(elm, &task_param.area); \ jlgpu_shift_rect(&task_param.area); \ } \ else \ { \ jlgpu_get_task_rect(task_parent, &task_param.area); \ } \ } \ else \ { \ is_overstepping_parent = true; \ /* ui_core_get_element_abs_rect(elm, &task_param.area); */ \ ui_core_get_element_rect_relative_screen(elm, &task_param.area); \ } \ /*log_info("%s(), parent area[%d, %d, %d, %d]\n", __func__, \ task_param.area.left, task_param.area.top, task_param.area.width, task_param.area.height); */ \ INHERIT_PARENT_AFFINE(tran); \ if (task_param.matrix && is_overstepping_parent) \ { \ gpu_boundbox_t parent_area; \ parent_area.minx = task_param.area.left; \ parent_area.maxx = task_param.area.width + parent_area.minx; \ parent_area.miny = task_param.area.top; \ parent_area.maxy = task_param.area.height + parent_area.miny; \ gpu_matrix_get_boundbox(task_param.matrix, &parent_area, &parent_area); \ task_param.area.left = parent_area.minx; \ task_param.area.width = parent_area.maxx - parent_area.minx; \ task_param.area.top = parent_area.miny; \ task_param.area.height = parent_area.maxy - parent_area.miny; \ } \ else if (is_overstepping_parent) \ { \ jlgpu_shift_rect(&task_param.area); \ /* struct lcd_info *lcd_info = &(((struct ui_priv *)__this)->info); */ \ /* task_param.area.left += ((GPU_BOUND_BOX_MAX - lcd_info->width) / 2); */ \ /* task_param.area.top += ((GPU_BOUND_BOX_MAX - lcd_info->height) / 2); */ \ } \ } \ } /* ------------------------------------------------------------------------------------*/ /** * @brief JLGPU_TASK_SCALE_CONFIG 配置GPU任务的缩放参数 */ /* ------------------------------------------------------------------------------------*/ #define JLGPU_TASK_SCALE_CONFIG(tran) \ { \ task_param.scale_en = dc->elm->ratio_en; \ if (task_param.scale_en && dc->elm->css.part) \ { \ (tran)->ratio_w = dc->elm->css.part->ratio.ratio_w; \ (tran)->ratio_h = dc->elm->css.part->ratio.ratio_h; \ log_debug("%s(), scale_en: %d, ratio_w: %f, ratio_h: %f\n", __func__, \ task_param.scale_en, (tran)->ratio_w, (tran)->ratio_h); \ } \ } /* ------------------------------------------------------------------------------------*/ /** * @brief INHERIT_PARENT_AFFINE 继承父控件的变换矩阵 */ /* ------------------------------------------------------------------------------------*/ #define INHERIT_PARENT_AFFINE(tran) \ { \ for (; elm; elm = elm->parent) \ { \ pJLGPUTaskUnit_t task_parent = jlgpu_get_task_by_id(dc->gpu_task_head, JLGPU_ID_NONE, elm->id); \ if (task_parent && task_parent->info.matrix) \ { \ log_info("%s(), get parent affine! parent elm_id: 0x%x\n", __func__, elm->id); \ task_param.matrix = task_parent->info.matrix; \ struct rect parent_rect = {0}; \ ui_core_get_element_rect_relative_screen(elm, &parent_rect); \ /* ui_core_get_element_rect_relative_parent(elm, &parent_rect); */ \ /* DUMP_RECT(__func__, __LINE__, "parent", &parent_rect); */ \ task_param.draw.left -= (/* parent_rect.left < 0 ? 0 : */ parent_rect.left); \ task_param.draw.top -= (/* parent_rect.top < 0 ? 0 : */ parent_rect.top); \ if (task_param.draw.height <= 0 || task_param.draw.width <= 0) \ { \ task_param.invisible = true; \ } \ if (task_param.rotate_en) \ { \ (tran)->rotate_dx -= parent_rect.left; \ (tran)->rotate_dy -= parent_rect.top; \ } \ } \ } \ } /* ------------------------------------------------------------------------------------*/ /** * @brief JLGPU_TASK_ROTATE_CONFIG 配置GPU任务的旋转参数 */ /* ------------------------------------------------------------------------------------*/ #define JLGPU_TASK_ROTATE_CONFIG(tran) \ { \ task_param.rotate_en = dc->elm->rotate_en; \ if (task_param.rotate_en && dc->elm->css.part) \ { \ (tran)->rotate_cx = dc->elm->css.part->rotate.cent_x; \ (tran)->rotate_cy = dc->elm->css.part->rotate.cent_y; \ (tran)->rotate_dx = dc->elm->css.part->rotate.dx; \ (tran)->rotate_dy = dc->elm->css.part->rotate.dy; \ (tran)->rotate_angle = dc->elm->css.part->rotate.angle; \ log_debug("%s(), rotate_en: %d, cx:%d, cy:%d, dx:%d, dy:%d, angle:%f\n", __func__, \ task_param.rotate_en, (tran)->rotate_cx, (tran)->rotate_cy, \ (tran)->rotate_dx, (tran)->rotate_dy, (tran)->rotate_angle); \ } \ } /* ------------------------------------------------------------------------------------*/ /** * @brief jlui_image_invert_deal 图片翻转处理 * * @param dc 绘图句柄 * @param taskp gpu任务 * * @return */ /* ------------------------------------------------------------------------------------*/ static int jlui_image_invert_deal(struct draw_context *dc, pJLGPUTaskUnit_t taskp) { gpu_matrix_t matrix; struct rect win_rect; jlgpu_get_win_rect(&win_rect); jlgpu_get_matrix(taskp, &matrix); // 获取任务矩阵 gpu_matrix_translate(&matrix, win_rect.left, win_rect.top); // 回到gpu原点 gpu_matrix_flip(&matrix, dc->draw_img.invert_x_en, dc->draw_img.invert_y_en); // 翻转 int invert_x_offset = 0; int invert_y_offset = 0; if (dc->draw_img.invert_x_en == 1) { invert_x_offset += 2 * taskp->info.image_width; invert_x_offset += dc->draw_img.invert_x_dist; } else if (dc->draw_img.invert_x_en == 2) { invert_x_offset -= dc->draw_img.invert_x_dist; } if (dc->draw_img.invert_y_en == 1) { invert_y_offset += 2 * taskp->info.image_height; invert_y_offset += dc->draw_img.invert_y_dist; } else if (dc->draw_img.invert_y_en == 2) { invert_y_offset -= dc->draw_img.invert_y_dist; } gpu_matrix_translate(&matrix, invert_x_offset, invert_y_offset); // 翻转后偏移 int trans_x = -win_rect.left; int trans_y = -win_rect.top; if (dc->draw_img.invert_x_en) { trans_x *= -1; } if (dc->draw_img.invert_y_en) { trans_y *= -1; } gpu_matrix_translate(&matrix, trans_x, trans_y); // 回到gpu中心 jlgpu_set_matrix(taskp, &matrix); // 配置任务矩阵 gpu_boundbox_t bbox; bbox.minx = 0; bbox.miny = 0; bbox.maxx = taskp->info.image_width; bbox.maxy = taskp->info.image_height; gpu_matrix_get_boundbox(&matrix, &bbox, &bbox); // 计算翻转后的有效区域 jlgpu_driver_set_bbox(&taskp->task_addr, &bbox); // 配置任务有效区域 return 0; } /* ------------------------------------------------------------------------------------*/ /** * @brief jlui_draw_jpeg_cb jpeg绘图回调 * * @param id 0 * @param dst_buf 屏显buffer * @param dst_r 屏显buffer rect * @param src_r 图片/控件尺寸 * @param bytes_per_pixel 格式 * @param priv jpeg句柄 * @param matrix 变换矩阵 */ /* ------------------------------------------------------------------------------------*/ static void jlui_draw_jpeg_cb(int id, u8 *dst_buf, struct rect *dst_r, struct rect *src_r, u8 bytes_per_pixel, void *priv, void *matrix) { if ((dst_r->width != lcd_get_screen_width()) && (dst_r->width + dst_r->left != lcd_get_screen_width())) { // 不支持局部刷新 return; } jdec_opj *opj = (jdec_opj *)priv; // jpeg解码句柄 opj->matrix = matrix; // 获取变换矩阵 // 计算缩放系数 float ratio_w = 1.0; float ratio_h = 1.0; if (opj->matrix) { gpu_matrix_t *m = matrix; ratio_w = m->m[0][0]; // ratio_h = m->m[1][1]; // /* log_info("ratio_w: %f, ratio_h: %f\n", ratio_w, ratio_h); */ } /* 计算变换后图片的输出区域 */ struct rect jpeg_draw_src; memcpy(&jpeg_draw_src, src_r, sizeof(struct rect)); // 放大时为中心放大 int center_y = src_r->top + src_r->height / 2; jpeg_draw_src.height = opj->height / ratio_h; jpeg_draw_src.top = center_y - jpeg_draw_src.height / 2; int center_x = src_r->left + src_r->width / 2; jpeg_draw_src.width = opj->width / ratio_w; jpeg_draw_src.left = center_x - jpeg_draw_src.width / 2; /* DUMP_RECT(__func__, __LINE__, "dst_r", dst_r); //推屏buf */ /* DUMP_RECT(__func__, __LINE__, "src_r", src_r); //jpeg显示区域 */ /* DUMP_RECT(__func__, __LINE__, "jpeg_draw_src", &jpeg_draw_src); //jpeg缩放后区域 */ /* 计算图片输出和推屏Buf的重叠区域 */ struct rect cover_r_t; if (!get_rect_cover(dst_r, src_r, &cover_r_t)) { return; } // 计算缩放后的区域*以推屏区域、jpeg缩放区域、控件显示区域的交集作为最终输出,放大时受控件尺寸限制会变成中心放大 struct rect cover_r; get_rect_cover(&cover_r_t, &jpeg_draw_src, &cover_r); /* DUMP_RECT(__func__, __LINE__, "cover_r", &cover_r); //重叠区域 */ // 计算jpeg解码的区域 int draw_top = cover_r.top - jpeg_draw_src.top; // 绘制的top int draw_btm = cover_r.height + draw_top; // 绘制的bottom int draw_h = cover_r.height; // 绘制高度 /* int dec_top = draw_top * ratio_h; //需要解码的top */ /* int dec_btm = ceilf((float)draw_btm * ratio_h); //需要解码的高度,这里需要向上取 */ /* int dec_h = dec_btm - dec_top; //解码高度 */ int max_dec_height = (opj->msy << 3) / ratio_h; // 一个mcu分块缩放后可以有效绘制的行数 int sub_top = 0; int sub_height = (draw_h > max_dec_height) ? max_dec_height : draw_h; int a = jiffies_usec(); // 按mcu快缩放后的有效高度进行分行处理 while (sub_top + draw_top < draw_btm) { int dec_sub_top = (draw_top + sub_top) * ratio_h; int dec_sub_btm = ceilf((float)(draw_top + sub_top + sub_height) * ratio_h); int dec_sub_h = dec_sub_btm - dec_sub_top; /* log_info("%s draw(%d %d) dec(%d %d)%d maxdec%d sub(%d %d) dec_sub(%d %d)", */ /* __func__, draw_top, draw_btm, dec_top, dec_btm, dec_h, max_dec_height, sub_top, sub_height, dec_sub_top, dec_sub_btm); */ struct rect jpeg_dec_r; /*jpeg解码区域*/ /* jpeg_dec_r.left = (cover_r.left - src_r->left) * ratio_w; */ jpeg_dec_r.left = (cover_r.left - jpeg_draw_src.left) * ratio_w; jpeg_dec_r.top = dec_sub_top; jpeg_dec_r.width = cover_r.width * ratio_w; jpeg_dec_r.height = dec_sub_h; struct rect block_r; /*缩放后显示的区域*/ block_r.top = cover_r.top + sub_top; block_r.height = sub_height; block_r.left = dst_r->left; block_r.width = dst_r->width; /* DUMP_RECT(__func__, __LINE__, "block_r", &block_r); */ /* DUMP_RECT(__func__, __LINE__, "jpeg_dec_r", &jpeg_dec_r); */ u8 *disp_buf = dst_buf + (block_r.top - dst_r->top) * 2 * lcd_get_screen_width(); /*启动解码*/ // log_info("--"); jljpeg_decode_start(opj, &jpeg_draw_src, &jpeg_dec_r, &cover_r, &block_r, disp_buf); sub_top += sub_height; sub_height = (sub_top + sub_height + draw_top < draw_btm) ? max_dec_height : draw_btm - draw_top - sub_top; } int b = jiffies_usec(); // log_info(">>>%d", b-a); } /* ------------------------------------------------------------------------------------*/ /** * @brief jlui_jpeg_infunc jpeg位流输入 * * @param opj 句柄 * @param buf 位流buf * @param len 预获取长度 * * @return 实际数据长度 */ /* ------------------------------------------------------------------------------------*/ static volatile const u32 psram_size = (u32)ps_ram_size; #define IS_SFC1_ADDR(addr) (!psram_size && ((addr) >= 0x08000000) && ((addr) < 0x0c000000)) static u32 jlui_jpeg_infunc(struct _jdec_opj *opj, u8 *buf, u32 len) { u32 rets; __asm__ volatile("%0 = rets" : "=r"(rets)); /* log_info("%s jpeg_info:%x %x buf:%x rets:0x%x\n", __func__, (int)opj, (int)opj->device, (int)buf, rets); */ struct flash_file_info *image_info = (struct flash_file_info *)opj->device; int start_addr, end_addr, curr_addr, read_len; if (image_info->tab) { // 使用mmu_table start_addr = image_info->tab[0] + image_info->offset; end_addr = image_info->tab[image_info->tab_size / 4 - 1] + image_info->last_tab_data_len; curr_addr = start_addr + opj->curr_offset; read_len = (curr_addr + len > end_addr) ? end_addr - curr_addr : len; } else { start_addr = image_info->offset; end_addr = image_info->offset + image_info->last_tab_data_len; curr_addr = start_addr + opj->curr_offset; read_len = (curr_addr + len > end_addr) ? end_addr - curr_addr : len; } /* log_info("%s start_addr:0x%x end_addr:0x%x file_len:%d curr_addr:0x%x len:%d read_len:%d", */ /* __func__, start_addr, end_addr, end_addr - start_addr, curr_addr, len, read_len); */ if (buf) { if (norflash_ext_sfc_en && IS_SFC1_ADDR(curr_addr)) { norflash_espi_mutex_enter(); } memcpy(buf, (const u8 *)curr_addr, read_len); if (norflash_ext_sfc_en && IS_SFC1_ADDR(curr_addr)) { norflash_espi_mutex_exit(); } } return read_len; } static u32 jlui_jpeg_infunc_by_fs(struct _jdec_opj *opj, u8 *buf, u32 len) { u32 rets; __asm__ volatile("%0 = rets" : "=r"(rets)); struct avi_fs_info *image_info = (struct avi_fs_info *)opj->device; if (image_info->fp) { static int all_time = 0; static int seek_time = 0; if (opj->curr_offset == 0) { // log_info("ss"); all_time = 0; seek_time = 0; } else { // log_info("cb"); } if (buf) { int a = jiffies_usec(); FILE *fp = image_info->fp; fseek(fp, opj->curr_offset + image_info->offset, SEEK_SET); if (opj->curr_offset + len > image_info->len) { len = image_info->len - opj->curr_offset; } int c = jiffies_usec(); int ret = avi_fread(buf, 1, len, fp); int b = jiffies_usec(); all_time += b - a; seek_time += c - a; // log_info("%d", opj->curr_offset); if (len == 0) { // log_info(">%d %d %d %d", all_time, seek_time, opj->curr_offset, len); } else { // log_info("=%d %d", b-a, opj->curr_offset + image_info->offset); } return ret; } else { return len; } } else { // RAM if (opj->curr_offset == 0) { // log_info("ss"); } else { // log_info("cb"); } if (buf) { if (opj->curr_offset + len > image_info->len) { len = image_info->len - opj->curr_offset; } memcpy(buf, (u8 *)image_info->offset + opj->curr_offset, len); return len; } else { return len; } } } static void _write_pcm(void *player, u8 *ptr, int len); static int _write_jlaudio(void *player, u8 *ptr, int len) { AVIPlayer *pp = (AVIPlayer *)player; // log_info("[_write_aivoice] len:%d\n", len); int cnt = 0; if (__this->param.is_audio_mute != 1) { while (len) { u32 w = cbuf_write(&avi_pcm_cbuf, ptr, len); // 直接按len写,返回实际写入 /* log_info("[cbuf_write] w:%d len=%d\n", w, len); */ if (w == 0) { os_time_dly(1); continue; } ptr += w; len -= w; } } pp->jump_mjpeg = jiffies_usec(); return len; } static void play_pcm(void *player, FILE *file, int data, int chunk_size) { AVIPlayer *ppp = (AVIPlayer *)player; if (ppp->audio_format.wFormatTag == 0) { return; } // int a = jiffies_usec(); uint8_t *pp = avi_malloc(chunk_size + 4); uint8_t *p = pp; p = (u8 *)(((int)p + 3) & ~3); avi_fread((void *)p, chunk_size, 1, file); // int b = jiffies_usec(); int rlen = _write_jlaudio(player, p, chunk_size); avi_free(pp); // log_info("pcm %d s %d",b-a, chunk_size); } static void display_mjpeg(void *player, FILE *file, int data, int chunk_size, int width, int height) { AVIPlayer *p = (AVIPlayer *)player; // 找空闲的buf int idle = -1; os_mutex_pend(&p->mutex, 0); int i = p->cache.pre_idx; i++; if (i >= p->cache.cnt) { i = 0; } if (i != p->cache.act_idx) { idle = i; } os_mutex_post(&p->mutex); if (idle != -1) { p->cache.m[idle].pos = data; p->cache.m[idle].len = chunk_size; // log_info("w %d %x", idle, 0); if (p->cache.m[idle].data) { // static void *temp; // if(temp==0){ // temp = malloc_psram(60*1024); // } int rlen = avi_fread(p->cache.m[idle].data, 1, chunk_size, file); // rlen = avi_fread(temp, 1, chunk_size, file); // int ret = memcmp(temp, p->cache.m[idle].data, chunk_size); // ASSERT(ret); } /* if (chunk_size == chunk_size) */ if (1) { // psram_flush_cache(p->cache.m[idle].data, chunk_size); os_mutex_pend(&p->mutex, 0); p->cache.pre_idx = idle; os_mutex_post(&p->mutex); p->buf_ok = 1; } else { log_info("read mjpeg error %d", 0); } } // os_mutex_post(&p->mutex); return; } // 等待上一帧GPU合成完成和忙标志,即等待文件系统空闲 // if(音频帧) // 遍历所有写cbuf // if(视频帧) // 直接ui 发送draw gpu任务,使能忙标志 int avi_fluh(void *priv) { return avi_exec(priv, display_mjpeg, play_pcm); } static void avi_timer(void *priv) { // log_info("%s %d", __func__, __LINE__); // log_info("priv %x", priv); AVIPlayer *p = (AVIPlayer *)priv; // if(p->ui_work){ // return; // } int a = jiffies_usec(); // 等待上一个画面绘制完成,这个过程不能操作所有avi_fread // p->ui_work = 0; // jlgpu_scheduler_wait_sync(); int b = jiffies_usec(); if (p->ui_work == 2 || -1 == avi_exec(priv, display_mjpeg, play_pcm)) { p->ui_work = 0; log_info("exti avitimer"); p->is_playing = 0; return; } // jlgpu_scheduler_wait_sync(); int c = jiffies_usec(); // log_info("avi_timer %d %d", b-a, c-b); int argv[3] = {0}; int retry = 3; argv[0] = (int)avi_timer; argv[1] = 1; argv[2] = (int)p; __try_again: int ret = os_taskq_post_type("ui", Q_CALLBACK, 3, argv); if (ret) { if (retry) { os_time_dly(1); retry--; goto __try_again; } log_info("%s post ret:%d retry:%d\n", __func__, ret, retry); } } // void avi_draw(void *dc){ // if(avi_fs.offset){ // jlui_draw_image(dc, 0, 0, 0, avi_fs); // } // } static void _write_pcm(void *player, u8 *ptr, int len) { // log_info(">>> _write_pcm len=%d\n", len); // int len = len; AVIPlayer *pp = (AVIPlayer *)player; while (len) { // 往 dac 写数据 // log_info("ptr %d len %d\n", ptr, len); // u8 * p = avi_malloc(len); // static u8 p[4096]; // memcpy(p, ptr, len); int wlen = audio_dac_write(&dac_hdl, ptr, len); // avi_free(p); if (wlen != len) { // dac缓存满了,延时 10ms 后再继续写 os_time_dly(1); } // if (wlen != len) { // dac缓存满了,延时 10ms 后再继续写d // int rate = pp->audio_format.nSamplesPerSec * pp->audio_format.nChannels / 100; // // os_time_dly(10); // // static int last_jiff = 0; // if(len - wlen >= rate){ // // log_info(">>> dac buffer full %d %d\n", wlen , len); // // log_info("D >>> %d", (jiffies_usec() - last_jiff)/1000); // // if(jiffies_usec() - last_jiff > 20*1000) { // // pp->jump_mjpeg = 1; // // } else { // // pp->jump_mjpeg = 0; // // } // // last_jiff = jiffies_usec(); // // if((len - wlen)/rate/2){ // // log_info("delay %d", (len - wlen)/rate); // os_time_dly(1); // // } // } // } ptr += wlen; // / 2 / ((dac_hdl.pd->bit_width) ? 2 : 1); len -= wlen; } pp->jump_mjpeg = jiffies_usec(); } // AVI线程 static void md_flush(MV_DRAW *md) { // 刷帧 // log_info("11 %d", jiffies_msec()); AVIPlayer *p = (AVIPlayer *)md->st; /* printf("[msg]%s-%d>>>>>>>>>>>md->pause=%d,p->avih_header.dwMicroSecPerFrame=%d p->audio_format.wFormatTag=%d", __FUNCTION__, __LINE__, md->pause, p->avih_header.dwMicroSecPerFrame, p->audio_format.wFormatTag); */ if (md->pause == 1) { md->flushTimer = sys_timeout_add(md, (void(*)(void *))md_flush, p->avih_header.dwMicroSecPerFrame / 2000); return ; } avi_fluh(md->st); if (p->audio_format.wFormatTag == 0x0001 && __this->param.is_audio_mute != 1) { md->flushTimer = sys_timeout_add(md, (void(*)(void *))md_flush, p->avih_header.dwMicroSecPerFrame / 2000); } else { md->flushTimer = sys_timeout_add(md, (void(*)(void *))md_flush, p->avih_header.dwMicroSecPerFrame / 1000); } } void play_task(MV_DRAW *md) { // void * test = avi_malloc(30000); // put_buf(test, 16); int msg[32]; int ret; while (1) { // 接收 memset(msg, 0, sizeof(msg)); ret = os_taskq_pend("taskq", msg, ARRAY_SIZE(msg)); log_info("ret %d %d %d %d", ret, msg[0], msg[1], msg[2]); if (ret != OS_TASKQ) { continue; } // play if (msg[1] == AVI_PLAY_STATUS) { log_info("avi play"); md->flushTimer = sys_timeout_add(md, (void(*)(void *))md_flush, 1); } // stop if (msg[1] == AVI_STOP_STATUS) { log_info("avi stop"); if (__this->param.is_audio_mute != 1) { avi_pcm_close(md->st); } if (md->flushTimer) { sys_timeout_del(md->flushTimer); md->flushTimer = 0; } os_sem_post(&md->mv_sem); os_time_dly(-1); } } } void *get_aviplay_handle() { return __this; } void *get_avi_player_st_handle() { return __this->st; } #if TCFG_APP_VIDEO_EN void app_video_mode_switch(void *priv) { int ret = FALSE; u16 flag = (u16)priv; log_info("%s", __func__); if (flag != task_switch_flag) { log_info("%s %d %d--%d", __func__, __LINE__, flag, task_switch_flag); return; } ++task_switch_flag; if (app_get_current_mode_name() == APP_MODE_VIDEO) { return; } #if TCFG_APP_BT_EN && TCFG_BT_BACKGROUND_ENABLE if (bt_check_already_initializes()) { #else if (1) { #endif ret = app_task_switch_to(APP_MODE_VIDEO, NULL_VALUE); } if (ret == FALSE) { sys_timeout_add((void *)(long)task_switch_flag, app_video_mode_switch, 500); } } #endif void *animig_open(char *name, AVI_PARAM param, int arg) { u8 type = 0; #if TCFG_APP_VIDEO_EN #if TCFG_APP_BT_EN && TCFG_BT_BACKGROUND_ENABLE if (app_get_current_mode_name() != APP_MODE_VIDEO && bt_check_already_initializes()) { #else if (app_get_current_mode_name() != APP_MODE_VIDEO) { #endif int ret = app_task_switch_to(APP_MODE_VIDEO, NULL_VALUE); if (ret == FALSE) { sys_timeout_add((void *)(long)task_switch_flag, app_video_mode_switch, 500); } } #if TCFG_APP_BT_EN && TCFG_BT_BACKGROUND_ENABLE else { sys_timeout_add((void *)(long)task_switch_flag, app_video_mode_switch, 500); } #endif #endif FILE *f = fopen(name, "r"); if (f == NULL) { log_info("open file fail %s", name); return NULL; } char chunk_id[5] = {0}; /* 读取RIFF头 */ if (avi_fread(chunk_id, 1, 4, f) != 4) { log_error("Failed to read chunk ID"); fclose(f); return NULL; } if (strcmp("RIFF", chunk_id)) { log_info("no avi %s", name); fclose(f); return NULL; } fclose(f); type = 2; if (__this != NULL) { log_error("__this not free"); avi_free(__this); } MV_DRAW *md = avi_zalloc(sizeof(MV_DRAW)); __this = md; ASSERT(__this); memcpy(&(__this->param), ¶m, sizeof(AVI_PARAM)); os_sem_create(&md->mv_sem, 0); md->type = type; md->repeat = -1; if (md->type == 2) { md->st = avi_open(name, arg, 0); AVIPlayer *p = (AVIPlayer *)(md->st); if (md->st == NULL) { goto __err; } if (p->width > LCD_WIDTH || p->height > LCD_HEIGHT) { log_error("vidoe width %d or height%d over lcd width %d or height %d", p->width, p->height, LCD_WIDTH, LCD_HEIGHT); goto __err; } if (__this->param.is_audio_mute != 1) { avi_pcm_open(md->st); } wdt_clear(); avi_fluh(md->st); } /* strcpy(__this->param.fname,name,) */ /* char *taskname = strrchr(name, '/'); */ /* taskname = taskname ? taskname + 1 : name; */ /* taskname = "avi_task"; */ u8 fname_len = (sizeof(__this->param.fname) / sizeof(__this->param.fname[0])); strncpy(__this->param.fname, name, fname_len); printf("[msg]%s-%d>>>>>>>>>>>__this->param.fname=%s", __FUNCTION__, __LINE__, __this->param.fname); __this->param.fname[fname_len - 1] = '\0'; log_info("create %s", AVI_TASK_NAME); task_create((void(*)(void *))play_task, md, AVI_TASK_NAME); os_taskq_post(AVI_TASK_NAME, 1, AVI_PLAY_STATUS); return md; __err: void avi_close(void *avip); avi_close(md->st); __this = NULL; if (md) { avi_free(md); } return NULL; } void animig_close(void *ap) { MV_DRAW *md = ap; // while (md->act != -1) { // md->act = 0; // os_time_dly(1); // }\\ os_taskq_post(AVI_TASK_NAME, 1, AVI_STOP_STATUS); os_sem_pend(&md->mv_sem, 0); log_info("kill %s", AVI_TASK_NAME); jlgpu_scheduler_wait_sync(); task_kill("avi_task"); avi_close(md->st); // jlgpu_scheduler_wait_sync(); // os_sem_del(&md->mv_sem, 0); // jlgpu_scheduler_wait_sync(); if (md) { avi_free(md); md = NULL; } } void avi_jpeg_init(void *priv) { log_debug("\n%s,line = %d\n", __func__, __LINE__); AVIPlayer *p = priv; ASSERT(p); os_mutex_pend(&p->mutex, 0); // 找出最接近act的已成功cache的jpg if (1) { log_debug("\n%s,line = %d\n", __func__, __LINE__); void *new = jpeg_malloc(sizeof(jdec_opj)); // 提前申请避免撞指针 wdt_clear(); /* log_info("\n%s,line = %d\n", __func__, __LINE__); */ /* log_info("\np->cache = %x\n", (u32)p->cache); */ /* log_info("\np->cache->idx = %x\n", (u32)p->cache.act_idx); */ /* log_info("\np->cache.m = %x\n", p->cache.m); */ /* log_info("\np->cache.m[p->cache.act_idx] = %x\n", p->cache.m[p->cache.act_idx]); */ /* log_info("\np->cache.m[p->cache.act_idx].opj = %x\n", p->cache.m[p->cache.act_idx].opj); */ /* log_info("\n%s,line = %d\n", __func__, __LINE__); */ if (p->cache.m[p->cache.act_idx].opj) { log_debug("\n%s,line = %d\n", __func__, __LINE__); jljpeg_decode_release(p->cache.m[p->cache.act_idx].opj, 1); jpeg_free(p->cache.m[p->cache.act_idx].opj->device); jpeg_free(p->cache.m[p->cache.act_idx].opj); if (jljpeg_get_decode_hd() == p->cache.m[p->cache.act_idx].opj) { jljpeg_get_decode_hd_clr(); } p->cache.m[p->cache.act_idx].opj = NULL; } log_debug("\n%s,line = %d\n", __func__, __LINE__); if (p->cache.act_idx != p->cache.pre_idx) { p->cache.act_idx++; if (p->cache.act_idx >= p->cache.cnt) { p->cache.act_idx = 0; } // log_info("->%d %x", p->cache.act_idx, p->cache.m[p->cache.act_idx].data ); } else { /* log_info("="); */ } if (p == NULL) { log_debug("P == NULL!!!\n"); return; } log_debug("p = %p \n", p); log_debug("p->cache = %x\n", (u32) & (p->cache)); log_debug("\n\n[avi_jpeg_init] idx =%d\n\n", p->cache.act_idx); p->cache.m[p->cache.act_idx].opj = new; // jpeg_malloc(sizeof(jdec_opj)); ASSERT(p->cache.m[p->cache.act_idx].opj); memset(p->cache.m[p->cache.act_idx].opj, 0, sizeof(jdec_opj)); p->cache.m[p->cache.act_idx].opj->device = jpeg_malloc(sizeof(struct flash_file_info)); memset(p->cache.m[p->cache.act_idx].opj->device, 0, sizeof(struct flash_file_info)); ASSERT(p->cache.m[p->cache.act_idx].opj->device); struct flash_file_info *f = p->cache.m[p->cache.act_idx].opj->device; if (avi_get_view_file_info(p)) { // TAB表 struct flash_file_info *info = avi_get_view_file_info(p); struct image_file file = {0}; file.offset = p->cache.m[p->cache.act_idx].pos; file.len = p->cache.m[p->cache.act_idx].len; ui_res_get_image_flash_info(f, info, &file); } else { f->offset = (u32)p->cache.m[p->cache.act_idx].data; f->last_tab_data_len = p->cache.m[p->cache.act_idx].len; } int ret; if (p->buf_ok) { ret = jpeg_module_opj_init((void *)p->cache.m[p->cache.act_idx].opj); } else { ret = -1; } // log_info("="); if (ret) { log_info("%s %d", __func__, __LINE__); if (p->cache.m[p->cache.act_idx].opj) { jljpeg_decode_release(p->cache.m[p->cache.act_idx].opj, 1); if (p->cache.m[p->cache.act_idx].opj->device) { jpeg_free(p->cache.m[p->cache.act_idx].opj->device); } jpeg_free(p->cache.m[p->cache.act_idx].opj); p->cache.m[p->cache.act_idx].opj = NULL; } } } __this->avi_now_idx = p->cache.act_idx; os_mutex_post(&p->mutex); } static void jpeg_draw_cb(int id, u8 *dst_buf, struct rect *dst_r, struct rect *src_r, u8 bytes_per_pixel, void *priv, void *matrix) { jdec_opj *opj = (jdec_opj *)priv; // jpeg解码句柄 if (jljpeg_get_decode_hd() && (opj != jljpeg_get_decode_hd())) { jljpeg_decode_reset_curr(); } opj->matrix = matrix; // 获取变换矩阵 // 计算缩放系数 float ratio_w = 1.0; float ratio_h = 1.0; if (opj->matrix) { gpu_matrix_t *m = matrix; ratio_w = m->m[0][0]; // ratio_h = m->m[1][1]; // /* log_debug("ratio_w: %f, ratio_h: %f\n", ratio_w, ratio_h); */ } /* 计算变换后图片的输出区域 */ struct rect jpeg_draw_src; memcpy(&jpeg_draw_src, src_r, sizeof(struct rect)); // 放大时为中心放大 int center_y = src_r->top + src_r->height / 2; jpeg_draw_src.height = opj->height / ratio_h; jpeg_draw_src.top = center_y - jpeg_draw_src.height / 2; int center_x = src_r->left + src_r->width / 2; jpeg_draw_src.width = opj->width / ratio_w; jpeg_draw_src.left = center_x - jpeg_draw_src.width / 2; /* DUMP_RECT(__func__, __LINE__, "dst_r", dst_r); //推屏buf */ /* DUMP_RECT(__func__, __LINE__, "src_r", src_r); //jpeg显示区域 */ /* DUMP_RECT(__func__, __LINE__, "jpeg_draw_src", &jpeg_draw_src); //jpeg缩放后区域 */ /* 计算图片输出和推屏Buf的重叠区域 */ struct rect cover_r_t; if (!get_rect_cover(dst_r, src_r, &cover_r_t)) { return; } // 计算缩放后的区域*以推屏区域、jpeg缩放区域、控件显示区域的交集作为最终输出,放大时受控件尺寸限制会变成中心放大 struct rect cover_r; get_rect_cover(&cover_r_t, &jpeg_draw_src, &cover_r); /* DUMP_RECT(__func__, __LINE__, "cover_r", &cover_r); //重叠区域 */ // 计算jpeg解码的区域 int draw_top = cover_r.top - jpeg_draw_src.top; // 绘制的top int draw_btm = cover_r.height + draw_top; // 绘制的bottom int draw_h = cover_r.height; // 绘制高度 /* int dec_top = draw_top * ratio_h; //需要解码的top */ /* int dec_btm = ceilf((float)draw_btm * ratio_h); //需要解码的高度,这里需要向上取 */ /* int dec_h = dec_btm - dec_top; //解码高度 */ int max_dec_height = (opj->msy << 3) / ratio_h; // 一个mcu分块缩放后可以有效绘制的行数 int sub_top = 0; int sub_height = (draw_h > max_dec_height) ? max_dec_height : draw_h; // 按mcu快缩放后的有效高度进行分行处理 while (sub_top + draw_top < draw_btm) { int dec_sub_top = (draw_top + sub_top) * ratio_h; int dec_sub_btm = ceilf((float)(draw_top + sub_top + sub_height) * ratio_h); int dec_sub_h = dec_sub_btm - dec_sub_top; /* log_debug("%s draw(%d %d) dec(%d %d)%d maxdec%d sub(%d %d) dec_sub(%d %d)", */ /* __func__, draw_top, draw_btm, dec_top, dec_btm, dec_h, max_dec_height, sub_top, sub_height, dec_sub_top, dec_sub_btm); */ struct rect jpeg_dec_r; /*jpeg解码区域*/ /* jpeg_dec_r.left = (cover_r.left - src_r->left) * ratio_w; */ jpeg_dec_r.left = (cover_r.left - jpeg_draw_src.left) * ratio_w; jpeg_dec_r.top = dec_sub_top; jpeg_dec_r.width = cover_r.width * ratio_w; jpeg_dec_r.height = dec_sub_h; struct rect block_r; /*缩放后显示的区域*/ block_r.top = cover_r.top + sub_top; block_r.height = sub_height; block_r.left = dst_r->left; block_r.width = dst_r->width; /* DUMP_RECT(__func__, __LINE__, "block_r", &block_r); */ /* DUMP_RECT(__func__, __LINE__, "jpeg_dec_r", &jpeg_dec_r); */ u8 *disp_buf = dst_buf + (block_r.top - dst_r->top) * 2 * dst_r->width; /*启动解码*/ jljpeg_decode_start(opj, &jpeg_draw_src, &jpeg_dec_r, &cover_r, &block_r, disp_buf); sub_top += sub_height; sub_height = (sub_top + sub_height + draw_top < draw_btm) ? max_dec_height : draw_btm - draw_top - sub_top; } } void __jpeg_draw_cb_gpu(int id, u8 *dst_buf, struct rect *dst_r, struct rect *src_r, u8 bytes_per_pixel, void *priv, void *matrix) { // return; // log_info("priv %d", priv); // avi_jpeg_init(priv); AVIPlayer *avip = (AVIPlayer *)(*((u32 *)priv)); // log_info("to avip %x", avip); if (!avip) { return; } // os_mutex_pend(&avip->mutex, 0); mjpegdata *m = &avip->cache.m[avip->cache.act_idx]; if (m && !m->opj) { log_info("opj == null"); // mem_stats(); // os_mutex_post(&avip->mutex); return; } // jljpeg_decode_reset_curr(); // jljpeg_get_decode_hd_clr(); // log_info("%x opj %x", priv, m->opj); ASSERT(__this->avi_now_idx == avip->cache.act_idx); jpeg_draw_cb(id, dst_buf, dst_r, src_r, bytes_per_pixel, m->opj, matrix); // os_mutex_post(&avip->mutex); } void avi_gpu_scheduler_kick_start(void *priv) { if (avi_player == NULL) { return; } void *gpu_task_head = priv; pJLGPUTaskUnit_t pp = jlgpu_task_find_first_task(gpu_task_head); while (pp) { extern void __jpeg_draw_cb_gpu(int id, u8 * dst_buf, struct rect * dst_r, struct rect * src_r, u8 bytes_per_pixel, void *priv, void *matrix); if (pp->info.draw_info) { if (pp->info.draw_info->cb_func == __jpeg_draw_cb_gpu) { extern void avi_jpeg_init(void *priv); u32 avip = *((u32 *)pp->info.draw_info->priv); avi_jpeg_init((void *)avip); } } // log_info("444"); pp = jlgpu_find_next_task(pp); } } #endif