Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources

This commit is contained in:
2026-07-06 11:30:13 +08:00
commit e76462eeb7
3451 changed files with 1415300 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "stream_define.h"
#include "user_work/user_work.h"
struct msi *h264_msi_init_with_mode(uint32_t drv1_from, uint16_t drv1_w, uint16_t drv1_h, uint16_t drv2_from, uint16_t drv2_w, uint16_t drv2_h);
struct msi *h264_msi_init_with_timeLapse(uint32_t drv1_from, uint16_t drv1_w, uint16_t drv1_h, uint32_t timer);
// 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
struct auto_h264_msi_s
{
struct os_work work;
struct msi *msi;
struct msi *register_h264_msi;
uint32_t timeLapse_timer;
uint16_t w0, w1, h0, h1;
uint8_t src_from0;
uint8_t src_from1;
// mode: 0:默认 1:缩时录影模式,其他:默认模式
uint8_t stop : 1, mode : 1, current_mode : 1;
};
static int32 auto_h264_work(struct os_work *work)
{
uint8_t delay_time = 40;
struct auto_h264_msi_s *auto_h264 = (struct auto_h264_msi_s *) work;
struct msi *auto_h264_msi = auto_h264->msi;
// 这里尝试发送数据?检查一下是否有接收的msi,如果有接收的msi,则启动mjpeg,如果没有,则将硬件停下来?
uint32_t send_count = msi_output_fb(auto_h264->msi, NULL);
if (send_count)
{
// 如果模式不一样,则将stop置位1,重新启动
uint8_t mode = auto_h264->mode;
if (mode != auto_h264->current_mode)
{
auto_h264->current_mode = mode;
auto_h264->stop = 1;
}
if (auto_h264->stop == 1)
{
// 启动h264
if (auto_h264->register_h264_msi)
{
msi_del_output(auto_h264->register_h264_msi, NULL, auto_h264_msi->name);
msi_destroy(auto_h264->register_h264_msi);
auto_h264->register_h264_msi = NULL;
}
// 尝试重新创建,如果创建失败,下一次再继续创建
if (auto_h264->current_mode == 1)
{
auto_h264->register_h264_msi = h264_msi_init_with_timeLapse(auto_h264->src_from0, auto_h264->w0, auto_h264->h0, auto_h264->timeLapse_timer);
}
else
{
auto_h264->register_h264_msi = h264_msi_init_with_mode(auto_h264->src_from0, auto_h264->w0, auto_h264->h0, auto_h264->src_from1, auto_h264->w1, auto_h264->h1);
}
if (auto_h264->register_h264_msi)
{
msi_add_output(auto_h264->register_h264_msi, NULL, auto_h264_msi->name);
auto_h264->stop = 0;
}
}
}
else if (send_count == 0)
{
if (auto_h264->stop == 0)
{
// 停止h264的启动
auto_h264->stop = 1;
if (auto_h264->register_h264_msi)
{
msi_del_output(auto_h264->register_h264_msi, NULL, auto_h264_msi->name);
msi_destroy(auto_h264->register_h264_msi);
auto_h264->register_h264_msi = NULL;
}
}
}
os_run_work_delay(work, delay_time);
return 0;
}
static int32_t auto_h264_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct auto_h264_msi_s *auto_h264 = (struct auto_h264_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
os_work_cancle2(&auto_h264->work, 1);
STREAM_LIBC_FREE(auto_h264);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_work_cancle2(&auto_h264->work, 1);
if (auto_h264->register_h264_msi)
{
msi_destroy(auto_h264->register_h264_msi);
auto_h264->register_h264_msi = NULL;
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// 如果是mjpeg,帮忙转发到其他数据流
// 如果需要支持264,也要帮忙转发
if (fb->mtype == F_H264)
{
// 类型匹配才可以转发
if ((fb->stype == FSTYPE_H264_VPP_DATA0 + auto_h264->src_from0) || (fb->stype == FSTYPE_H264_VPP_DATA0 + auto_h264->src_from1))
{
_os_printf("+");
struct framebuff *fb = (struct framebuff *) param1;
fb_get(fb);
msi_output_fb(msi, fb);
}
ret = RET_OK + 1;
break;
}
}
break;
case MSI_CMD_AUTO_H264:
{
uint32_t cmd_self = (uint32_t) param1;
switch (cmd_self)
{
case MSI_AUTO_H264_MODE:
auto_h264->mode = param2;
break;
case MSI_AUTO_H264_TIMELAPSE_TIME:
auto_h264->timeLapse_timer = param2;
break;
}
break;
}
default:
break;
}
return ret;
}
// 如果不使能,src_from0设置最大值,其他设置为0
// src_from为VPP_DATA0或者VPP_DATA1,是支持自动设置w和h
struct msi *auto_h264_msi_init(const char *auto_h264_name, uint8_t src_from0, uint16_t w0, uint16_t h0, uint8_t src_from1, uint16_t w1, uint16_t h1)
{
uint8_t isnew;
struct msi *msi = msi_new(auto_h264_name, 0, &isnew);
struct auto_h264_msi_s *auto_h264 = (struct auto_h264_msi_s *) msi->priv;
if (isnew)
{
auto_h264 = (struct auto_h264_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct auto_h264_msi_s));
msi->priv = (void *) auto_h264;
msi->action = auto_h264_msi_action;
auto_h264->msi = msi;
auto_h264->stop = 1;
auto_h264->src_from0 = src_from0;
auto_h264->src_from1 = src_from1;
auto_h264->w0 = w0;
auto_h264->w1 = w1;
auto_h264->h0 = h0;
auto_h264->h1 = h1;
auto_h264->timeLapse_timer = 1000;
msi->enable = 1;
// 启动workqueue
OS_WORK_INIT(&auto_h264->work, auto_h264_work, 0);
os_run_work(&auto_h264->work);
}
return msi;
}

View File

@@ -0,0 +1,232 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "jpg_concat_msi.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "lib/video/dvp/jpeg/jpg.h"
#include "stream_define.h"
#include "user_work/user_work.h"
#include "lib/video/dvp/jpeg/jpg_common.h"
#include "lib/video/vpp/vpp_dev.h"
// 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
#ifdef JPG_NODE_COUNT
#define AUTO_JPG_COUNT JPG_NODE_COUNT
#else
#define AUTO_JPG_COUNT 10
#endif
extern uint8_t get_vpp_scale_w_h(uint16_t *w, uint16_t *h);
struct auto_jpg_msi_s
{
struct os_work work;
struct msi *msi;
struct msi *register_jpg_msi; // 注册的jpg的msi
uint16_t w, h;
uint8_t src_from : 3, stop : 1, which_jpg : 1, rev : 3;
};
static int32 auto_jpg_work(struct os_work *work)
{
uint8_t last_value;
int32_t ret;
uint8_t delay_time = 40;
uint8_t stop = 0;
struct auto_jpg_msi_s *auto_jpg = (struct auto_jpg_msi_s *) work;
// 这里尝试发送数据?检查一下是否有接收的msi,如果有接收的msi,则启动mjpeg,如果没有,则将硬件停下来?
uint32_t send_count = msi_output_fb(auto_jpg->msi, NULL);
// 代表有需要接收的数据流,那么就要启动硬件,尝试获取锁,然后启动mjpg,如果上一次锁是mjpg,就不需要重新启动
if (send_count)
{
// 尝试查看是否已经停止过
ret = jpg_mutex_lock(auto_jpg->which_jpg, JPG_LOCK_ENCODE, &last_value);
// 获取到锁,并且锁的值不一致,则要重新去启动mjpeg
if (ret == 0)
{
// 如果锁的值不一致,代表该mjpg被其他地方使用过了,需要重新启动
// 如果锁的值一致,但是stop == 1,代表已经停止过了,也需要重新启动
if (last_value != JPG_LOCK_ENCODE || auto_jpg->stop == 1)
{
// 关闭mjpeg
if (auto_jpg->src_from == SCALER_DATA)
{
ret = get_vpp_scale_w_h(&auto_jpg->w, &auto_jpg->h);
if (!ret)
{
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_NODE_COUNT, AUTO_JPG_COUNT);
set_vpp_scale_w_h(1, auto_jpg->w, auto_jpg->h);
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_SET_SCALE1_TYPE, 0);
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_SET_SCALE1_AUTO_FLAG, 1);
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_MSG, auto_jpg->w << 16 | auto_jpg->h);
}
else
{
stop = 1;
}
}
else
{
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_NODE_COUNT, AUTO_JPG_COUNT);
}
if (!stop)
{
// 切回到默认源头
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_FROM, auto_jpg->src_from);
// 重新启动mjpg
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 1);
auto_jpg->stop = 0;
}
}
// 解锁,可以被其他线程打断使用,因为这个是从VPP_DATA0,可以被其他线程打断使用完再恢复,
// 这样只是丢部分帧,但是可以复用mjpg
jpg_mutex_unlock(auto_jpg->which_jpg, JPG_LOCK_ENCODE);
}
// 没有获取到锁,就延时一点时间后,继续获取
else
{
delay_time = 5;
}
}
else if (send_count == 0)
{
if (auto_jpg->stop == 0)
{
ret = jpg_mutex_lock(auto_jpg->which_jpg, JPG_LOCK_ENCODE, &last_value);
if (ret == 0)
{
// 如果锁的值一致,那么就要主动停止,因为已经在启动了
if (last_value == JPG_LOCK_ENCODE)
{
msi_do_cmd(auto_jpg->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
}
jpg_mutex_unlock_check(auto_jpg->which_jpg, JPG_LOCK_ENCODE);
}
auto_jpg->stop = 1;
}
}
os_run_work_delay(work, delay_time);
return 0;
}
static int32_t auto_jpg_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct auto_jpg_msi_s *auto_jpg = (struct auto_jpg_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
os_work_cancle2(&auto_jpg->work, 1);
STREAM_LIBC_FREE(auto_jpg);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_work_cancle2(&auto_jpg->work, 1);
if (auto_jpg->register_jpg_msi)
{
msi_destroy(auto_jpg->register_jpg_msi);
auto_jpg->register_jpg_msi = NULL;
}
}
break;
case MSI_CMD_AUTO_JPG:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
// 切换数据源
case MSI_AUTO_JPG_SWITCH_ENCODE_SRC:
{
auto_jpg->src_from = arg;
auto_jpg->stop = 1;
}
break;
default:
break;
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// 如果是mjpeg,帮忙转发到其他数据流
// 如果需要支持264,也要帮忙转发
if (fb->mtype == F_JPG)
{
// 类型匹配才可以转发
if (fb->stype == FSTYPE_VIDEO_VPP_DATA0 + auto_jpg->src_from)
{
_os_printf("+");
struct framebuff *fb = (struct framebuff *) param1;
fb_get(fb);
msi_output_fb(msi, fb);
}
ret = RET_OK + 1;
break;
}
}
break;
default:
break;
}
return ret;
}
struct msi *auto_jpg_msi_init(const char *auto_jpg_name, uint8_t which_jpg, uint8_t src_from)
{
uint8_t isnew;
struct msi *msi = NULL;
msi = msi_new(auto_jpg_name, 0, &isnew);
struct auto_jpg_msi_s *auto_jpg = (struct auto_jpg_msi_s *) msi->priv;
if (isnew)
{
auto_jpg = (struct auto_jpg_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct auto_jpg_msi_s));
msi->priv = (void *) auto_jpg;
msi->action = auto_jpg_msi_action;
auto_jpg->msi = msi;
auto_jpg->src_from = src_from;
auto_jpg->stop = 1;
auto_jpg->which_jpg = which_jpg;
auto_jpg->w = 0;
auto_jpg->h = 0;
// w和h在vpp_data0和vpp_data1的时候是没有作用的
auto_jpg->register_jpg_msi = jpg_concat_msi_init_start(auto_jpg->which_jpg, 0, 0, NULL, auto_jpg->src_from, 0);
if (auto_jpg->register_jpg_msi)
{
msi_add_output(auto_jpg->register_jpg_msi, NULL, msi->name);
}
else
{
msi_destroy(msi);
msi = NULL;
goto auto_jpg_msi_init_end;
}
msi->enable = 1;
// 启动workqueue
OS_WORK_INIT(&auto_jpg->work, auto_jpg_work, 0);
os_run_work(&auto_jpg->work);
}
auto_jpg_msi_init_end:
return msi;
}

View File

@@ -0,0 +1,102 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "stream_frame.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
// 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
struct avi_thumb_s
{
struct msi *msi;
uint8_t thumb_name[64];
uint8_t filter_type;
uint8_t srcID;
};
int32_t avi_thumb_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct avi_thumb_s *avi_thumb = (struct avi_thumb_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
STREAM_LIBC_FREE(avi_thumb);
}
break;
// 需要关闭,则将关联的msi关闭
case MSI_CMD_PRE_DESTROY:
{
}
break;
// 接收到数据,唤醒workqueue,去组图,理论有图都要重新组,然后通过output的时候过滤类型,这里也可以先过滤部分类型
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
if (fb->mtype == F_JPG && avi_thumb->srcID == fb->srcID && (!avi_thumb->filter_type || avi_thumb->filter_type == fb->stype))
{
struct framebuff *c_fb = fb_clone(fb, F_THUMB_JPG << 8 | fb->stype, msi);
if (c_fb)
{
c_fb->priv = avi_thumb->thumb_name;
msi_output_fb(msi, c_fb);
msi->enable = 0;
}
break;
}
ret = RET_ERR;
}
break;
case MSI_CMD_FREE_FB:
{
}
break;
default:
break;
}
return ret;
}
struct msi *avi_thumb_msi_init(const char *filename, uint8_t srcID, uint8_t filter)
{
uint8_t is_new;
struct msi *msi = msi_new(R_AVI_THUMB, 0, &is_new);
struct avi_thumb_s *avi_thumb = NULL;
if (is_new)
{
avi_thumb = (struct avi_thumb_s *) STREAM_LIBC_ZALLOC(sizeof(struct avi_thumb_s));
os_memcpy(avi_thumb->thumb_name, filename, strlen(filename) + 1);
msi->priv = avi_thumb;
avi_thumb->msi = msi;
avi_thumb->filter_type = filter;
avi_thumb->srcID = srcID;
msi->action = avi_thumb_msi_action;
// 给到解码然后生成缩略图
msi_add_output(msi, NULL, R_THUMB_USB);
msi->enable = 1;
msi_add_output(NULL, ROUTE_USB, R_AVI_THUMB); // avi缩略图?
}
else
{
avi_thumb = (struct avi_thumb_s *) msi->priv;
os_memcpy(avi_thumb->thumb_name, filename, strlen(filename) + 1);
msi->enable = 1;
}
return msi;
}

View File

@@ -0,0 +1,402 @@
#include "file_common_api.h"
#include "lib/fs/fatfs/osal_file.h"
#include "app/video_app/file_thumb.h"
#include "osal/string.h"
#include "lwip/sockets.h"
static uint8_t get_rtc_time_str(char *timestr, uint8_t timestr_len, struct timeval *t)
{
struct tm *time_info;
struct timeval *time = t;
struct timeval ptimeval;
if(!time)
{
time = &ptimeval;
gettimeofday(time, NULL);
}
time_t time_val = (time_t)time->tv_sec;
int ms = time->tv_usec / 1000;
time_info = gmtime(&time_val);
if (time_info == NULL) {
_os_printf("gmtime error\r\n");
return 1;
}
os_snprintf(timestr, timestr_len, "%04d%02d%02d%02d%02d%02d%03d",
time_info->tm_year + 1900, time_info->tm_mon + 1, time_info->tm_mday,
time_info->tm_hour, time_info->tm_min, time_info->tm_sec, ms);
return 0;
}
/*
* 获取JPG文件路径不按天存储
* img_dir: 录像目录0:/IMG
* file_path: 文件名,格式: 0:/IMG/20250918010101000.JPG
* filepath_size: 文件名大小
*/
uint8_t takephoto_name(const char *img_dir, char *file_path, int filepath_size)
{
char timestr[FILE_NAME_LEN + 1];
if(get_rtc_time_str(timestr, FILE_NAME_LEN + 1, NULL))
return 1;
os_snprintf(file_path, filepath_size, "%s/%.*s.JPG", img_dir, FILE_NAME_LEN, timestr);
return 0;
}
/*
* 获取JPG文件路径按天存储
* img_dir: 录像目录0:/IMG
* file_path: 文件名,格式: 0:/IMG/20250918/20250918010101000.JPG
* filepath_size: 文件名大小
*/
uint8_t takephoto_name_day(const char *img_dir, char *file_path, int filepath_size)
{
char timestr[FILE_NAME_LEN + 1];
if(get_rtc_time_str(timestr, FILE_NAME_LEN + 1, NULL))
return 1;
os_snprintf(file_path, filepath_size, "%s/%.*s/%.*s.JPG", img_dir, FILE_SUB_PATH_LEN, timestr, FILE_NAME_LEN, timestr);
return 0;
}
/*
* 获取JPG文件名称
* filename: 文件名,格式: 20250918010101000.JPG
* filename_size: 文件名大小
*/
int32_t takephoto_name_no_dir(char *filename, int filename_size)
{
char timestr[FILE_NAME_LEN + 1];
if(get_rtc_time_str(timestr, FILE_NAME_LEN + 1, NULL))
return 1;
os_snprintf(filename, filename_size, "%.*s.JPG", FILE_NAME_LEN, timestr);
return 0;
}
/*
* 获取JPG文件名称
* filename: 文件名,格式: 20250918010101000.JPG
* filename_size: 文件名大小
* time: 时间
*/
int32_t takephoto_name_no_dir_time(char *filename, int filename_size, struct timeval *time)
{
char timestr[FILE_NAME_LEN + 1];
if(get_rtc_time_str(timestr, FILE_NAME_LEN + 1, time))
return 1;
os_snprintf(filename, filename_size, "%.*s.JPG", FILE_NAME_LEN, timestr);
return 0;
}
/*
* 为JPG文件添加目录
* path: 文件路径,格式: dir_name/filename
* path_size: 文件路径大小
*/
int32_t takephoto_name_add_dir(char *filename, int filename_size, char *path, const char *dir_name)
{
if(dir_name)
{
os_snprintf(filename, filename_size,"%s/%s", dir_name, path);
}
else
{
os_snprintf(filename, filename_size,"%s", path);
}
return 0;
}
/*
* 获取MP4文件名
* rec_dir: 录像目录0:/REC
* sub_path: 子目录,格式: 0:/REC/20250918
* file_name: 文件名,格式: 20250918010101000.MP4
*/
uint8_t get_mp4_file_name(const char *rec_dir, char *sub_path, char *file_name)
{
char timestr[FILE_NAME_LEN + 1];
if(get_rtc_time_str(timestr, FILE_NAME_LEN + 1, NULL))
return 1;
os_sprintf(sub_path, "%s/%.*s", rec_dir, FILE_SUB_PATH_LEN, timestr);
os_sprintf(file_name, "%.*s.MP4", FILE_NAME_LEN, timestr);
return 0;
}
/*
* 获取文件名
* rec_dir: 录像目录0:/RECA
* sub_path: 子目录,格式: 0:/RECA/20250918
* file_name: 文件名,格式: 20250918010101000.MP4
* extension_name: 文件扩展名,格式: .MP4/.AVI/...
*/
uint8_t get_extension_file_name(const char *rec_dir, char *sub_path, char *file_name, const char *extension_name)
{
char timestr[FILE_NAME_LEN + 1];
if(get_rtc_time_str(timestr, FILE_NAME_LEN + 1, NULL))
return 1;
os_sprintf(sub_path, "%s/%.*s", rec_dir, FILE_SUB_PATH_LEN, timestr);
os_sprintf(file_name, "%.*s%s", FILE_NAME_LEN, timestr, extension_name);
return 0;
}
/*
* 获取文件名
* rec_dir: 录像目录0:/RECA
* sub_path: 子目录,格式: 0:/RECA/20250918
* file_name: 文件名,格式: 20250918010101000.MP4
* extension_name: 文件扩展名,格式: .MP4/.AVI/...
* time: 时间
*/
uint8_t get_extension_file_name_time(const char *rec_dir, char *sub_path, char *file_name, const char *extension_name, struct timeval *time)
{
char timestr[FILE_NAME_LEN + 1];
if(get_rtc_time_str(timestr, FILE_NAME_LEN + 1, time))
return 1;
os_sprintf(sub_path, "%s/%.*s", rec_dir, FILE_SUB_PATH_LEN, timestr);
os_sprintf(file_name, "%.*s%s", FILE_NAME_LEN, timestr, extension_name);
return 0;
}
/*
* 根据文件类型获取照片文件路径
* filename: 文件名
* path: 文件路径
* pathsize: 文件路径大小
*/
void gen_photo_path(const char *filename, char *path, uint32_t pathsize)
{
const char *rec_path = NULL;
const char* file_dot = strrchr(filename, '.');
if(file_dot == NULL) {
os_printf("%s file_dot is NULL\n", __FUNCTION__);
return;
}
if(os_strstr(filename, FRONT_SUFFIX)) {
rec_path = IMGA_PATH;
} else if(os_strstr(filename, INTER_SUFFIX)) {
rec_path = IMGB_PATH;
} else if(os_strstr(filename, BACK_SUFFIX)) {
rec_path = IMGC_PATH;
} else {
rec_path = IMG_PATH;
}
os_snprintf(path, pathsize, "%s/%.*s%s", rec_path, FILE_NAME_LEN, filename, file_dot);
os_printf("%s path: %s\n", __FUNCTION__, path);
}
/*
* 根据文件类型获取视频文件路径
* filename: 文件名
* path: 文件路径
* pathsize: 文件路径大小
*/
void gen_video_path(const char *filename, char *path, uint32_t pathsize)
{
uint8_t prefix_len = 0;
const char *rec_path = NULL;
const char *file_name = os_strrchr(filename, '/');
if(file_name == NULL) {
file_name = filename;
}
const char* file_dot = strrchr(filename, '.');
if(file_dot == NULL) {
os_printf("%s file_dot is NULL\n", __FUNCTION__);
return;
}
if(os_strstr(file_name, SOS_PREFIX)) {
prefix_len = strlen(SOS_PREFIX);
} else if(os_strstr(file_name, PARKF_PREFIX)) {
prefix_len = strlen(PARKF_PREFIX);
}
if(os_strstr(file_name, FRONT_SUFFIX)) {
rec_path = RECA_PATH;
} else if(os_strstr(file_name, INTER_SUFFIX)) {
rec_path = RECB_PATH;
} else if(os_strstr(file_name, BACK_SUFFIX)) {
rec_path = RECC_PATH;
} else {
rec_path = REC_PATH;
}
os_snprintf(path, pathsize, "%s/%.*s/%.*s%s", rec_path, FILE_SUB_PATH_LEN, file_name + prefix_len, FILE_NAME_LEN + prefix_len, file_name, file_dot);
os_printf("%s path: %s\n", __FUNCTION__, path);
}
void gen_photo_video_path(const char *filename, char *path, uint32_t pathsize)
{
if(os_strstr(filename, JPG_EXTENSION_NAME))
{
gen_photo_path(filename, path, pathsize);
}
else if(os_strstr(filename, MP4_EXTENSION_NAME) || os_strstr(filename, AVI_EXTENSION_NAME))
{
gen_video_path(filename, path, pathsize);
}
else
{
_os_printf("filename %s format err!\n", filename);
}
}
/***************************************************************
* 获取文件列表接口 *
* *
****************************************************************/
typedef void (* conver_send_fn)(int fd, struct file_info *file_info);
void send_chunk(int fd, const char* data, uint16_t data_len)
{
char chunk_buffer[256]; // 或者使用动态分配
int chunk_len = os_snprintf(chunk_buffer, sizeof(chunk_buffer), "%x\r\n%s\r\n", data_len, data);
send(fd, chunk_buffer, chunk_len, 0);
}
void conver_send(int fd, struct file_info *file_info)
{
char send_buffer[256];
char timestr[15];
os_snprintf(timestr, sizeof(timestr), "%04d%02d%02d%02d%02d%02d",
((file_info->file_date & 0xFE00)>>9)+1980,
(file_info->file_date & 0x1E0)>>5,
(file_info->file_date & 0x1F),
(file_info->file_time & 0xF800)>>11,
(file_info->file_time & 0x7E0)>>5,
(file_info->file_time & 0x1F)*2);
os_snprintf(send_buffer, sizeof(send_buffer), "%s{\"name\":\"%s\",\"size\":%d,\"createtimestr\":\"%s\"}",
(file_info->file_count++ > 0) ? "," : "",
file_info->file_name,
file_info->file_size / 1024,
timestr);
// 发送数据
send_chunk(fd, send_buffer, strlen(send_buffer));
}
int get_fileinfo_send(int fd, const char *search_dir, const char *ext_name, conver_send_fn cs_fn)
{
struct file_info file_info;
FILINFO *fil = NULL;
void *dir = osal_opendir(search_dir);
if (dir)
{
do
{
fil = osal_readdir(dir);
if(!fil) break;
if(fil->fname[0] == 0) break;
if (fil->fname[0] == '.') continue;
if(!osal_dirent_isdir(fil))
{
char *filename = osal_dirent_name(fil);
uint32_t filesize = osal_dirent_size(fil);
uint32_t filedate = osal_dirent_date(fil);
uint32_t filetime = osal_dirent_time(fil);
uint8_t filename_len = os_strlen(filename);
// 检查后缀名是否匹配
if(ext_name)
{
uint8_t ext_filename[16];
uint8_t extname_len = os_strlen(ext_name);
// 进行全部转换成大写
if (filename_len - extname_len > 0)
{
for (uint8_t i = 0; i < os_strlen(ext_name); i++)
{
ext_filename[i] = toupper(filename[filename_len - extname_len + i]);
}
// 后缀名匹配
if (memcmp(ext_name, ext_filename, extname_len) != 0)
{
continue;
}
}
}
file_info.file_name = filename;
file_info.file_size = filesize;
file_info.file_date = filedate;
file_info.file_time = filetime;
file_info.file_count++;
cs_fn(fd, &file_info);
}
} while (fil);
osal_closedir(dir);
}
return file_info.file_count;
}
// 发送demo采用分块发送的方式
void send_stream_filelist(int fd)
{
int video_count = 0;
int photo_count = 0;
char send_buffer[256];
// 创建HTTP响应头
snprintf(send_buffer, sizeof(send_buffer),
"HTTP/1.0 200 OK\r\n"
"Connection: close\r\n"
"Content-Type: application/json\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n");
send(fd, send_buffer, strlen(send_buffer), 0);
// 获取视频
os_snprintf(send_buffer, sizeof(send_buffer), "{\"result\":0,\"info\":[{\"folder\":\"%s\",\"files\":[", LOOP_PREFIX);
send_chunk(fd, send_buffer, strlen(send_buffer));
void *dir = osal_opendir(REC_PATH);
FILINFO *fil = NULL;
char path[64];
if(dir)
{
do
{
fil = osal_readdir(dir);
if(!fil) break;
if(fil->fname[0] == 0) break;
if (fil->fname[0] == '.') continue;
if(osal_dirent_isdir(fil))
{
os_snprintf(path, sizeof(path), "%s/%s", REC_PATH, fil->fname);
video_count += get_fileinfo_send(fd, path, MP4_EXTENSION_NAME, conver_send);
send_chunk(fd, ",", strlen(","));
}
} while(fil);
}
// 获取照片
os_snprintf(send_buffer, sizeof(send_buffer), "],\"count\":%d},{\"folder\":\"%s\",\"files\":[", video_count, EVENT_PREFIX);
send_chunk(fd, send_buffer, strlen(send_buffer));
photo_count = get_fileinfo_send(fd, IMG_PATH, JPG_EXTENSION_NAME, conver_send);
os_snprintf(send_buffer, sizeof(send_buffer), "],\"count\":%d}]}", photo_count);
send_chunk(fd, send_buffer, strlen(send_buffer));
// 发送分块结束标记
send_chunk(fd, "", 0); // 0长度块表示结束
}

