Просмотр исходного кода

feat: establish shared memory camera system

lujw2 3 месяцев назад
Сommit
879836921e
4 измененных файлов с 233 добавлено и 0 удалено
  1. 4 0
      .gitignore
  2. 8 0
      CMakeLists.txt
  3. 140 0
      src/create.cpp
  4. 81 0
      src/read.cpp

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+build*/
+
+.cache/
+.clang-format

+ 8 - 0
CMakeLists.txt

@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.18)
+project(demo)
+
+find_package(OpenCV REQUIRED)
+add_executable(create src/create.cpp)
+add_executable(read src/read.cpp)
+target_link_libraries(read PRIVATE ${OpenCV_LIBS})
+

+ 140 - 0
src/create.cpp

@@ -0,0 +1,140 @@
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <cstring>
+#include <iostream>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+struct shm_data {
+    int width;
+    int height;
+    int format;
+    int size;       // 新增:存储实际数据大小
+    char data[0];    // 柔性数组
+};
+
+int main() {
+    // 打开摄像头设备
+    int cam_fd = open("/dev/video0", O_RDWR);
+    if (cam_fd == -1) {
+        perror("open camera failed");
+        return 1;
+    }
+
+    // 设置视频格式(MJPEG)
+    v4l2_format fmt{};
+    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    fmt.fmt.pix.width = 640;
+    fmt.fmt.pix.height = 480;
+    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; // 修改为MJPEG格式
+    fmt.fmt.pix.field = V4L2_FIELD_NONE;
+
+    if (ioctl(cam_fd, VIDIOC_S_FMT, &fmt) == -1) {
+        perror("set format failed");
+        close(cam_fd);
+        return 1;
+    }
+
+    // 请求缓冲区
+    v4l2_requestbuffers req{};
+    req.count = 1;
+    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    req.memory = V4L2_MEMORY_MMAP;
+
+    if (ioctl(cam_fd, VIDIOC_REQBUFS, &req) == -1) {
+        perror("request buffers failed");
+        close(cam_fd);
+        return 1;
+    }
+
+    // 查询并映射缓冲区
+    v4l2_buffer buf{};
+    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buf.memory = V4L2_MEMORY_MMAP;
+    buf.index = 0;
+
+    if (ioctl(cam_fd, VIDIOC_QUERYBUF, &buf) == -1) {
+        perror("query buffer failed");
+        close(cam_fd);
+        return 1;
+    }
+
+    void* cam_buffer = mmap(nullptr, buf.length, PROT_READ | PROT_WRITE,
+                           MAP_SHARED, cam_fd, buf.m.offset);
+    if (cam_buffer == MAP_FAILED) {
+        perror("mmap camera buffer failed");
+        close(cam_fd);
+        return 1;
+    }
+
+    // 创建共享内存
+    int shm_fd = shm_open("/camera_shm", O_CREAT | O_RDWR, 0666);
+    if (shm_fd == -1) {
+        perror("shm_open failed");
+        close(cam_fd);
+        return 1;
+    }
+
+    // 计算共享内存大小(缓冲区长度+元数据)
+    size_t shm_size = sizeof(shm_data) + buf.length; // 使用实际缓冲区长度
+
+    if (ftruncate(shm_fd, shm_size) == -1) {
+        perror("ftruncate failed");
+        close(shm_fd);
+        close(cam_fd);
+        return 1;
+    }
+
+    // 映射共享内存
+    shm_data* shared_data = static_cast<shm_data*>(
+        mmap(nullptr, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0));
+    if (shared_data == MAP_FAILED) {
+        perror("mmap shared memory failed");
+        close(shm_fd);
+        close(cam_fd);
+        return 1;
+    }
+
+    // 初始化共享数据
+    shared_data->width = fmt.fmt.pix.width;
+    shared_data->height = fmt.fmt.pix.height;
+    shared_data->format = fmt.fmt.pix.pixelformat;
+
+    // 开始捕获
+    if (ioctl(cam_fd, VIDIOC_STREAMON, &buf.type) == -1) {
+        perror("start stream failed");
+        close(shm_fd);
+        close(cam_fd);
+        return 1;
+    }
+
+    // 捕获循环
+    while (true) {
+        if (ioctl(cam_fd, VIDIOC_QBUF, &buf) == -1) {
+            perror("queue buffer failed");
+            break;
+        }
+
+        if (ioctl(cam_fd, VIDIOC_DQBUF, &buf) == -1) {
+            perror("dequeue buffer failed");
+            break;
+        }
+
+        // 复制数据到共享内存(带实际数据大小)
+        memcpy(shared_data->data, cam_buffer, buf.bytesused);
+        shared_data->size = buf.bytesused;  // 记录实际数据大小
+    }
+
+    // 清理资源
+    ioctl(cam_fd, VIDIOC_STREAMOFF, &buf.type);
+    munmap(cam_buffer, buf.length);
+    munmap(shared_data, shm_size);
+    close(cam_fd);
+    close(shm_fd);
+    shm_unlink("/camera_shm");
+
+    return 0;
+}

+ 81 - 0
src/read.cpp

@@ -0,0 +1,81 @@
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <linux/videodev2.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <iostream>
+#include <opencv2/opencv.hpp>
+#include <vector>
+
+struct shm_data {
+    int width;
+    int height;
+    int format;
+    int size;       // 新增:存储实际数据大小
+    char data[0];    // 柔性数组
+};
+
+int main() {
+    // 打开共享内存
+    int shm_fd = shm_open("/camera_shm", O_RDONLY, 0666);
+    if (shm_fd == -1) {
+        perror("shm_open failed");
+        return 1;
+    }
+
+    // 获取共享内存大小
+    struct stat sb;
+    if (fstat(shm_fd, &sb) == -1) {
+        perror("fstat failed");
+        close(shm_fd);
+        return 1;
+    }
+    size_t shm_size = sb.st_size;
+
+    // 映射共享内存
+    shm_data* shared_data = static_cast<shm_data*>(
+        mmap(nullptr, shm_size, PROT_READ, MAP_SHARED, shm_fd, 0));
+    if (shared_data == MAP_FAILED) {
+        perror("mmap failed");
+        close(shm_fd);
+        return 1;
+    }
+
+    // 验证格式
+    if (shared_data->format != V4L2_PIX_FMT_MJPEG) { // 修改格式检查
+        std::cerr << "Unsupported format" << std::endl;
+        munmap(shared_data, shm_size);
+        close(shm_fd);
+        return 1;
+    }
+
+    // 创建OpenCV窗口
+    cv::namedWindow("Camera Stream", cv::WINDOW_AUTOSIZE);
+
+    while (true) {
+        // 创建JPEG数据缓冲区
+        std::vector<uchar> jpeg_data(
+            shared_data->data,
+            shared_data->data + shared_data->size
+        );
+
+        // 解码JPEG数据
+        cv::Mat frame = cv::imdecode(jpeg_data, cv::IMREAD_COLOR);
+
+        if (!frame.empty()) {
+            cv::imshow("Camera Stream", frame);
+        } else {
+            std::cerr << "Decode frame failed!" << std::endl;
+        }
+
+        // 按ESC退出
+        if (cv::waitKey(30) == 27) break;
+    }
+
+    // 清理资源
+    munmap(shared_data, shm_size);
+    close(shm_fd);
+    cv::destroyAllWindows();
+
+    return 0;
+}