#include "asm/jpeg_codec.h" #include "asm/hwi.h" #include "system/includes.h" #include "fs/fs.h" #include "os/os_api.h" #include "video_ioctl.h" #include "video.h" #include "yuv_to_rgb.h" #include "yuv_soft_scalling.h" #include "app_config.h" #define JPEG_SAVE_FILE_YUV 1//测试解码保存格式:0 RGB656, 1 YUV #define JPEG_ENC_ONLY_Y 0//编码JPEG只编灰色的Y #define RGB565_BE 0//rgb565大小端:0小端,1端 int yuv_enc_image(char *yuvdata, int yuvdata_size, char *jpg_buf, int jpg_buf_size, int width, int height, int q_val); int yuv_enc_large_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); static void path_change_suf_name(char *name, const char *suf, const char *path)//在原始的名称下把后缀修改成suf的值 { int len = strlen(path); char *find; int i = len - 1; while (i >= 0) { //找出第一个'.' if (path[i] == '.') { break; } i--; } find = (char *)&path[i]; if (i > 0) { memcpy(name, path, find - path); name[find - path] = 0; strcat(name, suf); } } //解码测试函数 int jpeg_decoder_test(const char *path)//path:存在SD文件的jpeg图片 { FILE *fd = NULL; u32 lenth = 0; u8 *cy = NULL, *cb = NULL, *cr = NULL, *yuv = NULL, *rgb16 = NULL; u8 *buf = NULL; u32 pix = 0; u16 width, height; u8 ytype = 0; u32 len; char name[64]; int err = 0; printf("open file : %s \n", path); //1.打开jpeg文件并读取jpeg数据到内存缓存区 fd = fopen(path, "rb"); if (fd == NULL) { printf("no file name : %s\n", path); goto exit; } lenth = flen(fd);//读取jpeg图片数据长度 buf = malloc(lenth); if (!buf) { printf("buf malloc err ...\n"); goto exit; } if (fread(buf, 1, lenth, fd) != lenth) {//读取jpeg图片数据 printf("read file lenth err ...\n"); goto exit; } fclose(fd); fd = NULL; //2.解析jpeg信息获取源JPEG YUV数据类型和分辨率 struct jpeg_image_info info = {0}; info.input.data.buf = buf; info.input.data.len = lenth; if (jpeg_decode_image_info(&info)) { printf("jpeg_decode_image_info err %s\n", path); goto exit; } width = info.width; height = info.height; pix = width * height; switch (info.sample_fmt) { case JPG_SAMP_FMT_YUV444: ytype = 1; printf("jpeg yuv444, pix : %dx%d \n", width, height); break;//444 case JPG_SAMP_FMT_YUV420: ytype = 4; printf("jpeg yuv420, pix : %dx%d \n", width, height); break;//420 default: ytype = 2; printf("jpeg yuv422, pix : %dx%d \n", width, height); break;//422 } //3.申请YUV内存缓存 len = pix + pix / ytype * 2; if (!yuv) { yuv = malloc(len); if (!yuv) { printf("yuv malloc err len : %d , width : %d , height : %d \n", width, height, len); goto exit; } } #if !JPEG_SAVE_FILE_YUV if (!rgb16) { //使用YUV转RGB rgb16 = malloc(pix * 2);//rgb565总需要申请内存大小为:分辨率*2,rgb24为:分辨率*3 } #endif //4.配置解码相关参数 cy = yuv; cb = cy + pix; cr = cb + pix / ytype; struct jpeg_decode_req req = {0}; req.input_type = JPEG_INPUT_TYPE_DATA; req.input.data.buf = buf; req.input.data.len = lenth; req.buf_y = cy;//配置Y地址 req.buf_u = cb;//配置U地址 req.buf_v = cr;//配置V地址 req.buf_width = width; req.buf_height = height; req.out_width = width; req.out_height = height; req.output_type = JPEG_DECODE_TYPE_DEFAULT; req.bits_mode = BITS_MODE_UNCACHE; //5.启动jpeg图片解码 err = jpeg_decode_one_image(&req); if (err) {//非0,解码失败 printf("jpeg_decode_one_image err \n"); goto exit; } switch (ytype) { case 1: #if JPEG_SAVE_FILE_YUV //YUV444p统一转YUV420 YUV444pToYUV420p(yuv, yuv, width, height); #else //YUV统一转rgb565 if (rgb16) { yuv444p_quto_rgb565(yuv, rgb16, width, height, RGB565_BE); } #endif break;//444 case 4: #if JPEG_SAVE_FILE_YUV //YUV420p统一转YUV420,次数数据源已经是YUV420,不用再转换 #else if (rgb16) { yuv420p_quto_rgb565(yuv, rgb16, width, height, RGB565_BE); } #endif break;//420 default: #if JPEG_SAVE_FILE_YUV //YUV422p统一转YUV420 YUV422pToYUV420p(yuv, yuv, width, height); #else if (rgb16) { yuv422p_quto_rgb565(yuv, rgb16, width, height, RGB565_BE); } #endif break;//422 } //6.保存RGB/YUV文件 #if JPEG_SAVE_FILE_YUV path_change_suf_name(name, ".yuv", path);//源名字改后缀.yuv fd = fopen(name, "wb+");//可以自行指定保存的名字 if (!fd) { printf("open file err : %s \n", name); goto exit; } fwrite(yuv, 1, pix * 3 / 2, fd);//YUV420p大小为:分辨率*1.5倍 #else path_change_suf_name(name, ".rgb", path);//源名字改后缀.rgb fd = fopen(name, "wb+");//可以自行指定保存的名字 if (!fd) { printf("open file err : %s \n", name); goto exit; } fwrite(rgb16, 1, pix * 2, fd);//rgb565大小为:分辨率*2倍 #endif printf("file write ok\n"); //7.关闭文件和释放内存 exit: if (yuv) { free(yuv); yuv = NULL; } if (rgb16) { free(rgb16); rgb16 = NULL; } if (fd) { fclose(fd); fd = NULL; } return 0; } //编码测试函数 int jpeg_encode_test(const char *path, int width, int height)//path:存在SD文件的YUV420p数据(本次测试使用的YUV为解码jpeg_decoder_test函数保存的YUV) { int err = 0; char name[64]; char ytype = 4;//该根据YUV的path的YUV数据格式配置(默认yuv420p): 1 YUV444p, 2 YUV422p, 4 YUV420p int pix = width * height; int yuv_size; int jpg_size = 100 * 1024;//指定分辨率编码的数据长度缓冲区(一般50K-100K,不够再加大) FILE *fd = NULL; char *yuvbuf = NULL; char *jpg_img = NULL; //1.打开YUV文件 fd = fopen(path, "rb"); if (fd == NULL) { printf("no file name : %s\n", path); goto exit; } yuv_size = flen(fd);//读取jYUV数据长度 if (yuv_size == pix * 3 / 2) { //此处根据文件大小判断yuv类型,用户使用时候直接固定就行 ytype = 4;//YUV420P } else if (yuv_size == pix * 2) { ytype = 2;//YUV422P } else if (yuv_size == pix * 3) { ytype = 1;//YUV444P } else if (yuv_size == pix) {//打开文件只有Y时候,由于硬件需要UV,需要申请buff,此时建议用yuv_enc_image()API函数来完成,本函数只是测试例子 printf("please use function to do : yuv_enc_image() \n"); goto exit; } //2.申请YUV内存块 yuvbuf = malloc(yuv_size);//申请YUV内存块 if (!yuvbuf) { printf("yuvbuf malloc err ...\n"); goto exit; } //3.读取YUV到YUV内存块 if (fread(yuvbuf, 1, yuv_size, fd) != yuv_size) {//读取YUV数据 printf("read file yuv_size err ...\n"); goto exit; } fclose(fd); fd = NULL; //4.申请编码的JPEG的内存缓冲区,一般50K-100K即可 /* VGA图片大小说明:低等质量(小于20K),中等质量(20K-40K),高质量(大于40K,极限70K) 720P图片大小说明:低等质量(小于50K),中等质量(50k-100K),高质量(大于100K,极限150K) */ jpg_img = malloc(jpg_size); if (!jpg_img) { printf("jpg img malloc err!!!\n"); goto exit; } memset(jpg_img, 0, jpg_size); //5.配置编码参数,此步骤可直接使用yuv_enc_image函数来完成 #if 1 struct jpeg_encode_req req = {0}; req.q = 5;//0-13 编码质量 req.format = JPG_SAMP_FMT_YUV420; req.data.buf = (u8 *)jpg_img; req.data.len = jpg_size; req.width = width; req.height = height; req.y = (u8 *)yuvbuf; req.u = req.y + pix; req.v = req.u + pix / ytype; #if JPEG_ENC_ONLY_Y memset(req.u, 0x80, pix / ytype);//只编码灰色,把UV全设置0x80 req.v = req.u; #endif //6.启动编码器 err = jpeg_encode_one_image(&req); if (err) {//非0失败 printf("jpeg_encode_one_image err \n\n"); goto exit; } jpg_size = req.data.len; #else /*yuv_enc_image使用例子*/ err = yuv_enc_image((char *)yuvbuf, yuv_size, (char *)jpg_img, jpg_size, width, height, 5); if (!err) { printf("jpeg_encode_one_image err \n\n"); goto exit; } jpg_size = err; #endif printf("jpeg_encode_one_image ok size = %dKb\n", jpg_size / 1024); //7.保存jpeg图片 path_change_suf_name(name, "_***.jpg", path); fd = fopen(name, "w+");//可以自行指定保存的名字 if (!fd) { printf("open file err : %s\n", name); goto exit; } fwrite(jpg_img, 1, jpg_size, fd);//保存jpeg图片 printf("file write ok\n"); //8.关闭文件和释放内存 exit: if (yuvbuf) { free(yuvbuf); yuvbuf = NULL; } if (jpg_img) { free(jpg_img); jpg_img = NULL; } if (fd) { fclose(fd); fd = NULL; } return err; } //编码测试函数 int jpeg_encode_large_test(const char *path, int width, int height, int out_width, int out_height)//path:存在SD文件的YUV420p数据(本次测试使用的YUV为解码jpeg_decoder_test函数保存的YUV) { int err = 0; char name[64]; char ytype = 4;//该根据YUV的path的YUV数据格式配置(默认yuv420p): 1 YUV444p, 2 YUV422p, 4 YUV420p int pix = width * height; int yuv_size; int jpg_size = 800 * 1024;//指定分辨率编码的数据长度缓冲区(一般50K-100K,不够再加大,大分辨率需要加大) FILE *fd = NULL; char *yuvbuf = NULL; char *jpg_img = NULL; //1.打开YUV文件 fd = fopen(path, "rb"); if (fd == NULL) { printf("no file name : %s\n", path); goto exit; } yuv_size = flen(fd);//读取jYUV数据长度 if (yuv_size == pix * 3 / 2) { //此处根据文件大小判断yuv类型,用户使用时候直接固定就行 ytype = 4;//YUV420P } else if (yuv_size == pix * 2) { ytype = 2;//YUV422P } else if (yuv_size == pix * 3) { ytype = 1;//YUV444P } else if (yuv_size == pix) {//打开文件只有Y时候,由于硬件需要UV,需要申请buff,此时建议用yuv_enc_image()API函数来完成,本函数只是测试例子 printf("please use function to do : yuv_enc_image() \n"); goto exit; } //2.申请YUV内存块 yuvbuf = malloc(yuv_size);//申请YUV内存块 if (!yuvbuf) { printf("yuvbuf malloc err ...\n"); goto exit; } //3.读取YUV到YUV内存块 if (fread(yuvbuf, 1, yuv_size, fd) != yuv_size) {//读取YUV数据 printf("read file yuv_size err ...\n"); goto exit; } fclose(fd); fd = NULL; //4.申请编码的JPEG的内存缓冲区,一般50K-100K即可 /* VGA图片大小说明:低等质量(小于20K),中等质量(20K-40K),高质量(大于40K,极限70K) 720P图片大小说明:低等质量(小于50K),中等质量(50k-100K),高质量(大于100K,极限150K) */ jpg_img = malloc(jpg_size); if (!jpg_img) { printf("jpg img malloc err!!!\n"); goto exit; } memset(jpg_img, 0, jpg_size); //6.启动编码器 /*yuv_enc_large_image使用例子*/ err = yuv_enc_large_image(yuvbuf, yuv_size, (char *)jpg_img, jpg_size, width, height, out_width, out_height, 10); if (err <= 0) { printf("jpeg_encode_one_image err \n\n"); goto exit; } jpg_size = err; printf("jpeg_encode_one_image ok size = %dKb\n", jpg_size / 1024); //7.保存jpeg图片 path_change_suf_name(name, "_***.jpg", path); fd = fopen(name, "w+");//可以自行指定保存的名字 if (!fd) { printf("open file err : %s\n", name); goto exit; } fwrite(jpg_img, 1, jpg_size, fd);//保存jpeg图片 printf("file write ok\n"); //8.关闭文件和释放内存 exit: if (yuvbuf) { free(yuvbuf); yuvbuf = NULL; } if (jpg_img) { free(jpg_img); jpg_img = NULL; } if (fd) { fclose(fd); fd = NULL; } return err; } //应用层使用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数据源的分辨率宽高, 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 q_val) { struct jpeg_encode_req req = {0}; int err = 0; char only_y = 0; char yuv_alloc = 0; char *yuv = yuvdata; if (yuvdata_size == width * height * 3) { req.format = JPG_SAMP_FMT_YUV444; } else if (yuvdata_size == width * height * 2) { req.format = JPG_SAMP_FMT_YUV422; } else { req.format = JPG_SAMP_FMT_YUV420; only_y = (yuvdata_size == width * height) ? 1 : 0; if (only_y) { yuv = malloc(width * height + width * height / 4); yuv_alloc = 1; } } if (!yuv) { printf("err yuv buf not enough\n"); return 0; } req.q = q_val; req.data.buf = (u8 *)jpg_buf; req.data.len = jpg_buf_size; req.width = width; req.height = height; req.y = (u8 *)yuv; req.u = req.y + req.width * req.height; req.v = req.u + req.width * req.height / 4; #if JPEG_ENC_ONLY_Y only_y = 1; #endif if (only_y) { if (yuv != yuvdata) { memcpy(yuv, yuvdata, width * height); } memset(yuv + width * height, 0x80, width * height / 4); req.v = req.u; } if (jpeg_encode_one_image(&req) == 0) { printf("jpeg_encode_one_image ok\n"); err = req.data.len; } else { printf("jpeg_encode_one_image err\n"); } if (yuv_alloc) { free(yuv); } return err; } //应用层使用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_large_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; char *out_y420 = NULL; char *out_u420 = NULL; char *out_v420 = NULL; 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; } #if YUV_SCAL_SAVE_SD //保存到sd卡的yuv buffer out_y420 = malloc(out_width * out_height * 3 / 2); if (!out_y420) { printf("err yuv buf not enough\n"); return 0; } memset(out_y420, 0, out_width * out_height * 3 / 2); out_u420 = out_y420 + out_width * out_height; out_v420 = out_u420 + out_width * out_height / 4; #endif //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 #if YUV_SCAL_SAVE_SD memcpy(out_y420 + out_y420_offset, out, out_width * YUV_ECN_IMG_LINE); out_y420_offset += out_width * YUV_ECN_IMG_LINE; #endif 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 #if YUV_SCAL_SAVE_SD memcpy(out_u420 + out_u420_offset, out + out_width * YUV_ECN_IMG_LINE, out_width * YUV_ECN_IMG_LINE / 4); out_u420_offset += out_width * YUV_ECN_IMG_LINE / 4; #endif 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 #if YUV_SCAL_SAVE_SD memcpy(out_v420 + out_v420_offset, out + out_width * YUV_ECN_IMG_LINE + out_width * YUV_ECN_IMG_LINE / 4, out_width * YUV_ECN_IMG_LINE / 4); out_v420_offset += out_width * YUV_ECN_IMG_LINE / 4; #endif 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 #if YUV_SCAL_SAVE_SD memcpy(out_u420 + out_u420_offset, out + out_width * YUV_ECN_IMG_LINE, out_width * YUV_ECN_IMG_LINE / 4); memcpy(out_v420 + out_v420_offset, out + out_width * YUV_ECN_IMG_LINE, out_width * YUV_ECN_IMG_LINE / 4); out_u420_offset += out_width * YUV_ECN_IMG_LINE / 4; out_v420_offset += out_width * YUV_ECN_IMG_LINE / 4; #endif 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; } } //保存到sd卡 #if YUV_SCAL_SAVE_SD void sdfile_save_test(char *buf, int len, char one_file, char close); sdfile_save_test(out_y420, out_width * out_height * 3 / 2, 1, 0); sdfile_save_test(out_y420, 0, 1, 1); #endif exit: //7.关闭JPEG硬件和释放内存 if (jpg_hdl) { mjpg_image_enc_close(jpg_hdl); } if (out_yuv) { free(out_yuv); } if (out_y420) { free(out_y420); } return err; }