Linux-Ubuntu16.04摄像头 客户端抓取帧并保存为PNG
1.0:client.c抓取帧并保存为PNG
#include <stdio.h> // 标准输入输出库
#include <stdlib.h> // 标准库,包含内存分配等函数
#include <string.h> // 字符串操作库
#include <linux/videodev2.h> // V4L2 视频设备接口库
#include <sys/ioctl.h> // 输入输出控制库
#include <fcntl.h> // 文件控制定义
#include <unistd.h> // UNIX 标准定义
#include <sys/mman.h> // 内存映射库
#include <png.h> // PNG 图像处理库
// 定义摄像头设备文件和图像分辨率
#define CAM_DEV "/dev/video0" // 摄像头设备文件路径
#define WIDTH 640 // 图像宽度
#define HEIGHT 480 // 图像高度
#define NB_BUFFER 4 // 缓冲区数量
// 定义用于存储图像数据的结构体
struct pic_data {
unsigned char *tmpbuffer[NB_BUFFER]; // 存储每个缓冲区的指针
unsigned int tmpbytesused[NB_BUFFER]; // 存储每个缓冲区的实际字节数
} pic;
// 定义摄像头文件描述符
int cam_fd;
// 声明函数
int v4l2_init(void); // 初始化摄像头
int v4l2Grab(void); // 抓取图像
int v4l2_close(void); // 关闭摄像头
int CLAMP(int value, int min, int max); // 辅助函数,用于限制数值范围
int yuv422_rgb24(unsigned char *yuv_buf, unsigned char *rgb_buf, unsigned int width, unsigned int height); // YUV422 转 RGB24
void write_data_to_png(const char *png_name, png_uint_32 width, png_uint_32 height, png_bytepp data, int color_type); // 将 RGB 数据写入 PNG 文件
// 定义用于存储 RGB 数据的结构体
typedef struct pixel_RGB {
png_byte red, green, blue; // 每个像素的红、绿、蓝分量
} pixel_RGB;
int main(int argc, char* argv[]) {
// 初始化摄像头
if (v4l2_init() == -1) {
fprintf(stderr, "Failed to initialize camera.\n");
return -1;
}
// 从摄像头抓取图像
if (v4l2Grab() == -1) {
fprintf(stderr, "Failed to grab image from camera.\n");
v4l2_close();
return -1;
}
// 分配 RGB 缓冲区
unsigned char *rgb_buf = (unsigned char *)malloc(WIDTH * HEIGHT * 3);
if (!rgb_buf) {
fprintf(stderr, "Failed to allocate RGB buffer.\n");
v4l2_close();
return -1;
}
// 将 YUV 数据转换为 RGB 数据
yuv422_rgb24(pic.tmpbuffer[0], rgb_buf, WIDTH, HEIGHT);
// 分配 PNG 图像数据
pixel_RGB *image_data_rgb = calloc(WIDTH * HEIGHT, sizeof(pixel_RGB));
if (!image_data_rgb) {
fprintf(stderr, "Failed to allocate image data for PNG.\n");
free(rgb_buf);
v4l2_close();
return -1;
}
// 将 RGB 数据复制到 PNG 图像数据结构体中
for (unsigned int i = 0; i < WIDTH * HEIGHT; i++) {
image_data_rgb[i].red = rgb_buf[i * 3 + 2];
image_data_rgb[i].green = rgb_buf[i * 3 + 1];
image_data_rgb[i].blue = rgb_buf[i * 3 + 0];
}
// 保存为 PNG 图像
const char *png_name = "camera_image.png";
write_data_to_png(png_name, WIDTH, HEIGHT, (png_bytepp)image_data_rgb, PNG_COLOR_TYPE_RGB);
// 释放资源
free(rgb_buf);
free(image_data_rgb);
v4l2_close();
return 0;
}
// 初始化摄像头
int v4l2_init(void) {
int i;
int ret = 0;
// 打开摄像头设备
if ((cam_fd = open(CAM_DEV, O_RDWR)) == -1) {
perror("ERROR opening V4L interface.");
return -1;
}
// 判断设备是否为摄像头
struct v4l2_capability cam_cap;
if (ioctl(cam_fd, VIDIOC_QUERYCAP, &cam_cap) == -1) {
perror("Error opening device %s: unable to query device.");
return -1;
}
if ((cam_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
perror("ERROR video capture not supported.");
return -1;
}
// 设置输出参数
struct v4l2_format v4l2_fmt;
v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_fmt.fmt.pix.width = WIDTH;
v4l2_fmt.fmt.pix.height = HEIGHT;
v4l2_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if (ioctl(cam_fd, VIDIOC_S_FMT, &v4l2_fmt) == -1) {
perror("ERROR camera VIDIOC_S_FMT Failed.");
return -1;
}
// 检查参数是否设置成功
if (ioctl(cam_fd, VIDIOC_G_FMT, &v4l2_fmt) == -1) {
perror("ERROR camera VIDIOC_G_FMT Failed.");
return -1;
}
if (v4l2_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
printf("Set VIDIOC_S_FMT successful\n");
}
// 请求缓冲区存储图像数据
struct v4l2_requestbuffers v4l2_req;
v4l2_req.count = NB_BUFFER;
v4l2_req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_req.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam_fd, VIDIOC_REQBUFS, &v4l2_req) == -1) {
perror("ERROR camera VIDIOC_REQBUFS Failed.");
return -1;
}
// 开始内存映射
struct v4l2_buffer v4l2_buf;
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
for (i = 0; i < NB_BUFFER; i++) {
v4l2_buf.index = i;
if (ioctl(cam_fd, VIDIOC_QUERYBUF, &v4l2_buf) < 0) {
perror("Unable to query buffer.");
return -1;
}
pic.tmpbuffer[i] = mmap(NULL, v4l2_buf.length, PROT_READ, MAP_SHARED, cam_fd, v4l2_buf.m.offset);
if (pic.tmpbuffer[i] == MAP_FAILED) {
perror("Unable to map buffer.");
return -1;
}
if (ioctl(cam_fd, VIDIOC_QBUF, &v4l2_buf) < 0) {
perror("Unable to queue buffer.");
return -1;
}
}
// 开启流输入
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(cam_fd, VIDIOC_STREAMON, &type) < 0) {
perror("Unable to start capture.");
return -1;
}
return 0;
}
//抓取图像
int v4l2Grab(void) {
// 获取图像
struct v4l2_buffer buff;
buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buff.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam_fd, VIDIOC_DQBUF, &buff) < 0) {
printf("camera VIDIOC_DBUF Failed.\n");
return -1;
}
pic.tmpbytesused[buff.index] = buff.bytesused;
printf("size : %d\n", pic.tmpbytesused[buff.index]);
// 保存图像
int jpg_fd = open("v4l2.yuyv", O_RDWR | O_CREAT, 00700);
if (jpg_fd == -1) {
printf("open ipg Failed!\n");
return -1;
}
int writesize = write(jpg_fd, pic.tmpbuffer[buff.index], pic.tmpbytesused[buff.index]);
printf("Write successfully size : %d\n", writesize);
close(jpg_fd);
// 将缓冲区重新入队列
if (ioctl(cam_fd, VIDIOC_QBUF, &buff) < 0) {
printf("camera VIDIOC_QBUF Failed.");
return -1;
}
return 0;
}
//关闭摄像头
int v4l2_close(void) {
// 解除内存映射
struct v4l2_buffer v4l2_buf;
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
for (int i = 0; i < NB_BUFFER; i++) {
v4l2_buf.index = i;
if (ioctl(cam_fd, VIDIOC_QUERYBUF, &v4l2_buf) == 0) {
munmap(pic.tmpbuffer[i], v4l2_buf.length);
}
}
close(cam_fd);
return 0;
}
//YUV422 转 RGB24
int yuv422_rgb24(unsigned char *yuv_buf, unsigned char *rgb_buf, unsigned int width, unsigned int height) {
unsigned int i, j;
unsigned char y0, y1, u, v;
int r, g, b; // 使用 int 类型进行中间计算,以处理溢出问题
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
// 提取 YUV 组件
y0 = yuv_buf[i * width * 2 + j * 2]; // 第一个像素的 Y 分量
u = yuv_buf[i * width * 2 + j * 2 + 1]; // U 是两个像素共享的
y1 = yuv_buf[i * width * 2 + j * 2 + 2]; // 第二个像素的 Y 分量
v = yuv_buf[i * width * 2 + j * 2 + 3]; // V 是两个像素共享的
// 转换第一个像素
r = (int)(y0 + 1.402 * (v - 128));
g = (int)(y0 - 0.344 * (u - 128) - 0.714 * (v - 128));
b = (int)(y0 + 1.772 * (u - 128));
// 限制并赋值到 RGB 缓冲区
rgb_buf[(i * width + j) * 3 + 0] = (unsigned char)CLAMP(b, 0, 255);
rgb_buf[(i * width + j) * 3 + 1] = (unsigned char)CLAMP(g, 0, 255);
rgb_buf[(i * width + j) * 3 + 2] = (unsigned char)CLAMP(r, 0, 255);
// 转换第二个像素,使用相同的 UV 值
r = (int)(y1 + 1.402 * (v - 128));
g = (int)(y1 - 0.344 * (u - 128) - 0.714 * (v - 128));
b = (int)(y1 + 1.772 * (u - 128));
// 限制并赋值到 RGB 缓冲区
rgb_buf[(i * width + j + 1) * 3 + 0] = (unsigned char)CLAMP(b, 0, 255);
rgb_buf[(i * width + j + 1) * 3 + 1] = (unsigned char)CLAMP(g, 0, 255);
rgb_buf[(i * width + j + 1) * 3 + 2] = (unsigned char)CLAMP(r, 0, 255);
}
}
return 0;
}
// 辅助函数:限制数值范围
int CLAMP(int value, int min, int max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
//将 RGB 数据写入 PNG 文件
void write_data_to_png(const char *png_name, png_uint_32 width, png_uint_32 height, png_bytepp data, int color_type) {
FILE *fp = fopen(png_name, "wb");
if (!fp) {
perror("Error opening PNG file for writing");
return;
}
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) {
perror("Error creating PNG write struct");
fclose(fp);
return;
}
png_infop info = png_create_info_struct(png);
if (!info) {
perror("Error creating PNG info struct");
png_destroy_write_struct(&png, NULL);
fclose(fp);
return;
}
png_init_io(png, fp);
png_set_IHDR(png, info, width, height, 8, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png, info);
// 分配行指针
png_bytep *row_pointers = malloc(height * sizeof(png_bytep));
if (!row_pointers) {
perror("Error allocating row pointers");
png_destroy_write_struct(&png, &info);
fclose(fp);
return;
}
// 分配每行的内存
for (unsigned int i = 0; i < height; ++i) {
row_pointers[i] = (png_bytep)&(((pixel_RGB *)data)[i * width]);
}
png_write_image(png, row_pointers);
png_write_end(png, NULL);
png_destroy_write_struct(&png, &info);
fclose(fp);
free(row_pointers);
}
更:客户端传获取到的图像给服务器
客户端:
client.c
#include <png.h>
#include "client.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <linux/videodev2.h>
// 摄像头文件描述符
int cam_fd;
// 用于存储图像数据的结构体
/*
struct pic_data {
unsigned char *tmpbuffer[NB_BUFFER];
unsigned int tmpbytesused[NB_BUFFER];
} pic;
*/
// 初始化摄像头的函数
int v4l2_init(void) {
int i;
// 打开摄像头设备
if ((cam_fd = open(CAM_DEV, O_RDWR)) == -1) {
perror("打开V4L接口出错");
return -1;
}
// 检查设备是否为摄像头
struct v4l2_capability cam_cap;
if (ioctl(cam_fd, VIDIOC_QUERYCAP, &cam_cap) == -1) {
perror("打开设备时查询设备能力出错");
return -1;
}
if ((cam_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
perror("不支持视频捕获");
return -1;
}
// 设置输出参数
struct v4l2_format v4l2_fmt;
v4l2_fmt.type = V4L2_CAP_VIDEO_CAPTURE;
v4l2_fmt.fmt.pix.width = WIDTH;
v4l2_fmt.fmt.pix.height = HEIGHT;
v4l2_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if (ioctl(cam_fd, VIDIOC_S_FMT, &v4l2_fmt) == -1) {
perror("设置摄像头格式失败");
return -1;
}
// 检查参数是否设置成功
if (ioctl(cam_fd, VIDIOC_G_FMT, &v4l2_fmt) == -1) {
perror("获取摄像头格式失败");
return -1;
}
if (v4l2_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
printf("设置VIDIOC_S_FMT成功\n");
}
// 请求缓冲区以存储图像数据
struct v4l2_requestbuffers v4l2_req;
v4l2_req.count = NB_BUFFER;
v4l2_req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_req.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam_fd, VIDIOC_REQBUFS, &v4l2_req) == -1) {
perror("请求摄像头缓冲区失败");
return -1;
}
// 映射内存
struct v4l2_buffer v4l2_buf;
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
for (i = 0; i < NB_BUFFER; i++) {
v4l2_buf.index = i;
if (ioctl(cam_fd, VIDIOC_QUERYBUF, &v4l2_buf) < 0) {
perror("查询缓冲区失败");
return -1;
}
pic.tmpbuffer[i] = mmap(NULL, v4l2_buf.length, PROT_READ, MAP_SHARED, cam_fd, v4l2_buf.m.offset);
if (pic.tmpbuffer[i] == MAP_FAILED) {
perror("映射缓冲区失败");
return -1;
}
if (ioctl(cam_fd, VIDIOC_QBUF, &v4l2_buf) < 0) {
perror("将缓冲区入队列失败");
return -1;
}
}
// 开启视频流
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(cam_fd, VIDIOC_STREAMON, &type) < 0) {
perror("启动捕获失败");
return -1;
}
return 0;
}
// 从摄像头抓取图像的函数
int v4l2Grab(void) {
// 获取图像
struct v4l2_buffer buff;
buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buff.memory = V4L2_MEMORY_MMAP;
if (ioctl(cam_fd, VIDIOC_DQBUF, &buff) < 0) {
printf("从摄像头获取缓冲区失败\n");
return -1;
}
pic.tmpbytesused[buff.index] = buff.bytesused;
printf("大小 : %d\n", pic.tmpbytesused[buff.index]);
// 查看部分YUV数据(调试用)
unsigned char *yuv_data = pic.tmpbuffer[buff.index];
printf("YUV数据示例: Y=%d, U=%d, V=%d\n", yuv_data[0], yuv_data[1], yuv_data[3]);
// 保存图像
int jpg_fd = open("v4l2.yuyv", O_RDWR | O_CREAT, 00700);
if (jpg_fd == -1) {
printf("打开文件失败!\n");
return -1;
}
int writesize = write(jpg_fd, pic.tmpbuffer[buff.index], pic.tmpbytesused[buff.index]);
printf("写入成功大小 : %d\n", writesize);
close(jpg_fd);
// 将缓冲区重新入队列
if (ioctl(cam_fd, VIDIOC_QBUF, &buff) < 0) {
printf("将缓冲区重新入队列失败");
return -1;
}
return 0;
}
// 关闭摄像头的函数
int v4l2_close(void) {
// 解除内存映射
struct v4l2_buffer v4l2_buf;
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
for (int i = 0; i < NB_BUFFER; i++) {
v4l2_buf.index = i;
if (ioctl(cam_fd, VIDIOC_QUERYBUF, &v4l2_buf) == 0) {
munmap(pic.tmpbuffer[i], v4l2_buf.length);
}
}
close(cam_fd);
return 0;
}
// 辅助函数:限制数值范围
int CLAMP(int value, int min, int max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
// 将YUV 422格式转换为RGB 24位格式的函数
int yuv422_rgb24(unsigned char *yuv_buf, unsigned char *rgb_buf, unsigned int width, unsigned int height) {
unsigned int i, j;
unsigned char y0, y1, u, v;
unsigned char r, g, b;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j += 2) {
// 提取 YUV 组件
y0 = yuv_buf[i * width * 2 + j * 2]; // 第一个像素的 Y 分量
u = yuv_buf[i * width * 2 + j * 2 + 1]; // U 是两个像素共享的
y1 = yuv_buf[i * width * 2 + j * 2 + 2]; // 第二个像素的 Y 分量
v = yuv_buf[i * width * 2 + j * 2 + 3]; // V 是两个像素共享的
// 转换第一个像素
r = (int)(y0 + 1.402 * (v - 128));
g = (int)(y0 - 0.344 * (u - 128) - 0.714 * (v - 128));
b = (int)(y0 + 1.772 * (u - 128));
// 限制并赋值到 RGB 缓冲区
rgb_buf[(i * width + j) * 3 + 0] = (unsigned char)CLAMP(b, 0, 255);
rgb_buf[(i * width + j) * 3 + 1] = (unsigned char)CLAMP(g, 0, 255);
rgb_buf[(i * width + j) * 3 + 2] = (unsigned char)CLAMP(r, 0, 255);
// 转换第二个像素,使用相同的 UV 值
r = (int)(y1 + 1.402 * (v - 128));
g = (int)(y1 - 0.344 * (u - 128) - 0.714 * (v - 128));
b = (int)(y1 + 1.772 * (u - 128));
// 限制并赋值到 RGB 缓冲区
rgb_buf[(i * width + j + 1) * 3 + 0] = (unsigned char)CLAMP(b, 0, 255);
rgb_buf[(i * width + j + 1) * 3 + 1] = (unsigned char)CLAMP(g, 0, 255);
rgb_buf[(i * width + j + 1) * 3 + 2] = (unsigned char)CLAMP(r, 0, 255);
}
}
return 0;
}
// 将RGB数据写入PNG文件的函数
void write_data_to_png(const char *png_name, png_uint_32 width, png_uint_32 height, png_bytepp data, int color_type) {
// 查看部分RGB数据(调试用)
for (int i = 0; i < 10; i++) {
pixel_RGB *pixel = &((pixel_RGB *)data)[i];
printf("RGB数据示例: R=%d, G=%d, B=%d\n", pixel->red, pixel->green, pixel->blue);
}
FILE *fp = fopen(png_name, "wb");
if (!fp) {
perror("打开PNG文件用于写入出错");
return;
}
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) {
perror("创建PNG写入结构体出错");
fclose(fp);
return;
}
png_infop info = png_create_info_struct(png);
if (!info) {
perror("创建PNG信息结构体出错");
png_destroy_write_struct(&png, NULL);
fclose(fp);
return;
}
png_init_io(png, fp);
png_set_IHDR(png, info, width, height, 8, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png, info);
// 分配行指针
png_bytep *row_pointers = malloc(height * sizeof(png_bytep));
if (!row_pointers) {
perror("分配行指针出错");
png_destroy_write_struct(&png, &info);
fclose(fp);
return;
}
// 分配每行的内存
for (unsigned int i = 0; i < height; ++i) {
row_pointers[i] = (png_bytep)&(((pixel_RGB *)data)[i * width]);
}
png_write_image(png, row_pointers);
png_write_end(png, NULL);
png_destroy_write_struct(&png, &info);
fclose(fp);
free(row_pointers);
}
// 设置客户端并发送PNG图像数据的函数
int client_setup_and_send(const char* server_ip) {
// 初始化摄像头
if (v4l2_init() == -1) {
fprintf(stderr, "初始化摄像头失败.\n");
return -1;
}
// 从摄像头抓取图像
if (v4l2Grab() == -1) {
fprintf(stderr, "从摄像头抓取图像失败.\n");
v4l2_close();
return -1;
}
// 分配RGB缓冲区
unsigned char *rgb_buf = (unsigned char *)malloc(WIDTH * HEIGHT * 3);
if (!rgb_buf) {
fprintf(stderr, "分配RGB缓冲区失败.\n");
v4l2_close();
return -1;
}
// 将YUV数据转换为RGB数据
yuv422_rgb24(pic.tmpbuffer[0], rgb_buf, WIDTH, HEIGHT);
// 分配PNG图像数据
typedef struct {
unsigned char red;
unsigned char green;
unsigned char blue;
} pixel_RGB;
pixel_RGB *image_data_rgb = calloc(WIDTH * HEIGHT, sizeof(pixel_RGB));
if (!image_data_rgb) {
fprintf(stderr, "分配PNG图像数据失败.\n");
free(rgb_buf);
v4l2_close();
return -1;
}
// 将RGB数据复制到PNG图像数据结构体中
for (unsigned int i = 0; i < WIDTH * HEIGHT; i++) {
image_data_rgb[i].red = rgb_buf[i * 3 + 2];
image_data_rgb[i].green = rgb_buf[i * 3 + 1];
image_data_rgb[i].blue = rgb_buf[i * 3 + 0];
}
// 保存为PNG图像
const char *png_name = "camera_image.png";
write_data_to_png(png_name, WIDTH, HEIGHT, (png_bytepp)image_data_rgb, PNG_COLOR_TYPE_RGB);
// 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("创建套接字出错");
free(rgb_buf);
free(image_data_rgb);
v4l2_close();
return -1;
}
// 设置服务器地址和端口
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(server_ip);
// 连接到服务器
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("连接到服务器出错");
close(sockfd);
free(rgb_buf);
free(image_data_rgb);
v4l2_close();
return -1;
}
// 获取PNG图像数据大小
FILE *png_file = fopen(png_name, "rb");
if (!png_file) {
perror("打开PNG文件出错");
close(sockfd);
free(rgb_buf);
free(image_data_rgb);
v4l2_close();
return -1;
}
fseek(png_file, 0, SEEK_END);
long png_data_size = ftell(png_file);
fseek(png_file, 0, SEEK_SET);
// 发送图像数据大小信息
if (send(sockfd, &png_data_size, sizeof(png_data_size), 0) == -1) {
perror("发送图像大小信息出错");
fclose(png_file);
close(sockfd);
free(rgb_buf);
free(image_data_rgb);
v4l2_close();
return -1;
}
// 发送图像数据
char *png_data_buffer = (char *)malloc(png_data_size);
if (!png_data_buffer) {
perror("为发送PNG数据分配缓冲区出错");
fclose(png_file);
close(sockfd);
free(rgb_buf);
free(image_data_rgb);
v4l2_close();
return -1;
}
fread(png_data_buffer, 1, png_data_size, png_file);
if (send(sockfd, png_data_buffer, png_data_size, 0) == -1) {
perror("发送PNG数据出错");
free(png_data_buffer);
fclose(png_file);
close(sockfd);
free(rgb_buf);
free(image_data_rgb);
v4l2_close();
return -1;
}
free(png_data_buffer);
fclose(png_file);
// 关闭套接字
close(sockfd);
// 释放资源
free(rgb_buf);
free(image_data_rgb);
v4l2_close();
return 0;
}
// 客户端主函数
int main(int argc, char *argv[]) {
if (argc!= 2) {
fprintf(stderr, "用法: %s <服务器IP地址>\n", argv[0]);
return -1;
}
const char* server_ip = argv[1];
if (client_setup_and_send(server_ip) == -1) {
fprintf(stderr, "客户端发送图像数据失败.\n");
return -1;
}
return 0;
}
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
//#include <time.h>
#include <linux/videodev2.h>
#include <png.h>
#define CAM_DEV "/dev/video0"
#define WIDTH 640
#define HEIGHT 480
#define NB_BUFFER 4
#define PORT 8888
// 用于存储图像数据的结构体
struct pic_data {
unsigned char *tmpbuffer[NB_BUFFER];
unsigned int tmpbytesused[NB_BUFFER];
} pic;
typedef struct {
unsigned char red;
unsigned char green;
unsigned char blue;
} pixel_RGB;
// 函数原型声明
int v4l2_init(void);
int v4l2Grab(void);
int v4l2_close(void);
// 辅助函数:限制数值范围
int CLAMP(int value, int min, int max);
int yuv422_rgb24(unsigned char *yuv_buf, unsigned char *rgb_buf, unsigned int width, unsigned int height);
void write_data_to_png(const char *png_name, png_uint_32 width, png_uint_32 height, png_bytepp data, int color_type);
int client_setup_and_send();
#endif
服务器
server.c
#include "server.h"
// 处理单个客户端连接的函数
void handle_single_connection(int client_socket) {
// 接收PNG图像数据的大小
long png_data_size;
if (recv(client_socket, &png_data_size, sizeof(png_data_size), 0) == -1) {
perror("接收图像大小出错");
close(client_socket);
return;
}
// 接收PNG图像数据
char *png_data_buffer = (char *)malloc(png_data_size);
if (!png_data_buffer) {
perror("为PNG数据分配缓冲区出错");
close(client_socket);
return;
}
long total_received = 0;
while (total_received < png_data_size) {
long received = recv(client_socket, png_data_buffer + total_received, png_data_size - total_received, 0);
if (received == -1) {
perror("接收PNG数据出错");
free(png_data_buffer);
close(client_socket);
return;
}
total_received += received;
}
// 将接收到的数据保存为PNG文件
const char *png_name = "received_image.png";
FILE *fp = fopen(png_name, "wb");
if (!fp) {
perror("打开PNG文件用于写入出错");
free(png_data_buffer);
close(client_socket);
return;
}
fwrite(png_data_buffer, 1, png_data_size, fp);
fclose(fp);
free(png_data_buffer);
// 关闭客户端套接字
close(client_socket);
}
// 启动服务器并监听传入连接的函数
int start_server() {
// 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("创建套接字出错");
return -1;
}
// 绑定套接字到特定地址和端口
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("绑定套接字出错");
close(sockfd);
return -1;
}
// 监听传入连接
if (listen(sockfd, 5) == -1) {
perror("监听连接出错");
close(sockfd);
return -1;
}
return sockfd;
}
// 服务器主函数,用于持续监听并处理客户端连接
int main() {
int server_socket = start_server();
if (server_socket == -1) {
fprintf(stderr, "服务器启动失败.\n");
return -1;
}
while (1) {
// 接受客户端连接
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_socket == -1) {
perror("接受客户端连接出错");
continue;
}
// 处理客户端连接
handle_single_connection(client_socket);
}
// 关闭服务器套接字(实际上这里不会执行到,因为循环一直运行)
close(server_socket);
return 0;
}
server.h
#ifndef SERVER_H
#define SERVER_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <png.h>
#define BUFFER_SIZE 1024
#define PORT 8888
// 函数原型声明
int start_server();
void handle_connection(int client_socket);
#endif
Makefile
CC = gcc
CFLAGS = -Wall --std=gnu99 -I/usr/src/linux-headers-3.2.0-23-generic-pae/include -Wno-cpp
LDFLAGS = -L/usr/lib/i386-linux-gnuall: server client
server: server.c server.h
$(CC) $(CFLAGS) $(LDFLAGS) -o server server.c -lpngclient: client.c client.h
$(CC) $(CFLAGS) $(LDFLAGS) -o client client.c -lpng
clean:
rm -f server client
这两个要换成自己的路径:
- CFLAGS = -Wall --std=gnu99 -I/usr/src/linux-headers-3.2.0-23-generic-pae/include -Wno-cpp
- LDFLAGS = -L/usr/lib/i386-linux-gnu