View File

@@ -0,0 +1,32 @@
#ifndef _FILE_COMMON_API_H_
#define _FILE_COMMON_API_H_
#include <stdint.h>
#include "typesdef.h"
// 文件名长度
#define FILE_NAME_LEN 17
#define FILE_SUB_PATH_LEN 8
struct file_info
{
char *file_name;
uint32_t file_size;
uint32_t file_date;
uint32_t file_time;
uint32_t file_count;
};
uint8_t takephoto_name(const char *img_dir, char *file_path, int filepath_size);
uint8_t takephoto_name_day(const char *img_dir, char *file_path, int filepath_size);
int32_t takephoto_name_no_dir(char *filename, int filename_size);
int32_t takephoto_name_no_dir_time(char *filename, int filename_size, struct timeval *time);
int32_t takephoto_name_add_dir(char *filename, int filename_size,char *path, const char *dir_name);
uint8_t get_mp4_file_name(const char *rec_dir, char *sub_path, char *file_name);
uint8_t get_extension_file_name(const char *rec_dir, char *sub_path, char *file_name, const char *extension_name);
uint8_t get_extension_file_name_time(const char *rec_dir, char *sub_path, char *file_name, const char *extension_name, struct timeval *time);
void gen_photo_path(const char *filename, char *path, uint32_t pathsize);
void gen_video_path(const char *filename, char *path, uint32_t pathsize);
void gen_photo_video_path(const char *filename, char *path, uint32_t pathsize);
#endif

View File

@@ -0,0 +1,298 @@
#include "cjson/cjson.h"
#include "lib/fs/fatfs/osal_file.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "vfs.h"
#include "file_thumb.h"
#include "file_common_api.h"
/*******************************************************************************
* 这个文件主要是为了读取图片和缩略图用的,所以文件路径是固定
*
******************************************************************************/
// 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 HASH_MOD (9)
uint32_t calc_hash(const char *filename)
{
uint32_t hash = 0;
const char *h_name = (const char *) filename;
uint8_t t;
// 遇到'.'代表后缀名,则不需要计算了
while (*h_name && (*h_name) != '.')
{
t = tolower(*h_name);
hash = (hash + (hash << 2)) ^ t;
h_name++;
}
hash = hash % HASH_MOD;
return hash;
}
uint32_t calc_hash2(const char *filename, uint32_t *pre_name_len)
{
uint32_t hash = 0;
const char *h_name = (const char *) filename;
uint8_t t;
// 遇到'.'代表后缀名,则不需要计算了
while (*h_name && (*h_name) != '.')
{
t = tolower(*h_name);
hash = (hash + (hash << 2)) ^ t;
h_name++;
}
hash = hash % HASH_MOD;
if (pre_name_len)
{
*pre_name_len = h_name - filename;
}
return hash;
}
// pathsize后续可以用来判断是否越界的,现在没有用上
// path保存路径的buf
uint8_t gen_thumb_path(const char *filename, char *path, uint32_t pathsize)
{
uint32_t name_len;
const char *p;
p = strrchr(filename, ':');
if(!p)
{
p = filename;
}
else
{
p++;
}
filename = p;
p = strrchr(p, '/');
if(p)
{
p++;
}
else
{
p = filename;
}
filename = p;
uint32_t hash = calc_hash2(filename, &name_len);
os_sprintf((char *) path, THUMB_PATH "/%d/%s", hash, filename);
// 将后缀名修改成jpg
uint32_t offset = os_strlen(path) - os_strlen(filename) + name_len;
path[offset++] = '.';
path[offset++] = 'J';
path[offset++] = 'P';
path[offset++] = 'G';
os_printf("%s:%d\tpath:%s\n", __FUNCTION__, __LINE__, path);
return 0;
}
uint8_t gen_normal_jpg_path2(const char *filename, char *path, uint32_t pathsize)
{
uint32_t name_len;
calc_hash2(filename, &name_len);
os_sprintf((char *) path, IMG_PATH "/%s", filename);
// 将后缀名修改成jpg
uint32_t offset = os_strlen(path) - os_strlen(filename) + name_len;
path[offset++] = '.';
path[offset++] = 'J';
path[offset++] = 'P';
path[offset++] = 'G';
os_printf("%s:%d\tpath:%s\n", __FUNCTION__, __LINE__, path);
return 0;
}
void *jpg_file_read(const char *filename, uint8_t thumb, int32_t *filesize)
{
char path[128];
uint8_t *file_buf = NULL;
// 寻找缩略图
if (thumb)
{
// 得到缩略图的路径
gen_thumb_path(filename, path, sizeof(path));
}
else
{
gen_photo_path(filename, path, sizeof(path));
}
// 读取文件
// int fd = VFS_open((const char *)path,O_RDONLY);
F_FILE *fd = osal_fopen((const char *) path, "rb");
// if(fd >= 0)
if (fd)
{
//*filesize = VFS_fsize(fd);
*filesize = osal_fsize(fd);
os_printf("filesize:%d\tpath:%s\n", *filesize, path);
if (*filesize <= 0)
{
goto jpg_file_read_end;
}
file_buf = STREAM_MALLOC((*filesize));
os_printf("file_buf:%X\n", file_buf);
if (!file_buf)
{
goto jpg_file_read_end;
}
osal_fread(file_buf, 1, *filesize, fd);
osal_fclose(fd);
// VFS_read(fd,file_buf,*filesize);
// VFS_close(fd);
// fd = -1;
fd = NULL;
}
jpg_file_read_end:
// if(fd >= 0)
if (fd)
{
// VFS_close(fd);
osal_fclose(fd);
}
return file_buf;
}
// 可以释放在buf增加容错
void jpg_file_free(void *buf)
{
if (buf)
{
STREAM_FREE(buf);
}
}
/***********************************************
返回图片的json结构,返回某给后缀名字的文件
参数:search_dir:搜索的目录
extension_name:后缀名
gen_prefix:生成特定前缀
*count:返回array数量,因为这个返回是文件列表,所以有数量,
***********************************************/
cJSON *get_extension_json(const char *search_dir, const char *extension_name, const char *gen_prefix, int type, int *count)
{
// fs_dir_t *dir;
// struct dirent *dent;
cJSON *array_json = NULL;
cJSON *path_array = NULL;
int event_count = 0;
char path[128];
FILINFO *fil;
// DIR dh;
// dir = VFS_opendir(search_dir);
void *dir = osal_opendir((char *) search_dir);
if (dir)
{
path_array = cJSON_CreateArray();
do
{
// dent = VFS_readdir(dir);
fil = osal_readdir(dir);
if (fil)
{
if (osal_dirent_isdir(fil))
{
continue;
}
// 检查后缀名是否匹配
uint8_t extension_filename[16]; // 文件后缀名转换,统一大写
uint8_t extension_name_len = strlen(extension_name);
char *fname = osal_dirent_name(fil);
uint8_t filename_len = strlen(fname);
uint32_t filesize = osal_dirent_size(fil);
// 进行全部转换成大写
if (filename_len - extension_name_len > 0)
{
for (uint8_t i = 0; i < strlen(extension_name); i++)
{
extension_filename[i] = toupper(fname[filename_len - extension_name_len + i]);
}
// 后缀名匹配
if (memcmp(extension_name, extension_filename, extension_name_len) == 0)
{
array_json = cJSON_CreateObject();
os_sprintf((char *) path, "%s%s", gen_prefix, fname);
cJSON_AddItemToObject(array_json, "name", cJSON_CreateString(path));
cJSON_AddItemToObject(array_json, "size", cJSON_CreateNumber(filesize / 1024));
cJSON_AddItemToObject(array_json, "createtimestr", cJSON_CreateString("20250318103006"));
cJSON_AddItemToObject(array_json, "type", cJSON_CreateNumber(type));
// 加入到json
cJSON_AddItemToArray(path_array, array_json);
event_count++;
}
}
}
} while (fil);
// VFS_closedir(dir);
osal_closedir(dir);
}
*count = event_count;
return path_array;
}
char *photo_json_path()
{
cJSON *root = NULL;
cJSON *loop = NULL;
cJSON *event = NULL;
cJSON *path_array = NULL;
cJSON *info_array = cJSON_CreateArray();
cJSON *video_array = NULL;
char *json_str = NULL;
int event_count = 0;
// 视频这里默认先返回空数据
loop = cJSON_CreateObject();
cJSON_AddItemToObject(loop, "folder", cJSON_CreateString("loop"));
video_array = get_extension_json("0:" MAIN_PATH "/" NORMAL_PATH, MP4_EXTENSION_NAME, "loop/RECA/", 2, &event_count);
if (video_array)
{
cJSON_AddItemToObject(loop, "files", video_array);
cJSON_AddItemToObject(loop, "count", cJSON_CreateNumber(event_count));
}
event = cJSON_CreateObject();
cJSON_AddItemToObject(event, "folder", cJSON_CreateString("event"));
path_array = get_extension_json("0:" MAIN_PATH "/" NORMAL_PATH, JPG_EXTENSION_NAME, "event/IMGA/", 1, &event_count);
if (path_array)
{
cJSON_AddItemToObject(event, "files", path_array);
cJSON_AddItemToObject(event, "count", cJSON_CreateNumber(event_count));
}
cJSON_AddItemToArray(info_array, loop);
cJSON_AddItemToArray(info_array, event);
root = cJSON_CreateObject();
cJSON_AddItemToObject(root, "result", cJSON_CreateNumber(0));
cJSON_AddItemToObject(root, "info", info_array);
// 生成json数据
json_str = cJSON_PrintUnformatted(root);
cJSON_Delete(root);
return json_str;
}
void path_json_free(void *str)
{
if (str)
{
cJSON_free(str);
}
}

View File

@@ -0,0 +1,39 @@
#ifndef __FILE_THUMB_H
#define __FILE_THUMB_H
#include "sys_config.h"
#include "typesdef.h"
#define MAIN_PATH ""
#define THUMB_PATH "0:/thumb"
#define NORMAL_PATH "DCIM"
#define IMG_PATH "0:/IMG"
#define IMGA_PATH "0:/IMGA"
#define IMGB_PATH "0:/IMGB"
#define IMGC_PATH "0:/IMGC"
#define REC_PATH "0:/REC"
#define RECA_PATH "0:/RECA"
#define RECB_PATH "0:/RECB"
#define RECC_PATH "0:/RECC"
#define EMR_PATH "0:/EMR"
#define PARK_PATH "0:/PARK"
#define JPG_EXTENSION_NAME ".JPG" // 根据文件系统是否区分大小写,可以在代码上进行不区分大小写,这里用大写,代码将获取的后缀名进行转换成大写
#define MP4_EXTENSION_NAME ".MP4"
#define AVI_EXTENSION_NAME ".AVI"
#define FRONT_SUFFIX "_F"
#define INTER_SUFFIX "_I"
#define BACK_SUFFIX "_B"
#define LOOP_PREFIX "LOOP"
#define EVENT_PREFIX "EVENT"
#define EMR_PREFIX "EMR"
#define PARK_PREFIX "PARK"
#define SOS_PREFIX "SOS_"
#define PARKF_PREFIX "PARK_"
uint8_t gen_thumb_path(const char *filename, char *path, uint32_t pathsize);
void *jpg_file_read(const char *filename,uint8_t thumb,int32_t *filesize);
void jpg_file_free(void *buf);
uint32_t calc_hash(const char *filename);
char *photo_json_path();
void path_json_free(void *str);
#endif

View File

@@ -0,0 +1,371 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "hal/h264.h"
#include "lib/video/gen/gen420_dev.h"
#include "dev/gen/hggen420.h"
#include "stream_define.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "gen420_hardware_msi.h"
#include "lib/video/dvp/jpeg/jpg.h"
// 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 GEN420_MAX_MSG (10)
#define MAX_GEN420_HARDWARE_RX 8
enum gen420_hardware_enum
{
GEN420_HARDWARE_READY = BIT(0), //将gen420的work停止
};
struct gen420_hardware_s
{
struct msi *msi;
struct gen420_device *gen420_dev;
os_msgqueue_t msgq;
struct os_event evt;
struct os_task task;
uint8_t *gen420_sram;
uint8_t *gen420_sram2;
uint8_t *gen420_sram3;
};
os_msgqueue_t *gen420_msgq = NULL;
volatile struct list_head gen420_queue_head;
static struct os_semaphore gen420wq_sem = {0,NULL};
void gen420wq_sema_init()
{
os_sema_init(&gen420wq_sem,0);
}
void gen420wq_sema_down(int32 tmo_ms)
{
os_sema_down(&gen420wq_sem,tmo_ms);
}
void gen420wq_sema_up()
{
os_sema_up(&gen420wq_sem);
}
int register_gen420_queue(uint8_t type,uint32_t w,uint32_t h,gen420_kick_fn kick_fn,gen420_free_fn free_fn,uint32 priv){
struct gen420_msg_s *wq;
struct msi *msi = msi_find("gen420_h", 0);
struct gen420_hardware_s *gen420 = (struct gen420_hardware_s *)msi->priv;
msi_put(msi);
if(gen420->gen420_sram == NULL){
gen420->gen420_sram = (uint8_t *)STREAM_LIBC_MALLOC(8 * 1024);
gen420->gen420_sram2 = (uint8_t *)STREAM_LIBC_MALLOC(2 * 1024);
gen420->gen420_sram3 = (uint8_t *)STREAM_LIBC_MALLOC(2 * 1024);
//任意一个空间申请不到,都算失败
if(gen420->gen420_sram && gen420->gen420_sram2 && gen420->gen420_sram3){
gen420_sram_linebuf_adr(gen420->gen420_dev, (uint32)gen420->gen420_sram, (uint32)(gen420->gen420_sram2), (uint32)(gen420->gen420_sram3));
}
else
{
os_printf(KERN_ERR"[%s] malloc failed!!!\n",__func__);
if(gen420->gen420_sram)
{
STREAM_LIBC_FREE(gen420->gen420_sram);
gen420->gen420_sram = NULL;
}
if(gen420->gen420_sram2)
{
STREAM_LIBC_FREE(gen420->gen420_sram2);
gen420->gen420_sram2 = NULL;
}
if(gen420->gen420_sram3)
{
STREAM_LIBC_FREE(gen420->gen420_sram3);
gen420->gen420_sram3 = NULL;
}
return -1;
}
}
wq = malloc(sizeof(struct gen420_msg_s));
wq->type = type;
wq->kick_fn = kick_fn;
wq->free_fn = free_fn;
wq->w = w;
wq->h = h;
wq->sta = 0;
wq->devid = type;
wq->fn_data = (void *)priv;
INIT_LIST_HEAD(&wq->list);
list_add_tail(&wq->list,(struct list_head*)&gen420_queue_head);
return 0;
}
int unregister_gen420_queue(uint32_t devid){
int ret = 0;
struct list_head *dlist;
struct gen420_msg_s* gen420dev;
struct msi *msi = msi_find("gen420_h", 0);
struct gen420_hardware_s *gen420 = (struct gen420_hardware_s *)msi->priv;
msi_put(msi);
if(list_empty((struct list_head *)&gen420_queue_head) != TRUE){
dlist = (struct list_head *)&gen420_queue_head;
do{
dlist = dlist->next;
if(dlist == &gen420_queue_head){
return -1;
}else{
gen420dev = list_entry((struct list_head *)dlist,struct gen420_msg_s,list);
if(gen420dev->devid == devid){
list_del(&gen420dev->list);
free(gen420dev);
if(list_empty((struct list_head *)&gen420_queue_head) == TRUE){
if(gen420->gen420_sram)
{
STREAM_LIBC_FREE(gen420->gen420_sram);
gen420->gen420_sram = NULL;
}
if(gen420->gen420_sram2)
{
STREAM_LIBC_FREE(gen420->gen420_sram2);
gen420->gen420_sram2 = NULL;
}
if(gen420->gen420_sram3)
{
STREAM_LIBC_FREE(gen420->gen420_sram3);
gen420->gen420_sram3 = NULL;
}
}
return 1;
}
}
}while(1);
}else{
ret = -1;
}
return ret;
}
int change_gen420dev_w_h(uint8_t devid,uint16_t w,uint16_t h){
struct list_head *dlist;
struct gen420_msg_s* gen420dev;
if(list_empty((struct list_head *)&gen420_queue_head) != TRUE){
dlist = (struct list_head *)&gen420_queue_head;
do{
dlist = dlist->next;
if(dlist == &gen420_queue_head){
return -1;
}else{
gen420dev = list_entry((struct list_head *)dlist,struct gen420_msg_s,list);
if(gen420dev->devid == devid){
gen420dev->w = w;
gen420dev->h = h;
return 1;
}
}
}while(1);
}
return 0;
}
int wake_up_gen420_queue(uint8_t devid,uint8_t *data_rom){
int ret = 0;
struct list_head *dlist;
struct gen420_msg_s* gen420dev;
if(list_empty((struct list_head *)&gen420_queue_head) != TRUE){
dlist = (struct list_head *)&gen420_queue_head;
do{
dlist = dlist->next;
if(dlist == &gen420_queue_head){
return -1;
}else{
gen420dev = list_entry((struct list_head *)dlist,struct gen420_msg_s,list);
if(gen420dev->devid == devid){
if(gen420dev->sta == 1){
return 2; //this id all ready running
}
gen420dev->sta = 1;
gen420dev->data = (uint8_t *)data_rom;
gen420wq_sema_up();
return 1;
}
}
}while(1);
}else{
ret = -1;
}
return ret;
}
extern volatile uint8 done_percent;
void gen420_hardware_thread(void *d)
{
uint32_t have_h264_jpg_stream = 0;
int32_t res;
// int32_t err;
int32_t kick_res;
struct list_head *dlist;
struct msi *msi = (struct msi*)d;
struct gen420_msg_s *msg = NULL;
struct gen420_hardware_s *gen420 = (struct gen420_hardware_s *)msi->priv;
struct gen420_msg_s *msgsub[GEN420_MAX_MSG]; //0: jpg for sub stream 1: h264 for sub stream 2-255: user 目前先限定最多3个用户同时使用
// int8_t msg_cnt = 0;
uint8_t itk = 0;
uint32_t flags;
while(1)
{
rewait:
gen420wq_sema_down(-1);
for(itk = 0;itk < GEN420_MAX_MSG;itk++){
msgsub[itk] = NULL;
}
flags = disable_irq();
if(list_empty((struct list_head *)&gen420_queue_head) != TRUE){ //每次都会把链表从头查到尾
dlist =(struct list_head *) &gen420_queue_head;
do{
dlist = dlist->next;
if(dlist == &gen420_queue_head){
break;
}else{
msg = list_entry((struct list_head *)dlist,struct gen420_msg_s,list);
if(msg->type < 2){
have_h264_jpg_stream = 1;
}
if(msg->sta == 1){
//memcpy(msgsub[msg->type],msg,sizeof(struct gen420_msg_s));
msgsub[msg->type] = msg;
//msg->sta = 0;
}
}
}while(1);
}else{
enable_irq(flags);
continue;
}
enable_irq(flags);
for(itk = 0;itk < GEN420_MAX_MSG;itk++)
{
if(msgsub[itk] != NULL){
//如果类型超过2,就是自定义的,那么要看看是否有副码流,如果有副码流,需要等待副码流去编码后再执行自定义的
//如果没有辅码流需要编码,则不需要执行去等待VPP小于80%去编码(这里80%是为了给足够时间去编辅码流,如果没有辅码流,这里就没有意义了)
if(msgsub[itk]->type >= 2 && have_h264_jpg_stream)
{
while(done_percent > 80){
if(have_h264_jpg_stream)
{
//os_sleep_ms(1);
//os_sleep_ms(1);
goto rewait;
}else{
os_sleep_ms(1);
}
}
}
msg = msgsub[itk];
if(msg->kick_fn){
kick_res = msg->kick_fn(msg); //配置对应的h264/jpg的数据源为gen420
//没有kick成功,直接释放
if(kick_res == 0)
{
gen420_frame_size(gen420->gen420_dev, msg->w, msg->h);
gen420_psram_adr(gen420->gen420_dev, (uint32)msg->data, (uint32)(msg->data + msg->w * msg->h), (uint32)(msg->data + msg->w * msg->h + msg->w * msg->h / 4));
gen420_dma_run(gen420->gen420_dev);
res = os_event_wait(&gen420->evt, GEN420_HARDWARE_READY, NULL, OS_EVENT_WMODE_CLEAR, 1000);
//有异常?正常不应该1s都没有完成
if(res)
{
os_printf("%s:%d err\n",__FUNCTION__,__LINE__);
}
}
else
{
os_printf(KERN_ERR"gen420_hardware kick error:%d\n",kick_res);
}
}
msg->sta = 0;
//如果副码流已经用完,就代表其他数据流可以使用了
if(msg->type < 2)
{
have_h264_jpg_stream = 0;
}
if(msg && msg->free_fn)
{
msg->free_fn(msg);
}
}
}
have_h264_jpg_stream = 0;
}
//gen420_hardware_thread_end:
return;
}
static int32 gen420_msi_isr(uint32 irq, uint32 irq_data, uint32 param1, uint32 param2)
{
struct gen420_hardware_s *gen420 = (struct gen420_hardware_s *)irq_data;
os_event_set(&gen420->evt, GEN420_HARDWARE_READY, NULL);
return 0;
}
struct msi *gen420_hardware_msi_init()
{
uint8_t is_new = 0;
struct msi *msi = msi_new("gen420_h", 0, &is_new);
if(is_new)
{
struct gen420_hardware_s *gen420 = (struct gen420_hardware_s *)msi->priv;
gen420 = (struct gen420_hardware_s *)STREAM_LIBC_ZALLOC(sizeof(struct gen420_hardware_s));
msi->priv = (void*)gen420;
gen420->gen420_dev = (struct gen420_device *)dev_get(HG_GEN420_DEVID);
os_msgq_init(&gen420->msgq,MAX_GEN420_HARDWARE_RX);
os_event_init(&gen420->evt);
gen420_open(gen420->gen420_dev);
gen420_request_irq(gen420->gen420_dev, GEN420_DONE_ISR, gen420_msi_isr, (uint32)gen420);
gen420_dst_h264_and_jpg(gen420->gen420_dev, 0);
INIT_LIST_HEAD((struct list_head *)&gen420_queue_head);
gen420wq_sema_init();
msi->enable = 1;
//创建线程
gen420_msgq = &gen420->msgq;
OS_TASK_INIT("gen420_s", &gen420->task, gen420_hardware_thread, (void*)msi, OS_TASK_PRIORITY_ABOVE_NORMAL+0xf, NULL, 768);
}
return msi;
}

