当前位置: 首页 > article >正文

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-gnu

all: server client

server: server.c server.h
    $(CC) $(CFLAGS) $(LDFLAGS) -o server server.c -lpng

client: 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


http://www.kler.cn/a/421605.html

相关文章:

  • 遇到问题:hive中的数据库和sparksql 操作的数据库不是同一个。
  • 华为HarmonyOS 让应用快速拥有账号能力 -- 1 华为账号一键登录
  • 电脑关机的趣味小游戏——system函数、strcmp函数、goto语句的使用
  • 《以 C++为笔,绘就手势识别人机交互新画卷》
  • 飞凌嵌入式受邀亮相OpenHarmony人才生态大会2024
  • 16asm - 汇编介绍 和 debug使用
  • Golang教程第24篇(语言接口)
  • Meta-Llama-3-8B-Instruct 模型的混合精度训练显存需求:AdamW优化器(中英双语)
  • STM32G4系列MCU的Direct memory access controller (DMA)功能之一
  • 更多开源创新 挑战OpenAI-o1的模型出现和AI个体模拟突破
  • 删除 MySQL 的多余实例
  • Redis使用场景-缓存-缓存击穿
  • pytest(二)excel数据驱动
  • winform跨线程更新界面
  • 【Unity基础】Unity中Transform.forward的详解与应用
  • Spring Boot集成Spring Security:深入探索授权机制
  • 《山海经》:北山
  • 显卡(Graphics Processing Unit,GPU)架构详细解读
  • 故障诊断 | Transformer-LSTM组合模型的故障诊断(Matlab)
  • 高级java每日一道面试题-2024年12月02日-JVM篇-虚拟机为什么使用元空间替换了永久代?
  • 【C++boost::asio网络编程】有关异步Server样例以及伪闭包延长连接生命周期方法的笔记
  • react-router-dom 快速上手
  • 最小有向包围盒——2D平面
  • 【机器学习】CatBoost 模型实践:回归与分类的全流程解析
  • commitlint——Git提交规范
  • HTMLCSS 创意工坊:卡片网格的鼠标魔法秀