Files
AC707N/SDK/apps/watch/video/avi_video.c
T
2025-12-15 14:39:25 +08:00

2410 lines
85 KiB
C

#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 <math.h>
#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"
#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;
/* 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(&param, &(__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(&param, &(__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(&param, &(__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, &param);
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
static void app_video_mode_switch(void *priv)
{
u16 flag = (u16)priv;
if (flag != task_switch_flag) {
log_info("\n\n %s, %d \n\n", __func__, __LINE__);
log_info("flag:%d, %d \n", flag, task_switch_flag);
return;
}
++task_switch_flag;
if (app_get_current_mode_name() == APP_MODE_VIDEO) {
return;
}
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);
}
}
#endif
void *animig_open(char *name, AVI_PARAM param, int arg)
{
u8 type = 0;
#if TCFG_APP_VIDEO_EN
if (app_get_current_mode_name() != APP_MODE_VIDEO) {
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);
}
}
#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), &param, 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