View File

@@ -0,0 +1,43 @@
#ifndef __GEN420_HARDWARE_MSI_H
#define __GEN420_HARDWARE_MSI_H
#include "basic_include.h"
//设置队列的宏,H264固定,其他是自定义
enum GEN420_QUEUE_ENUM
{
GEN420_QUEUE_NONE,
GEN420_QUEUE_H264,
GEN420_QUEUE_JPEG,
GEN420_QUEUE_JPEG_RECODE,
GEN420_QUEUE_THUMB_JPEG,
GEN420_QUEUE_THUMB_JPEG_USB,
GEN420_QUEUE_THUMB_JPEG_OVER_DPI,
};
extern os_msgqueue_t *gen420_msgq;
struct gen420_msg_s;
typedef int32_t (*gen420_kick_fn)(struct gen420_msg_s *msg);
typedef int32_t (*gen420_free_fn)(struct gen420_msg_s *msg);
//一个回调函数,私有结构,以及一些固定参数
struct gen420_msg_s
{
struct list_head list; //gen420 queue hand
gen420_kick_fn kick_fn; //启动kick,然后等待完成,返回值代表需要kick
gen420_free_fn free_fn; //内存释放?应该什么时候调用,不应该在中断调用才对
void *fn_data; //函数的私有结构
uint8_t type; //0: jpg for Sub stream 1:h264 for Sub stream 2-255:user
uint8_t *data;
uint16_t w;
uint16_t h;
uint32_t devid;
uint8_t sta; //0:idle 1:data ready
};
struct msi *gen420_hardware_msi_init();
int wake_up_gen420_queue(uint8_t devid,uint8_t *data_rom);
int unregister_gen420_queue(uint32_t devid);
int register_gen420_queue(uint8_t type,uint32_t w,uint32_t h,gen420_kick_fn kick_fn,gen420_free_fn free_fn,uint32 priv);
int32_t h264_gen420_kick();
#endif

View File

@@ -0,0 +1,417 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "dev/gen/hggen420.h"
#include "jpg_concat_msi.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "lib/video/dvp/jpeg/jpg.h"
#include "lib/video/gen/gen420_dev.h"
#include "stream_define.h"
#include "app/video_app/gen420_hardware_msi.h"
#include "lib/video/dvp/jpeg/jpg_common.h"
#include "gen420_hardware_msi.h"
#include "user_work/user_work.h"
#include "video_msi.h"
/****************************************************************************************************
* 该文件主要是利用jpg1通过gen420重新编码jpg
* 主要最终调用以下函数去触发gen420:
register_gen420_queue
wake_up_gen420_queue
unregister_gen420_queue
****************************************************************************************************/
// 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_GEN420_RX 8
#ifdef JPG_NODE_COUNT
#define GEN420_JPG_COUNT JPG_NODE_COUNT
#else
#define GEN420_JPG_COUNT 10
#endif
struct gen420_msi_s
{
struct os_work work;
struct msi *msi;
struct msi *register_jpg_msi; // 注册的jpg的msi,因为gen420需要与jpg或者其他硬件联动
struct framebuff *fb;
gen420_filter_fn fn;
uint32_t magic;
uint8_t hardware_ready;
uint16_t *filter_type;
uint8_t force_type;
uint16_t last_w, last_h;
uint8_t src_from;
uint8_t which_jpg;
uint8_t lock_value;
uint8_t queue_value;
uint8_t output_en;
uint8_t recv_type;
uint8_t wait_close_jpg;
uint8_t stop;
};
static int32_t gen420_kick(struct gen420_msg_s *msg)
{
int32_t ret = RET_OK;
struct gen420_msi_s *gen420 = (struct gen420_msi_s *) msg->fn_data;
// 配置一下jpg的类型
if (gen420->register_jpg_msi)
{
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_SET_DATATAG, gen420->fb->datatag, 0);
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_SET_GEN420_TYPE, gen420->force_type ? gen420->force_type : gen420->fb->stype);
}
// 关闭jpg
// msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_NODE_COUNT, GEN420_JPG_COUNT);
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_SET_TIME, gen420->fb->time);
// 修改jpg参数
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_MSG, gen420->last_w << 16 | gen420->last_h);
// 修改数据源头
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_FROM, GEN420_DATA);
// 重新启动mjpg
ret = msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 1);
return ret;
}
static int32_t gen420_free(struct gen420_msg_s *msg)
{
struct gen420_msi_s *gen420 = (struct gen420_msi_s *) msg->fn_data;
unregister_gen420_queue(gen420->queue_value);
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
// 移除
msi_delete_fb(NULL, gen420->fb);
gen420->fb = NULL;
jpg_mutex_unlock(gen420->which_jpg, gen420->lock_value);
gen420->hardware_ready = 1;
// 唤醒workqueue的任务
_os_printf("]");
os_run_work(&gen420->work);
return 0;
}
static int32_t gen420_err_free(struct gen420_msi_s *gen420)
{
// 移除
msi_delete_fb(NULL, gen420->fb);
gen420->fb = NULL;
jpg_mutex_unlock(gen420->which_jpg, gen420->lock_value);
gen420->hardware_ready = 1;
os_run_work(&gen420->work);
return 0;
}
static int32_t gen420_run(void *fn_data, struct framebuff *fb, uint16_t w, uint16_t h)
{
struct gen420_msi_s *gen420 = (struct gen420_msi_s *) fn_data;
// msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
int ret = register_gen420_queue(gen420->queue_value, w, h, gen420_kick, gen420_free, (uint32) fn_data);
if (ret == 0)
{
wake_up_gen420_queue(gen420->queue_value, fb->data);
}
else
{
gen420_err_free(gen420);
os_printf(KERN_ERR "gen420_run err:%d\n", ret);
}
return ret;
}
static int32 gen420_jpg_work(struct os_work *work)
{
uint32_t delay_time = 0;
struct gen420_msi_s *gen420 = (struct gen420_msi_s *) work;
uint8_t last_lock_value;
int32_t ret;
// 如果gen420没有启动,那么一定可以启动mjpg或者gen420
if (gen420->hardware_ready)
{
// 尝试看看是否有新的数据需要编码
if (!gen420->fb)
{
gen420->fb = msi_get_fb(gen420->msi, 0);
if (!gen420->output_en && gen420->fb)
{
uint32_t send_count = msi_output_fb(gen420->msi, NULL);
// 有下一级的流需要接收,那么就传递下去,如果没有,则移除,不需要做后面的动作
if (!send_count)
{
msi_delete_fb(NULL, gen420->fb);
gen420->fb = NULL;
if (gen420->stop)
{
return 0;
}
}
else
{
gen420->output_en = 1;
}
}
}
// 有新的数据需要编码,则尝试去编码
if (gen420->fb)
{
gen420->wait_close_jpg = 0;
// 去获取jpg1的锁
ret = jpg_mutex_lock(gen420->which_jpg, gen420->lock_value, &last_lock_value);
// 获取成功,则去编码
if (ret == 0)
{
// os_printf("lock jpg:%d\n",gen420->which_jpg);
// 获取成功后,初始化mjpg1相关硬件
if (!gen420->register_jpg_msi)
{
gen420->register_jpg_msi = jpg_concat_msi_init_start(gen420->which_jpg, 320, 180, NULL, gen420->src_from, 0);
if (gen420->register_jpg_msi)
{
// 主动停止一下
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
msi_add_output(gen420->register_jpg_msi, NULL, gen420->msi->name);
}
}
if (gen420->register_jpg_msi)
{
// 使用不一致,就先停再启动
if (last_lock_value != gen420->lock_value)
{
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
}
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_OPEN, GEN420_DATA);
}
// 异常,不应该出现,除非其他地方打开了占用该jpg需要检查,应该应用去规避
else
{
jpg_mutex_unlock_check(gen420->which_jpg, gen420->lock_value);
os_printf("gen420->register_jpg_msi init err\r\n");
delay_time = 1;
goto gen420_jpg_work_end;
}
struct yuv_arg_s *yuv_msg;
yuv_msg = (struct yuv_arg_s *) gen420->fb->priv;
if (yuv_msg)
{
uint32_t p_w, p_h;
p_w = yuv_msg->out_w;
p_h = yuv_msg->out_h;
gen420->last_w = p_w;
gen420->last_h = p_h;
gen420->hardware_ready = 0;
gen420->stop = 0;
gen420_run(gen420, gen420->fb, p_w, p_h);
}
// 不应该进入这里,可能fb给错了,下一次移除
else
{
// 移除
msi_delete_fb(NULL, gen420->fb);
gen420->fb = NULL;
}
}
else
{
delay_time = 1;
goto gen420_jpg_work_end;
}
}
// 没有找到等到新的yuv数据,那么就等100ms去检查是否需要关闭mjpeg
else
{
if (!gen420->wait_close_jpg)
{
gen420->wait_close_jpg = 1;
delay_time = 100;
}
else
{
ret = jpg_mutex_lock(gen420->which_jpg, gen420->lock_value, &last_lock_value);
if (ret == 0)
{
if (gen420->lock_value == last_lock_value)
{
if (gen420->register_jpg_msi)
{
gen420->stop = 1;
msi_do_cmd(gen420->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
}
}
jpg_mutex_unlock_check(gen420->which_jpg, gen420->lock_value);
}
}
}
}
else
{
delay_time = 1;
}
gen420_jpg_work_end:
if (delay_time)
{
os_run_work_delay(work, delay_time);
}
return 0;
}
static int32_t gen420_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct gen420_msi_s *gen420 = (struct gen420_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
os_work_cancle2(&gen420->work, 1);
STREAM_LIBC_FREE(gen420);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_work_cancle2(&gen420->work, 1);
if (gen420->register_jpg_msi)
{
msi_destroy(gen420->register_jpg_msi);
gen420->register_jpg_msi = NULL;
}
}
break;
case MSI_CMD_JPG_RECODE:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
// 需要注册jpg,中断可能需要用到
case MSI_JPG_RECODE_MAGIC:
{
gen420->magic = arg;
}
break;
case MSI_JPG_RECODE_FORCE_TYPE:
{
gen420->force_type = (uint16_t) arg;
}
break;
default:
break;
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// 判断是否要转发
if (gen420->fn && gen420->fn((void *) fb, gen420->recv_type) == 0)
{
// 所有图片都转发
struct framebuff *fb = (struct framebuff *) param1;
fb_get(fb);
int send = msi_output_fb(msi, fb);
if (!send)
{
gen420->output_en = 0;
}
ret = RET_OK + 1;
break;
}
if (fb->mtype != F_YUV)
{
ret = RET_ERR;
}
else
{
if (gen420->filter_type)
{
// 轮询类型是否有一致
ret = RET_ERR;
uint16_t *each = gen420->filter_type;
while (*each)
{
// 如果一致,返回OK
if (*each == fb->stype)
{
ret = RET_OK;
break;
}
each++;
}
}
if (ret == RET_OK)
{
struct yuv_arg_s *yuv_msg;
yuv_msg = (struct yuv_arg_s *) fb->priv;
ret = RET_ERR;
// 如果magic为0,不需要处理
if (!gen420->magic)
{
ret = RET_OK;
}
// magic不是0,则需要匹配
else if (gen420->magic == yuv_msg->magic)
{
ret = RET_OK;
}
}
}
}
break;
case MSI_CMD_TRANS_FB_END:
{
os_run_work(&gen420->work);
}
break;
default:
break;
}
return ret;
}
struct msi *gen420_jpg_msi_init(const char *msi_name, uint8_t which_jpg, uint8_t recv_type, uint8_t lock_value, uint8_t queue_value, uint16_t *filter_type, gen420_filter_fn fn)
{
uint8_t isnew;
ASSERT(which_jpg == 0 || which_jpg == 1);
struct msi *msi = msi_new(msi_name, MAX_GEN420_RX, &isnew);
struct gen420_msi_s *gen420 = (struct gen420_msi_s *) msi->priv;
if (isnew)
{
gen420 = (struct gen420_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct gen420_msi_s));
msi->priv = (void *) gen420;
msi->action = gen420_msi_action;
gen420->msi = msi;
gen420->filter_type = filter_type;
gen420->hardware_ready = 1;
gen420->force_type = 0; // 强转类型
gen420->which_jpg = which_jpg;
gen420->lock_value = lock_value;
gen420->queue_value = queue_value;
gen420->recv_type = recv_type;
gen420->fn = fn;
gen420->output_en = 0;
msi->enable = 1;
// 启动workqueue
OS_WORK_INIT(&gen420->work, gen420_jpg_work, 0);
os_run_work(&gen420->work);
}
return msi;
}

View File

