#include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "board.h" #include "chip.h" #include "sfud.h" #include "ota_update.h" #include "mfcapi.h" #include "jpegdecapi.h" #include "animation.h" static TaskHandle_t animation_task = NULL; static SemaphoreHandle_t animation_mutex; #if ANIMATION_POLICY == ANIMATION_USE_SMALL_MEM static BANIHEADER ani_header; static BANIHEADER *aniheader = &ani_header; #else static BANIHEADER *aniheader; #endif static unsigned int anioffset; static unsigned int anisize; static int ani_playing = 0; static int ani_replay = 0; static int ani_stop = 0; static int ani_frames = 0; static int ani_display_index = 0; //static int ani_take_vdisbuf = 0; static unsigned int ani_frame_addr; static uint32_t first_show_done = 0; static void animation_thread(void *param) { MFCHandle *mfc_handle = NULL; #if DEVICE_TYPE_SELECT != EMMC_FLASH sfud_flash *sflash; #endif uint32_t size; uint32_t stick, etick, delaytick; uint32_t display_addr; uint32_t *tmp_addr = NULL; anioffset = get_upfile_offset(UPFILE_TYPE_ANIMATION, 0); anisize = get_upfile_size(UPFILE_TYPE_ANIMATION); if (anisize > 0) { void *anibuf = pvPortMalloc(anisize); if (anibuf) { #if DEVICE_TYPE_SELECT != EMMC_FLASH sflash = sfud_get_device(0); sfud_read(sflash, anioffset, anisize, anibuf); #else emmc_read(anioffset, anisize, anibuf); #endif aniheader = (BANIHEADER*)anibuf; if (aniheader->magic != MKTAG('B', 'A', 'N', 'I')) { printf("Read animation file error!\n"); vPortFree(anibuf); vTaskDelete(NULL); return; } CP15_clean_dcache_for_dma((uint32_t)anibuf, (uint32_t)anibuf + anisize); } else { printf("Error! No enough memory for animation file.\n"); vTaskDelete(NULL); return; } } else { printf("Warning! Not found animation in update file.\n"); vTaskDelete(NULL); return; } ani_frame_addr = (uint32_t)aniheader + sizeof(BANIHEADER); for (;;) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); xSemaphoreTake(animation_mutex, portMAX_DELAY); if (ani_replay) { ani_frame_addr = (uint32_t)aniheader + sizeof(BANIHEADER); ani_frames = 0; ani_replay = 0; } stick = xTaskGetTickCount(); ani_playing = 1; size = *(uint32_t*)ani_frame_addr; if (xVideoDisplayBufTake(pdMS_TO_TICKS(10)) == pdTRUE) { if(!mfc_handle) mfc_handle = mfc_init(RAW_STRM_TYPE_JPEG); if(!mfc_handle) { printf("%s, mfc_init failed.\n", __func__); continue ; } display_addr = ulVideoDisplayBufGet(); JpegHeaderInfo jpegInfo = {0}; jpegInfo.handle = mfc_handle; jpegInfo.jpg_addr = ani_frame_addr + 8; jpegInfo.jpg_size = size; jpegInfo.dec_addry = display_addr; jpegInfo.dec_size = ulVideoDisplayBufGetSize(); #if LCD_ROTATE_ANGLE != LCD_ROTATE_ANGLE_0 if(!tmp_addr) { tmp_addr = (u32 *)pvPortMalloc(jpegInfo.dec_size); if(!tmp_addr) { printf("tmp_addr pvPortMalloc failed.\n"); } } if(tmp_addr) { jpegInfo.dec_addry = VIRT_TO_PHY((u32)tmp_addr); } #endif if (mfc_jpegdec(&jpegInfo) != 0) { printf("animation decode %d frame fail.\n", ani_frames); vVideoDisplayBufFree(display_addr); } else { int pxp_format = PXP_S0_FORMAT_YUV2P420; int lcd_format = LCD_OSD_FORAMT_Y_UV420; /* avoid warnning */ (void) pxp_format; (void) lcd_format; /* YUV format only support y_uv420/y_u_v420/y_uv422/y_u_v422 */ if (jpegInfo.dec_format == JPEGDEC_YCbCr420_SEMIPLANAR) { pxp_format = PXP_S0_FORMAT_YUV2P420; lcd_format = LCD_OSD_FORAMT_Y_UV420; } else if (jpegInfo.dec_format == JPEGDEC_YCbCr422_SEMIPLANAR) { pxp_format = PXP_S0_FORMAT_YUV2P422; lcd_format = LCD_OSD_FORAMT_Y_UV422; } else { printf("%s Invalid jpegdec format.\n", __func__); goto next_frame; } if(!first_show_done) { #if LCD_ROTATE_ANGLE == LCD_ROTATE_ANGLE_0 int align_width = (LCD_WIDTH + 0xF) & (~0xF); int align_height = (LCD_HEIGHT + 0xF) & (~0xF); if((align_width == jpegInfo.dec_width) && (align_height == jpegInfo.dec_height)) { ark_lcd_set_osd_size(LCD_VIDEO_LAYER, LCD_WIDTH, LCD_HEIGHT); } else { ark_lcd_set_osd_size(LCD_VIDEO_LAYER, jpegInfo.dec_width, jpegInfo.dec_height); } ark_lcd_set_osd_format(LCD_VIDEO_LAYER, lcd_format); #else ark_lcd_set_osd_size(LCD_VIDEO_LAYER, LCD_WIDTH, LCD_HEIGHT); ark_lcd_set_osd_format(LCD_VIDEO_LAYER, LCD_OSD_FORAMT_RGB565); #endif ark_lcd_osd_enable(LCD_VIDEO_LAYER, 1); ark_lcd_osd_enable(LCD_UI_LAYER, 0); first_show_done = 1; } #if LCD_ROTATE_ANGLE == LCD_ROTATE_ANGLE_0 ark_lcd_set_osd_yaddr(LCD_VIDEO_LAYER, jpegInfo.dec_addry); ark_lcd_set_osd_uaddr(LCD_VIDEO_LAYER, jpegInfo.dec_addru); ark_lcd_set_osd_vaddr(LCD_VIDEO_LAYER, jpegInfo.dec_addrv); #else pxp_scaler_rotate(jpegInfo.dec_addry, jpegInfo.dec_addru, jpegInfo.dec_addrv, pxp_format, jpegInfo.dec_width, jpegInfo.dec_height, display_addr, 0, PXP_OUT_FORMAT_RGB565, LCD_WIDTH, LCD_HEIGHT, LCD_ROTATE_ANGLE); ark_lcd_set_osd_yaddr(LCD_VIDEO_LAYER, display_addr); #endif ark_lcd_set_osd_sync(LCD_VIDEO_LAYER); ark_lcd_set_osd_sync(LCD_UI_LAYER); ark_lcd_wait_for_vsync(); vVideoDisplayBufRender(display_addr); // ani_take_vdisbuf = 1; } } next_frame: ani_frame_addr += size; ani_display_index = !ani_display_index; if (ani_stop || (++ani_frames >= aniheader->aniCount + aniheader->hasBootlogo ? 1 : 0) #if ANIMATION_POLICY == ANIMATION_USE_SMALL_MEM || (ani_frame_addr > anioffset + anisize)) #else || (ani_frame_addr > (uint32_t)aniheader + anisize)) #endif { printf("animation end.\n"); ani_stop = 0; if (aniheader->aniCount > 0) vTaskDelay(aniheader->aniDelayHideTime); else if (aniheader->hasBootlogo) vTaskDelay(aniheader->bootlogoDisplayTime); // if (ani_take_vdisbuf) { ark_lcd_osd_enable(LCD_VIDEO_LAYER, 0); ark_lcd_osd_enable(LCD_UI_LAYER, 1); ark_lcd_set_osd_sync(LCD_UI_LAYER); ark_lcd_set_osd_sync(LCD_VIDEO_LAYER); // } vVideoDisplayBufFree(display_addr); vVideoDisplayBufGive(); ani_playing = 0; if (ani_replay) xTaskNotifyGive(animation_task); xSemaphoreGive(animation_mutex); if(mfc_handle) { mfc_uninit(mfc_handle); mfc_handle = NULL; } if(tmp_addr) { vPortFree(tmp_addr); tmp_addr = NULL; } continue; } vVideoDisplayBufGive(); xSemaphoreGive(animation_mutex); etick = xTaskGetTickCount(); if (aniheader->hasBootlogo && ani_frames == 1) delaytick = pdMS_TO_TICKS(aniheader->bootlogoDisplayTime) > (etick - stick) ? pdMS_TO_TICKS(aniheader->bootlogoDisplayTime) - (etick - stick) : 0; else delaytick = pdMS_TO_TICKS(1000 / aniheader->aniFps) > (etick - stick) ? pdMS_TO_TICKS(1000 / aniheader->aniFps) - (etick - stick) : 0; if (delaytick) vTaskDelay(delaytick); xTaskNotifyGive(animation_task); } } int animation_init(void) { if (animation_task) { printf("animation is already inited.\n"); return 0; } animation_mutex = xSemaphoreCreateMutex(); /* Create a task to play animation */ if (xTaskCreate(animation_thread, "animation", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, &animation_task) != pdPASS) { printf("create animation task fail.\n"); animation_task = NULL; return -1; } return 0; } void animation_start(void) { xSemaphoreTake(animation_mutex, portMAX_DELAY); first_show_done=0; if (ani_playing) { printf("animation is already playing.\n"); xSemaphoreGive(animation_mutex); return; } #if ANIMATION_POLICY == ANIMATION_USE_SMALL_MEM ani_frame_addr = anioffset + sizeof(BANIHEADER); #else ani_frame_addr = (uint32_t)aniheader + sizeof(BANIHEADER); #endif ani_frames = 0; if (animation_task) xTaskNotifyGive(animation_task); xSemaphoreGive(animation_mutex); } /* replay animation even if animation is already playing */ void animation_restart(void) { xSemaphoreTake(animation_mutex, portMAX_DELAY); first_show_done=0; ani_stop=0; if (ani_playing) { ani_replay = 1; xSemaphoreGive(animation_mutex); return; } #if ANIMATION_POLICY == ANIMATION_USE_SMALL_MEM ani_frame_addr = anioffset + sizeof(BANIHEADER); #else ani_frame_addr = (uint32_t)aniheader + sizeof(BANIHEADER); #endif ani_frames = 0; if (animation_task) xTaskNotifyGive(animation_task); xSemaphoreGive(animation_mutex); } void animation_stop(void) { xSemaphoreTake(animation_mutex, portMAX_DELAY); ani_stop = 1; xSemaphoreGive(animation_mutex); } int get_animation_status(void) { return ani_playing; }