doubao_chat/apps/wifi_camera/yuv2jpeg_thumbnail.c

454 lines
15 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

#include "asm/jpeg_codec.h"
#include "fs/fs.h"
#include "os/os_api.h"
#include "yuv_soft_scalling.h"
#include "app_config.h"
/***********************yuv编码jpeg存放缩略图**************************
*编码放大图片,图片存放小分辨率的缩略图信息
*thumb_width、thumb_height为缩略图大小
*jpeg_get_thumbnail函数获取缩略图图片
*********************************************************************/
//应用层使用YUV编码JPG API,该函数:可缩小和放大图片
//yuvdata:YUV或Y数据地址,yuvdata_size:数据长度
//jpg_buf:JPG图片编码预先存放数据缓存区,jpg_buf_size:JPG数据缓存区长度(30K-100K:<=320*240->30K,480*320->40K,640*480->50K,1280*720*90K)
//width/height:YUV/Y数据源的分辨率宽高, out_width/out_height:输出的分辨率, q_val:图片编码质量:0-13
//返回值:<=0失败, >0:JPG实际编码的图片数据长度(用户调用该函数后保存JPG数据为jpg_buf,长度为返回值)
int yuv_enc_image(char *yuvdata, int yuvdata_size, char *jpg_buf, int jpg_buf_size, int width, int height, int out_width, int out_height, u8 q_val)
{
#define YUV_ECN_IMG_LINE 16 //一次编码行数
#define YUV_SCAL_SAVE_SD 0
int err = 0;
int i;
int out_yuv_size = 0;
char only_y = 0;
char *src, *out;
char *out_yuv = NULL;
char *yuv = yuvdata;
void *jpg_hdl = NULL;
int in_yuv_enc_line = 0;
int in_yuv_enc_line_reserv = 0;
int all_in_line = 0;
int all_out_line = 0;
float tmp;
float reserv = 0;
int out_y420_offset = 0;
int out_u420_offset = 0;
int out_v420_offset = 0;
void jlve_scale_bilinear(image_info * src, image_info * dst);
if ((out_width % 16) != 0 || (out_height % 16) != 0) { //分辨率不是16对齐则警告
printf("waning: out_width or out_height no align 16 bytes, %d x %d\n", out_width, out_height);
}
//1.分辨率对齐
out_width = (out_width + 15) / 16 * 16;
out_height = (out_height + 15) / 16 * 16;
tmp = (float)YUV_ECN_IMG_LINE * height / out_height;
in_yuv_enc_line = tmp;
//2.YUV统一转YUV420
if (yuvdata_size == width * height * 3) {
YUV444pToYUV420p(yuv, yuv, width, height);//统一转YUV420
} else if (yuvdata_size == width * height * 2) {
YUV422pToYUV420p(yuv, yuv, width, height);//统一转YUV420
} else {
only_y = (yuvdata_size == width * height) ? 1 : 0;
}
#if JPEG_ENC_ONLY_Y
only_y = 1;
#endif
if (only_y) {
out_yuv_size = out_width * YUV_ECN_IMG_LINE + out_width * YUV_ECN_IMG_LINE / 4;
} else {
out_yuv_size = out_width * YUV_ECN_IMG_LINE * 3 / 2;
}
//3.申请YUV内存
out_yuv = malloc(out_yuv_size);
if (!yuv || !out_yuv) {
printf("err yuv buf not enough\n");
return 0;
}
//4.打开JPEG硬件编码器
jpg_hdl = mjpg_image_enc_open(NULL, JPEG_ENC_MANU_IMAGE);
if (!jpg_hdl) {
printf("mjpg_image_enc_open err\n");
goto exit;
}
//5.完成1行MCU(16行像素点)缩放送进编码器编码
image_info src_img;
image_info dst_img;
struct YUV_frame_data input_frame = {0};
input_frame.width = out_width;
input_frame.height = out_height;
input_frame.data_height = YUV_ECN_IMG_LINE;
src = yuv;
out = out_yuv;
for (i = 0, reserv = 0, input_frame.line_num = 0; i <= (out_height - YUV_ECN_IMG_LINE); i += YUV_ECN_IMG_LINE, input_frame.line_num += YUV_ECN_IMG_LINE) {
reserv += (float)(tmp - in_yuv_enc_line);
if (reserv >= 2.0) { //补偿,2对齐
in_yuv_enc_line_reserv = in_yuv_enc_line + (int)reserv;
reserv -= (int)reserv;
} else {
in_yuv_enc_line_reserv = in_yuv_enc_line;
}
//y
src_img.w = width;
src_img.h = in_yuv_enc_line_reserv;
src_img.pixels = (unsigned char *)(src + all_in_line * width);
dst_img.w = out_width;
dst_img.h = YUV_ECN_IMG_LINE;
dst_img.pixels = (unsigned char *)out;
jlve_scale_bilinear(&src_img, &dst_img);
//copy y
input_frame.y = (u8 *)dst_img.pixels;
if (!only_y) {
//u
src_img.w = width / 2;
src_img.h = in_yuv_enc_line_reserv / 2;
src_img.pixels = (unsigned char *)(src + width * height + all_in_line / 2 * width / 2);
dst_img.w = out_width / 2;
dst_img.h = YUV_ECN_IMG_LINE / 2;
dst_img.pixels = (unsigned char *)(out + out_width * YUV_ECN_IMG_LINE);
jlve_scale_bilinear(&src_img, &dst_img);
//copy u
input_frame.u = (u8 *)dst_img.pixels;
//v
src_img.w = width / 2;
src_img.h = in_yuv_enc_line_reserv / 2;
src_img.pixels = (unsigned char *)(src + width * height + width / 2 * height / 2 + all_in_line / 2 * width / 2);
dst_img.w = out_width / 2;
dst_img.h = YUV_ECN_IMG_LINE / 2;
dst_img.pixels = (unsigned char *)(out + out_width * YUV_ECN_IMG_LINE + out_width / 2 * YUV_ECN_IMG_LINE / 2);
jlve_scale_bilinear(&src_img, &dst_img);
//copy v
input_frame.v = (u8 *)dst_img.pixels;
} else {
memset(out + out_width * YUV_ECN_IMG_LINE, 0x80, out_width * YUV_ECN_IMG_LINE / 4);
//copy u v
input_frame.u = (u8 *)(out + out_width * YUV_ECN_IMG_LINE);
input_frame.v = input_frame.u;
}
all_in_line += in_yuv_enc_line_reserv;
all_out_line += YUV_ECN_IMG_LINE;
//6.每1行MCU(16行像素点)编码一次
err = mjpg_image_enc_start(jpg_hdl, &input_frame, jpg_buf, jpg_buf_size, q_val);
if (err < 0 || (err <= 0 && all_out_line >= out_height)) {
printf("jpg_image_enc err = %d, all_out_line = %d \n", err, all_out_line);
goto exit;
}
}
exit:
//7.关闭JPEG硬件和释放内存
if (jpg_hdl) {
mjpg_image_enc_close(jpg_hdl);
}
if (out_yuv) {
free(out_yuv);
}
return err;
}
//yuv420p YUV数据,width,height:YUV数据源宽高
//zoom_width,zoom_height:照片放大的宽高
//thumb_width,thumb_height:缩略图宽高
//jpg_img照片缓存,jpg_img_size照片缓存大小,q_val:编码质量:0-13
//返回值:>0为图片数据长度,<=0编码失败
int yuv420p_enc_jpeg_thumbnail(unsigned char *yuv420p, int width, int height,
int zoom_width, int zoom_height,
unsigned char *jpg_img, int jpg_img_size, int q_val,
int thumb_width, int thumb_height)
{
int err;
int i, j;
int ret = 0;
int thumbjpg_addr = 0;
unsigned char thumbjpg_img_ok = 0;
unsigned char ffe2_table[8];
unsigned char *thumbjpg_img = NULL;
int thumbjpg_img_size = 0;
int thumbjpg_enc_size = 0;
int jpg_len = 0;
if (thumb_width && thumb_height) {
thumb_width = (thumb_width + 15) / 16 * 16;
thumb_height = (thumb_height + 15) / 16 * 16;
if (thumb_width >= width || thumb_height >= height) {
printf("no need to set thumbjpg\n");
goto enc_jpg;
}
thumbjpg_img_size = thumb_width * thumb_height * 10 / 60;
thumbjpg_img = malloc(thumbjpg_img_size);
if (!thumbjpg_img) {
printf("err no %d mem in %s \n", thumbjpg_img_size, __func__);
ret = -ENOMEM;
goto exit;
}
//1.编码缩略图
err = yuv_enc_image(yuv420p, width * height * 3 / 2,
thumbjpg_img, thumbjpg_img_size,
width, height,
thumb_width, thumb_height,
q_val);
if (!err) {
printf("err in thumbjpg_img\n");
ret = -EINVAL;
goto exit;
}
thumbjpg_img_ok = TRUE;
thumbjpg_enc_size = err;
}
enc_jpg:
//2.编码缩放需要的JPG图
err = yuv_enc_image(yuv420p, width * height * 3 / 2,
jpg_img, jpg_img_size,
width, height,
zoom_width, zoom_height,
q_val);
if (!err) {
printf("err in yuv_enc_image \n");
ret = -EINVAL;
goto exit;
}
jpg_len = err;
//3.检查buffer够不够存储缩略图
if (thumbjpg_img_ok && (jpg_len + 8 + thumbjpg_enc_size) > jpg_img_size) {
printf("jpg_img_size no enough, need size = %d , jpg_img_size = %d \n\n", (jpg_len + 8 + thumbjpg_enc_size), jpg_img_size);
ret = -EINVAL;
goto exit;
}
//4.存储缩略图在缩放JPEG图上
if (thumbjpg_img_ok) {
memset(ffe2_table, 0, 8);
j = jpg_len;
i = 0;
while (j--) {
if (jpg_img[i] == (unsigned char)0xFF && jpg_img[i + 1] == (unsigned char)0xDB) {
break;
}
i++;
}
if (j > 0) {
ffe2_table[0] = 0xFF;
ffe2_table[1] = 0xE2;
ffe2_table[2] = 0x0;
ffe2_table[3] = 0x6;
thumbjpg_addr = jpg_len + 8;
ffe2_table[4] = (unsigned char)(thumbjpg_addr & 0xFF);
ffe2_table[5] = (unsigned char)((thumbjpg_addr >> 8) & 0xFF);
ffe2_table[6] = (unsigned char)((thumbjpg_addr >> 16) & 0xFF);
ffe2_table[7] = (unsigned char)((thumbjpg_addr >> 24) & 0xFF);
int end_new = jpg_len + 8;
int end = jpg_len;
j = jpg_len - i + 1;
while (j--) {
jpg_img[end_new] = jpg_img[end];
end_new--;
end--;
}
memcpy(jpg_img + i, ffe2_table, 8);
jpg_len += 8;
memcpy(jpg_img + jpg_len, thumbjpg_img, thumbjpg_enc_size);
jpg_len += thumbjpg_enc_size;
}
}
exit:
//5.释放内存
if (thumbjpg_img) {
free(thumbjpg_img);
}
if (!ret) {
ret = jpg_len;
}
return ret;
}
//path:jpeg文件路径
//jpg_img:存放缩略图缓冲区
//jpg_img_size:存放缩略图缓冲区长度
//返回值:>0为缩略图图片数据长度,<=0失败
int jpeg_get_thumbnail(const char *path, unsigned char *jpg_img, int jpg_img_size)
{
int ret = 0;
int len;
int rm_len;
int i, j;
int thumbjpg_addr = 0;
FILE *fd = NULL;
int buf_size = 512;
unsigned char *buf = malloc(buf_size);
if (!buf) {
printf("err no %d mem in %s \n", buf_size, __func__);
ret = -ENOMEM;
goto exit;
}
fd = fopen(path, "r");
if (!fd) {
printf("open file %s err \n\n", path);
ret = -EINVAL;
goto exit;
}
len = flen(fd);
rm_len = len;
while (rm_len) {
if (fread(buf, 1, buf_size, fd) != buf_size) {
printf("read file err !!!\n");
ret = -EINVAL;
goto exit;
}
j = buf_size;
i = 0;
while (j--) {
if (buf[i] == (unsigned char)0xFF && buf[i + 1] == (unsigned char)0xE2) {
break;
}
if (buf[i] == (unsigned char)0xFF && buf[i + 1] == (unsigned char)0xDB) {
printf("no found thumbnail_jpg\n");
ret = -EINVAL;
goto exit;
}
i++;
}
if (j > 0) {
thumbjpg_addr |= (unsigned char)buf[i + 4] & 0xFF;
thumbjpg_addr |= ((unsigned char)buf[i + 5] & 0xFF) << 8;
thumbjpg_addr |= ((unsigned char)buf[i + 6] & 0xFF) << 16;
thumbjpg_addr |= ((unsigned char)buf[i + 7] & 0xFF) << 24;
break;
}
rm_len -= buf_size;
}
/*printf("read thumbjpg_addr = 0x%x, len = %d \n", thumbjpg_addr, len);*/
if (thumbjpg_addr && thumbjpg_addr < len) {
rm_len = len - thumbjpg_addr;
if (jpg_img_size < rm_len) {
printf("jpg_img_size no enough , need %d ,jpg_img_size = %d \n", rm_len, jpg_img_size);
} else {
fseek(fd, thumbjpg_addr, SEEK_SET);
if (fread(jpg_img, 1, rm_len, fd) != rm_len) {
printf("read file err !!!\n");
ret = -EINVAL;
goto exit;
}
ret = rm_len;
}
}
exit:
if (fd) {
fclose(fd);
}
if (buf) {
free(buf);
}
return ret;
}
#if 0
void yuv2jpeg_test(void)
{
int width = 640;
int height = 480;
int zoom_width = 960;
int zoom_height = 640;
int thumb_width = 320;
int thumb_height = 240;
int ret;
int file_len;
char name[64];
char *jpg_img = NULL;
//这个长度则应该是:正常图片大小+缩略图大小, 图片最大值:1280x720:130K, 960x640:90K, 640X480:50K, 320*240:20K, 160x128:10K
//如果内存不够则可以适当减少,一般图片质量:1280x720:90K, 960x640:70K, 640X480:45K, 320*240:15K, 160x128:5K
int jpg_img_size = 90 * 1024 + 20 * 1024;//按照最大计算:90K+20K,大于1280x720具体要看看实际大小
FILE *fd = NULL;
/***************编码带缩略图*********************/
//1 malloc buffer
char *yuv = malloc(width * height * 3 / 2);
if (!yuv) {
return ;
}
jpg_img = malloc(jpg_img_size);
if (!jpg_img) {
goto exit;
}
sprintf(name, "%sYUV_TEST.YUV", CONFIG_ROOT_PATH);
printf("name : %s\n", name);
//2 读取YUV
fd = fopen(name, "r");
if (!fd) {
printf("file open err \n");
goto exit;
}
file_len = flen(fd);
fread(yuv, 1, file_len, fd);
fclose(fd);
//3 YUV编码带缩略图JPEG
ret = yuv420p_enc_jpeg_thumbnail(yuv, width, height,
zoom_width, zoom_height,
jpg_img, jpg_img_size, 5,
thumb_width, thumb_height);
if (ret <= 0) {
printf("yuv420p_enc_jpeg_thumbnail err = %d \n", ret);
goto exit;
}
//4 保存JPEG照片
sprintf(name, "%stest_.jpg", CONFIG_ROOT_PATH);
printf("name : %s\n", name);
fd = fopen(name, "wb+");
if (!fd) {
printf("file open err \n");
goto exit;
}
fwrite(jpg_img, 1, ret, fd);
fclose(fd);
/***************打开带缩略图照片*********************/
//1 打开打开带缩略图照片
memset(jpg_img, 0, jpg_img_size);
ret = jpeg_get_thumbnail(name, jpg_img, jpg_img_size);
if (ret <= 0) {
printf("jpeg_get_thumbnail err \n\n");
goto exit;
}
//2 拿到缩略图可以做保存或者图片传输,此处保存到SD卡查看
sprintf(name, "%stest_thumbnail.jpg", CONFIG_ROOT_PATH);
printf("name : %s\n", name);
fd = fopen(name, "wb+");
if (!fd) {
printf("file open err \n");
goto exit;
}
fwrite(jpg_img, 1, ret, fd);
fclose(fd);
exit:
if (jpg_img) {
free(jpg_img);
}
if (yuv) {
free(yuv);
}
}
#endif