@@ -0,0 +1,464 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "stream_define.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "jpg_concat_msi.h"
#include "user_work/user_work.h"
extern uint8_t get_vpp_w_h(uint16_t *w, uint16_t *h);
extern uint8_t get_vpp1_w_h(uint16_t *w, uint16_t *h);
// 这里是默认值,最好在project_config.h那里配置
#ifndef JPG_NODE_COUNT
#define JPG_NODE_COUNT 10
#endif
// 最大支持处理200K的图片
#define MAX_JPG_SIZE (200 * 1024)
// data申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#ifdef MORE_SRAM
#define STREAM_LIBC_MALLOC av_psram_malloc
#define STREAM_LIBC_FREE av_psram_free
#define STREAM_LIBC_ZALLOC av_psram_zalloc
#else
#define STREAM_LIBC_MALLOC av_malloc
#define STREAM_LIBC_FREE av_free
#define STREAM_LIBC_ZALLOC av_zalloc
#endif
#define MAX_JPG_CONCAT_RECV (30)
extern struct msi *hardware_jpg_msi(uint8_t which_jpg, uint8_t src_from, uint16_t jpg_node_len, uint16_t jpg_node_count);
int32_t jpg_concat_msg_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct jpg_concat_msi_s *jpg_concat_msg = (struct jpg_concat_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
os_work_cancle2(&jpg_concat_msg->work, 1);
fbpool_destroy(&jpg_concat_msg->tx_pool);
STREAM_LIBC_FREE(jpg_concat_msg);
}
break;
// 需要关闭,则将关联的msi关闭
case MSI_CMD_PRE_DESTROY:
{
os_work_cancle2(&jpg_concat_msg->work, 1);
if (jpg_concat_msg->jpg_msi)
{
msi_destroy(jpg_concat_msg->jpg_msi);
jpg_concat_msg->jpg_msi = NULL;
}
}
break;
case MSI_CMD_GET_RUNNING:
{
uint32_t running = 0;
if (jpg_concat_msg->jpg_msi)
{
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_GET_RUNNING, (uint32_t) &running, 0);
}
*(uint32_t *) param1 = running;
}
break;
case MSI_CMD_JPEG_CONCAT:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
case MSI_JPEG_OPEN:
{
// 如果jpg_msi已经启动,则报错,需要应用层检查是否停止
if (jpg_concat_msg->jpg_msi)
{
ret = 1;
}
else
{
jpg_concat_msg->from = arg;
}
}
break;
// 配置JPEG的参数?(这样就是固定size了,如果希望编码不同size,如何解决呢,注册形式?注册两个分辨率,然后由中断去切换?)
// 暂时没有实现
// 发命令配置jpg编码的参数
case MSI_JPEG_MSG:
{
jpg_concat_msg->w = arg >> 16;
jpg_concat_msg->h = arg & 0xffff;
// msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_MSG, arg);
}
break;
// 启动mjpeg
case MSI_JPEG_START:
{
if (arg)
{
if (!jpg_concat_msg->jpg_msi)
{
jpg_concat_msg->jpg_msi = hardware_jpg_msi(jpg_concat_msg->which_jpg, jpg_concat_msg->from, 16 * 1024, jpg_concat_msg->jpg_node_count);
if (jpg_concat_msg->jpg_msi)
{
msi_add_output(jpg_concat_msg->jpg_msi, NULL, msi->name);
}
}
if (jpg_concat_msg->jpg_msi)
{
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_SET_DATATAG, jpg_concat_msg->datatag, 0);
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_FROM, jpg_concat_msg->from);
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_SET_GEN420_TYPE, jpg_concat_msg->gen420_type);
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_SET_SCALE1_TYPE, jpg_concat_msg->scale1_type);
uint16_t jpg_w, jpg_h;
if (jpg_concat_msg->from == VPP_DATA0)
{
get_vpp_w_h(&jpg_w, &jpg_h);
}
else if (jpg_concat_msg->from == VPP_DATA1)
{
get_vpp1_w_h(&jpg_w, &jpg_h);
}
else
{
jpg_w = jpg_concat_msg->w;
jpg_h = jpg_concat_msg->h;
}
// 其他的来源,则使用配置参数
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_SET_TIME, jpg_concat_msg->set_time);
// 判断jpg_w?如果超过某些size,就通知jpg的内存池,增加部分内存?
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_MSG, jpg_w << 16 | jpg_h);
if (jpg_concat_msg->scale1_flag)
{
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_SET_SCALE1_FLAG, 1);
}
//是否转发取决于启动参数
jpg_concat_msg->force_node = (arg&0x02)?1:0;
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_START, 0);
}
else
{
ret = RET_ERR;
}
}
else
{
if (jpg_concat_msg->jpg_msi)
{
// scale1的自动模式,所以需要发送命令,让它关闭scale1
if (jpg_concat_msg->scale1_flag)
{
jpg_concat_msg->scale1_flag = 0;
}
msi_do_cmd(jpg_concat_msg->jpg_msi, MSI_CMD_HARDWARE_JPEG, MSI_JPEG_HARDWARE_STOP, 0);
if (jpg_concat_msg->auto_free)
{
// 删除jpg_msi,主要为了释放空间
msi_destroy(jpg_concat_msg->jpg_msi);
jpg_concat_msg->jpg_msi = NULL;
}
}
}
}
break;
// 配置mjpeg的源头
case MSI_JPEG_FROM:
{
jpg_concat_msg->from = arg;
}
break;
case MSI_SET_GEN420_TYPE:
{
jpg_concat_msg->gen420_type = arg;
break;
}
// 设置scale1为自动模式
case MSI_SET_SCALE1_AUTO_FLAG:
{
jpg_concat_msg->scale1_flag = arg;
}
break;
case MSI_SET_SCALE1_TYPE:
{
jpg_concat_msg->scale1_type = arg;
break;
}
case MSI_JPEG_NODE_COUNT:
{
jpg_concat_msg->jpg_node_count = arg;
break;
}
case MSI_GET_JPEG_MSI:
{
struct msi **jpg_msi = (struct msi **) param2;
// 返回jpg_msi,主要为了外部获取(比如gen420,因为硬件之间耦合,所以这里做了中间层去获取,可能是jpg0可能是jpg1)
if (jpg_concat_msg->jpg_msi && jpg_msi)
{
*jpg_msi = jpg_concat_msg->jpg_msi;
}
else
{
ret = RET_ERR;
}
}
break;
case MSI_SET_TIME:
{
jpg_concat_msg->set_time = arg;
}
break;
default:
break;
}
}
break;
// 接收到数据,唤醒workqueue,去组图,理论有图都要重新组,然后通过output的时候过滤类型,这里也可以先过滤部分类型
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
if (jpg_concat_msg->filter_type)
{
ret = RET_ERR;
// 轮询类型是否有一致
uint16_t *each = jpg_concat_msg->filter_type;
while (*each)
{
// 如果一致,返回OK
if (*each == fb->stype)
{
ret = RET_OK;
break;
}
each++;
}
}
// 判断图片大小是否超过一定size,如果是,就不支持拼接(主要是空间问题)
// 除了类型外,其他都是克隆发出去,其他模块处理要注意
// data是没有用的,因为是节点的形式
if (ret == RET_OK)
{
struct jpg_node_s *jpg_priv = (struct jpg_node_s *) fb->priv;
if (jpg_priv)
{
if (jpg_concat_msg->force_node || jpg_priv->jpg_len > MAX_JPG_SIZE)
{
struct framebuff *send_fb = fb_clone(fb, fb->mtype << 8 | fb->stype, msi);
send_fb->len = jpg_priv->jpg_len;
send_fb->data = NULL;
msi_output_fb(msi, send_fb);
ret = RET_OK + 1;
}
}
}
if (ret == RET_OK)
{
os_run_work(&jpg_concat_msg->work);
}
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
if (fb->mtype == F_JPG)
{
if (fb->data)
{
STREAM_FREE(fb->data);
fb->data = NULL;
}
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
fb->priv = NULL;
}
fbpool_put(&jpg_concat_msg->tx_pool, fb);
// 不需要内核去释放fb
ret = RET_OK + 1;
}
// 那么应该是克隆的,由msi内核管理
else if (fb->clone)
{
}
// 异常情况
else
{
os_printf(KERN_ERR "%s:%d free fb type err:%d\n", __FUNCTION__, __LINE__, fb->mtype);
}
}
break;
case MSI_CMD_SET_DATATAG:
{
jpg_concat_msg->datatag = param1;
}
break;
default:
break;
}
return ret;
}
static int32 jpg_concat_msi_work(struct os_work *work)
{
struct jpg_concat_msi_s *jpg_concat_msg = (struct jpg_concat_msi_s *) work;
struct framebuff *fb;
struct framebuff *send_fb;
uint32_t jpg_len;
uint8_t *jpg_psram_space;
// 接收图片数据看看
fb = msi_get_fb(jpg_concat_msg->msi, 0);
if (fb)
{
struct jpg_node_s *jpg_priv = (struct jpg_node_s *) fb->priv;
if (jpg_priv)
{
jpg_len = jpg_priv->jpg_len;
jpg_psram_space = (uint8_t *) STREAM_MALLOC(jpg_len);
if (jpg_psram_space)
{
sys_dcache_invalid_range((uint32_t *) jpg_psram_space, jpg_len);
send_fb = fbpool_get(&jpg_concat_msg->tx_pool, 0, jpg_concat_msg->msi);
if (!send_fb)
{
_os_printf("=");
STREAM_FREE(jpg_psram_space);
}
else
{
uint32_t offset = 0;
uint32_t remain_len = jpg_len;
uint32_t cp_len = 0;
struct framebuff *tmp_fb = fb;
// 开始拷贝数据
while (tmp_fb && remain_len)
{
if (remain_len > tmp_fb->len)
{
cp_len = tmp_fb->len;
}
else
{
cp_len = remain_len;
}
// os_printf("cp_len:%X\n",cp_len);
hw_memcpy_no_cache(jpg_psram_space + offset, tmp_fb->data, cp_len);
offset += cp_len;
remain_len -= cp_len;
tmp_fb = tmp_fb->next;
}
send_fb->data = jpg_psram_space;
send_fb->len = jpg_len;
send_fb->mtype = F_JPG;
send_fb->stype = fb->stype;
send_fb->time = fb->time;
send_fb->datatag = fb->datatag;
send_fb->srcID = fb->srcID;
_os_printf("V");
struct jpg_node_s *jpg_msg = (struct jpg_node_s *) STREAM_LIBC_MALLOC(sizeof(struct jpg_node_s));
if (jpg_msg)
{
memcpy(jpg_msg, jpg_priv, sizeof(struct jpg_node_s));
send_fb->priv = (void *) jpg_msg;
msi_output_fb(jpg_concat_msg->msi, send_fb);
}
// 申请不到内存,则不发送?正常不应该申请不到空间
else
{
os_printf("%s:%d malloc jpg_node_s fail\n", __FUNCTION__, __LINE__);
msi_delete_fb(jpg_concat_msg->msi, send_fb);
}
}
}
}
else
{
os_printf("%s:%d find jpg msg fail\n", __FUNCTION__, __LINE__);
}
// 将数据拷贝,然后发送出去
msi_delete_fb(NULL, fb);
}
return 0;
}
// 这里还需要做一个事情,就是如果用了VPP_DATA0,应该自动识别分辨率,而不是通过外部传进来
// 暂时自动识别还没有做
struct msi *jpg_concat_msi_init_start(uint32_t jpgid, uint16_t w, uint16_t h, uint16_t *filter_type, uint8_t src_from, uint8_t run)
{
ASSERT((jpgid == 0) || (jpgid == 1));
const char *msi_name = NULL;
uint8_t which_jpg = jpgid;
if (jpgid == 0)
{
msi_name = RS_JPG_CONCAT;
}
else if (jpgid == 1)
{
msi_name = RS_JPG_CONCAT1;
}
uint8_t isnew;
struct msi *msi = msi_new(msi_name, MAX_JPG_CONCAT_RECV, &isnew);
ASSERT(msi);
struct jpg_concat_msi_s *jpg_concat_msg = (struct jpg_concat_msi_s *) msi->priv;
if (msi && !jpg_concat_msg)
{
jpg_concat_msg = (struct jpg_concat_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct jpg_concat_msi_s));
ASSERT(jpg_concat_msg);
msi->priv = (void *) jpg_concat_msg;
msi->enable = 1;
jpg_concat_msg->which_jpg = which_jpg; // 默认使用jpg0
jpg_concat_msg->msi = msi;
jpg_concat_msg->msi->action = jpg_concat_msg_msi_action;
jpg_concat_msg->msi->priv = (void *) jpg_concat_msg;
jpg_concat_msg->msi->enable = 1;
jpg_concat_msg->filter_type = filter_type;
jpg_concat_msg->from = src_from;
jpg_concat_msg->force_node = 0;
#ifndef FAST_JPG
jpg_concat_msg->auto_free = 1;
#else
jpg_concat_msg->auto_free = 0;
#endif
jpg_concat_msg->jpg_node_count = JPG_NODE_COUNT;
fbpool_init(&jpg_concat_msg->tx_pool, MAX_JPG_CONCAT_RECV);
jpg_concat_msg->w = w;
jpg_concat_msg->h = h;
// 启动workqueue
OS_WORK_INIT(&jpg_concat_msg->work, jpg_concat_msi_work, 0);
msi_do_cmd(msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_OPEN, src_from);
// 关闭jpg
msi_do_cmd(msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
// 修改jpg参数
msi_do_cmd(msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_MSG, w << 16 | h);
// 启动mjpg
if (run)
{
msi_do_cmd(msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 1);
}
}
return msi;
}

View File

@@ -0,0 +1,87 @@
#ifndef __JPG_CONCAT_MSI_H
#define __JPG_CONCAT_MSI_H
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "lib/video/dvp/jpeg/jpg.h"
#ifndef FAST_JPG
struct jpg_V3_msi_s
{
struct os_work work;
char msi_name[16];
struct msi *msi;
struct jpg_device *jpg;
struct scale_device *scale_dev;
struct os_msgqueue msgq;
struct os_event evt;
struct fbpool pool;
struct framebuff *now_fb; // 当前中断使用的fb的头(需要释放)
struct framebuff *use_last_fb; // 最后配置的fb(不需要释放,这个是记录使用)
struct framebuff *last_fb; // 最后配置fb的地址,没有放到链表的,(需要释放)
uint32_t *jpg_node_buf; // 节点buf数组空间,不再一次申请(一次申请会导致可能申请不到,这样申请则有可能导致碎片化)
uint32_t err;
uint32_t set_time;
uint16_t w, h;
uint16_t jpg_node_len;
uint8_t jpg_node_count;
uint8_t qt;
uint8_t which : 1, running : 1, src_from : 3, scale1_flag : 1, vpp_close_flag : 1, rev : 1;
uint8_t datatag;
uint8_t gen420_type; // 如果是gen420的编码,这里需要配置一下类型,因为gen420来源很多地方,也因为是手动kick的,所以这里可以gen420配置了类型再kick,done的时候配置对应类型
uint8_t scale1_type;
int32_t dqtable_index;
int32_t diff_prev;
int32_t diff_sum;
int32_t target_len;
};
#else
// 不考虑不带psram的情况
struct jpg_V3_msi_s
{
struct os_work work;
char msi_name[16];
struct msi *msi;
struct jpg_device *jpg;
struct scale_device *scale_dev;
struct os_msgqueue msgq;
struct os_event evt;
void *now_node;
void *use_last_node;
void *last_node;
uint32_t err;
uint32_t set_time;
uint16_t w, h;
uint16_t jpg_node_len;
uint8_t jpg_node_count;
uint8_t qt;
uint8_t which : 1, running : 1, src_from : 3, scale1_flag : 1, vpp_close_flag : 1, rev : 1;
uint8_t datatag;
uint8_t gen420_type; // 如果是gen420的编码,这里需要配置一下类型,因为gen420来源很多地方,也因为是手动kick的,所以这里可以gen420配置了类型再kick,done的时候配置对应类型
uint8_t scale1_type;
uint32_t count;
int32_t dqtable_index;
int32_t diff_prev;
int32_t diff_sum;
int32_t target_len;
};
#endif
struct jpg_concat_msi_s
{
struct os_work work;
struct msi *msi;
struct msi *jpg_msi; // 硬件jpg0的msi
struct fbpool tx_pool;
uint16_t *filter_type;
uint32_t set_time;
uint16_t w, h;
uint8_t jpg_node_count; // 支持修改,后续可以代码根据不同情况申请空间
uint8_t jpg_running : 1, which_jpg : 1, auto_free : 1, from : 3, scale1_flag : 1, force_node : 1; // which_jpg 0:jpg0 1:jpg1 auto_free: 是否自动停止
uint8_t datatag;
uint8_t gen420_type;
uint8_t scale1_type;
};
struct msi *jpg_concat_msi_init_start(uint32_t jpgid, uint16_t w, uint16_t h, uint16_t *filter_type, uint8_t src_from, uint8_t run);
#endif

View File

@@ -0,0 +1,191 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "osal/string.h"
#include "stream_frame.h"
#include "file_thumb.h"
#include "lib/fs/fatfs/osal_file.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "lib/video/dvp/jpeg/jpg.h"
#include "vfs.h"
#include "user_work/user_work.h"
#include "video_app/file_common_api.h"
/*******************************************************************
* 这个模块主要是拍照用,同时会尝试生成缩略图(转发到下一个msi去生成)
*******************************************************************/
// 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 JPG_THUMB_RECV_MAX (8)
struct jpg_thumb_s
{
struct os_work work;
struct msi *msi;
struct msi *gen420_msi;
char filepath[64];
char *dirpath;
uint8_t filter;
uint8_t thumb_stype;
uint8_t takephoto_photo;
};
// 正常应该是这里去切换成拍照模式
static int32 jpg_thumb_work(struct os_work *work)
{
struct jpg_thumb_s *jpg_thumb = (struct jpg_thumb_s *) work;
struct framebuff *fb;
struct framebuff *c_fb;
// 执行完一次后,继续去获取图片,如果没有就等待下次信号量唤醒
jpg_thumb_work_again:
// 尝试获取一下原图
fb = msi_get_fb(jpg_thumb->msi, 0);
if (fb)
{
// 判断一下是否为原图
if (jpg_thumb->takephoto_photo)
{
jpg_thumb->takephoto_photo--;
if (takephoto_name(jpg_thumb->dirpath, jpg_thumb->filepath, sizeof(jpg_thumb->filepath)))
{
_os_printf("%s %d\tget file path err\r\n", __FUNCTION__, __LINE__);
msi_delete_fb(NULL, fb);
fb = NULL;
return 1;
}
// 设置缩略图的stype
c_fb = fb_clone(fb, F_THUMB_JPG << 8 | jpg_thumb->thumb_stype, jpg_thumb->msi);
// 设置来源是正常缩略图
c_fb->priv = (void *) jpg_thumb->filepath; // 记录路径
// 转发,生成缩略图
msi_output_fb(jpg_thumb->msi, c_fb);
// 转发到给到文件去保存(独立线程)
struct framebuff *send_fb = fb_clone(fb, F_FILE_T << 8 | fb->stype, jpg_thumb->msi);
ASSERT(send_fb);
struct file_msg_s *file_msg = (struct file_msg_s *) STREAM_LIBC_MALLOC(sizeof(struct file_msg_s));
ASSERT(file_msg);
os_memcpy(file_msg->path, jpg_thumb->filepath, strlen(jpg_thumb->filepath) + 1);
file_msg->ishid = 0;
send_fb->priv = (void *) file_msg;
msi_output_fb(jpg_thumb->msi, send_fb);
}
if (!jpg_thumb->takephoto_photo)
{
jpg_thumb->msi->enable = 0;
}
msi_delete_fb(NULL, fb);
fb = NULL;
goto jpg_thumb_work_again;
}
return 0;
}
static int32_t jpg_thumb_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct jpg_thumb_s *jpg_thumb = (struct jpg_thumb_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
STREAM_LIBC_FREE(jpg_thumb);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_work_cancle2(&jpg_thumb->work, 1);
}
break;
case MSI_CMD_JPG_THUMB:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
// 启动拍照,一定要拍照完毕才能退出?如果需要强行退出,需要设置另外接口,这里默认一定能拍照成功
case MSI_JPG_THUMB_TAKEPHOTO:
{
jpg_thumb->takephoto_photo += arg;
jpg_thumb->msi->enable = 1;
}
break;
case MSI_JPG_THUMB_TAKEPHOTO_SETPATH:
{
jpg_thumb->dirpath = (char *) arg;
}
break;
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
if (fb->mtype != F_JPG)
{
ret = RET_ERR;
}
else
{
// 检查是否来自需要的数据源头
if (fb->srcID != jpg_thumb->filter)
{
ret = RET_ERR;
}
}
}
break;
case MSI_CMD_TRANS_FB_END:
{
os_run_work(&jpg_thumb->work);
}
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// fb由内部管理,仅仅删除私有结构
if (fb && fb->mtype == F_FILE_T)
{
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
fb->priv = NULL;
}
}
}
break;
}
return ret;
}
struct msi *jpg_thumb_msi_init(const char *msi_name, uint8_t filter, uint8_t thumb_stype)
{
uint8_t is_new;
struct msi *msi = msi_new(msi_name, JPG_THUMB_RECV_MAX, &is_new);
struct jpg_thumb_s *jpg_thumb = NULL;
if (is_new)
{
jpg_thumb = (struct jpg_thumb_s *) STREAM_LIBC_ZALLOC(sizeof(struct jpg_thumb_s));
msi->priv = jpg_thumb;
jpg_thumb->msi = msi;
jpg_thumb->filter = filter;
jpg_thumb->thumb_stype = thumb_stype;
jpg_thumb->dirpath = IMG_PATH;
msi->action = jpg_thumb_msi_action;
msi->enable = 0;
msi_get(jpg_thumb->msi);
OS_WORK_INIT(&jpg_thumb->work, jpg_thumb_work, 0);
}
return msi;
}

View File

@@ -0,0 +1,102 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "stream_frame.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
// 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
struct mp4_thumb_s
{
struct msi *msi;
uint8_t thumb_name[64];
uint8_t filter_type;
uint8_t srcID;
};
int32_t mp4_thumb_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct mp4_thumb_s *mp4_thumb = (struct mp4_thumb_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
STREAM_LIBC_FREE(mp4_thumb);
}
break;
// 需要关闭,则将关联的msi关闭
case MSI_CMD_PRE_DESTROY:
{
}
break;
// 接收到数据,唤醒workqueue,去组图,理论有图都要重新组,然后通过output的时候过滤类型,这里也可以先过滤部分类型
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
if (fb->mtype == F_JPG && mp4_thumb->srcID == fb->srcID && (!mp4_thumb->filter_type || mp4_thumb->filter_type == fb->stype))
{
struct framebuff *c_fb = fb_clone(fb, F_THUMB_JPG << 8 | fb->stype, msi);
if (c_fb)
{
c_fb->priv = mp4_thumb->thumb_name;
msi_output_fb(msi, c_fb);
msi->enable = 0;
}
break;
}
ret = RET_ERR;
}
break;
case MSI_CMD_FREE_FB:
{
}
break;
default:
break;
}
return ret;
}
struct msi *mp4_thumb_msi_init(const char *filename, uint8_t srcID, uint8_t filter)
{
uint8_t is_new;
struct msi *msi = msi_new(R_MP4_THUMB, 0, &is_new);
struct mp4_thumb_s *mp4_thumb = NULL;
if (is_new)
{
mp4_thumb = (struct mp4_thumb_s *) STREAM_LIBC_ZALLOC(sizeof(struct mp4_thumb_s));
os_memcpy(mp4_thumb->thumb_name, filename, strlen(filename) + 1);
msi->priv = mp4_thumb;
mp4_thumb->msi = msi;
mp4_thumb->filter_type = filter;
mp4_thumb->srcID = srcID;
msi->action = mp4_thumb_msi_action;
// 给到解码然后生成缩略图
msi_add_output(msi, NULL, R_THUMB);
msi->enable = 1;
msi_add_output(NULL, AUTO_JPG, R_MP4_THUMB); // mp4缩略图?
}
else
{
mp4_thumb = (struct mp4_thumb_s *) msi->priv;
os_memcpy(mp4_thumb->thumb_name, filename, strlen(filename) + 1);
msi->enable = 1;
}
return msi;
}

View File

