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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/928321.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

威联通-001 手机相册备份

文章目录 前言1.Qfile Pro2.Qsync Pro总结 前言 威联通有两种数据备份手段&#xff1a;1.Qfile Pro和2.Qsync Pro&#xff0c;实践使用中存在一些区别&#xff0c;针对不同备份环境选择是不同。 1.Qfile Pro 用来备份制定目录内容的。 2.Qsync Pro 主要用来查看和操作文…

【机器学习】分类任务: 二分类与多分类

二分类与多分类&#xff1a;概念与区别 二分类和多分类是分类任务的两种类型&#xff0c;区分的核心在于目标变量&#xff08;label&#xff09;的类别数&#xff1a; 二分类&#xff1a;目标变量 y 只有两个类别&#xff0c;通常记为 y∈{0,1} 或 y∈{−1,1}。 示例&#xff…

GaussDB(类似PostgreSQL)常用命令和注意事项

文章目录 前言GaussDB&#xff08;类似PostgreSQL&#xff09;常用命令和注意事项1. 连接到GaussDB数据库2. 查看当前数据库中的所有Schema3. 进入指定的Schema4. 查看Schema下的表、序列、视图5. 查看Schema下所有的表6. 查看表结构7. 开始事务8. 查询表字段注释9. 注意事项&a…

点灯大师——WIFI控制灯

在之前的教程中&#xff0c;我们学习了 ESP6266 的原理&#xff0c;并动手写了驱动&#xff0c;实现了串口的通讯和 STA、AP、STAAP 三种模式。本次我们就来教大家如何使用 ESP8266 控制灯。这是一个简单的示例&#xff0c;展示了如何将 WIFI 通信与硬件控制相结合&#xff0c;…

如何使用brew安装phpredis扩展?

如何使用brew安装phpredis扩展&#xff1f; phpredis扩展是一个用于PHP语言的Redis客户端扩展&#xff0c;它提供了一组PHP函数&#xff0c;用于与Redis服务器进行交互。 1、cd到php某一版本的bin下 /usr/local/opt/php8.1/bin 2、下载 phpredis git clone https://githu…

Android 使用OpenGLES + MediaPlayer 获取视频截图

概述 Android 获取视频缩略图的方法通常有: ContentResolver: 使用系统数据库MediaMetadataRetriever: 这个是android提供的类&#xff0c;用来获取本地和网络media相关文件的信息ThumbnailUtils: 是在android2.2&#xff08;api8&#xff09;之后新增的一个&#xff0c;该类为…

面向对象(二)——类和对象(上)

1 类的定义 做了关于对象的很多介绍&#xff0c;终于进入代码编写阶段。 本节中重点介绍类和对象的基本定义&#xff0c;属性和方法的基本使用方式。 【示例】类的定义方式 // 每一个源文件必须有且只有一个public class&#xff0c;并且类名和文件名保持一致&#xff01; …

echarts的双X轴,父级居中的相关配置

前言&#xff1a;折腾了一个星期&#xff0c;在最后一天中午&#xff0c;都快要放弃了&#xff0c;后来坚持下来&#xff0c;才有下面结果。 这个效果就相当是复合表头&#xff0c;第一行是子级&#xff0c;第二行是父级。 子级是奇数个时&#xff0c;父级label居中很简单&…

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Maltab)

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09; 目录 顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…

Agile VMO分享:海尔案例

海尔集团是全球最大的家电制造商之一&#xff0c;拥有超过76 000名员工。它获得了2018-2019年全球智能家电品牌前10名和2018-2019年全球消费电子品牌前50名的荣誉。 海尔利用价值流结构将自己组织成一些可以自管理的微型企业。这些微型企业拥有决策&#xff0c;设计和交付新产品…

第七课 Unity编辑器创建的资源优化_UI篇(UGUI)

上期我们学习了简单的Scene优化&#xff0c;接下来我们继续编辑器创建资源的UGUI优化 UI篇&#xff08;UGUI&#xff09; 优化UGUI应从哪些方面入手&#xff1f; 可以从CPU和GPU两方面考虑&#xff0c;CPU方面&#xff0c;避免触发或减少Canvas的Rebuild和Rebatch&#xff0c…

LabVIEW MathScript工具包对运行速度的影响及优化方法

LabVIEW 的 MathScript 工具包 在运行时可能会影响程序的运行速度&#xff0c;主要是由于以下几个原因&#xff1a; 1. 解释型语言执行方式 MathScript 使用的是类似于 MATLAB 的解释型语言&#xff0c;这意味着它不像编译型语言&#xff08;如 C、C 或 LabVIEW 本身的 VI&…

中国移动量子云平台:算力并网590量子比特!

在技术革新的浪潮中&#xff0c;量子计算以其独特的并行处理能力和指数级增长的计算潜力&#xff0c;有望成为未来技术范式变革和颠覆式创新应用的新源泉。中国移动作为通信行业的领军企业&#xff0c;致力于量子计算技术研究&#xff0c;推动量子计算产业的跨越式发展。 量子云…

pytest(二)excel数据驱动

一、excel数据驱动 excel文件内容 excel数据驱动使用方法 import openpyxl import pytestdef get_excel():excel_obj openpyxl.load_workbook("../pytest结合数据驱动-excel/data.xlsx")sheet_obj excel_obj["Sheet1"]values sheet_obj.valuescase_li…

文库 | 从嬴图的技术文档聊起

在技术的浩瀚海洋中&#xff0c;一份优秀的技术文档宛如精准的航海图。它是知识传承的载体&#xff0c;是团队协作的桥梁&#xff0c;更是产品成功的幕后英雄。然而&#xff0c;打造这样一份出色的技术文档并非易事。你是否在为如何清晰阐释复杂技术而苦恼&#xff1f;是否纠结…

flask的第一个应用

本文编写一个简单的实例来记录下flask的使用 文章目录 简单实例flask中的路由无参形式有参形式 参数类型本文小结 简单实例 flask的依赖包都安装好之后&#xff0c;我们就可以写一个最简单的web应用程序了&#xff0c;我们把这个应用程序命名为first.py: from flask import Fla…

【UE5 C++】判断两点连线是否穿过球体

目录 前言 方法一 原理 代码 测试 结果 方法二 原理 一、检查连线与球体的相交情况 二、检查距离与球体半径的关系 三、检查连线与球体的相交 代码 前言 通过数学原理判断空间中任意两点的连线是否穿过球体&#xff0c;再通过射线检测检验算法的正确性。 方法一 …

Python办公——openpyxl处理Excel每个sheet每行 修改为软雅黑9号剧中+边框线

目录 专栏导读背景1、库的介绍①&#xff1a;openpyxl 2、库的安装3、核心代码4、完整代码5、最快的方法(50万行44秒)——表头其余单元格都修改样式总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍…

Figma入门-约束与对齐

Figma入门-约束与对齐 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&#xff0c…

8. Debian系统中显示屏免密码自动登录

本文介绍如何在Debian系统上&#xff0c;启动后&#xff0c;自动免密登录&#xff0c;不卡在登录界面。 1. 修改lightDM配置文件 嵌入式Debian系统采用lightDM显示管理器&#xff0c;所以&#xff0c;一般需要修改它的配置文件/etc/lightdm/lightdm.conf&#xff0c;找到[Seat…