Files
TAIXIN/sdk/app/app_lcd/sim_video_more_msi.c

554 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**********************************************************************************************************
* 虚拟屏幕,支持更多的屏,顶层序号大,底层序号低
* ********************************************************************************************************/
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "osal/work.h"
#include "stream_define.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "app_lcd/app_lcd.h"
#include "user_work/user_work.h"
extern void yuv_blk_cpy(uint8 *des, uint8* src,uint32 des_w,uint32 des_h, uint32 src_w, uint32_t src_h,uint32 x,uint32 y);
extern void yuv_blk_reduce(uint8 *des, uint8* src,uint32 des_w,uint32 des_h, uint32 src_w, uint32_t src_h,uint32 x,uint32 y);
// data申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#define STREAM_LIBC_MALLOC av_malloc
#define STREAM_LIBC_FREE av_free
#define STREAM_LIBC_ZALLOC av_zalloc
#define MAX_RX 8
#define MAX_TX 2
struct window_msg{
uint16_t w;
uint16_t h;
uint16_t x;
uint16_t y;
};
struct sim_video_more_msi_s
{
struct os_work work;
struct msi *msi;
struct fbpool tx_pool;
uint16_t w, h;
uint16_t *filter; // 数组去进行过滤,0为结束
struct framebuff **screen; // 不同屏幕的framebuff,屏幕数量与filter数量一致
uint16_t screen_num;
uint32_t bufadr[MAX_TX];
struct framebuff *last_fb;
struct framebuff *last_input_fb;
uint32_t subwindow_cnt[10]; //本地buf所记录的cnt
uint32_t dispwindow_cnt[10]; //显示的buf所记录的cnt
struct window_msg dsp_window_msg[10];
uint8_t video_display_mask[10]; //记录哪个界面打开了独显,正常只可能有一个设备配置上,或者没人配置上
uint8_t lcd_fb_updata;
};
//只做双层
static int32 sim_video_more_work(struct os_work *work)
{
struct sim_video_more_msi_s *sim_video = (struct sim_video_more_msi_s *)work;
struct framebuff *fb;
struct framebuff *output_fb;
uint32_t dispbuf;
fb = msi_get_fb(sim_video->msi, 0);
// 接收到一个fb,就需要判断类型(p0,p1),然后申请空间去进行叠层
if (fb)
{
// 查看类型是否匹配(正常到这里一定匹配,因为接收的时候就进行过滤了)
uint16_t match_count = 0;
uint8_t match_flag = 0;
while (match_count < sim_video->screen_num)
{
if (fb->stype == sim_video->filter[match_count]) //类型检查看一下当前的fb类型是否存在于过滤网内
{
if (sim_video->screen[match_count]) //检查当前的screen是否存在如果存在则删除更新新的fb到screen
{
msi_delete_fb(NULL, sim_video->screen[match_count]);
}
sim_video->screen[match_count] = fb;
match_flag = 1;
break;
}
match_count++;
}
// 没有匹配,删除,并且不需要刷新
if (!match_flag)
{
msi_delete_fb(NULL, fb);
fb = NULL;
goto sim_video_work_end;
}
output_fb = fbpool_get(&sim_video->tx_pool, 0, sim_video->msi); //从sim_video的发送池里找到对应的fb
if (!output_fb)
{
goto sim_video_work_end;
}
if((uint32_t)output_fb->data == sim_video->bufadr[0]){
dispbuf = sim_video->bufadr[1]; //提取当前正在显示的buf地址,用来拷数据
}else{
dispbuf = sim_video->bufadr[0];
}
// 这个模式是默认都是叠层,不再判断有某些情况不需要叠层的情况(效率低,但是既然应用设定多个屏幕,就应该去实现吧)
// 叠层
{
// 开始尝试叠层
uint16_t tmp = 0;
uint16_t p_w, p_h;
struct yuv_arg_s *yuv_msg = NULL;
while (tmp < sim_video->screen_num)
{
if (sim_video->screen[tmp])
{
yuv_msg = (struct yuv_arg_s *)sim_video->screen[tmp]->priv;
p_w = yuv_msg->out_w;
p_h = yuv_msg->out_h;
yuv_blk_cpy((uint8_t*)output_fb->data, sim_video->screen[tmp]->data, sim_video->w, sim_video->h, p_w, p_h, yuv_msg->x, yuv_msg->y);
}
tmp++;
}
output_fb->mtype = F_YUV;
output_fb->stype = FSTYPE_YUV_P0;
msi_output_fb(sim_video->msi, output_fb);
}
}
sim_video_work_end:
os_run_work_delay(work, 1);
return 0;
}
static int32 sim_video_more_work_more(struct os_work *work)
{
#define REFRESH_LCD_INTERNAL_TIME 10
struct sim_video_more_msi_s *sim_video = (struct sim_video_more_msi_s *)work;
struct framebuff *fb;
struct framebuff *output_fb;
static uint16_t oldbigx,oldbigy;
uint32_t dispbuf;
uint32_t *cache_buf[10];
uint8_t push_lcd_updata=0;
uint8_t display_only = 0;
uint16_t p_w, p_h;
uint16_t max_w;
uint16_t tmp = 0;
struct yuv_arg_s *yuv_msg = NULL;
struct app_lcd_s *lcd_s = &lcd_msg_s;
output_fb = NULL;
if(sim_video->last_input_fb != NULL){
fb = sim_video->last_input_fb;
}else{
fb = msi_get_fb(sim_video->msi, 0);
}
// 接收到一个fb,就需要判断类型(p0,p1),然后申请空间去进行叠层
if (fb)
{
// 查看类型是否匹配(正常到这里一定匹配,因为接收的时候就进行过滤了)
uint16_t match_count = 0;
uint8_t match_flag = 0;
while (match_count < sim_video->screen_num)
{
if (fb->stype == sim_video->filter[match_count]) //类型检查看一下当前的fb类型是否存在于过滤网内
{
yuv_msg = (struct yuv_arg_s *)fb->priv;
if(sim_video->subwindow_cnt[match_count] != sim_video->dispwindow_cnt[match_count]){ //重复更新,当前要刷的叠层已在备刷区,但又来了新的数据,那要把备刷区推出去
push_lcd_updata = 1;
sim_video->last_input_fb = fb;
output_fb = sim_video->last_fb;
goto sim_video_push_lcd;
}
match_flag = 1;
break;
}
match_count++;
}
// 没有匹配,删除,并且不需要刷新
if (!match_flag)
{
msi_delete_fb(NULL, fb);
if(fb == sim_video->last_input_fb) {
sim_video->last_input_fb = NULL;
}
fb = NULL;
goto sim_video_work_end;
}
if(sim_video->lcd_fb_updata){
sim_video->lcd_fb_updata = 0;
sim_video->last_input_fb = fb;
output_fb = fbpool_get(&sim_video->tx_pool, 0, sim_video->msi); //从sim_video的发送池里找到对应的fb
if(!output_fb){ //需要更新时无法申请到fb,则下次重新申请,直到能申请为止
sim_video->lcd_fb_updata = 1;
goto sim_video_work_end;
}else{ //申请到了表示当前input_fb可被处理掉指针清空一下
if((uint32_t)output_fb->data == sim_video->bufadr[0]){
dispbuf = sim_video->bufadr[1]; //提取当前正在显示的buf地址,用来拷数据
}else{
dispbuf = sim_video->bufadr[0];
}
//hw_memcpy(output_fb->data,dispbuf,output_fb->len);
yuv_blk_cpy((uint8_t*)output_fb->data, (uint8_t*)dispbuf, sim_video->w, sim_video->h, sim_video->w,sim_video->h, 0,0);
sim_video->last_input_fb = NULL;
}
}
if (!output_fb)
{
output_fb = sim_video->last_fb;
}else{
sim_video->last_fb = output_fb; //记录当前要修改的fb
}
sim_video->subwindow_cnt[match_count] = yuv_msg->dispcnt;
sim_video->dsp_window_msg[match_count].w = yuv_msg->out_w;
sim_video->dsp_window_msg[match_count].h = yuv_msg->out_h;
sim_video->dsp_window_msg[match_count].x = yuv_msg->x;
sim_video->dsp_window_msg[match_count].y = yuv_msg->y;
sim_video->video_display_mask[match_count] = yuv_msg->video_only;
if((uint32_t)output_fb->data == sim_video->bufadr[0]){
dispbuf = sim_video->bufadr[1]; //提取当前正在显示的buf地址,用来拷数据
}else{
dispbuf = sim_video->bufadr[0];
}
display_only = 0;
tmp = 0;
while (tmp < sim_video->screen_num) { //选出是否配置独显了
if(sim_video->video_display_mask[tmp] != 0){
if(yuv_msg->video_only){ //当前数据流是否就是要显示的数据流
display_only |= BIT(7);
}
display_only++;
}
tmp++;
}
if((display_only&0x7f) > 1){
_os_printf("too much stream set display only mode\r\n");
}
max_w = 0;
tmp = 0;
while (tmp < sim_video->screen_num) { //选出最大的图片
if(sim_video->dsp_window_msg[tmp].w > max_w){
max_w = sim_video->dsp_window_msg[tmp].w;
}
tmp++;
}
yuv_msg = (struct yuv_arg_s *)fb->priv;
p_w = yuv_msg->out_w;
p_h = yuv_msg->out_h;
if(display_only){ //有设备打开了独显
if((display_only&BIT(7)) == BIT(7)){
hw_memset((uint8_t*)output_fb->data, 0, sim_video->w * sim_video->h); //清黑屏
hw_memset((uint8_t*)output_fb->data + sim_video->w * sim_video->h, 0x80, sim_video->w * sim_video->h / 2);
yuv_blk_cpy((uint8_t*)output_fb->data, fb->data, sim_video->w, sim_video->h, p_w, p_h, yuv_msg->x, yuv_msg->y); //把大图拷好
}
}
else
{
if(max_w != yuv_msg->out_w){ //如果并非大图
yuv_blk_cpy((uint8_t*)output_fb->data, fb->data, sim_video->w, sim_video->h, p_w, p_h, yuv_msg->x, yuv_msg->y); //拷到对应的位置上
}else{ //如果是大图
tmp = 0;
while(tmp < sim_video->screen_num){
cache_buf[tmp] = NULL;
if( (match_count != tmp) && (sim_video->dsp_window_msg[tmp].w != 0)){
if(sim_video->subwindow_cnt[tmp] > sim_video->dispwindow_cnt[tmp]){ //如果备用区数值是更新的,那就拷出来
cache_buf[tmp] = (uint32_t *)STREAM_MALLOC(sim_video->dsp_window_msg[tmp].w * sim_video->dsp_window_msg[tmp].h * 3 / 2); //申请小图空间
sys_dcache_invalid_range(cache_buf[tmp], sim_video->dsp_window_msg[tmp].w * sim_video->dsp_window_msg[tmp].h * 3 / 2);
if(cache_buf[tmp] != NULL){
yuv_blk_reduce((uint8_t*)cache_buf[tmp],(uint8_t*)output_fb->data,sim_video->dsp_window_msg[tmp].w,sim_video->dsp_window_msg[tmp].h,sim_video->w,sim_video->h,sim_video->dsp_window_msg[tmp].x,sim_video->dsp_window_msg[tmp].y);
}else{
_os_printf("malloc psram for sim video error1\r\n");
}
}
}
tmp++;
}
if((oldbigx != yuv_msg->x) || (oldbigy != yuv_msg->y)){
hw_memset((uint8_t*)output_fb->data, 0, sim_video->w * sim_video->h); //大图产生偏移,清黑屏
hw_memset((uint8_t*)output_fb->data + sim_video->w * sim_video->h, 0x80, sim_video->w * sim_video->h / 2);
}
yuv_blk_cpy((uint8_t*)output_fb->data, fb->data, sim_video->w, sim_video->h, p_w, p_h, yuv_msg->x, yuv_msg->y); //把大图拷好
oldbigx = yuv_msg->x;
oldbigy = yuv_msg->y;
tmp = 0;
while(tmp < sim_video->screen_num){
if( (match_count != tmp) && (sim_video->dsp_window_msg[tmp].w != 0)){
if(cache_buf[tmp]){ //图片是从之前的备用区提取出来的,在缓存中
yuv_blk_cpy((uint8_t*)output_fb->data, (uint8_t*)cache_buf[tmp], sim_video->w, sim_video->h, sim_video->dsp_window_msg[tmp].w,sim_video->dsp_window_msg[tmp].h, sim_video->dsp_window_msg[tmp].x,sim_video->dsp_window_msg[tmp].y); //拷到对应的psram块中
STREAM_FREE((uint8_t*)cache_buf[tmp]);
}
else{ //图片得从刷新区那里提取
cache_buf[tmp] = (uint32_t *)STREAM_MALLOC(sim_video->dsp_window_msg[tmp].w * sim_video->dsp_window_msg[tmp].h * 3 / 2);
sys_dcache_invalid_range(cache_buf[tmp],sim_video->dsp_window_msg[tmp].w * sim_video->dsp_window_msg[tmp].h * 3 / 2);
if(cache_buf[tmp] != NULL){
yuv_blk_reduce((uint8_t*)cache_buf[tmp],(uint8_t*)dispbuf,sim_video->dsp_window_msg[tmp].w,sim_video->dsp_window_msg[tmp].h,sim_video->w,sim_video->h,sim_video->dsp_window_msg[tmp].x,sim_video->dsp_window_msg[tmp].y);
yuv_blk_cpy((uint8_t*)output_fb->data, (uint8_t*)cache_buf[tmp], sim_video->w, sim_video->h, sim_video->dsp_window_msg[tmp].w,sim_video->dsp_window_msg[tmp].h, sim_video->dsp_window_msg[tmp].x,sim_video->dsp_window_msg[tmp].y);
STREAM_FREE((uint8_t*)cache_buf[tmp]);
}else{
_os_printf("malloc psram for sim video error2\r\n");
}
}
}
tmp++;
}
}
}
sim_video_push_lcd:
if(push_lcd_updata == 1){
memcpy(sim_video->dispwindow_cnt,sim_video->subwindow_cnt,10*4);
output_fb->mtype = F_YUV;
output_fb->stype = FSTYPE_YUV_P0;
msi_output_fb(sim_video->msi, output_fb); //将当前推屏的fb推出去
sim_video->lcd_fb_updata = 1; //下次提取新的刷屏帧
sim_video->last_fb = NULL;
}else{
msi_delete_fb(NULL, fb); //删除对应的输入到msi video的fb
fb = NULL;
}
}
sim_video_work_end:
if((os_jiffies() - lcd_s->start_time) > (lcd_s->refresh_time - REFRESH_LCD_INTERNAL_TIME)){ //当距离结束刷屏时间为REFRESH_LCD_INTERNAL_TIME时检查是否需要更新需要的话就先更新
if(output_fb){ //先检查推屏的fb是否存在
tmp = 0;
while(tmp < sim_video->screen_num){ //检查各个叠层
if(sim_video->dispwindow_cnt[tmp] != sim_video->subwindow_cnt[tmp]){ //发现有叠层出现更新了
memcpy(sim_video->dispwindow_cnt,sim_video->subwindow_cnt,10*4);
output_fb->mtype = F_YUV;
output_fb->stype = FSTYPE_YUV_P0;
msi_output_fb(sim_video->msi, output_fb); //将当前推屏的fb推出去
sim_video->lcd_fb_updata = 1; //下次提取新的刷屏帧
sim_video->last_fb = NULL;
break;
}
tmp++;
}
}
}
os_run_work_delay(work, 1);
return 0;
}
static int sim_video_msi_action(struct msi *msi, uint32 cmd_id, uint32 param1, uint32 param2)
{
struct sim_video_more_msi_s *sim_video = (struct sim_video_more_msi_s *)msi->priv;
int ret = RET_OK;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
struct framebuff *fb;
// 释放资源fb资源文件,priv是独立申请的
while (1)
{
fb = fbpool_get(&sim_video->tx_pool, 0, NULL);
if (!fb)
{
break;
}
if (fb->data)
{
STREAM_FREE(fb->data);
fb->data = NULL;
}
// 预分配空间释放
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
}
}
fbpool_destroy(&sim_video->tx_pool);
STREAM_LIBC_FREE(sim_video);
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *)param1;
if (fb->mtype != F_YUV)
{
// os_printf("not recv fb:%X\tcount:%d\n",fb,fb->users.counter);
ret = RET_ERR;
}
else
{
if (sim_video->filter)
{
// 轮询类型是否有一致
ret = RET_ERR;
uint16_t *each = sim_video->filter;
while (*each)
{
// 如果一致,返回OK
if (*each == fb->stype)
{
ret = RET_OK;
break;
}
each++;
}
}
}
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *)param1;
// 如果不是克隆的,就自己释放
if (!fb->clone)
{
// if (fb->data)
// {
// STREAM_FREE(fb->data);
// fb->data = NULL;
// }
fbpool_put(&sim_video->tx_pool, fb);
// 不需要内核去释放fb
ret = RET_OK + 1;
}
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_work_cancle2(&sim_video->work, 1);
uint16_t tmp = 0;
while (tmp < sim_video->screen_num)
{
if (sim_video->screen[tmp])
{
msi_delete_fb(NULL, sim_video->screen[tmp]);
}
tmp++;
}
while (1)
{
struct framebuff *fb = msi_get_fb(msi, 0);
if (fb) {
msi_delete_fb(NULL, fb);
} else {
break;
}
}
if (sim_video->last_fb){
msi_delete_fb(NULL, sim_video->last_fb);
sim_video->last_fb = NULL;
}
if(sim_video->last_input_fb){
msi_delete_fb(NULL, sim_video->last_input_fb);
sim_video->last_input_fb = NULL;
}
}
break;
default:
{
}
break;
}
return ret;
}
struct msi *sim_video_more_msi(const char *name, uint16_t w, uint16_t h, uint16_t *sim_filter)
{
// 设置1个接收,只是为了可以output_fb给到这个msi
uint16_t count = 0;
uint32_t *buf;
struct msi *msi = msi_new(name, MAX_RX, NULL);
if (msi)
{
struct sim_video_more_msi_s *sim_video = (struct sim_video_more_msi_s *)msi->priv;
if (!sim_video)
{
// 统计sim_filter数量来决定screen的数量
while (*(sim_filter + count))
{
count++;
}
sim_video = (struct sim_video_more_msi_s *)STREAM_LIBC_ZALLOC(sizeof(struct sim_video_more_msi_s) + (sizeof(struct framebuff *) * count));
}
sim_video->w = w;
sim_video->h = h;
sim_video->msi = msi;
sim_video->filter = sim_filter;
sim_video->screen_num = count;
// 申请空间的时候就已经多申请了,所有screen放在末尾
sim_video->screen = (struct framebuff **)(sim_video + 1);
sim_video->lcd_fb_updata = 1;
msi->priv = (void *)sim_video;
msi->action = sim_video_msi_action;
// 预分配结构体空间
uint32_t init_count = 0;
void *priv;
fbpool_init(&sim_video->tx_pool, MAX_TX);
while (init_count < MAX_TX)
{
priv = (void *)STREAM_LIBC_ZALLOC(sizeof(struct yuv_arg_s));
struct yuv_arg_s *yuv_msg = (struct yuv_arg_s *)priv;
buf = (uint32_t *)STREAM_MALLOC(sim_video->w * sim_video->h * 3 / 2);
hw_memset((uint8_t*)buf, 0, sim_video->w * sim_video->h); //清空整个空间,将显示背景显示全黑
hw_memset((uint8_t*)buf + sim_video->w * sim_video->h, 0x80, sim_video->w * sim_video->h / 2);
sys_dcache_invalid_range(buf, sim_video->w * sim_video->h * 3 / 2); //清cache
yuv_msg->out_w = w;
yuv_msg->out_h = h;
yuv_msg->y_size = w * h;
yuv_msg->y_off = 0;
yuv_msg->uv_off = 0;
yuv_msg->del = NULL;
sim_video->bufadr[init_count] = (uint32_t)buf;
FBPOOL_SET_INFO(&sim_video->tx_pool, init_count, (uint8_t*)buf, sim_video->w * sim_video->h * 3 / 2, priv);
init_count++;
}
msi->enable = 1;
// msi_add_output(sim_video->msi, NULL, R_VIDEO_P0);
OS_WORK_INIT(&sim_video->work, sim_video_more_work_more, 0);
os_run_work_delay(&sim_video->work, 1);
}
return msi;
}