@@ -0,0 +1,591 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "dev/gen/hggen420.h"
#include "jpg_concat_msi.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "lib/video/dvp/jpeg/jpg.h"
#include "lib/video/gen/gen420_dev.h"
#include "stream_define.h"
#include "app/video_app/gen420_hardware_msi.h"
#include "lib/video/dvp/jpeg/jpg_common.h"
#include "gen420_hardware_msi.h"
#include "hal/scale.h"
#include "dev/scale/hgscale.h"
#include "video_msi.h"
#include "lib/video/vpp/vpp_dev.h"
#define STATIC_SCALE1_BUF 1
#define SCALE1_JPG_COUNT 20
enum
{
MSI_SCALE1_THREAD_DEAD = BIT(0),
MSI_SCALE1_THREAD_STOP = BIT(1),
MSI_SCALE1_THREAD_KICK = BIT(2),
MSI_SCALE1_THREAD_VPP_CLOSE = BIT(3),
};
// 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 SCALE1_RECV_MAX 1
struct scale1_jpg_msi_s
{
struct msi *msi;
struct msi *register_jpg_msi; // 注册的jpg的msi,因为gen420需要与jpg或者其他硬件联动
struct scale_device *scale_dev;
struct framebuff *fb;
scale1_filter_fn fn;
struct os_event evt;
uint32_t magic;
uint16_t last_w, last_h;
uint16_t jpg_w, jpg_h;
uint8_t which_jpg : 1, output_en : 1, src_from : 3, stop : 1, force_node : 1, rev : 1;
uint8_t lock_value;
uint8_t recv_type;
uint8_t *scale1_buf;
uint32_t scale1_buf_size;
uint16_t force_type; // 设置最后输出图片的类型
};
static int32_t scale1_soft_ov_isr(uint32_t irq_flag, uint32_t irq_data, uint32_t param1)
{
// struct scale_device *scale_dev = (struct scale_device *)irq_data;
//_os_printf("ov");
return 0;
}
static int32_t scale1_done(uint32_t irq_flag, uint32_t irq_data, uint32_t param1)
{
struct scale1_jpg_msi_s *scale1_jpg = (struct scale1_jpg_msi_s *) irq_data;
// struct scale_device *scale_dev = scale1_jpg->scale_dev;
// 完成后,再次kick一下线程,检查是否有新的数据
os_event_set(&scale1_jpg->evt, MSI_SCALE1_THREAD_KICK, NULL);
return 0;
}
static void scale1_soft_from_psram_to_enc(struct scale1_jpg_msi_s *scale1_jpg, uint8_t *line_buf, int line_num, uint8_t *psram_data, uint32_t w, uint32 h, uint32_t ow, uint32_t oh)
{
uint16 icount = 2;
uint32_t running = 0;
struct scale_device *scale_dev = scale1_jpg->scale_dev;
os_printf("%s %d*%d====>%d*%d\r\n", __func__, w, h, ow, oh);
scale_close(scale_dev);
scale_set_in_out_size(scale_dev, w, h, ow, oh);
scale_set_step(scale_dev, w, h, ow, oh);
scale_set_start_addr(scale_dev, 0, 0);
scale_set_data_from_vpp(scale_dev, 0);
scale_set_line_buf_num(scale_dev, line_num * 2); // soft的line buf
scale_request_irq(scale_dev, FRAME_END, scale1_done, (uint32_t) scale1_jpg);
//没有用到的中断主动关闭
scale_release_irq(scale_dev,INBUF_OV);
scale_release_irq(scale_dev,ERROR_PEND);
scale_open(scale_dev);
scale_set_inbuf_num(scale_dev, 0, 0);
if (scale_get_inbuf_num(scale_dev) == 0)
{
icount = 2;
scale_set_new_frame(scale_dev, 1);
}
else
{
return;
}
hw_memcpy_no_cache(line_buf, psram_data, (line_num * icount) * w);
hw_memcpy_no_cache(line_buf + (line_num * icount) * w, psram_data + h * w, (line_num * icount) * w / 4);
hw_memcpy_no_cache(line_buf + (line_num * icount) * w + (line_num * icount) * w / 4, psram_data + h * w + h * w / 4, (line_num * icount) * w / 4);
scale_set_in_yaddr(scale_dev, (uint32) line_buf);
scale_set_in_uaddr(scale_dev, (uint32) line_buf + (line_num * icount) * w);
scale_set_in_vaddr(scale_dev, (uint32) line_buf + (line_num * icount) * w + (line_num * icount) * w / 4);
scale_set_inbuf_num(scale_dev, icount * line_num - 1, line_num * 2 - 1 /*31*/); // 0~31
uint32_t count = 0;
while (1)
{
if (scale_get_heigh_cnt(scale_dev) > ((icount - 1) * line_num))
{
if ((icount % 2) == 0)
{
// os_printf("get_height_cnt:%d===>up head\r\n",scale_get_heigh_cnt(scale_dev));
hw_memcpy_no_cache(line_buf, psram_data + (line_num * icount) * w, line_num * w);
hw_memcpy_no_cache(line_buf + line_num * 2 * w, psram_data + h * w + (line_num * icount) * w / 4, line_num * w / 4);
hw_memcpy_no_cache(line_buf + line_num * 2 * w + line_num * 2 * w / 4, psram_data + h * w + h * w / 4 + (line_num * icount) * w / 4, line_num * w / 4);
}
else
{
// os_printf("get_height_cnt:%d===>up tail\r\n",scale_get_heigh_cnt(scale_dev));
hw_memcpy_no_cache(line_buf + line_num * w, psram_data + (line_num * icount) * w, line_num * w);
hw_memcpy_no_cache(line_buf + line_num * 2 * w + line_num * 2 * w / 8, psram_data + h * w + (line_num * icount) * w / 4, line_num * w / 4);
hw_memcpy_no_cache(line_buf + line_num * 2 * w + line_num * 2 * w / 4 + line_num * 2 * w / 8, psram_data + h * w + h * w / 4 + (line_num * icount) * w / 4, line_num * w / 4);
}
icount++;
if (icount == ((h + line_num - 1) / line_num))
{
if ((icount % 2) == 1) // 3 start
{
scale_set_inbuf_num(scale_dev, icount * line_num, line_num);
}
else
{
scale_set_inbuf_num(scale_dev, icount * line_num, 0);
}
// 延时,为了硬件可以去编码(看起来不是绝对安全)
os_sleep_ms(1);
break;
}
if ((icount % 2) == 1) // 3 start
{
scale_set_inbuf_num(scale_dev, icount * line_num, line_num);
}
else
{
scale_set_inbuf_num(scale_dev, icount * line_num, 0);
}
}
else
{
msi_do_cmd(scale1_jpg->register_jpg_msi, MSI_CMD_GET_RUNNING, (uint32_t) &running, 0);
if (running == 0)
{
os_printf("%s:%d\n", __FUNCTION__, __LINE__);
os_printf("scale_get_heigh_cnt(scale_dev):%d\n", scale_get_heigh_cnt(scale_dev));
break;
}
count++;
os_sleep_ms(1);
}
if (count % 1000 == 0)
{
os_printf("%s:%d\n", __FUNCTION__, __LINE__);
}
}
scale_close(scale_dev);
}
static int scale1_vpp_close(uint32_t d)
{
struct scale1_jpg_msi_s *scale1_jpg_s = (struct scale1_jpg_msi_s *) d;
struct vpp_device *vpp_dev;
//struct jpg_V3_msi_s *jpg_msg = (struct jpg_V3_msi_s *) d;
os_event_set(&scale1_jpg_s->evt, MSI_SCALE1_THREAD_VPP_CLOSE, NULL);
vpp_dev = (struct vpp_device *) dev_get(HG_VPP_DEVID);
vpp_close(vpp_dev);
return 1;
}
static void scale1_jpg_work(void *d)
{
uint8_t last_value = 0;
uint8_t already_kick = 0;
uint32_t start_arg = 1;
uint32_t delay_time = -1;
uint32_t flags;
int32_t ret;
uint8_t *scale1_buf;
// struct msi *msi = scale1_jpg_s->msi;
struct scale1_jpg_msi_s *scale1_jpg_s = (struct scale1_jpg_msi_s *) d;
while (1)
{
scale1_jpg_work_again:
if (scale1_jpg_s->scale1_buf || already_kick)
{
delay_time = 1000;
}
else
{
delay_time = -1;
}
flags = 0;
ret = os_event_wait(&scale1_jpg_s->evt, MSI_SCALE1_THREAD_KICK | MSI_SCALE1_THREAD_STOP, &flags, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, delay_time);
// 如果有对应的line_buf,则设置为超时1000ms,否则不需要超时
if (flags & MSI_SCALE1_THREAD_STOP)
{
goto scale1_jpg_work_end;
}
// 如果gen420没有启动,那么一定可以启动mjpg或者gen420
// 尝试看看是否有新的数据需要编码
if (!scale1_jpg_s->fb)
{
scale1_jpg_s->fb = msi_get_fb(scale1_jpg_s->msi, 0);
}
// 有yuv数据,开始去拉伸编码
if (scale1_jpg_s->fb)
{
// 没有接收的目标,直接放弃产生大分辨率图片
if (msi_output_fb(scale1_jpg_s->msi, NULL) == 0)
{
msi_delete_fb(NULL, scale1_jpg_s->fb);
scale1_jpg_s->fb = NULL;
goto scale1_jpg_work_again;
}
// 去获取jpg的锁
int32_t ret = jpg_mutex_lock(scale1_jpg_s->which_jpg, scale1_jpg_s->lock_value, NULL);
// 获取成功,则去编码
if (ret == 0)
{
// 获取成功后,初始化mjpg1相关硬件
if (!scale1_jpg_s->register_jpg_msi)
{
scale1_jpg_s->register_jpg_msi = jpg_concat_msi_init_start(scale1_jpg_s->which_jpg, scale1_jpg_s->jpg_w, scale1_jpg_s->jpg_h, NULL, scale1_jpg_s->src_from, 0);
if (scale1_jpg_s->register_jpg_msi)
{
// 主动停止一下
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
msi_add_output(scale1_jpg_s->register_jpg_msi, NULL, scale1_jpg_s->msi->name);
}
}
if (scale1_jpg_s->register_jpg_msi)
{
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_OPEN, SCALER_DATA);
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_NODE_COUNT, SCALE1_JPG_COUNT);
}
// 异常,不应该出现,除非其他地方打开了占用该jpg需要检查,应该应用去规避
else
{
jpg_mutex_unlock(scale1_jpg_s->which_jpg, scale1_jpg_s->lock_value);
os_printf("scale1_jpg_s->register_jpg_msi init err\r\n");
delay_time = 1;
goto scale1_jpg_work_again;
}
// 关闭mjpeg
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_SET_DATATAG, scale1_jpg_s->fb->datatag, 0);
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_SET_SCALE1_TYPE, scale1_jpg_s->force_type ? scale1_jpg_s->force_type : scale1_jpg_s->fb->stype);
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_FROM, scale1_jpg_s->src_from);
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_MSG, scale1_jpg_s->jpg_w << 16 | scale1_jpg_s->jpg_h);
// 重新启动mjpg
if(scale1_jpg_s->force_node)
{
start_arg = BIT(scale1_jpg_s->force_node) | start_arg;
}
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, start_arg);
struct yuv_arg_s *yuv_msg;
yuv_msg = (struct yuv_arg_s *) scale1_jpg_s->fb->priv;
if (yuv_msg)
{
uint32_t p_w, p_h;
p_w = yuv_msg->out_w;
p_h = yuv_msg->out_h;
scale1_jpg_s->last_w = p_w;
scale1_jpg_s->last_h = p_h;
scale1_jpg_s->stop = 0;
// 行数不够,直接复用yuv的buf
if (STATIC_SCALE1_BUF || scale1_jpg_s->jpg_w <= 2 * p_w)
{
// yuvbuf需要给28行,传入14行
scale1_buf = (uint8_t *) get_vpp_buf(0);
if (scale1_buf)
{
vppdone_func_register(SCALE1_JPG_ENCODE, scale1_vpp_close, (uint32_t) d);
int32_t timeout = os_event_wait(&scale1_jpg_s->evt, MSI_SCALE1_THREAD_VPP_CLOSE, NULL, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, 50);
// 超时,就强行关闭vpp,理论应该需要先判断vpp是否被打开,这里暂时默认是被打开的
if (timeout)
{
vppdone_func_unregister(SCALE1_JPG_ENCODE);
scale1_vpp_close((uint32_t)d);
os_event_wait(&scale1_jpg_s->evt, MSI_SCALE1_THREAD_VPP_CLOSE, NULL, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, 0);
}
uint32_t start_encode = os_jiffies();
scale1_soft_from_psram_to_enc(scale1_jpg_s, scale1_buf, 14, scale1_jpg_s->fb->data, p_w, p_h, scale1_jpg_s->jpg_w, scale1_jpg_s->jpg_h);
vpp_dev_open();
os_printf(KERN_INFO "spend1 time:%d\n", (uint32_t) os_jiffies() - start_encode);
already_kick = 1;
jpg_mutex_unlock(scale1_jpg_s->which_jpg, scale1_jpg_s->lock_value);
msi_delete_fb(NULL, scale1_jpg_s->fb);
scale1_jpg_s->fb = NULL;
}
// 空间申请失败,下次尝试申请
else
{
jpg_mutex_unlock(scale1_jpg_s->which_jpg, scale1_jpg_s->lock_value);
delay_time = 1;
goto scale1_jpg_work_again;
}
}
else
{
if (!scale1_jpg_s->scale1_buf || scale1_jpg_s->scale1_buf_size < 16 * p_w * 3 / 2)
{
if (scale1_jpg_s->scale1_buf)
{
STREAM_LIBC_FREE(scale1_jpg_s->scale1_buf);
}
scale1_jpg_s->scale1_buf_size = (10 * 2) * p_w * 3 / 2;
scale1_jpg_s->scale1_buf = STREAM_LIBC_MALLOC(scale1_jpg_s->scale1_buf_size);
if (!scale1_jpg_s->scale1_buf)
{
scale1_jpg_s->scale1_buf_size = 0;
}
}
if (scale1_jpg_s->scale1_buf)
{
uint32_t start_encode = os_jiffies();
scale1_soft_from_psram_to_enc(scale1_jpg_s, scale1_jpg_s->scale1_buf, 10, scale1_jpg_s->fb->data, p_w, p_h, scale1_jpg_s->jpg_w, scale1_jpg_s->jpg_h);
os_printf("spend2 time:%d\n", (uint32_t) os_jiffies() - start_encode);
already_kick = 1;
jpg_mutex_unlock(scale1_jpg_s->which_jpg, scale1_jpg_s->lock_value);
msi_delete_fb(NULL, scale1_jpg_s->fb);
scale1_jpg_s->fb = NULL;
}
// 空间申请失败,下次尝试申请
else
{
jpg_mutex_unlock(scale1_jpg_s->which_jpg, scale1_jpg_s->lock_value);
delay_time = 1;
goto scale1_jpg_work_again;
}
}
}
// 不应该进入这里,可能fb给错了,下一次移除
else
{
// 移除
msi_delete_fb(NULL, scale1_jpg_s->fb);
scale1_jpg_s->fb = NULL;
}
}
else
{
delay_time = 1;
goto scale1_jpg_work_again;
}
}
else
{
// 超时,所以去释放内存,充分利用内存
if (ret)
{
if (scale1_jpg_s->scale1_buf)
{
STREAM_LIBC_FREE(scale1_jpg_s->scale1_buf);
scale1_jpg_s->scale1_buf = NULL;
}
scale1_jpg_s->scale1_buf_size = 0;
int32_t close_ret = jpg_mutex_lock(scale1_jpg_s->which_jpg, scale1_jpg_s->lock_value, &last_value);
// 既然释放了scale1的空间,那么也去停止jpg硬件吧
if (close_ret == 0)
{
if ((last_value == scale1_jpg_s->lock_value))
{
if (scale1_jpg_s->register_jpg_msi)
{
// 主动停止一下
msi_do_cmd(scale1_jpg_s->register_jpg_msi, MSI_CMD_JPEG_CONCAT, MSI_JPEG_START, 0);
}
}
jpg_mutex_unlock(scale1_jpg_s->which_jpg, scale1_jpg_s->lock_value);
}
already_kick = 0;
}
}
}
scale1_jpg_work_end:
if (scale1_jpg_s->fb)
{
msi_delete_fb(NULL, scale1_jpg_s->fb);
scale1_jpg_s->fb = NULL;
}
os_event_set(&scale1_jpg_s->evt, MSI_SCALE1_THREAD_DEAD, NULL);
msi_put(scale1_jpg_s->msi);
return;
}
static int32_t scale1_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct scale1_jpg_msi_s *scale1_jpg_s = (struct scale1_jpg_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
os_event_wait(&scale1_jpg_s->evt, MSI_SCALE1_THREAD_DEAD, NULL, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, -1);
if (scale1_jpg_s->register_jpg_msi)
{
msi_destroy(scale1_jpg_s->register_jpg_msi);
scale1_jpg_s->register_jpg_msi = NULL;
}
if (scale1_jpg_s->scale1_buf)
{
STREAM_LIBC_FREE(scale1_jpg_s->scale1_buf);
}
os_event_del(&scale1_jpg_s->evt);
STREAM_LIBC_FREE(scale1_jpg_s);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_event_set(&scale1_jpg_s->evt, MSI_SCALE1_THREAD_STOP, NULL);
}
break;
// 由于拥有重编码的能力,这里就监听MSI_CMD_JPG_RECODE事件
case MSI_CMD_JPG_RECODE:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
// 需要注册jpg,中断可能需要用到
case MSI_JPG_RECODE_MAGIC:
{
scale1_jpg_s->magic = arg;
}
break;
case MSI_JPG_RECODE_FORCE_TYPE:
{
scale1_jpg_s->force_type = (uint16_t) arg;
}
break;
default:
break;
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
uint8_t send_flag = 0;
// 判断是否要转发
if (scale1_jpg_s->fn)
{
if (scale1_jpg_s->fn((void *) fb, scale1_jpg_s->recv_type) == 0)
{
send_flag = 1;
}
}
// 如果没有过滤,将所有jpg都转发?
else if ((fb->mtype == F_JPG) || (fb->mtype == F_JPG_NODE))
{
send_flag = 1;
}
if (send_flag)
{
// 所有图片都转发
struct framebuff *fb = (struct framebuff *) param1;
fb_get(fb);
int send = msi_output_fb(msi, fb);
if (!send)
{
scale1_jpg_s->output_en = 0;
}
ret = RET_OK + 1;
break;
}
if (fb->mtype != F_YUV)
{
ret = RET_ERR;
}
else
{
struct yuv_arg_s *yuv_msg;
yuv_msg = (struct yuv_arg_s *) fb->priv;
// 如果magic为0,不需要处理
if (!scale1_jpg_s->magic)
{
ret = RET_OK;
}
// magic不是0,则需要匹配
else if (scale1_jpg_s->magic == yuv_msg->magic)
{
ret = RET_OK;
}
else
{
ret = RET_ERR;
}
}
}
break;
case MSI_CMD_TRANS_FB_END:
{
os_event_set(&scale1_jpg_s->evt, MSI_SCALE1_THREAD_KICK, NULL);
break;
}
case MSI_CMD_SCALE1:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
case MSI_SCALE1_RESET_DPI:
{
scale1_jpg_s->jpg_w = arg >> 16;
scale1_jpg_s->jpg_h = arg & 0xffff;
}
break;
default:
break;
}
}
break;
default:
break;
}
return ret;
}
struct msi *scale1_jpg_msi_init(const char *msi_name, uint8_t which_jpg, uint8_t recv_type, uint8_t lock_value, uint16_t force_type, scale1_filter_fn fn, uint8_t force_node)
{
uint8_t isnew;
ASSERT(which_jpg == 0 || which_jpg == 1);
struct msi *msi = msi_new(msi_name, SCALE1_RECV_MAX, &isnew);
struct scale1_jpg_msi_s *scale1_jpg_s = (struct scale1_jpg_msi_s *) msi->priv;
if (isnew)
{
scale1_jpg_s = (struct scale1_jpg_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct scale1_jpg_msi_s));
msi->priv = (void *) scale1_jpg_s;
msi->action = scale1_msi_action;
scale1_jpg_s->msi = msi;
scale1_jpg_s->force_type = force_type; // 强转类型
scale1_jpg_s->stop = 1;
scale1_jpg_s->which_jpg = which_jpg;
scale1_jpg_s->lock_value = lock_value;
scale1_jpg_s->fn = fn;
scale1_jpg_s->output_en = 0;
scale1_jpg_s->jpg_w = 4000;
scale1_jpg_s->jpg_h = 2992;
scale1_jpg_s->src_from = SCALER_DATA;
scale1_jpg_s->scale_dev = (struct scale_device *) dev_get(HG_SCALE1_DEVID);
scale1_jpg_s->recv_type = recv_type;
scale1_jpg_s->force_node = force_node;
os_event_init(&scale1_jpg_s->evt);
// 将大分辨率拍照改成线程(因为消耗时间过长)
// 这里get一次,线程内部去释放
msi_get(msi);
msi->enable = 1;
os_task_create("scale1_jpg_thread", scale1_jpg_work, scale1_jpg_s, OS_TASK_PRIORITY_ABOVE_NORMAL, 0, NULL, 1024);
}
return msi;
}

View File

