455 lines
14 KiB
C
455 lines
14 KiB
C
#include "app_config.h"
|
||
#ifdef CONFIG_UI_ENABLE //上电执行则打开app_config.h TCFG_DEMO_UI_RUN = 1
|
||
|
||
#include "device/device.h"//u8
|
||
#include "storage_device.h"//SD
|
||
#include "server/video_dec_server.h"//fopen
|
||
#include "system/includes.h"//GPIO
|
||
#include "lcd_drive.h"
|
||
#include "sys_common.h"
|
||
#include "yuv_soft_scalling.h"
|
||
#include "asm/jpeg_codec.h"
|
||
#include "get_yuv_data.h"
|
||
#include "lcd_config.h"
|
||
#include "asm/includes.h"
|
||
#include "ui_demo.h"
|
||
#include "video/camera_effect.h"
|
||
#include "asm/jpeg_encoder.h"
|
||
#include "lcd_data_driver.h"
|
||
|
||
|
||
|
||
/************视频部分*将摄像头数据显示到lcd上**********************************/
|
||
/* 开关打印提示 */
|
||
#if 1
|
||
#define log_info(x, ...) printf("\n[camera_to_lcd_test]>>>>>>>>>>>>>>>>>>>###" x " \n", ## __VA_ARGS__)
|
||
#else
|
||
#define log_info(...)
|
||
#endif
|
||
|
||
struct scale_img {
|
||
int yuv_pixformat;
|
||
int in_w;
|
||
int in_h;
|
||
u8 *yuv;
|
||
u8 *yuv1;
|
||
u8 *y_in;
|
||
u8 *u_in;
|
||
u8 *v_in;
|
||
int out_w;
|
||
int out_h;
|
||
u8 *y_out;
|
||
u8 *u_out;
|
||
u8 *v_out;
|
||
u8 *line_buf;
|
||
int line_buf_size;
|
||
u8 line;
|
||
u8 jpg_q;
|
||
u32 jpg_buf_size;
|
||
u8 *jpg_buf;
|
||
struct jpeg_encoder_fd *jpg;
|
||
struct YUV_frame_data *input_frame;
|
||
};
|
||
|
||
static struct scale_img *hdl;
|
||
|
||
|
||
#if PROCESS_EFFECT
|
||
struct camera_effect_config eft_cfg = {0};//配置变量
|
||
effect_mode_t kaleidoscope_mode = EM_END;//万花筒样式
|
||
#endif
|
||
|
||
|
||
|
||
u8 get_lcd_turn_mode(void);
|
||
|
||
static void Calculation_frame(void)
|
||
{
|
||
static u32 tstart = 0, tdiff = 0;
|
||
static u8 fps_cnt = 0;
|
||
|
||
fps_cnt++ ;
|
||
|
||
if (!tstart) {
|
||
tstart = timer_get_ms();
|
||
} else {
|
||
tdiff = timer_get_ms() - tstart;
|
||
|
||
if (tdiff >= 1000) {
|
||
printf("\n [MSG]fps_count = %d\n", fps_cnt * 1000 / tdiff);
|
||
tstart = 0;
|
||
fps_cnt = 0;
|
||
}
|
||
}
|
||
}
|
||
/********保存JPG图片到SD卡中************/
|
||
static void save_jpg_ontime(u8 *buf, u32 len)
|
||
{
|
||
if (storage_device_ready()) {//自动重命名文件数量限制配置搜索 FILE_AUTO_RENAME_NUM
|
||
char file_name[64];//定义路径存储
|
||
snprintf(file_name, 64, CONFIG_ROOT_PATH"JPG/JPG****.JPG");
|
||
FILE *fd = fopen(file_name, "wb+");
|
||
printf("[msg]>>>>>>>>>>>file_name=%s", file_name);
|
||
fwrite(buf, 1, len, fd);
|
||
fclose(fd);
|
||
log_info("JPG save ok name=JPG\r\n");
|
||
}
|
||
}
|
||
/********保存RGB数据到SD卡中************/
|
||
static void get_RGB_save_to_SD(char *RGB, u32 len)
|
||
{
|
||
if (storage_device_ready()) {
|
||
char file_name[64];//定义路径存储
|
||
snprintf(file_name, 64, CONFIG_ROOT_PATH"rgb/RGB***.bin");
|
||
FILE *fd = fopen(file_name, "wb+");
|
||
fwrite(RGB, 1, len, fd);
|
||
fclose(fd);
|
||
log_info("RGB save ok name=RGB\r\n");
|
||
}
|
||
}
|
||
/********保存YUV数据到SD卡中************/
|
||
static void save_YUV_date_ontime(u8 *buf, u32 len)
|
||
{
|
||
if (storage_device_ready()) {
|
||
char file_name[64];//定义路径存储
|
||
snprintf(file_name, 64, CONFIG_ROOT_PATH"YUV/YUV***.bin");
|
||
FILE *fd = fopen(file_name, "wb+");
|
||
fwrite(buf, 1, len, fd);
|
||
fclose(fd);
|
||
log_info("YUV save ok name=YUV\r\n");
|
||
}
|
||
}
|
||
|
||
/********读取JPG数据并保存在SD卡中该数据经过缩放保存大小1280*720********/
|
||
/* #define yuv_out_w 1280 */
|
||
/* #define yuv_out_h 720 */
|
||
/* #define in_w 640 */
|
||
/* #define in_h 480 */
|
||
|
||
#define IN_W 640 //图像输入尺寸 宽
|
||
#define IN_H 480 //图像输入尺寸 高
|
||
#define AIM_W 4800 //图像输出尺寸 宽
|
||
#define AIM_H 2400 //图像输出尺寸 宽
|
||
#define CALCULATE_H 16 //图像每次处理行数 //只能处理16行的整数倍
|
||
#define CNT (AIM_H/CALCULATE_H) //累计需要处理次数为输出高/每次行数
|
||
#define YUV_BUF_16_LINE_SIZE (AIM_W*CALCULATE_H*3/2) //一次处理数据需要的内存长度
|
||
#define JPG_BUF_LEN (1*1024*1024) //jpg数据处理需要的内存长度 //需要评估最大内存
|
||
#define JPG_Q 10 //输出jpg的质量 最大16
|
||
|
||
static void get_JPG_save_to_SD(char *yuv_buf, u8 mode)
|
||
{
|
||
int err;
|
||
u8 *ui_data;
|
||
u8 *rgb_buf;
|
||
u8 *yuv_420;
|
||
//1.申请全局结构体内存
|
||
hdl = malloc(sizeof(struct scale_img));
|
||
if (hdl == NULL) {
|
||
printf("[error]>>>>>>>>>>>>malloc fail");
|
||
}
|
||
//2.申请16行buf
|
||
hdl->line_buf = malloc(YUV_BUF_16_LINE_SIZE);
|
||
if (!hdl->line_buf) {
|
||
goto exit;
|
||
}
|
||
//3.申请jpg输入帧结构体
|
||
hdl->input_frame = malloc(sizeof(struct YUV_frame_data));
|
||
if (hdl->input_frame == NULL) {
|
||
goto exit;
|
||
}
|
||
//4.申请jpg_buf用于输出jpg照片
|
||
hdl->jpg_buf = malloc(JPG_BUF_LEN);
|
||
if (hdl->jpg_buf == NULL) {
|
||
goto exit;
|
||
}
|
||
if (mode == PHOTO_MODE_TAKE_BOOTH) {
|
||
//5.申请yuv_buf用于将ui数据放大到摄像头图像大小用于图像合成
|
||
hdl->yuv = malloc(IN_W * IN_H * 2);
|
||
if (hdl->yuv == NULL) {
|
||
goto exit;
|
||
}
|
||
}
|
||
|
||
rgb_buf = hdl->jpg_buf;
|
||
yuv_420 = hdl->jpg_buf + lcd_w * lcd_h * 2;
|
||
|
||
//获取ui保存的大头贴图片
|
||
if (mode == PHOTO_MODE_TAKE_BOOTH) {
|
||
ui_data = get_ui_buf();
|
||
//将ui大头贴数据竖屏的RGB数据转为横屏的RGB数据
|
||
RGB565_Soft_90(0, rgb_buf, ui_data, lcd_w, lcd_h);
|
||
//将RGB转为YUV数据
|
||
rgb565_to_yuv420p(rgb_buf, yuv_420, lcd_h, lcd_w, 1);
|
||
//将屏幕那么大的YUV数据转为摄像头那么大的YUV数据
|
||
YUV420p_Soft_Scaling(yuv_420, hdl->yuv, lcd_h, lcd_w, IN_W, IN_H);
|
||
//进行yuv图像合成处理
|
||
yuv_picture_compose(hdl->yuv, yuv_buf, IN_W, IN_H);
|
||
}
|
||
|
||
//5.配置全局参数配置
|
||
hdl->in_w = IN_W; //配置输入图像yuv420宽
|
||
hdl->in_h = IN_H; //配置输入图像yuv420高
|
||
hdl->out_w = AIM_W; //配置输出图像yuv420宽
|
||
hdl->out_h = AIM_H; //配置输出图像yuv420高
|
||
hdl->line = CALCULATE_H; //配置每次放大行数必须为16的整数倍
|
||
hdl->line_buf_size = YUV_BUF_16_LINE_SIZE; //配置yuv420 16行buf长度
|
||
hdl->jpg_buf_size = JPG_BUF_LEN; //配置jpg内存长度
|
||
hdl->yuv_pixformat = JPG_SAMP_FMT_YUV420; //配置jpg编码数据格式
|
||
hdl->jpg_q = JPG_Q; //配置编码q值
|
||
hdl->input_frame->width = hdl->out_w; //配置jpg编码器输出jpg图像宽
|
||
hdl->input_frame->height = hdl->out_h; //配置jpg编码器输出jpg图像高
|
||
hdl->input_frame->data_height = hdl->line; //配置jpg编码器每次编码行数
|
||
hdl->input_frame->pixformat = JPG_SAMP_FMT_YUV420; //配置jpg编码器编码数据格式
|
||
hdl->jpg = mjpg_image_enc_open(NULL, JPEG_ENC_MANU_IMAGE);
|
||
if (!hdl->jpg) {
|
||
printf("mjpg_image_enc_open err\n");
|
||
goto exit;
|
||
}
|
||
|
||
u32 time1 = timer_get_ms();
|
||
//缩放处理
|
||
for (u16 i = 0; i < CNT; i++) {
|
||
hdl->y_in = yuv_buf;
|
||
hdl->y_out = hdl->line_buf;
|
||
//16行yuv有16行y
|
||
jl_nearest_inter_line(hdl->line, i, hdl->in_w, hdl->in_h, hdl->out_w, hdl->out_h, hdl->y_in, hdl->y_out);
|
||
hdl->u_in = yuv_buf + hdl->in_w * hdl->in_h;
|
||
hdl->u_out = hdl->line_buf + hdl->out_w * hdl->line;
|
||
//16行yuv有8行u
|
||
jl_nearest_inter_line(hdl->line / 2, i, hdl->in_w / 2, hdl->in_h / 2, hdl->out_w / 2, hdl->out_h / 2, hdl->u_in, hdl->u_out);
|
||
hdl->v_in = yuv_buf + hdl->in_w * hdl->in_h * 5 / 4;
|
||
hdl->v_out = hdl->line_buf + hdl->out_w * hdl->line * 5 / 4;
|
||
//16行yuv有8行v
|
||
jl_nearest_inter_line(hdl->line / 2, i, hdl->in_w / 2, hdl->in_h / 2, hdl->out_w / 2, hdl->out_h / 2, hdl->v_in, hdl->v_out);
|
||
//6.每1行MCU(16行像素点)编码一次
|
||
hdl->input_frame->line_num = i * hdl->line;
|
||
hdl->input_frame->y = hdl->y_out;
|
||
hdl->input_frame->u = hdl->u_out;
|
||
hdl->input_frame->v = hdl->v_out;
|
||
err = mjpg_image_enc_start(hdl->jpg, hdl->input_frame, hdl->jpg_buf, hdl->jpg_buf_size, hdl->jpg_q);
|
||
if (err < 0) {
|
||
printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__);
|
||
goto exit;
|
||
}
|
||
}
|
||
|
||
save_jpg_ontime(hdl->jpg_buf, hdl->jpg->file_size);
|
||
printf("[msg]>>>>>>>>>>>hdl->jpg->file_size=%dk", hdl->jpg->file_size / 1024);
|
||
u32 time = timer_get_ms() - time1;
|
||
printf("[msg]>>>>>>>>>>>take_photo time=%dms", time);
|
||
mjpg_image_enc_close(hdl->jpg);
|
||
|
||
exit:
|
||
printf("\n [ERROR] %s -[yuyu] %d\n", __FUNCTION__, __LINE__);
|
||
if (mode == PHOTO_MODE_TAKE_BOOTH) {
|
||
free(hdl->yuv);
|
||
}
|
||
free(hdl->jpg_buf);
|
||
free(hdl->input_frame);
|
||
free(hdl->line_buf);
|
||
free(hdl);
|
||
}
|
||
|
||
#if PROCESS_EFFECT /*万花筒功能*/
|
||
get_kaleidoscope_data(u8 *in, u8 *out, int yuv_in_w, int yuv_in_h)
|
||
{
|
||
eft_cfg.eff_t.w = yuv_in_w;
|
||
eft_cfg.eff_t.h = yuv_in_h;
|
||
eft_cfg.eff_t.wh_mode = 0; // 横屏-0
|
||
eft_cfg.eff_t.mode_full = 0;//暂时没有用到
|
||
eft_cfg.indicator = 1;//标志,不影响数据值,0-src=dst(把原来的数据也改了),1-dst(原来数据不变,dst是处理过的数据)
|
||
eft_cfg.dst_buf = out; //dst
|
||
eft_cfg.mode = kaleidoscope_mode;//样式选择
|
||
|
||
if (!effect_init(&eft_cfg.eff_t, eft_cfg.eff_t.wh_mode, eft_cfg.eff_t.w, eft_cfg.eff_t.h, &eft_cfg.mode)) {
|
||
process_effect(&eft_cfg.eff_t, in, eft_cfg.dst_buf, &eft_cfg.mode, &eft_cfg.indicator);
|
||
effect_free(&eft_cfg.eff_t);
|
||
} else {
|
||
log_e("camera effect init err!\n");
|
||
}
|
||
}
|
||
|
||
|
||
void set_kaleidoscope_mode(u8 demo)
|
||
{
|
||
kaleidoscope_mode = demo;
|
||
}
|
||
#endif //PROCESS_EFFECT
|
||
|
||
/*********摄像头应用部分***********/
|
||
static void test_main(u8 *buf, u32 len, int yuv_in_w, int yuv_in_h)
|
||
{
|
||
#if CONFIG_LCD_QR_CODE_ENABLE
|
||
char *ssid = NULL;
|
||
char *pwd = NULL;
|
||
qr_code_get_one_frame_YUV_420(buf, yuv_in_w, yuv_in_h);
|
||
qr_get_ssid_pwd(&ssid, &pwd);
|
||
printf(">>>>>>>>>>>>>>>sssid = %s, pwd = %s", ssid, pwd);
|
||
#endif //CONFIG_LCD_QR_CODE_ENABLE
|
||
|
||
#if PROCESS_EFFECT /*万花筒功能*/
|
||
u8 *baddr = NULL;
|
||
if (kaleidoscope_mode != EM_END) {
|
||
baddr = (u8 *)malloc(len);
|
||
get_kaleidoscope_data(buf, baddr, yuv_in_w, yuv_in_h);
|
||
buf = baddr;
|
||
if (get_take_photo_status() == PHOTO_MODE_TAKE_KALEIDOSCOPE) {
|
||
get_JPG_save_to_SD(buf, PHOTO_MODE_TAKE_KALEIDOSCOPE);
|
||
set_take_photo_ok();
|
||
}
|
||
}
|
||
#endif //PROCESS_EFFECT
|
||
|
||
|
||
u8 mode = get_take_photo_status();
|
||
if (mode != 0 && mode != PHOTO_MODE_TAKE_KALEIDOSCOPE) {
|
||
get_JPG_save_to_SD(buf, PHOTO_MODE_TAKE_BOOTH); //拍照大头贴 | 多图像合成,保存成JPG的方法一样
|
||
set_take_photo_ok();
|
||
}
|
||
switch (mode) {
|
||
case PHOTO_MODE_TAKE_BOOTH://拍照大头贴 | 多图像合成,保存成JPG的方法一样
|
||
printf(">>>>>>>PHOTO_MODE_TAKE_BOOTH<<<<<<<<\n");
|
||
break;
|
||
case PHOTO_MODE_TAKE_MULTIPLE_1://高二等分 图像合成模式
|
||
printf(">>>>>>>PHOTO_MODE_TAKE_MULTIPLE_1<<<<<<<<\n");
|
||
break;
|
||
case PHOTO_MODE_TAKE_MULTIPLE_2://高三等分 图像合成模式
|
||
printf(">>>>>>>PHOTO_MODE_TAKE_MULTIPLE_2<<<<<<<<\n");
|
||
break;
|
||
case PHOTO_MODE_TAKE_MULTIPLE_3://对角等分 图像合成模式
|
||
printf(">>>>>>>PHOTO_MODE_TAKE_BOOTH<<<<<<<<\n");
|
||
break;
|
||
case PHOTO_MODE_TAKE_MULTIPLE_4://不规则 图像合成模式
|
||
printf(">>>>>>>PHOTO_MODE_TAKE_BOOTH<<<<<<<<\n");
|
||
break;
|
||
}
|
||
|
||
lcd_show_frame(buf, len, yuv_in_w, yuv_in_h); //240*320*2=153600
|
||
|
||
#if PROCESS_EFFECT /*万花筒功能*/
|
||
if (kaleidoscope_mode != EM_END) {
|
||
if (baddr != NULL) {
|
||
free(baddr);
|
||
}
|
||
}
|
||
#endif //PROCESS_EFFECT
|
||
|
||
Calculation_frame();
|
||
}
|
||
|
||
void set_video_rt_cb(u32(*cb)(void *, u8 *, u32), void *priv);
|
||
int user_net_video_rec_open(char forward);
|
||
int user_net_video_rec_close(char forward);
|
||
int jpeg2yuv_open(void);
|
||
void jpeg2yuv_yuv_callback_register(void (*cb)(u8 *data, u32 len, int width, int height));
|
||
int jpeg2yuv_jpeg_frame_write(u8 *buf, u32 len);
|
||
void jpeg2yuv_close(void);
|
||
static void uvc_video_test(void);
|
||
|
||
void get_yuv_init(void (*cb)(u8 *data, u32 len, int width, int height));
|
||
|
||
void camera_to_lcd_init(char camera_ID)//摄像头选择 并初始化
|
||
{
|
||
#ifdef CONFIG_UVC_VIDEO2_ENABLE
|
||
uvc_video_test();
|
||
#else
|
||
get_yuv_init(test_main);
|
||
#endif
|
||
}
|
||
|
||
void camera_to_lcd_uninit(void)
|
||
{
|
||
void get_yuv_uninit(void);
|
||
get_yuv_uninit();
|
||
}
|
||
int isc_log_en()//如果定义该函数丢帧信息屏蔽
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
static void uvc_jpeg_cb(void *hdr, void *data, u32 len)
|
||
{
|
||
#define JPEG_HEAD 0xE0FFD8FF
|
||
#define JPEG_HEAD1 0xC0FFD8FF
|
||
u32 *head = (u32 *)data;
|
||
if (*head == JPEG_HEAD || *head == JPEG_HEAD1) {
|
||
//video
|
||
jpeg2yuv_jpeg_frame_write((u8 *)data, len);
|
||
} else {
|
||
//audio
|
||
}
|
||
}
|
||
|
||
static void camera_show_lcd(u8 *buf, u32 size, int width, int height)
|
||
{
|
||
char *ssid = NULL;
|
||
char *pwd = NULL;
|
||
#if CONFIG_LCD_QR_CODE_ENABLE
|
||
qr_code_get_one_frame_YUV_420(buf, width, height);
|
||
qr_get_ssid_pwd(&ssid, &pwd);
|
||
printf(">>>>>>>>>>>>>>>sssid = %s, pwd = %s", ssid, pwd);
|
||
#endif
|
||
lcd_show_frame(buf, size, width, height);
|
||
Calculation_frame();
|
||
}
|
||
|
||
static void uvc_video_task(void *priv)
|
||
{
|
||
int ret;
|
||
start:
|
||
//0.等待UVC上线
|
||
while (!dev_online("uvc")) {
|
||
os_time_dly(10);
|
||
}
|
||
//1.打开jpeg解码YUV
|
||
ret = jpeg2yuv_open();
|
||
if (ret) {
|
||
return;
|
||
}
|
||
//2.注册YUV数据回调函数
|
||
jpeg2yuv_yuv_callback_register(camera_show_lcd);
|
||
|
||
#if (APP_VIDEO_REC_RUN)
|
||
/* extern OS_SEM video_sem; */
|
||
/* os_sem_set(&video_sem, 0); */
|
||
/* os_sem_pend(&video_sem, 0); */
|
||
#endif
|
||
|
||
//3.打开UVC实时流
|
||
ret = user_net_video_rec_open(1);
|
||
if (ret) {
|
||
jpeg2yuv_close();
|
||
return;
|
||
}
|
||
//3.注册jpeg数据回调函数
|
||
set_video_rt_cb(uvc_jpeg_cb, NULL);
|
||
|
||
#if TCFG_LCD_USB_SHOW_COLLEAGUE
|
||
void set_tcp_uvc_rt_cb(u32(*cb)(void *priv, void *data, u32 len));
|
||
void set_udp_uvc_rt_cb(u32(*cb)(void *priv, void *data, u32 len));
|
||
set_udp_uvc_rt_cb(uvc_jpeg_cb); //wifi udp
|
||
set_tcp_uvc_rt_cb(uvc_jpeg_cb); //wifi tcp
|
||
#endif
|
||
|
||
//4.检查是否掉线
|
||
struct lbuf_test_head *rbuf = NULL;
|
||
|
||
while (dev_online("uvc")) {
|
||
os_time_dly(5);
|
||
}
|
||
#if (!APP_VIDEO_REC_RUN)
|
||
//5.关闭UVC实时流
|
||
user_net_video_rec_close(1);
|
||
//6.关闭jpeg解码YUV
|
||
jpeg2yuv_close();
|
||
#endif
|
||
goto start;
|
||
|
||
}
|
||
static void uvc_video_test(void)
|
||
{
|
||
thread_fork("uvc_video_task", 5, 1024, 0, 0, uvc_video_task, NULL);
|
||
}
|
||
#endif
|
||
|