Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources
This commit is contained in:
186
sdk/app/video_app/auto_h264_msi.c
Normal file
186
sdk/app/video_app/auto_h264_msi.c
Normal 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;
|
||||
}
|
||||
232
sdk/app/video_app/auto_jpg_msi.c
Normal file
232
sdk/app/video_app/auto_jpg_msi.c
Normal 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;
|
||||
}
|
||||
102
sdk/app/video_app/avi_thumb_msi.c
Normal file
102
sdk/app/video_app/avi_thumb_msi.c
Normal 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;
|
||||
}
|
||||
402
sdk/app/video_app/file_common_api.c
Normal file
402
sdk/app/video_app/file_common_api.c
Normal 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长度块表示结束
|
||||
}
|
||||
32
sdk/app/video_app/file_common_api.h
Normal file
32
sdk/app/video_app/file_common_api.h
Normal 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
|
||||
298
sdk/app/video_app/file_thumb.c
Normal file
298
sdk/app/video_app/file_thumb.c
Normal 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);
|
||||
}
|
||||
}
|
||||
39
sdk/app/video_app/file_thumb.h
Normal file
39
sdk/app/video_app/file_thumb.h
Normal 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
|
||||
371
sdk/app/video_app/gen420_hardware_msi.c
Normal file
371
sdk/app/video_app/gen420_hardware_msi.c
Normal 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;
|
||||
}
|
||||
43
sdk/app/video_app/gen420_hardware_msi.h
Normal file
43
sdk/app/video_app/gen420_hardware_msi.h
Normal 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
|
||||
417
sdk/app/video_app/gen420_jpg_msi.c
Normal file
417
sdk/app/video_app/gen420_jpg_msi.c
Normal 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;
|
||||
}
|
||||
464
sdk/app/video_app/jpg_concat_msi.c
Normal file
464
sdk/app/video_app/jpg_concat_msi.c
Normal 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;
|
||||
}
|
||||
87
sdk/app/video_app/jpg_concat_msi.h
Normal file
87
sdk/app/video_app/jpg_concat_msi.h
Normal 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
|
||||
191
sdk/app/video_app/jpg_thumb_msi.c
Normal file
191
sdk/app/video_app/jpg_thumb_msi.c
Normal 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;
|
||||
}
|
||||
102
sdk/app/video_app/mp4_thumb_msi.c
Normal file
102
sdk/app/video_app/mp4_thumb_msi.c
Normal 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;
|
||||
}
|
||||
591
sdk/app/video_app/scale1_jpg_msi.c
Normal file
591
sdk/app/video_app/scale1_jpg_msi.c
Normal 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;
|
||||
}
|
||||
537
sdk/app/video_app/thumb_over_dpi_msi.c
Normal file
537
sdk/app/video_app/thumb_over_dpi_msi.c
Normal 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
|
||||
191
sdk/app/video_app/usb_jpg_thumb_msi.c
Normal file
191
sdk/app/video_app/usb_jpg_thumb_msi.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
472
sdk/app/video_app/usb_thumb_over_dpi_msi.c
Normal file
472
sdk/app/video_app/usb_thumb_over_dpi_msi.c
Normal 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
|
||||
85
sdk/app/video_app/usb_to_recode.c
Normal file
85
sdk/app/video_app/usb_to_recode.c
Normal 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);
|
||||
}
|
||||
}
|
||||
27
sdk/app/video_app/video_app.h
Normal file
27
sdk/app/video_app/video_app.h
Normal 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
|
||||
249
sdk/app/video_app/video_app_csc_msi.c
Normal file
249
sdk/app/video_app/video_app_csc_msi.c
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
15
sdk/app/video_app/video_app_csc_msi.h
Normal file
15
sdk/app/video_app/video_app_csc_msi.h
Normal 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
|
||||
1428
sdk/app/video_app/video_app_h264_msi.c
Normal file
1428
sdk/app/video_app/video_app_h264_msi.c
Normal file
File diff suppressed because it is too large
Load Diff
7
sdk/app/video_app/video_app_h264_msi.h
Normal file
7
sdk/app/video_app/video_app_h264_msi.h
Normal 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
|
||||
598
sdk/app/video_app/video_app_usb_msi.c
Normal file
598
sdk/app/video_app/video_app_usb_msi.c
Normal 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;
|
||||
}
|
||||
}
|
||||
56
sdk/app/video_app/video_app_usb_msi.h
Normal file
56
sdk/app/video_app/video_app_usb_msi.h
Normal 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
|
||||
14
sdk/app/video_app/video_msi.h
Normal file
14
sdk/app/video_app/video_msi.h
Normal 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
|
||||
Reference in New Issue
Block a user