@@ -0,0 +1,537 @@
// #include "thumb_msi.h"
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "lib/fs/fatfs/osal_file.h"
#include "stream_define.h"
#include "video_app/file_thumb.h"
#include "user_work/user_work.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "video_msi.h"
#include "video_app/file_common_api.h"
#define USER_WORK 1
#define MAX_THUMB_MSI 16
#define MAX_RECV_MAX 16
// data申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#ifdef MORE_SRAM
#define STREAM_LIBC_MALLOC av_psram_malloc
#define STREAM_LIBC_FREE av_psram_free
#define STREAM_LIBC_ZALLOC av_psram_zalloc
#else
#define STREAM_LIBC_MALLOC av_malloc
#define STREAM_LIBC_FREE av_free
#define STREAM_LIBC_ZALLOC av_zalloc
#endif
enum thumb_thread_enum
{
THUMB_THREAD_RUNNING = BIT(0),
THUMB_THREAD_EXIT = BIT(1),
THUMB_THREAD_DEADTH = BIT(2),
};
struct thumb_queue_fb
{
uint8_t datatag; // 对应的stype,按照顺序递增,只有stype匹配才可以
uint16_t time16; // 记录时间戳,只有16位,仅仅简单作为超时使用
uint8_t path[64]; // 记录缩略图的路径
};
#if USER_WORK == 0
struct thumb_msi_s
{
os_task_t task;
struct msi *msi;
struct os_event evt;
const char *normal_base_dir; // 设置如果是原图,则有个基础文件夹地址
uint32_t magic;
uint8_t ramdom_id; // 随机ID,实际最后与stype匹配用
uint8_t jpg_type; // 0:是缩略图 1:原图
uint8_t stype; // 设置自己的专有类型
RBUFFER_DEF(rb, struct thumb_queue_fb, MAX_THUMB_MSI);
};
static void thumb_msi_thread(void *d)
{
struct thumb_msi_s *thumb_msi = (struct thumb_msi_s *) d;
struct msi *msi = thumb_msi->msi;
struct framebuff *fb;
struct thumb_queue_fb t_fb;
uint8_t ret;
uint8_t path[80];
int32_t res;
uint32_t rflags;
msi_get(msi);
while (1)
{
rflags = 0;
res = os_event_wait(&thumb_msi->evt, THUMB_THREAD_RUNNING | THUMB_THREAD_EXIT, &rflags, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, -1);
if (rflags & THUMB_THREAD_EXIT)
{
os_event_set(&thumb_msi->evt, THUMB_THREAD_DEADTH, NULL);
break;
}
thumb_msi_thread_get_fb_again:
fb = msi_get_fb(msi, 0);
// 获取到图片,判断stype与rb中的stype是否匹配
if (fb)
{
thumb_msi_thread_again:
ret = RB_INT_GET(&thumb_msi->rb, t_fb);
if (ret)
{
if (fb->datatag == t_fb.datatag)
{
// 生成缩略图
if (thumb_msi->jpg_type == 0)
{
gen_thumb_path((char *) t_fb.path, path, sizeof(path));
}
else
{
takephoto_name_add_dir(path, sizeof(path), (char *) t_fb.path, thumb_msi->normal_base_dir);
}
// 保存地址,转发出去
struct framebuff *send_fb = fb_clone(fb, F_FILE_T << 8 | fb->stype, msi);
ASSERT(send_fb);
struct file_msg_s *file_msg = (struct file_msg_s *) STREAM_LIBC_ZALLOC(sizeof(struct file_msg_s));
ASSERT(file_msg);
os_memcpy(file_msg->path, path, strlen(path) + 1);
if (thumb_msi->jpg_type == 0)
{
file_msg->ishid = 1;
}
send_fb->priv = (void *) file_msg;
msi_output_fb(msi, send_fb);
}
else
{
// 如果fb->stype > t_fb.stype?代表可能已经有漏数据,则忽略,继续寻找洗一张图
if (fb->datatag > t_fb.datatag)
{
goto thumb_msi_thread_again;
}
else
{
os_printf("thumb_msi_thread: no match fb->datatag:%X\tt_fb.datatag:%X\n", fb->datatag, t_fb.datatag);
}
}
}
else
{
os_printf("thumb_msi_thread: no match fb,datatag:%X\tfb:%X\tret:%d\n", fb->datatag, fb, ret);
}
msi_delete_fb(NULL, fb);
// 去缓冲区继续查找是否有图片需要保存
goto thumb_msi_thread_get_fb_again;
}
}
msi_put(msi);
}
static int32_t thumb_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct thumb_msi_s *thumb_msi = (struct thumb_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
os_event_wait(&thumb_msi->evt, THUMB_THREAD_DEADTH, NULL, OS_EVENT_WMODE_OR, -1);
os_event_del(&thumb_msi->evt);
STREAM_LIBC_FREE(thumb_msi);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_event_set(&thumb_msi->evt, THUMB_THREAD_EXIT, NULL);
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// fb由内部管理,仅仅删除私有结构
if (fb && fb->mtype == F_FILE_T)
{
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
fb->priv = NULL;
}
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// 通过YUV去生成缩略图
if (fb->mtype == F_YUV)
{
struct takephoto_yuv_arg_s *arg = (struct takephoto_yuv_arg_s *) fb->priv;
if (!thumb_msi->magic || arg->yuv_arg.magic == thumb_msi->magic)
{
// 记录path以及转发去生成真的缩略图
// 这里采用clone方式,将类型修改
thumb_msi->ramdom_id = (thumb_msi->ramdom_id + 1);
struct thumb_queue_fb t_fb;
t_fb.datatag = thumb_msi->ramdom_id;
t_fb.time16 = (uint16_t) (os_jiffies() & 0xffff);
// 配置参数,转发
if (arg && arg->yuv_arg.type == YUV_ARG_TAKEPHOTO)
{
os_memcpy(t_fb.path, arg->name, strlen(arg->name) + 1);
if (RB_INT_SET(&thumb_msi->rb, t_fb))
{
// 转发出去
struct framebuff *c_fb = fb_clone(fb, fb->mtype << 8 | thumb_msi->stype, msi);
if (c_fb)
{
c_fb->datatag = thumb_msi->ramdom_id;
msi_output_fb(msi, c_fb);
}
}
}
else
{
msi_delete_fb(NULL, fb);
}
}
// 不需要接收到队列
ret = RET_ERR;
}
// 通过mjpeg去生成缩略图(需要转发到解码器去实现)
else if (fb->mtype == F_THUMB_JPG)
{
// 记录path以及转发去生成真的缩略图
// 这里采用clone方式,将类型修改
thumb_msi->ramdom_id = (thumb_msi->ramdom_id + 1);
struct thumb_queue_fb t_fb;
t_fb.datatag = thumb_msi->ramdom_id;
t_fb.time16 = (uint16_t) (os_jiffies() & 0xffff);
if (fb->priv)
{
os_memcpy(t_fb.path, fb->priv, strlen(fb->priv) + 1);
}
else
{
os_memset(t_fb.path, 0, sizeof(t_fb.path));
}
if (RB_INT_SET(&thumb_msi->rb, t_fb))
{
// 转发出去
struct framebuff *c_fb = fb_clone(fb, F_JPG << 8 | thumb_msi->stype, msi);
if (c_fb)
{
c_fb->datatag = thumb_msi->ramdom_id;
msi_output_fb(msi, c_fb);
}
}
ret = RET_ERR;
}
// 接收stype匹配才去保存图片,datatag作为id
else if ((fb->mtype == F_JPG || fb->mtype == F_JPG_NODE) && (fb->stype == thumb_msi->stype))
{
// 唤醒线程
os_event_set(&thumb_msi->evt, THUMB_THREAD_RUNNING, NULL);
}
else
{
ret = RET_ERR;
}
}
break;
}
return ret;
}
struct msi *thumb_over_dpi_msi_init(const char *msi_name, uint8_t jpg_type, uint8_t stype, uint32_t magic)
{
uint8_t is_new;
struct msi *msi = msi_new(msi_name, MAX_RECV_MAX, &is_new);
struct thumb_msi_s *thumb_msi = NULL;
if (is_new)
{
thumb_msi = (struct thumb_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct thumb_msi_s));
msi->priv = thumb_msi;
thumb_msi->msi = msi;
thumb_msi->jpg_type = jpg_type;
thumb_msi->magic = magic;
thumb_msi->stype = stype;
thumb_msi->normal_base_dir = IMG_PATH;
RB_INIT(&thumb_msi->rb, MAX_THUMB_MSI);
msi->action = thumb_msi_action;
os_event_init(&thumb_msi->evt);
msi->enable = 1;
OS_TASK_INIT("thumb_msi_over_dpi", &thumb_msi->task, thumb_msi_thread, thumb_msi, OS_TASK_PRIORITY_NORMAL, NULL, 1500);
}
return msi;
}
#else
struct thumb_msi_s
{
struct os_work work;
struct msi *msi;
struct os_event evt;
const char *normal_base_dir; // 设置如果是原图,则有个基础文件夹地址
uint32_t magic;
uint8_t ramdom_id; // 随机ID,实际最后与stype匹配用
uint8_t jpg_type; // 0:是缩略图 1:原图
uint8_t stype; // 设置自己的专有类型
RBUFFER_DEF(rb, struct thumb_queue_fb, MAX_THUMB_MSI);
};
static int32_t thumb_msi_work(struct os_work *work)
{
struct thumb_msi_s *thumb_msi = (struct thumb_msi_s *) work;
struct msi *msi = thumb_msi->msi;
struct framebuff *fb;
struct thumb_queue_fb t_fb;
uint8_t ret;
char path[80];
int32_t res;
uint32_t rflags;
rflags = 0;
res = os_event_wait(&thumb_msi->evt, THUMB_THREAD_RUNNING | THUMB_THREAD_EXIT, &rflags, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, -1);
if (rflags & THUMB_THREAD_EXIT)
{
os_event_set(&thumb_msi->evt, THUMB_THREAD_DEADTH, NULL);
goto thumb_msi_exit;
}
thumb_msi_thread_get_fb_again:
fb = msi_get_fb(msi, 0);
// 获取到图片,判断stype与rb中的stype是否匹配
if (fb)
{
thumb_msi_thread_again:
ret = RB_INT_GET(&thumb_msi->rb, t_fb);
if (ret)
{
if (fb->datatag == t_fb.datatag)
{
// 生成缩略图
if (thumb_msi->jpg_type == 0)
{
gen_thumb_path((char *) t_fb.path, path, sizeof(path));
}
else
{
takephoto_name_add_dir(path, sizeof(path), (char *) t_fb.path, thumb_msi->normal_base_dir);
}
// 保存地址,转发出去
struct framebuff *send_fb = fb_clone(fb, F_FILE_T << 8 | fb->stype, msi);
ASSERT(send_fb);
struct file_msg_s *file_msg = (struct file_msg_s *) STREAM_LIBC_ZALLOC(sizeof(struct file_msg_s));
ASSERT(file_msg);
os_memcpy(file_msg->path, path, strlen(path) + 1);
if (thumb_msi->jpg_type == 0)
{
file_msg->ishid = 1;
}
send_fb->priv = (void *) file_msg;
msi_output_fb(msi, send_fb);
}
else
{
// 如果fb->stype > t_fb.stype?代表可能已经有漏数据,则忽略,继续寻找洗一张图
if (fb->datatag > t_fb.datatag)
{
goto thumb_msi_thread_again;
}
else
{
os_printf("thumb_msi_thread: no match fb->datatag:%X\tt_fb.datatag:%X\n", fb->datatag, t_fb.datatag);
}
}
}
else
{
os_printf("thumb_msi_thread: no match fb,datatag:%X\tfb:%X\tret:%d\n", fb->datatag, fb, ret);
}
msi_delete_fb(NULL, fb);
// 去缓冲区继续查找是否有图片需要保存
goto thumb_msi_thread_get_fb_again;
}
thumb_msi_exit:
return 0;
}
static int32_t thumb_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct thumb_msi_s *thumb_msi = (struct thumb_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
// os_event_wait(&thumb_msi->evt, THUMB_THREAD_DEADTH, NULL, OS_EVENT_WMODE_OR, -1);
os_event_del(&thumb_msi->evt);
STREAM_LIBC_FREE(thumb_msi);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_event_set(&thumb_msi->evt, THUMB_THREAD_EXIT, NULL);
os_work_cancle2(&thumb_msi->work, 1);
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// fb由内部管理,仅仅删除私有结构
if (fb && fb->mtype == F_FILE_T)
{
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
fb->priv = NULL;
}
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// 通过YUV去生成缩略图
if (fb->mtype == F_YUV)
{
struct takephoto_yuv_arg_s *arg = (struct takephoto_yuv_arg_s *) fb->priv;
if (!thumb_msi->magic || arg->yuv_arg.magic == thumb_msi->magic)
{
// 记录path以及转发去生成真的缩略图
// 这里采用clone方式,将类型修改
thumb_msi->ramdom_id = (thumb_msi->ramdom_id + 1);
struct thumb_queue_fb t_fb;
t_fb.datatag = thumb_msi->ramdom_id;
t_fb.time16 = (uint16_t) (os_jiffies() & 0xffff);
// 配置参数,转发
if (arg && arg->yuv_arg.type == YUV_ARG_TAKEPHOTO)
{
os_memcpy(t_fb.path, arg->name, os_strlen(arg->name) + 1);
if (RB_INT_SET(&thumb_msi->rb, t_fb))
{
// 转发出去
struct framebuff *c_fb = fb_clone(fb, fb->mtype << 8 | thumb_msi->stype, msi);
if (c_fb)
{
c_fb->datatag = thumb_msi->ramdom_id;
msi_output_fb(msi, c_fb);
}
}
}
else
{
msi_delete_fb(NULL, fb);
}
}
// 不需要接收到队列
ret = RET_ERR;
}
// 通过mjpeg去生成缩略图(需要转发到解码器去实现)
else if (fb->mtype == F_THUMB_JPG)
{
// 记录path以及转发去生成真的缩略图
// 这里采用clone方式,将类型修改
thumb_msi->ramdom_id = (thumb_msi->ramdom_id + 1);
struct thumb_queue_fb t_fb;
t_fb.datatag = thumb_msi->ramdom_id;
t_fb.time16 = (uint16_t) (os_jiffies() & 0xffff);
if (fb->priv)
{
os_memcpy(t_fb.path, fb->priv, strlen(fb->priv) + 1);
}
else
{
os_memset(t_fb.path, 0, sizeof(t_fb.path));
}
if (RB_INT_SET(&thumb_msi->rb, t_fb))
{
// 转发出去
struct framebuff *c_fb = fb_clone(fb, F_JPG << 8 | thumb_msi->stype, msi);
if (c_fb)
{
c_fb->datatag = thumb_msi->ramdom_id;
msi_output_fb(msi, c_fb);
}
}
ret = RET_ERR;
}
// 接收stype匹配才去保存图片,datatag作为id
else if ((fb->mtype == F_JPG || fb->mtype == F_JPG_NODE) && (fb->stype == thumb_msi->stype))
{
// 唤醒线程
// os_event_set(&thumb_msi->evt, THUMB_THREAD_RUNNING, NULL);
}
else
{
ret = RET_ERR;
}
}
break;
case MSI_CMD_TRANS_FB_END:
{
// 唤醒线程
os_event_set(&thumb_msi->evt, THUMB_THREAD_RUNNING, NULL);
os_run_work(&thumb_msi->work);
}
break;
case MSI_CMD_JPG_THUMB:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
case MSI_JPG_THUMB_TAKEPHOTO_SETPATH:
{
thumb_msi->normal_base_dir = (char *) arg;
}
break;
}
}
break;
}
return ret;
}
struct msi *thumb_over_dpi_msi_init(const char *msi_name, uint8_t jpg_type, uint8_t stype, uint32_t magic)
{
uint8_t is_new;
struct msi *msi = msi_new(msi_name, MAX_RECV_MAX, &is_new);
struct thumb_msi_s *thumb_msi = NULL;
if (is_new)
{
thumb_msi = (struct thumb_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct thumb_msi_s));
msi->priv = thumb_msi;
thumb_msi->msi = msi;
thumb_msi->jpg_type = jpg_type;
thumb_msi->magic = magic;
thumb_msi->stype = stype;
thumb_msi->normal_base_dir = IMG_PATH;
RB_INIT(&thumb_msi->rb, MAX_THUMB_MSI);
msi->action = thumb_msi_action;
os_event_init(&thumb_msi->evt);
msi->enable = 1;
OS_WORK_INIT(&thumb_msi->work, thumb_msi_work, 0);
// OS_TASK_INIT("thumb_msi_over_dpi", &thumb_msi->task, thumb_msi_thread, thumb_msi, OS_TASK_PRIORITY_NORMAL, NULL, 1500);
}
return msi;
}
#endif

View File

@@ -0,0 +1,191 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "osal/string.h"
#include "stream_frame.h"
#include "lib/fs/fatfs/osal_file.h"
#include "user_work/user_work.h"
#include "file_thumb.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
/*******************************************************************
* 这个模块主要是 USB 拍照用,同时会尝试生成缩略图(转发到下一个msi去生成)
*******************************************************************/
// 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 USB_JPG_THUMB_RECV_MAX (8)
struct usb_jpg_thumb_s
{
struct os_work work;
struct msi *msi;
struct msi *gen420_msi;
char filepath[64];
char *dirpath;
uint8_t filter;
uint8_t thumb_stype;
uint8_t takephoto_photo;
};
uint8_t takephoto_name(const char *img_dir, char *file_path, int filepath_size);
// 正常应该是这里去切换成拍照模式
static int32 usb_jpg_thumb_work(struct os_work *work)
{
struct usb_jpg_thumb_s *usb_jpg_thumb = (struct usb_jpg_thumb_s *) work;
struct framebuff *fb;
struct framebuff *c_fb;
// 执行完一次后,继续去获取图片,如果没有就等待下次信号量唤醒
jpg_thumb_work_again:
// 尝试获取一下原图
fb = msi_get_fb(usb_jpg_thumb->msi, 0);
if (fb)
{
// 判断一下是否为原图
if (usb_jpg_thumb->takephoto_photo)
{
usb_jpg_thumb->takephoto_photo--;
if (takephoto_name((const char *)usb_jpg_thumb->dirpath, usb_jpg_thumb->filepath, sizeof(usb_jpg_thumb->filepath)))
{
_os_printf("%s %d\tget file path err\r\n", __FUNCTION__, __LINE__);
msi_delete_fb(NULL, fb);
fb = NULL;
return 1;
}
// 设置缩略图的stype
c_fb = fb_clone(fb, F_THUMB_JPG << 8 | usb_jpg_thumb->thumb_stype, usb_jpg_thumb->msi);
// 设置来源是正常缩略图
c_fb->priv = (void *) usb_jpg_thumb->filepath; // 记录路径
// 转发,生成缩略图
msi_output_fb(usb_jpg_thumb->msi, c_fb);
// 转发到给到文件去保存(独立线程)
struct framebuff *send_fb = fb_clone(fb, F_FILE_T << 8 | fb->stype, usb_jpg_thumb->msi);
ASSERT(send_fb);
struct file_msg_s *file_msg = (struct file_msg_s *) STREAM_LIBC_MALLOC(sizeof(struct file_msg_s));
ASSERT(file_msg);
os_memcpy(file_msg->path, usb_jpg_thumb->filepath, strlen(usb_jpg_thumb->filepath) + 1);
file_msg->ishid = 0;
send_fb->priv = (void *) file_msg;
msi_output_fb(usb_jpg_thumb->msi, send_fb);
}
if (!usb_jpg_thumb->takephoto_photo)
{
usb_jpg_thumb->msi->enable = 0;
}
msi_delete_fb(NULL, fb);
fb = NULL;
goto jpg_thumb_work_again;
}
return 0;
}
static int32_t usb_jpg_thumb_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct usb_jpg_thumb_s *usb_jpg_thumb = (struct usb_jpg_thumb_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
STREAM_LIBC_FREE(usb_jpg_thumb);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_work_cancle2(&usb_jpg_thumb->work, 1);
}
break;
case MSI_CMD_JPG_THUMB:
{
uint32_t cmd_self = (uint32_t) param1;
uint32_t arg = param2;
switch (cmd_self)
{
// 启动拍照,一定要拍照完毕才能退出?如果需要强行退出,需要设置另外接口,这里默认一定能拍照成功
case MSI_JPG_THUMB_TAKEPHOTO:
{
usb_jpg_thumb->takephoto_photo += arg;
usb_jpg_thumb->msi->enable = 1;
}
break;
case MSI_JPG_THUMB_TAKEPHOTO_SETPATH:
{
usb_jpg_thumb->dirpath = (char *) arg;
}
break;
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
if (fb->mtype != F_JPG)
{
ret = RET_ERR;
}
else
{
// 检查是否来自需要的数据源头
if (fb->srcID != usb_jpg_thumb->filter)
{
ret = RET_ERR;
}
}
}
break;
case MSI_CMD_TRANS_FB_END:
{
os_run_work(&usb_jpg_thumb->work);
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// fb由内部管理,仅仅删除私有结构
if (fb && fb->mtype == F_FILE_T)
{
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
fb->priv = NULL;
}
}
}
break;
}
return ret;
}
struct msi *usb_jpg_thumb_msi_init(const char *msi_name, uint8_t filter, uint8_t thumb_stype)
{
uint8_t is_new;
struct msi *msi = msi_new(msi_name, USB_JPG_THUMB_RECV_MAX, &is_new);
struct usb_jpg_thumb_s *usb_jpg_thumb = NULL;
if (is_new)
{
usb_jpg_thumb = (struct usb_jpg_thumb_s *) STREAM_LIBC_ZALLOC(sizeof(struct usb_jpg_thumb_s));
msi->priv = usb_jpg_thumb;
usb_jpg_thumb->msi = msi;
usb_jpg_thumb->filter = filter;
usb_jpg_thumb->thumb_stype = thumb_stype;
usb_jpg_thumb->dirpath = IMG_PATH;
msi->action = usb_jpg_thumb_msi_action;
msi->enable = 0;
msi_get(usb_jpg_thumb->msi);
OS_WORK_INIT(&usb_jpg_thumb->work, usb_jpg_thumb_work, 0);
}
return msi;
}

View File

