【VOFA+上位机图传-V4L2】
1..RGB565->RGB888->JPEG->VOFA--大概的流程
头文件
#ifndef _SOCKET_CLINET_H_ #define _SOCKET_CLINET_H_ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include "main.h" enum ImgFormat { Format_Invalid, Format_Mono, Format_MonoLSB, Format_Indexed8, Format_RGB32, Format_ARGB32, Format_ARGB32_Premultiplied, Format_RGB16, Format_ARGB8565_Premultiplied, Format_RGB666, Format_ARGB6666_Premultiplied, Format_RGB555, Format_ARGB8555_Premultiplied, Format_RGB888, Format_RGB444, Format_ARGB4444_Premultiplied, Format_RGBX8888, Format_RGBA8888, Format_RGBA8888_Premultiplied, Format_BGR30, Format_A2BGR30_Premultiplied, Format_RGB30, Format_A2RGB30_Premultiplied, Format_Alpha8, Format_Grayscale8, // 以下格式发送时,IMG_WIDTH和IMG_HEIGHT不需要强制指定,设置为-1即可 Format_BMP, Format_GIF, Format_JPG, Format_PNG, Format_PBM, Format_PGM, Format_PPM, Format_XBM, Format_XPM, Format_SVG, }; // RGB565转JPEG内存数组 int rgb565_to_jpeg(const uint16_t *rgb565_data, int width, int height, uint8_t **jpeg_buffer, size_t *jpeg_size, int quality); // 校验JPEG数据 int verify_jpeg_data(const uint8_t *jpeg_buffer, size_t jpeg_size);
.c
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <string.h> #include <linux/fb.h> #include <sys/mman.h> #include <jpeglib.h> #include "main.h" #include "show_jpeg_image.h" #include <setjmp.h> // RGB565转RGB888 void rgb565_to_rgb888(uint16_t rgb565, uint8_t *r, uint8_t *g, uint8_t *b) { *r = ((rgb565 >> 11) & 0x1F) << 3; *g = ((rgb565 >> 5) & 0x3F) << 2; *b = (rgb565 & 0x1F) << 3; } // RGB888转RGB565 uint16_t rgb888_to_rgb565(uint8_t r, uint8_t g, uint8_t b) { return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); } // 自定义内存目标管理器(用于编码JPEG到内存) typedef struct { struct jpeg_destination_mgr pub; JOCTET *buffer; size_t bufsize; size_t written; } my_destination_mgr; // 初始化目标缓冲区 static void init_destination(j_compress_ptr cinfo) { my_destination_mgr *dest = (my_destination_mgr *)cinfo->dest; dest->bufsize = 102400; // 初始32KB dest->buffer = (JOCTET *) (*cinfo->mem->alloc_small)( (j_common_ptr)cinfo, JPOOL_IMAGE, dest->bufsize); dest->pub.next_output_byte = dest->buffer; dest->pub.free_in_buffer = dest->bufsize; } // 缓冲区满时扩展 static boolean empty_output_buffer(j_compress_ptr cinfo) { my_destination_mgr *dest = (my_destination_mgr *)cinfo->dest; size_t oldsize = dest->bufsize; dest->bufsize *= 2; // 每次扩展一倍 dest->buffer = (JOCTET *) (*cinfo->mem->alloc_small)( (j_common_ptr)cinfo, JPOOL_IMAGE, dest->bufsize); memcpy(dest->buffer, dest->pub.next_output_byte - oldsize, oldsize); dest->pub.next_output_byte = dest->buffer + oldsize; dest->pub.free_in_buffer = dest->bufsize - oldsize; return TRUE; } // 终止目标处理 static void term_destination(j_compress_ptr cinfo) { my_destination_mgr *dest = (my_destination_mgr *)cinfo->dest; dest->written = dest->bufsize - dest->pub.free_in_buffer; } // 自定义内存源管理器(用于从内存解码JPEG) typedef struct { struct jpeg_source_mgr pub; const JOCTET *buffer; size_t buffer_size; boolean start_of_file; } my_source_mgr; // 初始化源缓冲区 static void init_source(j_decompress_ptr cinfo) { my_source_mgr *src = (my_source_mgr *)cinfo->src; src->start_of_file = TRUE; } // 填充输入缓冲区(本例中数据已全部在内存中) static boolean fill_input_buffer(j_decompress_ptr cinfo) { // 这种情况不会发生,因为我们一次性提供所有数据 return TRUE; } // 跳过输入数据 static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) { my_source_mgr *src = (my_source_mgr *)cinfo->src; if (num_bytes > 0) { while (num_bytes > (long)src->pub.bytes_in_buffer) { num_bytes -= (long)src->pub.bytes_in_buffer; src->pub.next_input_byte = NULL; src->pub.bytes_in_buffer = 0; if (!fill_input_buffer(cinfo)) return; } src->pub.next_input_byte += num_bytes; src->pub.bytes_in_buffer -= num_bytes; } } // 终止源处理 static void term_source(j_decompress_ptr cinfo) { // 无需操作,数据在内存中 } // 自定义错误处理 struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; static void my_error_exit(j_common_ptr cinfo) { struct my_error_mgr *myerr = (struct my_error_mgr *)cinfo->err; (*cinfo->err->output_message)(cinfo); longjmp(myerr->setjmp_buffer, 1); } // 校验JPEG数据 int verify_jpeg_data(const uint8_t *data, size_t size) { if (size < 4) return 0; if (data[0] != 0xFF || data[1] != 0xD8) return 0; // SOI标记 if (data[size-2] != 0xFF || data[size-1] != 0xD9) return 0; // EOI标记 struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); return 0; } jpeg_create_decompress(&cinfo); jpeg_mem_src(&cinfo, data, size); if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) { jpeg_destroy_decompress(&cinfo); return 0; } jpeg_destroy_decompress(&cinfo); return 1; } // RGB565转JPEG内存数组 int rgb565_to_jpeg(const uint16_t *rgb565_data, int width, int height, uint8_t **jpeg_buffer, size_t *jpeg_size, int quality) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; my_destination_mgr *dest; uint8_t *rgb888_data; int i, j; // 分配RGB888缓冲区 rgb888_data = (uint8_t *)malloc(width * height * 3); if (!rgb888_data) return -1; // 转换为RGB888 for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { uint16_t rgb565 = rgb565_data[i * width + j]; int idx = (i * width + j) * 3; rgb565_to_rgb888(rgb565, &rgb888_data[idx], &rgb888_data[idx+1], &rgb888_data[idx+2]); } } // 初始化JPEG压缩对象 cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); // 设置内存目标管理器 dest = (my_destination_mgr *)malloc(sizeof(my_destination_mgr)); if (!dest) { free(rgb888_data); return -1; } cinfo.dest = (struct jpeg_destination_mgr *)dest; dest->pub.init_destination = init_destination; dest->pub.empty_output_buffer = empty_output_buffer; dest->pub.term_destination = term_destination; // 设置压缩参数 cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); // 执行压缩 jpeg_start_compress(&cinfo, TRUE); JSAMPROW row_pointer[1]; while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = &rgb888_data[cinfo.next_scanline * width * 3]; jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); // 返回结果 *jpeg_buffer = dest->buffer; *jpeg_size = dest->written; // 清理资源 free(dest); free(rgb888_data); jpeg_destroy_compress(&cinfo); return 0; }
主函数
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/ipc.h> #include <sys/msg.h> #include "socket_client.h" #define SERVER_PORT 1347 //服务器的端口号 #define SERVER_IP "192.168.8.101" //服务器的IP地址 struct sockaddr_in server_addr = {0}; static char buf[2000]; static int sockfd; static int ret; static int i; int jpegmain(void) { // 测试图像尺寸 const int width = 1024; const int height = 600; // 分配测试数据内存 uint16_t *rgb565_original = (uint16_t *)malloc(width * height * sizeof(uint16_t)); if (!rgb565_original) { fprintf(stderr, "内存分配失败\n"); return 1; } // 生成测试图案(渐变) for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { uint8_t r = (x * 255) / width; uint8_t g = (y * 255) / height; uint8_t b = ((x ^ y) * 255) / (width > height ? width : height); rgb565_original[y * width + x] = rgb888_to_rgb565(r, g, b); } } // 步骤1: RGB565转JPEG uint8_t *jpeg_buffer = NULL; size_t jpeg_size = 0; if (rgb565_to_jpeg(rgb565_original, width, height, &jpeg_buffer, &jpeg_size, 95) != 0) { fprintf(stderr, "RGB565转JPEG失败\n"); free(rgb565_original); } printf("JPEG转换成功 - 尺寸: %dx%d, 大小: %zu 字节\n", width, height, jpeg_size); // 强制验证 JPEG 数据//...先验证我们的压缩..到这一步说面我们的压缩正确.. if (!verify_jpeg_data(jpeg_buffer, jpeg_size)) { fprintf(stderr, "JPEG 数据无效!\n"); // 保存损坏的 JPEG 用于分析 FILE *fp = fopen("corrupted.jpg", "wb"); if (fp) { fwrite(jpeg_buffer, 1, jpeg_size, fp); fclose(fp); printf("已保存损坏的 JPEG 到 corrupted.jpg\n"); } free(rgb565_original); } //开始发送图片到VOFA+代码 /* 打开套接字,得到套接字描述符 */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (0 > sockfd) { perror("socket error"); } /* 调用connect连接远端服务器 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); //端口号 inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP地址 ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (0 > ret) { perror("connect error"); close(sockfd); } printf("服务器连接成功...\n\n"); uint16_t count,countbk; //开始打包数据发送 sprintf(buf,"\nimage:%d,%d,%d,%d,%d\n",1,jpeg_size,-1,-1,Format_JPG); ret = send(sockfd,buf,strlen(buf),0); if(0 > ret) { perror("send error_0"); } count = jpeg_size/1024; for (i = 0; i < count; i++) { ret = send(sockfd,&jpeg_buffer[i*1024],1024,0); if(0 > ret) { perror("send error_1"); } } countbk = jpeg_size%1024; ret = send(sockfd,&jpeg_buffer[i*1024],countbk,0); if(0 > ret) { perror("send error_1"); } sprintf(buf,"0"); free(rgb565_original); }

这个通过V4L2摄像头获取的一帧RGB565数据..无伤大雅..只要替换一下参数就可以了

浙公网安备 33010602011771号