@@ -0,0 +1,472 @@
// #include "thumb_msi.h"
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "lib/fs/fatfs/osal_file.h"
#include "stream_define.h"
#include "video_app/file_thumb.h"
#include "user_work/user_work.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "video_msi.h"
#include "video_app/file_common_api.h"
#define USER_WORK 1
#define MAX_THUMB_MSI 16
#define MAX_RECV_MAX 16
// data申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#ifdef MORE_SRAM
#define STREAM_LIBC_MALLOC av_psram_malloc
#define STREAM_LIBC_FREE av_psram_free
#define STREAM_LIBC_ZALLOC av_psram_zalloc
#else
#define STREAM_LIBC_MALLOC av_malloc
#define STREAM_LIBC_FREE av_free
#define STREAM_LIBC_ZALLOC av_zalloc
#endif
enum thumb_thread_enum
{
THUMB_THREAD_RUNNING = BIT(0),
THUMB_THREAD_EXIT = BIT(1),
THUMB_THREAD_DEADTH = BIT(2),
};
struct thumb_queue_fb
{
uint8_t datatag; // 对应的stype,按照顺序递增,只有stype匹配才可以
uint16_t time16; // 记录时间戳,只有16位,仅仅简单作为超时使用
uint8_t path[64]; // 记录缩略图的路径
};
#if USER_WORK == 0
struct thumb_msi_s
{
os_task_t task;
struct msi *msi;
struct os_event evt;
const char *normal_base_dir; // 设置如果是原图,则有个基础文件夹地址
uint32_t magic;
uint8_t ramdom_id; // 随机ID,实际最后与stype匹配用
uint8_t jpg_type; // 0:是缩略图 1:原图
uint8_t stype; // 设置自己的专有类型
RBUFFER_DEF(rb, struct thumb_queue_fb, MAX_THUMB_MSI);
};
static void usb_thumb_msi_thread(void *d)
{
struct thumb_msi_s *thumb_msi = (struct thumb_msi_s *) d;
struct msi *msi = thumb_msi->msi;
struct framebuff *fb;
struct thumb_queue_fb t_fb;
uint8_t ret;
uint8_t path[80];
int32_t res;
uint32_t rflags;
msi_get(msi);
while (1)
{
rflags = 0;
res = os_event_wait(&thumb_msi->evt, THUMB_THREAD_RUNNING | THUMB_THREAD_EXIT, &rflags, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, -1);
if (rflags & THUMB_THREAD_EXIT)
{
os_event_set(&thumb_msi->evt, THUMB_THREAD_DEADTH, NULL);
break;
}
usb_thumb_msi_thread_get_fb_again:
fb = msi_get_fb(msi, 0);
// 获取到图片,判断stype与rb中的stype是否匹配
if (fb)
{
usb_thumb_msi_thread_again:
ret = RB_INT_GET(&thumb_msi->rb, t_fb);
if (ret)
{
if (fb->datatag == t_fb.datatag)
{
// 生成缩略图
if (thumb_msi->jpg_type == 0)
{
gen_thumb_path((char *) t_fb.path, path, sizeof(path));
}
else
{
takephoto_name_add_dir(path, sizeof(path), (char *) t_fb.path, thumb_msi->normal_base_dir);
}
// 保存地址,转发出去
struct framebuff *send_fb = fb_clone(fb, F_FILE_T << 8 | fb->stype, msi);
ASSERT(send_fb);
struct file_msg_s *file_msg = (struct file_msg_s *) STREAM_LIBC_ZALLOC(sizeof(struct file_msg_s));
ASSERT(file_msg);
os_memcpy(file_msg->path, path, strlen(path) + 1);
if (thumb_msi->jpg_type == 0)
{
file_msg->ishid = 1;
}
send_fb->priv = (void *) file_msg;
msi_output_fb(msi, send_fb);
}
else
{
// 如果fb->stype > t_fb.stype?代表可能已经有漏数据,则忽略,继续寻找洗一张图
if (fb->datatag > t_fb.datatag)
{
goto usb_thumb_msi_thread_again;
}
else
{
os_printf("usb_thumb_msi_thread: no match fb->datatag:%X\tt_fb.datatag:%X\n", fb->datatag, t_fb.datatag);
}
}
}
else
{
os_printf("usb_thumb_msi_thread: no match fb,datatag:%X\tfb:%X\tret:%d\n", fb->datatag, fb, ret);
}
msi_delete_fb(NULL, fb);
// 去缓冲区继续查找是否有图片需要保存
goto usb_thumb_msi_thread_get_fb_again;
}
}
msi_put(msi);
}
static int32_t usb_thumb_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct thumb_msi_s *thumb_msi = (struct thumb_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
os_event_wait(&thumb_msi->evt, THUMB_THREAD_DEADTH, NULL, OS_EVENT_WMODE_OR, -1);
os_event_del(&thumb_msi->evt);
STREAM_LIBC_FREE(thumb_msi);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_event_set(&thumb_msi->evt, THUMB_THREAD_EXIT, NULL);
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// fb由内部管理,仅仅删除私有结构
if (fb && fb->mtype == F_FILE_T)
{
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
fb->priv = NULL;
}
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// 通过YUV去生成缩略图
if (fb->mtype == F_YUV)
{
struct takephoto_yuv_arg_s *arg = (struct takephoto_yuv_arg_s *) fb->priv;
if (!thumb_msi->magic || arg->yuv_arg.magic == thumb_msi->magic)
{
// 记录path以及转发去生成真的缩略图
// 这里采用clone方式,将类型修改
thumb_msi->ramdom_id = (thumb_msi->ramdom_id + 1);
struct thumb_queue_fb t_fb;
t_fb.datatag = thumb_msi->ramdom_id;
t_fb.time16 = (uint16_t) (os_jiffies() & 0xffff);
// 配置参数,转发
if (arg && arg->yuv_arg.type == YUV_ARG_TAKEPHOTO)
{
os_memcpy(t_fb.path, arg->name, strlen(arg->name) + 1);
if (RB_INT_SET(&thumb_msi->rb, t_fb))
{
// 转发出去
struct framebuff *c_fb = fb_clone(fb, fb->mtype << 8 | thumb_msi->stype, msi);
if (c_fb)
{
c_fb->datatag = thumb_msi->ramdom_id;
msi_output_fb(msi, c_fb);
}
}
}
else
{
msi_delete_fb(NULL, fb);
}
}
// 不需要接收到队列
ret = RET_ERR;
}
// 通过mjpeg去生成缩略图(需要转发到解码器去实现)
else if (fb->mtype == F_THUMB_JPG)
{
// 记录path以及转发去生成真的缩略图
// 这里采用clone方式,将类型修改
thumb_msi->ramdom_id = (thumb_msi->ramdom_id + 1);
struct thumb_queue_fb t_fb;
t_fb.datatag = thumb_msi->ramdom_id;
t_fb.time16 = (uint16_t) (os_jiffies() & 0xffff);
if (fb->priv)
{
os_memcpy(t_fb.path, fb->priv, strlen(fb->priv) + 1);
}
else
{
os_memset(t_fb.path, 0, sizeof(t_fb.path));
}
if (RB_INT_SET(&thumb_msi->rb, t_fb))
{
// 转发出去
struct framebuff *c_fb = fb_clone(fb, F_JPG << 8 | thumb_msi->stype, msi);
if (c_fb)
{
c_fb->datatag = thumb_msi->ramdom_id;
msi_output_fb(msi, c_fb);
}
}
ret = RET_ERR;
}
// 接收stype匹配才去保存图片,datatag作为id
else if ((fb->mtype == F_JPG || fb->mtype == F_JPG_NODE) && (fb->stype == thumb_msi->stype))
{
// 唤醒线程
os_event_set(&thumb_msi->evt, THUMB_THREAD_RUNNING, NULL);
}
else
{
ret = RET_ERR;
}
}
break;
}
return ret;
}
struct msi *usb_thumb_over_dpi_msi_init(const char *msi_name, uint8_t jpg_type, uint8_t stype, uint32_t magic)
{
uint8_t is_new;
struct msi *msi = msi_new(msi_name, MAX_RECV_MAX, &is_new);
struct thumb_msi_s *thumb_msi = NULL;
if (is_new)
{
thumb_msi = (struct thumb_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct thumb_msi_s));
msi->priv = thumb_msi;
thumb_msi->msi = msi;
thumb_msi->jpg_type = jpg_type;
thumb_msi->magic = magic;
thumb_msi->stype = stype;
thumb_msi->normal_base_dir = IMG_PATH;
RB_INIT(&thumb_msi->rb, MAX_THUMB_MSI);
msi->action = usb_thumb_msi_action;
os_event_init(&thumb_msi->evt);
msi->enable = 1;
OS_TASK_INIT("thumb_msi_over_dpi", &thumb_msi->task, usb_thumb_msi_thread, thumb_msi, OS_TASK_PRIORITY_NORMAL, NULL, 1500);
}
return msi;
}
#else
struct thumb_msi_s
{
struct os_work work;
struct msi *msi;
struct os_event evt;
uint32_t magic;
uint8_t ramdom_id; // 随机ID,实际最后与stype匹配用
uint8_t stype; // 设置自己的专有类型
RBUFFER_DEF(rb, struct thumb_queue_fb, MAX_THUMB_MSI);
};
static int32_t usb_thumb_msi_work(struct os_work *work)
{
struct thumb_msi_s *thumb_msi = (struct thumb_msi_s *) work;
struct msi *msi = thumb_msi->msi;
struct framebuff *fb;
struct thumb_queue_fb t_fb;
uint8_t ret;
char path[80];
int32_t res;
uint32_t rflags;
rflags = 0;
res = os_event_wait(&thumb_msi->evt, THUMB_THREAD_RUNNING | THUMB_THREAD_EXIT, &rflags, OS_EVENT_WMODE_OR | OS_EVENT_WMODE_CLEAR, -1);
if (rflags & THUMB_THREAD_EXIT)
{
os_event_set(&thumb_msi->evt, THUMB_THREAD_DEADTH, NULL);
goto thumb_msi_exit;
}
usb_thumb_msi_thread_get_fb_again:
fb = msi_get_fb(msi, 0);
// 获取到图片判断stype与rb中的stype是否匹配
if (fb)
{
usb_thumb_msi_thread_again:
ret = RB_INT_GET(&thumb_msi->rb, t_fb);
if (ret)
{
if (fb->datatag == t_fb.datatag)
{
// 生成缩略图
gen_thumb_path((char *) t_fb.path, path, sizeof(path));
// 保存地址,转发出去
struct framebuff *send_fb = fb_clone(fb, F_FILE_T << 8 | fb->stype, msi);
ASSERT(send_fb);
struct file_msg_s *file_msg = (struct file_msg_s *) STREAM_LIBC_ZALLOC(sizeof(struct file_msg_s));
ASSERT(file_msg);
os_memcpy(file_msg->path, path, strlen(path) + 1);
file_msg->ishid = 1;
send_fb->priv = (void *) file_msg;
msi_output_fb(msi, send_fb);
}
else
{
// 如果fb->stype > t_fb.stype?代表可能已经有漏数据,则忽略,继续寻找新的一张图
if (fb->datatag > t_fb.datatag)
{
goto usb_thumb_msi_thread_again;
}
else
{
os_printf("usb_thumb_msi_thread: no match fb->datatag:%X\tt_fb.datatag:%X\n", fb->datatag, t_fb.datatag);
}
}
}
else
{
os_printf("usb_thumb_msi_thread: no match fb,datatag:%X\tfb:%X\tret:%d\n", fb->datatag, fb, ret);
}
msi_delete_fb(NULL, fb);
// 去缓冲区继续查找是否有图片需要保存
goto usb_thumb_msi_thread_get_fb_again;
}
thumb_msi_exit:
return 0;
}
static int32_t usb_thumb_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct thumb_msi_s *thumb_msi = (struct thumb_msi_s *) msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
// os_event_wait(&thumb_msi->evt, THUMB_THREAD_DEADTH, NULL, OS_EVENT_WMODE_OR, -1);
os_event_del(&thumb_msi->evt);
STREAM_LIBC_FREE(thumb_msi);
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_event_set(&thumb_msi->evt, THUMB_THREAD_EXIT, NULL);
os_work_cancle2(&thumb_msi->work, 1);
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// fb由内部管理,仅仅删除私有结构
if (fb && fb->mtype == F_FILE_T)
{
if (fb->priv)
{
STREAM_LIBC_FREE(fb->priv);
fb->priv = NULL;
}
}
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *) param1;
// 通过mjpeg去生成缩略图(需要转发到解码器去实现)
if (fb->mtype == F_THUMB_JPG)
{
// 记录path以及转发去生成真的缩略图
// 这里采用clone方式,将类型修改
thumb_msi->ramdom_id = (thumb_msi->ramdom_id + 1);
struct thumb_queue_fb t_fb;
t_fb.datatag = thumb_msi->ramdom_id;
t_fb.time16 = (uint16_t) (os_jiffies() & 0xffff);
if (fb->priv)
{
os_memcpy(t_fb.path, fb->priv, strlen(fb->priv) + 1);
}
else
{
os_memset(t_fb.path, 0, sizeof(t_fb.path));
}
if (RB_INT_SET(&thumb_msi->rb, t_fb))
{
// 转发出去
struct framebuff *c_fb = fb_clone(fb, F_JPG << 8 | thumb_msi->stype, msi);
if (c_fb)
{
c_fb->datatag = thumb_msi->ramdom_id;
msi_output_fb(msi, c_fb);
}
}
ret = RET_ERR;
}
// 接收stype匹配才去保存图片,datatag作为id
else if ((fb->mtype == F_JPG || fb->mtype == F_JPG_NODE) && (fb->stype == thumb_msi->stype))
{
// 唤醒线程
// os_event_set(&thumb_msi->evt, THUMB_THREAD_RUNNING, NULL);
}
else
{
ret = RET_ERR;
}
}
break;
case MSI_CMD_TRANS_FB_END:
{
// 唤醒线程
os_event_set(&thumb_msi->evt, THUMB_THREAD_RUNNING, NULL);
os_run_work(&thumb_msi->work);
}
break;
}
return ret;
}
struct msi *usb_thumb_over_dpi_msi_init(const char *msi_name, uint8_t stype, uint32_t magic)
{
uint8_t is_new;
struct msi *msi = msi_new(msi_name, MAX_RECV_MAX, &is_new);
struct thumb_msi_s *thumb_msi = NULL;
if (is_new)
{
thumb_msi = (struct thumb_msi_s *) STREAM_LIBC_ZALLOC(sizeof(struct thumb_msi_s));
msi->priv = thumb_msi;
thumb_msi->msi = msi;
thumb_msi->magic = magic;
thumb_msi->stype = stype;
RB_INIT(&thumb_msi->rb, MAX_THUMB_MSI);
msi->action = usb_thumb_msi_action;
os_event_init(&thumb_msi->evt);
msi->enable = 1;
OS_WORK_INIT(&thumb_msi->work, usb_thumb_msi_work, 0);
}
return msi;
}
#endif

View File

@@ -0,0 +1,85 @@
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "stream_define.h"
#include "gen420_hardware_msi.h"
#include "lib/video/dvp/jpeg/jpg.h"
#include "video_msi.h"
/*******************************************
* usb图片增加水印重新编码
*******************************************/
extern struct msi *route_msi(const char *name);
extern struct msi *jpg_decode_msg_msi(const char *name, uint16_t out_w, uint16_t out_h, uint16_t step_w, uint16_t step_h, uint32_t filter);
extern struct msi *jpg_decode_msi(const char *name);
extern struct msi *yuv_time_watermark(const char *name, uint8_t filter);
static uint8_t recode_filter(void *f, uint8_t recv_type)
{
struct framebuff *fb = (struct framebuff *) f;
uint8_t res = 1;
if ((fb->mtype == F_JPG) && fb->stype == recv_type)
{
res = 0;
}
return res;
}
void usb_to_recode_init(uint8_t jpg_num)
{
uint32_t magic;
// ROUTE_USB是usb图片获取来源
struct msi *route_m = route_msi(ROUTE_USB);
if (route_m)
{
msi_add_output(route_m, NULL, SR_USB_RECODE_DEOCDE);
}
// 接收USB的图片数据,然后配置需要解码的参数,最后给到硬件去解码
struct msi *decode_msg_msi = jpg_decode_msg_msi(SR_USB_RECODE_DEOCDE, 1280, 720, 1280, 720, FSTYPE_USB_CAM0);
if (decode_msg_msi)
{
// 这里配置特定的类型,就是重新编码后会设置类型,这样应用层是可以进行识别是否为重新编码的,这个可以自定义,但不要和其他
// 已经运行的有冲突,不然可能其他msi接收到重编码的数据
msi_do_cmd(decode_msg_msi, MSI_CMD_DECODE_JPEG_MSG, MSI_JPEG_DECODE_FORCE_TYPE, FSTYPE_JPG_GEN420_REJPG);
msi_add_output(decode_msg_msi, NULL, S_JPG_DECODE);
}
// 把解码模块打开,接收来自SR_USB_RECODE_DEOCDE的数据,解码后,给到水印的msi去添加水印
struct msi *decode_msi = jpg_decode_msi(S_JPG_DECODE);
if (decode_msi)
{
msi_add_output(decode_msi, NULL, SR_YUV_WATERMARK);
}
do
{
magic = os_jiffies();
magic ^= (uint32_t) decode_msg_msi;
} while (!magic);
if (decode_msg_msi && decode_msi)
{
// 设置magic
msi_do_cmd(decode_msg_msi, MSI_CMD_DECODE_JPEG_MSG, MSI_JPEG_DECODE_MAGIC, magic);
msi_add_output(decode_msg_msi, NULL, decode_msi->name);
}
// 水印添加后,将yuv数据给到R_GEN420_JPG_RECODE重新编码,并且仅仅进行过滤类型
struct msi *watermark = yuv_time_watermark(SR_YUV_WATERMARK, FSTYPE_JPG_GEN420_REJPG);
if (watermark)
{
msi_add_output(watermark, NULL, R_GEN420_JPG_RECODE);
}
// 过滤类型,由于从usb过来,由于decode_msg_msi设置了类型,所以这里需要修改
static const uint16_t watermark_filter[] = {FSTYPE_JPG_GEN420_REJPG, FSTYPE_NONE};
struct msi *gen420_jpg_msi = gen420_jpg_msi_init(R_GEN420_JPG_RECODE, jpg_num, FSTYPE_JPG_GEN420_REJPG, JPG_LOCK_GEN420_RECODE, GEN420_QUEUE_JPEG_RECODE, (uint16_t*)watermark_filter, recode_filter);
if (gen420_jpg_msi)
{
// 如果需要默认添加到某个msi,在这里添加,也可以后续在其他地方通过msi_add_output(NULL, R_GEN420_JPG_RECODE, "XXX");的方式添加
msi_do_cmd(gen420_jpg_msi, MSI_CMD_JPG_RECODE, MSI_JPG_RECODE_MAGIC, magic);
}
}

View File

@@ -0,0 +1,27 @@
#ifndef __VIDEO_APP_H
#define __VIDEO_APP_H
#include "sys_config.h"
#include "tx_platform.h"
#include "list.h"
#include "dev.h"
#include "typesdef.h"
#include "stream_frame.h"
extern void *get_jpeg_node_buf(void *d);
extern int get_node_count(volatile struct list_head *head);
extern void del_jpeg_node(void *d,uint32_t jpg_num);
extern int get_jpg_node_len();
extern uint32 get_jpg_node_len_new(void *get_f);
extern void jpg_recfg();
extern void jpg_start(uint8 jpgid);
extern void jpg_stop(uint32_t jpg_num);
extern bool csi_recfg();
extern bool csi_open();
extern bool csi_close();
void jpeg_stream_deinit();
stream* jpeg_stream_init();
void stop_jpeg();
void start_jpeg();
#endif

View File

@@ -0,0 +1,249 @@
#include "basic_include.h"
#include "video_app_csc_msi.h"
#include "stream_define.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "lib/multimedia/msi.h"
#include "user_work/user_work.h"
// 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 CSC_MSI_RX_NUM 2
#define CSC_MSI_TX_NUM 2
struct video_app_csc_priv
{
struct os_work work;
struct csc_device* csc_dev;
struct msi* msi;
struct fbpool tx_pool;
uint8_t hardware_ready : 1,
wait_irq_done : 1,
rev : 6;
uint32_t input_format;
uint32_t output_format;
uint32_t width;
uint32_t height;
struct framebuff* current_rx_fb;
struct framebuff* current_tx_fb;
};
typedef struct video_app_csc_priv* video_app_csc_priv_t;
static int32_t video_app_csc_msi_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
video_app_csc_priv_t csc_priv = (video_app_csc_priv_t)msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
if(csc_priv) {
struct framebuff *fb = NULL;
while (1)
{
fb = fbpool_get(&csc_priv->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);
fb->priv = NULL;
}
}
fbpool_destroy(&csc_priv->tx_pool);
STREAM_LIBC_FREE(csc_priv);
msi->priv = NULL;
}
}
break;
case MSI_CMD_PRE_DESTROY:
{
os_work_cancle2(&csc_priv->work, 1);
}
break;
case MSI_CMD_TRANS_FB:
{
struct framebuff *fb = (struct framebuff *)param1;
if (fb->srcID != FRAMEBUFF_SOURCE_CSC) {
ret = RET_OK + 1;
}
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *)param1;
fbpool_put(&csc_priv->tx_pool, fb);
// 不需要内核去释放fb
ret = RET_OK + 1;
}
break;
case MSI_CMD_CSC:
{
uint32_t cmd_self = (uint32_t)param1;
uint32_t arg = param2;
switch (cmd_self)
{
case MSI_CSC_MSI_ENABLE:
{
csc_priv->msi->enable = arg;
}
break;
}
}
}
return ret;
}
static void csc_irq_done(uint32 irq_flag, uint32 irq_data, uint32 param1)
{
video_app_csc_priv_t csc_priv = (video_app_csc_priv_t)irq_data;
csc_priv->hardware_ready = 1;
}
static int32 video_app_csc_msi_work(struct os_work *work)
{
video_app_csc_priv_t csc_priv = (video_app_csc_priv_t)work;
if (csc_priv->hardware_ready) {
if (csc_priv->wait_irq_done) {
csc_priv->current_tx_fb->time = os_jiffies();
msi_output_fb(csc_priv->msi, csc_priv->current_tx_fb);
msi_delete_fb(csc_priv->msi, csc_priv->current_rx_fb);
csc_priv->current_tx_fb = NULL;
csc_priv->current_rx_fb = NULL;
csc_priv->wait_irq_done = 0;
}
if (!csc_priv->current_rx_fb) {
csc_priv->current_rx_fb = msi_get_fb(csc_priv->msi, 0);
if (!csc_priv->current_rx_fb) {
goto __video_app_csc_msi_work_end;
}
}
if (csc_priv->current_rx_fb) {
if (!csc_priv->current_tx_fb) {
csc_priv->current_tx_fb = fbpool_get(&csc_priv->tx_pool, 0, csc_priv->msi);
if (!csc_priv->current_tx_fb) {
goto __video_app_csc_msi_work_end;
}
}
uint8_t *csc_input_addr = csc_priv->current_rx_fb->data;
uint8_t *csc_output_addr = csc_priv->current_tx_fb->data;
csc_priv->hardware_ready = 0;
csc_priv->wait_irq_done = 1;
csc_priv->current_tx_fb->mtype = F_YUV;
csc_priv->current_tx_fb->stype = FSTYPE_YUV_P0;
csc_priv->current_tx_fb->srcID = FRAMEBUFF_SOURCE_CSC;
struct yuv_arg_s * yuv_msg = csc_priv->current_tx_fb->priv;
yuv_msg->y_size = csc_priv->width*csc_priv->height;
yuv_msg->uv_off = 0;
yuv_msg->y_off = 0;
yuv_msg->x = 0;
yuv_msg->y = 0;
yuv_msg->out_w = csc_priv->width;
yuv_msg->out_h = csc_priv->height;
csc_init(csc_priv->csc_dev);
csc_set_type(csc_priv->csc_dev, 2); /* RGB -> YUV */
csc_set_photo_size(csc_priv->csc_dev, csc_priv->width, csc_priv->height);
csc_set_format(csc_priv->csc_dev, csc_priv->input_format, csc_priv->output_format); /* RGB565 -> YUV420P */
csc_set_input_addr(csc_priv->csc_dev, (uint32_t)csc_input_addr, 0, 0);
csc_set_output_addr(csc_priv->csc_dev, \
(uint32_t)(csc_output_addr), \
(uint32_t)(csc_output_addr + csc_priv->width * csc_priv->height), \
(uint32_t)(csc_output_addr + csc_priv->width * csc_priv->height + csc_priv->width * csc_priv->height / 4));
csc_start_run(csc_priv->csc_dev);
}
} else {
/* 转换未完成,直接退出 */
}
__video_app_csc_msi_work_end:
os_run_work_delay(&csc_priv->work, 1);
return 0;
}
/* 目前 CSC 数据流仅用在 LVGL 由 RGB565 -> YUV420P -> VIDEO P0 的通路上 */
void video_app_csc_msi_init(const char* csc_msi_name, uint32_t input_format, uint32_t output_format, uint32_t width, uint32_t height)
{
struct msi *msi = NULL;
video_app_csc_msi_init_start:
msi = msi_new(csc_msi_name, CSC_MSI_RX_NUM, NULL);
if (!msi) {
return ;
}
video_app_csc_priv_t csc_priv = (video_app_csc_priv_t)msi->priv;
if (!csc_priv)
{
csc_priv = (video_app_csc_priv_t)STREAM_LIBC_ZALLOC(sizeof(struct video_app_csc_priv));
msi->priv = (void *)csc_priv;
msi->action = (msi_action)video_app_csc_msi_action;
csc_priv->csc_dev = (struct csc_device *)dev_get(HG_CSC_DEVID);
csc_priv->msi = msi;
csc_priv->input_format = input_format;
csc_priv->output_format = output_format;
csc_priv->width = width;
csc_priv->height = height;
csc_priv->hardware_ready = 1;
fbpool_init(&csc_priv->tx_pool, CSC_MSI_TX_NUM);
for(int i = 0; i < CSC_MSI_TX_NUM; i++) {
/* 预分配 CSC 输出的 YUV420P 格式的空间 */
uint8_t *csc_output_addr = (uint8_t*)STREAM_MALLOC(csc_priv->width*csc_priv->height*3/2);
if (!csc_output_addr) {
return ;
}
sys_dcache_invalid_range((uint32_t*)csc_output_addr, csc_priv->width*csc_priv->height*3/2);
struct yuv_arg_s * yuv_msg = (struct yuv_arg_s *)STREAM_LIBC_ZALLOC(sizeof(struct yuv_arg_s));
if (!yuv_msg) {
return ;
}
FBPOOL_SET_INFO(&csc_priv->tx_pool, i, csc_output_addr, csc_priv->width*csc_priv->height*3/2, yuv_msg);
}
csc_request_irq(csc_priv->csc_dev, CSC_DONE_IRQ, (csc_irq_hdl )&csc_irq_done, (uint32)csc_priv);
OS_WORK_INIT(&csc_priv->work, video_app_csc_msi_work, 0);
os_run_work_delay(&csc_priv->work, 1);
msi->enable = 1;
}
else
{
msi->name = NULL;
msi_destroy(msi);
os_sleep_ms(1);
goto video_app_csc_msi_init_start;
}
}

View File

@@ -0,0 +1,15 @@
#ifndef __VIDEO_APP_CSC_MSI_H_
#define __VIDEO_APP_CSC_MSI_H_
#include "hal/csc.h"
#include "dev/csc/hgcsc.h"
#define LV_HW_CSC_2_VIDEO_DISABLE 0
#define LV_REFRESH_LINE_BUFFER 1 // 行缓存刷新模式
#define LV_REFRESH_FULL_SCREEN 2 // 整帧直接渲染模式
#define LVGL_HW_CSC_2_LCD_VIDEO LV_HW_CSC_2_VIDEO_DISABLE
void video_app_csc_msi_init(const char* csc_msi_name, uint32_t input_format, uint32_t output_format, uint32_t width, uint32_t height);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#ifndef __VIDEO_APP_H264_MSI_H
#define __VIDEO_APP_H264_MSI_H
#include "lib/multimedia/msi.h"
struct msi *h264_msi_init();
struct msi *h264_msi_init_with_mode(uint32_t drv1_from, uint32_t drv1_w, uint32_t drv1_h, uint32_t drv2_from, uint32_t drv2_w, uint32_t drv2_h);
struct msi *h264_msi_init_with_mode_for_264wq(uint32_t drv1_from, uint16_t drv1_w, uint16_t drv1_h);
#endif

View File

@@ -0,0 +1,598 @@
/*
在硬件初始化后,这里就是相关video的接口
*/
#include "video_app_usb_msi.h"
#include "stream_frame.h"
#include "utlist.h"
#include "usbh_video.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "sysevt_usb/sysevt_usb.h"
#define MAX_UVC_APP 10
#define UVC_YUV422_MJPEG_FORMAT 0 /* 对于 U、V 量化表单独指向的 Mjpeg 图片,需要软件修改指向同一份量化表 (Mjpeg 硬件解码需要) */
// 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 UVC_PSRAM_MALLOC_SIZE_INIT (100 * 1024)
#if UVC_YUV422_MJPEG_FORMAT
// 获取jpeg的w和h,没有做太多容错,所以尽量给的是正确的jpeg,否则可能异常
#define GET_16(p) (((p)[0] << 8) | (p)[1])
static int parse_SOF(uint8_t *d, uint32_t *w, uint32_t *h)
{
if (d[0] != 8)
{
printf("Invalid precision %d in SOF0\n", d[0]);
return -1;
}
*h = GET_16(d + 1);
*w = GET_16(d + 3);
*(d+14) = 0x1; /* 对于 U、V 量化表单独指向的 Mjpeg 图片,需要软件修改指向同一份量化表 */
return 0;
}
static int parse_jpg(uint8_t *jpg_buf, uint32_t maxsize, uint32_t *w, uint32_t *h)
{
uint8_t *buf = jpg_buf;
uint8_t EOI_flag = 0;
uint32_t i = 0;
int res = 1;
int blen = 0;
for (i = 0; i < maxsize; i += blen + 2)
{
if (buf[i] != 0xFF)
{
printf("usb Found %02X at %d, expecting FF\n", buf[i], i);
goto parse_jpg_end;
}
while (buf[i + 1] == 0xFF)
++i;
if (buf[i + 1] == 0xD8)
blen = 0;
else
blen = GET_16(buf + i + 2);
switch (buf[i + 1])
{
case 0xDB: /* Quantization Table */
break;
case 0xC0: /* Start of Frame */
parse_SOF(buf + i + 4, w, h);
res = 0;
// printf("w:%d\th:%d\n",*w,*h);
break;
case 0xC4: /* Huffman Table */
break;
case 0xDD: /* DRI */
break;
case 0xDA: /* Start of Scan */
goto parse_jpg_end;
}
}
parse_jpg_end:
if (!res) {
for (i = maxsize - 1; i >= maxsize - 16; i--)
{
if (buf[i] != 0xFF) {
// os_printf("EOI Found %02X at %d, expecting FF\n", buf[i], i);
continue;
}
if ((i < maxsize - 1) && (buf[i + 1] == 0xD9)) {
EOI_flag = 1;
break;
}
}
}
if ((!res) && (!EOI_flag)) {
res = 1;
os_printf("EOI No found FF D9\n");
}
return res;
}
#endif
static void msi_get_usb_psram_thread(void *d)
{
// 这里为了让usb那边初始化好对应的空间
os_printf("%s:%d\n", __FUNCTION__, __LINE__);
struct msi *msi = (struct msi *)d;
struct list_head *get_f;
UVC_MANAGE *uvc_message = NULL;
UVC_BLANK *uvc_b;
struct framebuff *fb = NULL;
struct uvc_msi_s *uvc = (struct uvc_msi_s *)msi->priv;
// uint8_t *buf;
uint32_t irq_time = 0;
uint8_t *malloc_buf = NULL;
uint32_t current_photo_size = 0;
uint32_t current_photo_max_size;
uint8_t exit_err = 0;
uint32_t usb_time_out = 0;
uint32_t usb_dma_irq_count = 0;
uint32_t ctlnum = 0;
uint32_t offset = 0;
uint32_t stop_flag = 0;
uint32_t flags;
uint32_t err;
uint8_t uvc_fps = 0;
uint32_t last_record_time = 0;
switch ((uvc->uvc_format >> 8) & 0xFF)
{
case F_JPG:
os_printf("F_JPG DETECT\n");
current_photo_max_size = 80 * 1024;
ctlnum = 30;
break;
case F_H264:
os_printf("F_H264 DETECT\n");
current_photo_max_size = 180 * 1024;
ctlnum = 200;
break;
default:
current_photo_max_size = 80 * 1024;
ctlnum = 30;
break;
}
system_event_usbh_video_new_event(uvc->func->dev_num, SYSEVT_USB_DEVICE_CONNECT);
while (1)
{
uvc_get_psram_again:
malloc_buf = NULL;
flags = disable_irq();
get_f = uvc->func->get_frame(uvc->priv);
enable_irq(flags);
// 如果其他任务通知,可以在这里判断,然后退出
os_event_wait(&uvc->evt, UVC_TASK_STOP, (uint32 *)&stop_flag, OS_EVENT_WMODE_OR, 0);
if (stop_flag)
{
os_printf("%s %d\n",__FUNCTION__,__LINE__);
goto uvc_get_psram_err_deal;
}
if (usb_time_out > 500)
{
if (usb_dma_irq_count == *(uvc->p_usb_dma_irq_time))
{
os_printf("%s:%d\n", __FUNCTION__, __LINE__);
exit_err = 1;
goto uvc_get_psram_err_deal;
}
usb_dma_irq_count = *(uvc->p_usb_dma_irq_time);
usb_time_out = 0;
}
if (!get_f)
{
os_sleep_ms(1);
usb_time_out++;
goto uvc_get_psram_again;
}
usb_time_out = 0;
err = 0;
offset = 0;
current_photo_size = 0;
flags = disable_irq();
uvc_message = list_entry(get_f, UVC_MANAGE, list);
uvc->func->set_frame_using(uvc_message);
enable_irq(flags);
while ((uvc_message->frame_end != UVC_DEVICE_FRAME_ERROR) && ((list_empty(&uvc_message->list) != TRUE) || (uvc_message->frame_end == UVC_DEIVCE_FRAME_NOT_END)))
{
if (stop_flag)
{
goto uvc_get_psram_err_deal;
}
flags = disable_irq();
uvc_b = list_entry(uvc_message->list.prev, UVC_BLANK, list);
enable_irq(flags);
if (uvc_b && uvc_b->busy == 2)
{
// 数据头,所以要申请一帧数据
if (uvc_b->blank_loop == 0)
{
// 获取是否有节点,有节点的话,才可以填充数据
fb = fbpool_get(&uvc->tx_pool, 0, uvc->msi);
// os_printf("malloc fb:%X\n",fb);
}
// 没有帧,那么只能将bank删除,应该是rtsp处理速度不够
if (fb)
{
// 会申请一个连续的空间有可能空间不够,后续会重新申请,如果不够,则丢帧
if (!malloc_buf)
{
_os_printf("#");
malloc_buf = (uint8_t *)STREAM_MALLOC(uvc->malloc_max_size);
if(malloc_buf)
{
sys_dcache_invalid_range((uint32_t*)malloc_buf, uvc->malloc_max_size);
}
uvc->malloc_count_near++;
// os_printf("uvc->malloc_max_size:%d\tmalloc_buf:%X\n",uvc->malloc_max_size,malloc_buf);
}
if (malloc_buf)
{
// 判断一下偏移后是否大于当前最大的size,如果是,则需要重新去申请空间了,这种情况申请大于20%,自动调整,则增加10%
if (offset + (uvc_b->blank_len-uvc_b->re_space) > uvc->malloc_max_size)
{
_os_printf("{}\n");
uint32_t last_malloc_size = uvc->malloc_max_size;
uvc->malloc_max_size = (uint32_t)((offset + uvc_b->blank_len) << 1);
uvc->malloc_time = os_jiffies();
uvc->malloc_count_near = 0;
uint8_t *m_buf = (void *)STREAM_MALLOC(uvc->malloc_max_size);
os_printf("uvc->malloc_max_size:%d\t%X\n", uvc->malloc_max_size, m_buf);
if (m_buf)
{
sys_dcache_invalid_range((uint32_t*)m_buf, uvc->malloc_max_size);
hw_memcpy_no_cache(m_buf, malloc_buf, offset);
STREAM_FREE(malloc_buf);
malloc_buf = m_buf;
}
else
{
uvc->malloc_max_size = last_malloc_size;
if (malloc_buf)
{
STREAM_FREE(malloc_buf);
malloc_buf = NULL;
}
}
}
}
// 申请不到空间,则要去移除这张图片,并且释放data_s,但是要这张图片接收完成才能进行操作
if (!malloc_buf)
{
err = 1;
_os_printf("[");
if (fb)
{
msi_delete_fb(NULL, fb);
fb = NULL;
}
}
else
{
// 申请空间
sys_dcache_clean_range((uint32_t*)uvc_b->buf_ptr, (uvc_b->blank_len - uvc_b->re_space));
hw_memcpy_no_cache(malloc_buf + offset, uvc_b->buf_ptr, (uvc_b->blank_len - uvc_b->re_space));
offset += (uvc_b->blank_len - uvc_b->re_space);
}
}
else
{
_os_printf("=");
}
uvc->func->free_node(uvc->priv, (struct list_head *)uvc_b);
uvc_b = NULL;
irq_time = 0;
}
else
{
// 释放cpu
os_sleep_ms(1);
irq_time++;
if (irq_time > 500)
{
_os_printf("&");
os_printf("uvc_b:%X\n", uvc_b);
if (uvc_b)
{
os_printf("busy:%d\n", uvc_b->busy);
}
os_printf("uvc_message:%X\t%d\n", uvc_message, uvc_message->frame_end);
if (usb_dma_irq_count == *(uvc->p_usb_dma_irq_time))
{
exit_err = 1;
os_printf("%s usb maybe disconnect\n", __FUNCTION__);
goto uvc_get_psram_err_deal;
}
usb_dma_irq_count = *(uvc->p_usb_dma_irq_time);
irq_time = 0;
}
}
}
if (err || !malloc_buf)
{
os_printf("%s %d\n",__FUNCTION__,__LINE__);
goto uvc_get_psram_err_deal;
}
if (uvc_message->frame_end == UVC_DEVICE_FRAME_ERROR || uvc_message->frame_end == UVC_DEIVCE_FRAME_NOT_END)
{
// os_printf("%s %d\n",__FUNCTION__,__LINE__);
goto uvc_get_psram_err_deal;
}
else
{
// 设置状态,等于发送frame
if (fb)
{
fb->len = uvc_message->frame_len;
fb->data = (uint8_t *)malloc_buf;
current_photo_size = uvc_message->frame_len;
if (current_photo_max_size < current_photo_size)
{
current_photo_max_size = current_photo_size;
}
// os_printf("current_photo_size:%d\tcurrent_photo_max_size:%d\n",current_photo_size,current_photo_max_size);
// os_printf("msi:%X\tname:%s\tfb:%X\n",msi,msi->name,fb);
fb->mtype = (uvc->uvc_format >> 8) & 0xFF;
fb->stype = uvc->uvc_format & 0xFF;
fb->srcID = FRAMEBUFF_SOURCE_USB;
fb->time = os_jiffies();
fb->datatag ++;
// os_printf("fb mtype:%x stype:%x\n",fb->mtype,fb->stype);
#if UVC_YUV422_MJPEG_FORMAT
if (fb->mtype == F_JPG) {
int res = 0;
uint32_t decode_w = 0;
uint32_t decode_h = 0;
res = parse_jpg(fb->data, fb->len, &decode_w, &decode_h);
if (!res)
{
sys_dcache_invalid_range((uint32_t *)fb->data, fb->len);
msi_output_fb(msi, fb);
}
else
{
msi_delete_fb(msi, fb);
}
} else {
msi_output_fb(msi, fb);
}
#else
msi_output_fb(msi, fb);
#endif
uvc_fps ++;
if(os_jiffies() - last_record_time >= 1000)
{
os_printf("[%s] current fps:%d\n", msi->name, uvc_fps);
uvc_fps = 0;
last_record_time = os_jiffies();
}
fb = NULL;
malloc_buf = NULL;
}
uvc->func->set_frame_idle(uvc_message);
}
if (uvc->malloc_count_near < 2)
{
os_printf("malloc_max_size:%d\tcurrent_photo_max_size:%d\n", uvc->malloc_max_size, current_photo_max_size);
}
// 这里用算法开始对malloc_max_size进行计算,尽量贴近申请的空间大小,在申请超过30次,并且没有调整过后,才考虑将
// 申请最大内存减少到合适位置,然后重新统计
if (uvc->malloc_count_near > ctlnum && current_photo_max_size * 1.1 < uvc->malloc_max_size)
{
// 重新调整一下
uvc->malloc_max_size = current_photo_max_size * 1.1;
current_photo_max_size = 0;
uvc->malloc_count_near = 0;
}
// 重新统计一下
else if (uvc->malloc_count_near > ctlnum)
{
uvc->malloc_count_near = 0;
current_photo_max_size = 0;
}
// 一直要保证图片有10%的冗余度来容纳下一帧可能变化太大的情况,如果这一次调整了,则malloc_count_near清0
else if (current_photo_max_size * 1.1 > uvc->malloc_max_size)
{
uvc->malloc_max_size = current_photo_max_size * 1.15;
uvc->malloc_count_near = 0;
}
// 重新获取图片
goto uvc_get_psram_again;
// 异常处理,可能是空间不够之类
uvc_get_psram_err_deal:
_os_printf("!");
uvc->func->del_frame(uvc->priv, get_f);
uvc->func->set_frame_idle(uvc_message);
if (fb)
{
msi_delete_fb(NULL, fb);
fb = NULL;
}
if (malloc_buf)
{
STREAM_FREE(malloc_buf);
malloc_buf = NULL;
}
// 如果有必要,还要看看是不是usb断开,是不是应该退出线程
// 如果其他任务通知,可以在这里判断,然后退出
if (stop_flag || exit_err)
{
break;
}
}
system_event_usbh_video_new_event(uvc->func->dev_num, SYSEVT_USB_DEVICE_DISCONNECT);
// 发送一个evt,通知线程需要退出了
os_event_set(&uvc->evt, UVC_TASK_EXIT, NULL);
// 代表任务退出,并且将流也退出
msi_destroy(msi);
// 标志一下退出标志
return;
}
static int32_t uvc_host_action(struct msi *msi, uint32_t cmd_id, uint32_t param1, uint32_t param2)
{
int32_t ret = RET_OK;
struct uvc_msi_s *uvc = (struct uvc_msi_s *)msi->priv;
switch (cmd_id)
{
case MSI_CMD_POST_DESTROY:
{
if(uvc) {
fbpool_destroy(&uvc->tx_pool);
os_event_del(&uvc->evt);
uvc->func->deinit_cb(uvc->priv);
STREAM_LIBC_FREE(uvc);
msi->priv = NULL;
}
}
break;
case MSI_CMD_PRE_DESTROY:
{
}
break;
case MSI_CMD_FREE_FB:
{
struct framebuff *fb = (struct framebuff *)param1;
// os_printf("fb:%X\n",fb);
// os_printf("uvc2:%X\n",uvc);
if (fb->data)
{
STREAM_FREE(fb->data);
fb->data = NULL;
}
fbpool_put(&uvc->tx_pool, fb);
// 不需要内核去释放fb
ret = RET_OK + 1;
}
break;
}
return ret;
}
// usb host枚举成功后会执行的这个函数
/**********************************************************************
* 由于这个是硬件的msi,所以只是绑定发送到中间流,然后让中间流去进行转发,这样
* 外部就不需要等到usb插入才能使用了
***********************************************************************/
extern struct list_head *uvc_device_user_get_frame(void* device);
extern void uvc_device_user_free_blank_node(void* device, struct list_head *del);
extern void uvc_device_user_del_frame(void* device, void *get_f);
extern void uvc_device_user_set_frame_using(void *uvc_msg_frame);
extern void uvc_device_user_set_frame_idle(void *uvc_msg_frame);
extern void* register_uvc_device(uint8_t dev_num, uint8_t blank_num, uint32_t blank_len, void* p_dev);
extern bool unregister_uvc_device(void* device);
/* 一个UVC镜头对应一个结构体 */
struct usbh_video_priv_func uvc_host_stream_1 =
{
.get_frame = uvc_device_user_get_frame,
.free_node = uvc_device_user_free_blank_node,
.del_frame = uvc_device_user_del_frame,
.set_frame_using = uvc_device_user_set_frame_using,
.set_frame_idle = uvc_device_user_set_frame_idle,
.deinit_cb = unregister_uvc_device,
};
struct usbh_video_priv_func uvc_host_stream_2 =
{
.get_frame = uvc_device_user_get_frame,
.free_node = uvc_device_user_free_blank_node,
.del_frame = uvc_device_user_del_frame,
.set_frame_using = uvc_device_user_set_frame_using,
.set_frame_idle = uvc_device_user_set_frame_idle,
.deinit_cb = unregister_uvc_device,
};
void usbh_video_enum_finish_init(const char *video_dev_name, uint32_t uvc_format ,struct usbh_video_priv_func* func, int* p_usb_dma_irq_time)
{
struct msi *msi = NULL;
usb_host_enum_finish_init_start:
msi = msi_new(video_dev_name, 0, NULL);
// 申请不成功,有两种可能:1、空间不够 2、正在删除,没有办法,需要等待资源释放
if (!msi)
{
return;
}
struct uvc_msi_s *uvc = (struct uvc_msi_s *)msi->priv;
// 没有创建过,则创建新的
if (!uvc)
{
uvc = (struct uvc_msi_s *)STREAM_LIBC_ZALLOC(sizeof(struct uvc_msi_s));
msi->priv = (void *)uvc;
uvc->msi = msi;
msi->action = (msi_action)uvc_host_action;
os_event_init(&uvc->evt);
os_printf("uvc1:%X\n", uvc);
fbpool_init(&uvc->tx_pool, MAX_UVC_APP);
uvc->uvc_format = uvc_format;
uvc->malloc_max_size = UVC_PSRAM_MALLOC_SIZE_INIT;
uvc->malloc_count_near = 0;
uvc->malloc_time = os_jiffies();
uvc->func = func;
uvc->p_usb_dma_irq_time = p_usb_dma_irq_time;
uvc->priv = register_uvc_device(uvc->func->dev_num, 8, 16*1024, uvc->func->p_dev);
msi->enable = 1;
// 默认绑定到一个usb的中间流,外部调用就使用ROUTE_USB这个去接收
msi_add_output(msi, NULL, ROUTE_USB);
// 创建任务
os_task_create(video_dev_name, msi_get_usb_psram_thread, (void *)msi, OS_TASK_PRIORITY_NORMAL + 2, 0, NULL, 2048);
}
// 已经被创建了,需要等待资源释放完成后才能重新创建硬件了?或者检测到线程退出了,就将msi->name设置为NULL
// 因为这个时候代表上一次线程已经完成了,msi会后面自动释放?
else
{
// 发送一个evt,通知线程需要退出了
os_event_set(&uvc->evt, UVC_TASK_STOP, NULL);
os_printf("usbh_video_enum_finish_init:uvc wait task exit\n");
// 等到线程已经退出
os_event_wait(&uvc->evt, UVC_TASK_EXIT, NULL, OS_EVENT_WMODE_OR, -1);
os_printf("usbh_video_enum_finish_init:uvc task exit\n");
// 这里设置name为NULL,是为了msi_new可以立刻申请,因为这个时候上一次的uvc线程已经退出了,所以资源等待后面慢慢释放,不需要管了
// 前提没有msi绑定uvc这个msi,所以这个一定要注意
msi->name = NULL;
msi_destroy(msi);
// 重新msi_new,然后重新跑流程,理论一定可以申请成功
goto usb_host_enum_finish_init_start;
}
}

View File

@@ -0,0 +1,56 @@
#ifndef __VIDEO_APP_USB_MSI_H
#define __VIDEO_APP_USB_MSI_H
#include "basic_include.h"
#include "lib/multimedia/msi.h"
#include "lib/video/uvc/rtt_uvc_host.h"
enum uvc_app_usb
{
UVC_TASK_EXIT = BIT(0),
UVC_TASK_STOP = BIT(1),
};
typedef struct list_head* (*usbh_video_get_frame)(void *dev);
typedef void (*usbh_video_free_node)(void *dev, struct list_head* del);
typedef void (*usbh_video_del_frame)(void *dev, void *get_f);
typedef void (*usbh_video_set_frame_using)(void *frame);
typedef void (*usbh_video_set_frame_idle)(void*frame);
typedef int (*usbh_video_get_frame_blank_node_num)(void *dev, struct list_head *head);
typedef bool (*usbh_video_deinit_cb)(void* dev);
struct usbh_video_priv_func
{
usbh_video_get_frame get_frame;
usbh_video_free_node free_node;
usbh_video_del_frame del_frame;
usbh_video_set_frame_using set_frame_using;
usbh_video_set_frame_idle set_frame_idle;
usbh_video_get_frame_blank_node_num get_frame_node_num;
usbh_video_deinit_cb deinit_cb;
int dev_num;
void *p_dev;
};
struct uvc_msi_s
{
struct os_task task;
struct msi *msi;
struct os_event evt;
struct fbpool tx_pool;
uint32_t malloc_max_size; // 记录当前应该申请的最大size
uint32_t current_photo_max_size;
// 记录的时间与次数主要为了动态修改malloc_max_size,尽可能节约空间
uint32_t malloc_time; // 记录malloc_max_size修改的size
uint32_t malloc_count_near; // 记录从记录malloc_max_size开始计算,申请的次数
uint32_t uvc_format;
struct usbh_video_priv_func *func;
int* p_usb_dma_irq_time;
void *priv;
};
void usbh_video_enum_finish_init(const char *video_dev_name, uint32_t uvc_format ,struct usbh_video_priv_func* func, int* p_usb_dma_irq_time);
#endif

View File

@@ -0,0 +1,14 @@
#ifndef __VIDEO_MSI_H
#define __VIDEO_MSI_H
typedef uint8_t (*scale1_filter_fn)(void* f, uint8_t stype);
typedef uint8_t (*gen420_filter_fn)(void *fb,uint8_t src_id);
struct msi *auto_jpg_msi_init(const char *auto_jpg_name, uint8_t which_jpg, uint8_t src_from);
struct msi *auto_h264_msi_init(const char *auto_h264_name, uint8_t src_from0, uint16_t w0, uint16_t h0, uint8_t src_from1, uint16_t w1, uint16_t h1);
struct msi *thumb_over_dpi_msi_init(const char *msi_name, uint8_t jpg_type, uint8_t stype, uint32_t magic);
struct msi *jpg_concat_msi_init_start(uint32_t jpgid, uint16_t w, uint16_t h, uint16_t *filter_type, uint8_t src_from, uint8_t run);
struct msi *mp4_thumb_msi_init(const char *filename, uint8_t srcID, uint8_t filter);
void usb_to_recode_init(uint8_t jpg_num);
struct msi *h264_msi_init_with_mode(uint32_t drv1_from, uint16_t drv1_w, uint16_t drv1_h, uint16_t drv2_from, uint16_t drv2_w, uint16_t drv2_h);
struct msi *gen420_jpg_msi_init(const char *msi_name, uint8_t which_jpg, uint8_t recv_type, uint8_t lock_value, uint8_t queue_value, uint16_t *filter_type, gen420_filter_fn fn);
struct msi *scale1_jpg_msi_init(const char *msi_name, uint8_t which_jpg, uint8_t recv_type, uint8_t lock_value, uint16_t force_type, scale1_filter_fn fn, uint8_t force_node);
#endif