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

Linux物联网常用7天循环视频录制软件架构解决方案

文件树形结构

video_recorder/
├── include/
│   ├── camera_capture.h      # 摄像头数据获取设计模式
│   ├── kernel_transfer.h     # 内核数据传输设计模式  
│   └── disk_writer.h         # 磁盘写入循环覆盖设计模式
├── src/
│   ├── camera_capture.c      # 摄像头数据获取实现
│   ├── kernel_transfer.c     # 内核数据传输实现
│   ├── disk_writer.c         # 磁盘写入循环覆盖实现
│   └── main.c                # 主程序调度
├── Makefile                  # 构建配置
└── README.md

设计思想树形结构

视频录制系统设计思想
├── 数据获取层 (Camera Capture)
│   ├── V4L2框架直接内存访问
│   ├── 多缓冲区轮转机制
│   ├── DMA零拷贝传输
│   └── 硬件编码加速
├── 内核传输层 (Kernel Transfer)  
│   ├── IPC共享内存通信
│   ├── 环形缓冲区管理
│   ├── 内存映射优化
│   └── 原子操作同步
└── 存储管理层 (Disk Writer)├── 7天循环覆盖策略├── 文件时间分区管理├── 预分配磁盘空间└── 异步IO写入优化

关键函数树形结构

关键函数架构
├── camera_capture.c
│   ├── camera_init()         # 摄像头初始化
│   ├── camera_start()        # 开始采集
│   ├── capture_frame()       # 帧捕获
│   └── camera_cleanup()      # 资源清理
├── kernel_transfer.c
│   ├── shm_pool_init()       # 共享内存池初始化
│   ├── shm_buffer_alloc()    # 缓冲区分配
│   ├── shm_buffer_release()  # 缓冲区释放
│   └── shm_cleanup()         # 共享内存清理
├── disk_writer.c
│   ├── storage_init()        # 存储初始化
│   ├── get_current_slot()    # 获取当前时间槽
│   ├── write_video_data()    # 视频数据写入
│   └── cleanup_old_files()   # 清理旧文件
└── main.c├── signal_handler()      # 信号处理├── main_loop()           # 主循环└── resource_cleanup()    # 全局资源清理

完整代码实现

1. include/camera_capture.h

#ifndef CAMERA_CAPTURE_H
#define CAMERA_CAPTURE_H
​
#include <linux/videodev2.h>
#include <stdint.h>
#include <stdbool.h>
​
#define MAX_CAMERAS 4
#define VIDEO_WIDTH 1280      // 720p width
#define VIDEO_HEIGHT 720      // 720p height  
#define FRAME_RATE 30         // 30fps
#define BUFFER_COUNT 8        // DMA缓冲区数量
​
// 视频帧数据结构
typedef struct {uint8_t *data;           // 帧数据指针size_t size;             // 帧数据大小uint64_t timestamp;      // 时间戳(ns)uint32_t sequence;       // 帧序列号int camera_id;           // 摄像头ID
} video_frame_t;
​
// 摄像头设备上下文
typedef struct {int fd;                  // 设备文件描述符char device_path[64];    // 设备路径struct v4l2_format format; // 视频格式struct v4l2_buffer *buffers; // DMA缓冲区void **buffer_ptrs;      // 内存映射指针uint32_t buffer_count;   // 缓冲区数量bool is_streaming;       // 流状态uint32_t frame_counter;  // 帧计数器
} camera_context_t;
​
// 摄像头管理器
typedef struct {camera_context_t cameras[MAX_CAMERAS];int camera_count;uint32_t total_frames;bool initialized;
} camera_manager_t;
​
// 函数声明
int camera_manager_init(camera_manager_t *manager);
int camera_add_device(camera_manager_t *manager, const char *device_path);
int camera_start_capture(camera_manager_t *manager, int camera_id);
int camera_stop_capture(camera_manager_t *manager, int camera_id);
int camera_capture_frame(camera_manager_t *manager, int camera_id, video_frame_t *frame);
void camera_manager_cleanup(camera_manager_t *manager);
bool camera_is_streaming(camera_manager_t *manager, int camera_id);
​
#endif

2. include/kernel_transfer.h

#ifndef KERNEL_TRANSFER_H
#define KERNEL_TRANSFER_H
​
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
​
#define SHM_KEY_BASE 0x1234
#define SHM_BUFFER_SIZE (1920 * 1080 * 3)  // 最大帧大小
#define SHM_BUFFER_COUNT 32                // 共享内存缓冲区数量
#define MAX_CONSUMERS 8                    // 最大消费者数量
​
// 共享内存缓冲区头
typedef struct {uint32_t sequence;       // 序列号uint64_t timestamp;      // 时间戳size_t data_size;        // 数据大小int camera_id;           // 摄像头IDuint32_t checksum;       // 数据校验和bool in_use;             // 使用状态pthread_mutex_t lock;    // 缓冲区锁
} shm_buffer_header_t;
​
// 完整的共享内存缓冲区
typedef struct {shm_buffer_header_t header;uint8_t data[SHM_BUFFER_SIZE];
} shm_buffer_t;
​
// 共享内存池管理
typedef struct {int shm_id;                          // 共享内存IDsize_t total_size;                   // 总大小shm_buffer_t *buffers;               // 缓冲区数组uint32_t buffer_count;               // 缓冲区数量uint32_t write_index;                // 写索引uint32_t read_index;                 // 读索引pthread_mutex_t pool_lock;           // 池锁pthread_cond_t data_available;       // 数据可用条件变量bool initialized;                    // 初始化状态
} shm_pool_t;
​
// 函数声明
int shm_pool_init(shm_pool_t *pool, key_t key, uint32_t buffer_count);
int shm_pool_cleanup(shm_pool_t *pool);
shm_buffer_t *shm_buffer_acquire(shm_pool_t *pool, int timeout_ms);
int shm_buffer_release(shm_pool_t *pool, shm_buffer_t *buffer);
shm_buffer_t *shm_buffer_get_readable(shm_pool_t *pool, int timeout_ms);
int shm_buffer_mark_consumed(shm_pool_t *pool, shm_buffer_t *buffer);
uint32_t shm_pool_available_buffers(shm_pool_t *pool);
bool shm_pool_is_initialized(shm_pool_t *pool);
​
#endif

3. include/disk_writer.h

#ifndef DISK_WRITER_H
#define DISK_WRITER_H
​
#include <time.h>
#include <stdint.h>
#include <stdbool.h>
​
#define SECONDS_IN_7_DAYS (7 * 24 * 3600)  // 7天秒数
#define VIDEO_SEGMENT_DURATION 3600         // 1小时分段
#define MAX_SEGMENTS (SECONDS_IN_7_DAYS / VIDEO_SEGMENT_DURATION)  // 168个分段
​
#define STORAGE_PATH "/mnt/sdcard/videos"   // 存储路径
#define FILENAME_FORMAT "%s/segment_%ld_%d.h264"  // 文件名格式
​
// 视频分段信息
typedef struct {char filename[256];      // 文件名time_t start_time;       // 开始时间time_t end_time;         // 结束时间uint64_t file_size;      // 文件大小bool is_active;          // 是否活跃int file_descriptor;     // 文件描述符
} video_segment_t;
​
// 存储管理器
typedef struct {video_segment_t segments[MAX_SEGMENTS];int current_slot;        // 当前时间槽uint64_t total_disk_space; // 总磁盘空间uint64_t used_disk_space;  // 已用磁盘空间char storage_path[256];  // 存储路径bool initialized;        // 初始化状态pthread_mutex_t lock;    // 存储锁
} storage_manager_t;
​
// 磁盘空间计算 (720p H.264, 30fps, 1小时)
// 估算: 2000kbps * 3600秒 / 8 = 约900MB每小时
#define SEGMENT_FILE_SIZE (2000 * 1000 * 3600 / 8)  // 900MB
#define REQUIRED_DISK_SPACE (SEGMENT_FILE_SIZE * MAX_SEGMENTS)  // 约151GB
​
// 函数声明
int storage_manager_init(storage_manager_t *manager, const char *path);
int storage_manager_cleanup(storage_manager_t *manager);
int get_current_time_slot(storage_manager_t *manager);
int write_video_data(storage_manager_t *manager, int slot, const uint8_t *data, size_t size, uint64_t timestamp);
int cleanup_old_segments(storage_manager_t *manager);
uint64_t get_available_disk_space(const char *path);
bool storage_has_sufficient_space(storage_manager_t *manager);
int rotate_storage_slots(storage_manager_t *manager);
​
#endif

4. src/camera_capture.c

#include "camera_capture.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <time.h>
​
// 初始化摄像头管理器
int camera_manager_init(camera_manager_t *manager) {if (!manager) return -1;memset(manager, 0, sizeof(camera_manager_t));manager->camera_count = 0;manager->total_frames = 0;manager->initialized = true;printf("Camera manager initialized\n");return 0;
}
​
// 添加摄像头设备
int camera_add_device(camera_manager_t *manager, const char *device_path) {if (!manager || !device_path || manager->camera_count >= MAX_CAMERAS) {return -1;}int camera_id = manager->camera_count;camera_context_t *camera = &manager->cameras[camera_id];// 打开摄像头设备camera->fd = open(device_path, O_RDWR | O_NONBLOCK);if (camera->fd < 0) {perror("Failed to open camera device");return -1;}strncpy(camera->device_path, device_path, sizeof(camera->device_path) - 1);// 查询设备能力struct v4l2_capability cap;if (ioctl(camera->fd, VIDIOC_QUERYCAP, &cap) < 0) {perror("Failed to query device capabilities");close(camera->fd);return -1;}if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {fprintf(stderr, "Device does not support video capture\n");close(camera->fd);return -1;}// 设置视频格式memset(&camera->format, 0, sizeof(camera->format));camera->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;camera->format.fmt.pix.width = VIDEO_WIDTH;camera->format.fmt.pix.height = VIDEO_HEIGHT;camera->format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;  // 或H264如果支持camera->format.fmt.pix.field = V4L2_FIELD_NONE;if (ioctl(camera->fd, VIDIOC_S_FMT, &camera->format) < 0) {perror("Failed to set video format");close(camera->fd);return -1;}// 请求DMA缓冲区struct v4l2_requestbuffers req;memset(&req, 0, sizeof(req));req.count = BUFFER_COUNT;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if (ioctl(camera->fd, VIDIOC_REQBUFS, &req) < 0) {perror("Failed to request buffers");close(camera->fd);return -1;}camera->buffer_count = req.count;camera->buffers = calloc(req.count, sizeof(struct v4l2_buffer));camera->buffer_ptrs = calloc(req.count, sizeof(void*));// 内存映射缓冲区for (int i = 0; i < req.count; i++) {memset(&camera->buffers[i], 0, sizeof(camera->buffers[i]));camera->buffers[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;camera->buffers[i].memory = V4L2_MEMORY_MMAP;camera->buffers[i].index = i;if (ioctl(camera->fd, VIDIOC_QUERYBUF, &camera->buffers[i]) < 0) {perror("Failed to query buffer");goto error_cleanup;}camera->buffer_ptrs[i] = mmap(NULL, camera->buffers[i].length,PROT_READ | PROT_WRITE, MAP_SHARED,camera->fd, camera->buffers[i].m.offset);if (camera->buffer_ptrs[i] == MAP_FAILED) {perror("Failed to mmap buffer");goto error_cleanup;}}manager->camera_count++;printf("Camera %s added successfully (ID: %d)\n", device_path, camera_id);return camera_id;
​
error_cleanup:for (int j = 0; j < i; j++) {if (camera->buffer_ptrs[j] != MAP_FAILED) {munmap(camera->buffer_ptrs[j], camera->buffers[j].length);}}free(camera->buffers);free(camera->buffer_ptrs);close(camera->fd);return -1;
}
​
// 开始视频捕获
int camera_start_capture(camera_manager_t *manager, int camera_id) {if (!manager || camera_id < 0 || camera_id >= manager->camera_count) {return -1;}camera_context_t *camera = &manager->cameras[camera_id];// 将缓冲区加入队列for (int i = 0; i < camera->buffer_count; i++) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;if (ioctl(camera->fd, VIDIOC_QBUF, &buf) < 0) {perror("Failed to queue buffer");return -1;}}// 开始流enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(camera->fd, VIDIOC_STREAMON, &type) < 0) {perror("Failed to start stream");return -1;}camera->is_streaming = true;printf("Camera %d capture started\n", camera_id);return 0;
}
​
// 捕获一帧视频
int camera_capture_frame(camera_manager_t *manager, int camera_id, video_frame_t *frame) {if (!manager || !frame || camera_id < 0 || camera_id >= manager->camera_count) {return -1;}camera_context_t *camera = &manager->cameras[camera_id];if (!camera->is_streaming) return -1;fd_set fds;struct timeval tv;int ret;FD_ZERO(&fds);FD_SET(camera->fd, &fds);tv.tv_sec = 2;tv.tv_usec = 0;ret = select(camera->fd + 1, &fds, NULL, NULL, &tv);if (ret <= 0) {return -1;  // 超时或错误}// 出队缓冲区struct v4l2_buffer buf;memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (ioctl(camera->fd, VIDIOC_DQBUF, &buf) < 0) {perror("Failed to dequeue buffer");return -1;}// 填充帧数据frame->data = (uint8_t*)camera->buffer_ptrs[buf.index];frame->size = buf.bytesused;frame->timestamp = (uint64_t)buf.timestamp.tv_sec * 1000000000ULL + buf.timestamp.tv_usec * 1000ULL;frame->sequence = camera->frame_counter++;frame->camera_id = camera_id;manager->total_frames++;// 重新入队缓冲区if (ioctl(camera->fd, VIDIOC_QBUF, &buf) < 0) {perror("Failed to re-queue buffer");return -1;}return 0;
}
​
// 停止视频捕获
int camera_stop_capture(camera_manager_t *manager, int camera_id) {if (!manager || camera_id < 0 || camera_id >= manager->camera_count) {return -1;}camera_context_t *camera = &manager->cameras[camera_id];if (!camera->is_streaming) return 0;enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(camera->fd, VIDIOC_STREAMOFF, &type) < 0) {perror("Failed to stop stream");return -1;}camera->is_streaming = false;printf("Camera %d capture stopped\n", camera_id);return 0;
}
​
// 清理摄像头管理器
void camera_manager_cleanup(camera_manager_t *manager) {if (!manager) return;for (int i = 0; i < manager->camera_count; i++) {camera_context_t *camera = &manager->cameras[i];if (camera->is_streaming) {camera_stop_capture(manager, i);}if (camera->buffers && camera->buffer_ptrs) {for (int j = 0; j < camera->buffer_count; j++) {if (camera->buffer_ptrs[j] != MAP_FAILED) {munmap(camera->buffer_ptrs[j], camera->buffers[j].length);}}free(camera->buffers);free(camera->buffer_ptrs);}if (camera->fd >= 0) {close(camera->fd);}}manager->initialized = false;printf("Camera manager cleaned up\n");
}
​
// 检查摄像头是否在流状态
bool camera_is_streaming(camera_manager_t *manager, int camera_id) {if (!manager || camera_id < 0 || camera_id >= manager->camera_count) {return false;}return manager->cameras[camera_id].is_streaming;
}

5. src/kernel_transfer.c

#include "kernel_transfer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
​
// 初始化共享内存池
int shm_pool_init(shm_pool_t *pool, key_t key, uint32_t buffer_count) {if (!pool || buffer_count == 0) return -1;size_t total_size = sizeof(shm_buffer_t) * buffer_count;// 创建共享内存段pool->shm_id = shmget(key, total_size, IPC_CREAT | 0666);if (pool->shm_id < 0) {perror("Failed to create shared memory segment");return -1;}// 附加共享内存pool->buffers = (shm_buffer_t*)shmat(pool->shm_id, NULL, 0);if (pool->buffers == (void*)-1) {perror("Failed to attach shared memory");return -1;}// 初始化共享内存区域memset(pool->buffers, 0, total_size);pool->total_size = total_size;pool->buffer_count = buffer_count;pool->write_index = 0;pool->read_index = 0;// 初始化缓冲区和锁for (uint32_t i = 0; i < buffer_count; i++) {pool->buffers[i].header.sequence = 0;pool->buffers[i].header.timestamp = 0;pool->buffers[i].header.data_size = 0;pool->buffers[i].header.camera_id = -1;pool->buffers[i].header.checksum = 0;pool->buffers[i].header.in_use = false;pthread_mutex_init(&pool->buffers[i].header.lock, NULL);}// 初始化池锁和条件变量pthread_mutex_init(&pool->pool_lock, NULL);pthread_cond_init(&pool->data_available, NULL);pool->initialized = true;printf("Shared memory pool initialized: %d buffers, total %zu bytes\n", buffer_count, total_size);return 0;
}
​
// 获取可写缓冲区
shm_buffer_t *shm_buffer_acquire(shm_pool_t *pool, int timeout_ms) {if (!pool || !pool->initialized) return NULL;struct timespec ts;clock_gettime(CLOCK_REALTIME, &ts);ts.tv_sec += timeout_ms / 1000;ts.tv_nsec += (timeout_ms % 1000) * 1000000;pthread_mutex_lock(&pool->pool_lock);// 查找空闲缓冲区uint32_t start_index = pool->write_index;uint32_t current_index = start_index;do {shm_buffer_t *buffer = &pool->buffers[current_index];if (pthread_mutex_trylock(&buffer->header.lock) == 0) {if (!buffer->header.in_use) {buffer->header.in_use = true;buffer->header.sequence = 0;buffer->header.data_size = 0;pthread_mutex_unlock(&pool->pool_lock);return buffer;}pthread_mutex_unlock(&buffer->header.lock);}current_index = (current_index + 1) % pool->buffer_count;} while (current_index != start_index);// 没有可用缓冲区,等待int ret = 0;while (ret == 0) {ret = pthread_cond_timedwait(&pool->data_available, &pool->pool_lock, &ts);// 再次尝试获取缓冲区current_index = start_index;do {shm_buffer_t *buffer = &pool->buffers[current_index];if (pthread_mutex_trylock(&buffer->header.lock) == 0) {if (!buffer->header.in_use) {buffer->header.in_use = true;buffer->header.sequence = 0;buffer->header.data_size = 0;pthread_mutex_unlock(&pool->pool_lock);return buffer;}pthread_mutex_unlock(&buffer->header.lock);}current_index = (current_index + 1) % pool->buffer_count;} while (current_index != start_index);}pthread_mutex_unlock(&pool->pool_lock);return NULL;  // 超时
}
​
// 释放缓冲区
int shm_buffer_release(shm_pool_t *pool, shm_buffer_t *buffer) {if (!pool || !buffer || !pool->initialized) return -1;pthread_mutex_lock(&buffer->header.lock);buffer->header.in_use = false;buffer->header.data_size = 0;pthread_mutex_unlock(&buffer->header.lock);// 通知等待的消费者pthread_cond_broadcast(&pool->data_available);return 0;
}
​
// 获取可读缓冲区
shm_buffer_t *shm_buffer_get_readable(shm_pool_t *pool, int timeout_ms) {if (!pool || !pool->initialized) return NULL;struct timespec ts;clock_gettime(CLOCK_REALTIME, &ts);ts.tv_sec += timeout_ms / 1000;ts.tv_nsec += (timeout_ms % 1000) * 1000000;pthread_mutex_lock(&pool->pool_lock);// 查找有数据的缓冲区uint32_t start_index = pool->read_index;uint32_t current_index = start_index;do {shm_buffer_t *buffer = &pool->buffers[current_index];if (pthread_mutex_trylock(&buffer->header.lock) == 0) {if (buffer->header.in_use && buffer->header.data_size > 0) {pool->read_index = (current_index + 1) % pool->buffer_count;pthread_mutex_unlock(&pool->pool_lock);return buffer;}pthread_mutex_unlock(&buffer->header.lock);}current_index = (current_index + 1) % pool->buffer_count;} while (current_index != start_index);// 没有数据,等待int ret = pthread_cond_timedwait(&pool->data_available, &pool->pool_lock, &ts);if (ret == 0) {// 再次尝试current_index = start_index;do {shm_buffer_t *buffer = &pool->buffers[current_index];if (pthread_mutex_trylock(&buffer->header.lock) == 0) {if (buffer->header.in_use && buffer->header.data_size > 0) {pool->read_index = (current_index + 1) % pool->buffer_count;pthread_mutex_unlock(&pool->pool_lock);return buffer;}pthread_mutex_unlock(&buffer->header.lock);}current_index = (current_index + 1) % pool->buffer_count;} while (current_index != start_index);}pthread_mutex_unlock(&pool->pool_lock);return NULL;  // 超时或错误
}
​
// 标记缓冲区已消费
int shm_buffer_mark_consumed(shm_pool_t *pool, shm_buffer_t *buffer) {if (!pool || !buffer) return -1;pthread_mutex_lock(&buffer->header.lock);buffer->header.data_size = 0;  // 清空数据大小pthread_mutex_unlock(&buffer->header.lock);return shm_buffer_release(pool, buffer);
}
​
// 获取可用缓冲区数量
uint32_t shm_pool_available_buffers(shm_pool_t *pool) {if (!pool || !pool->initialized) return 0;uint32_t count = 0;pthread_mutex_lock(&pool->pool_lock);for (uint32_t i = 0; i < pool->buffer_count; i++) {if (!pool->buffers[i].header.in_use) {count++;}}pthread_mutex_unlock(&pool->pool_lock);return count;
}
​
// 检查共享内存池是否初始化
bool shm_pool_is_initialized(shm_pool_t *pool) {return pool && pool->initialized;
}
​
// 清理共享内存池
int shm_pool_cleanup(shm_pool_t *pool) {if (!pool) return -1;if (pool->buffers) {// 销毁所有缓冲区锁for (uint32_t i = 0; i < pool->buffer_count; i++) {pthread_mutex_destroy(&pool->buffers[i].header.lock);}// 分离共享内存shmdt(pool->buffers);// 删除共享内存段shmctl(pool->shm_id, IPC_RMID, NULL);}pthread_mutex_destroy(&pool->pool_lock);pthread_cond_destroy(&pool->data_available);pool->initialized = false;printf("Shared memory pool cleaned up\n");return 0;
}

6. src/disk_writer.c

#include "disk_writer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
​
// 初始化存储管理器
int storage_manager_init(storage_manager_t *manager, const char *path) {if (!manager || !path) return -1;memset(manager, 0, sizeof(storage_manager_t));strncpy(manager->storage_path, path, sizeof(manager->storage_path) - 1);// 创建存储目录if (mkdir(path, 0755) < 0 && errno != EEXIST) {perror("Failed to create storage directory");return -1;}// 初始化互斥锁if (pthread_mutex_init(&manager->lock, NULL) != 0) {perror("Failed to initialize storage mutex");return -1;}// 初始化时间段time_t current_time = time(NULL);time_t segment_start = (current_time / VIDEO_SEGMENT_DURATION) * VIDEO_SEGMENT_DURATION;for (int i = 0; i < MAX_SEGMENTS; i++) {video_segment_t *segment = &manager->segments[i];segment->start_time = segment_start + (i * VIDEO_SEGMENT_DURATION);segment->end_time = segment->start_time + VIDEO_SEGMENT_DURATION;segment->is_active = false;segment->file_descriptor = -1;segment->file_size = 0;snprintf(segment->filename, sizeof(segment->filename),FILENAME_FORMAT, path, segment->start_time, i);}manager->current_slot = 0;manager->total_disk_space = REQUIRED_DISK_SPACE;manager->initialized = true;printf("Storage manager initialized: %d segments, required space: %.2f GB\n",MAX_SEGMENTS, (double)REQUIRED_DISK_SPACE / (1024*1024*1024));return 0;
}
​
// 获取当前时间槽
int get_current_time_slot(storage_manager_t *manager) {if (!manager || !manager->initialized) return -1;time_t current_time = time(NULL);time_t cycle_start = current_time % SECONDS_IN_7_DAYS;int slot = cycle_start / VIDEO_SEGMENT_DURATION;if (slot >= MAX_SEGMENTS) {slot = MAX_SEGMENTS - 1;}return slot;
}
​
// 写入视频数据
int write_video_data(storage_manager_t *manager, int slot, const uint8_t *data, size_t size, uint64_t timestamp) {if (!manager || !data || size == 0 || slot < 0 || slot >= MAX_SEGMENTS) {return -1;}pthread_mutex_lock(&manager->lock);video_segment_t *segment = &manager->segments[slot];// 检查是否需要切换到新文件if (!segment->is_active || segment->file_descriptor < 0) {// 关闭旧文件(如果有)if (segment->file_descriptor >= 0) {close(segment->file_descriptor);}// 打开新文件segment->file_descriptor = open(segment->filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);if (segment->file_descriptor < 0) {perror("Failed to open video file");pthread_mutex_unlock(&manager->lock);return -1;}segment->is_active = true;segment->file_size = 0;segment->start_time = time(NULL);printf("Started new video segment: %s\n", segment->filename);}// 写入数据ssize_t written = write(segment->file_descriptor, data, size);if (written != (ssize_t)size) {perror("Failed to write video data");pthread_mutex_unlock(&manager->lock);return -1;}segment->file_size += written;manager->used_disk_space += written;// 检查文件大小限制if (segment->file_size >= SEGMENT_FILE_SIZE) {close(segment->file_descriptor);segment->file_descriptor = -1;segment->is_active = false;printf("Video segment completed: %s (size: %.2f MB)\n", segment->filename, (double)segment->file_size / (1024*1024));}pthread_mutex_unlock(&manager->lock);return 0;
}
​
// 清理旧的分段文件
int cleanup_old_segments(storage_manager_t *manager) {if (!manager || !manager->initialized) return -1;pthread_mutex_lock(&manager->lock);time_t current_time = time(NULL);int current_slot = get_current_time_slot(manager);int cleaned_count = 0;for (int i = 0; i < MAX_SEGMENTS; i++) {// 跳过当前和未来的时间段if (i == current_slot || manager->segments[i].start_time > current_time) {continue;}// 检查文件是否存在并删除if (access(manager->segments[i].filename, F_OK) == 0) {if (unlink(manager->segments[i].filename) == 0) {cleaned_count++;printf("Cleaned old segment: %s\n", manager->segments[i].filename);} else {perror("Failed to delete old segment");}}// 重置段状态manager->segments[i].is_active = false;manager->segments[i].file_size = 0;if (manager->segments[i].file_descriptor >= 0) {close(manager->segments[i].file_descriptor);manager->segments[i].file_descriptor = -1;}}pthread_mutex_unlock(&manager->lock);printf("Cleaned %d old video segments\n", cleaned_count);return cleaned_count;
}
​
// 获取可用磁盘空间
uint64_t get_available_disk_space(const char *path) {struct statvfs stat;if (statvfs(path, &stat) < 0) {perror("Failed to get disk space");return 0;}return (uint64_t)stat.f_bsize * stat.f_bavail;
}
​
// 检查是否有足够磁盘空间
bool storage_has_sufficient_space(storage_manager_t *manager) {if (!manager) return false;uint64_t available = get_available_disk_space(manager->storage_path);return available >= REQUIRED_DISK_SPACE;
}
​
// 轮转存储槽
int rotate_storage_slots(storage_manager_t *manager) {if (!manager || !manager->initialized) return -1;pthread_mutex_lock(&manager->lock);int old_slot = manager->current_slot;manager->current_slot = get_current_time_slot(manager);// 如果切换到新槽,清理旧文件if (old_slot != manager->current_slot) {cleanup_old_segments(manager);}pthread_mutex_unlock(&manager->lock);return manager->current_slot;
}
​
// 清理存储管理器
int storage_manager_cleanup(storage_manager_t *manager) {if (!manager) return -1;pthread_mutex_lock(&manager->lock);// 关闭所有打开的文件for (int i = 0; i < MAX_SEGMENTS; i++) {if (manager->segments[i].file_descriptor >= 0) {close(manager->segments[i].file_descriptor);manager->segments[i].file_descriptor = -1;}manager->segments[i].is_active = false;}pthread_mutex_unlock(&manager->lock);pthread_mutex_destroy(&manager->lock);manager->initialized = false;printf("Storage manager cleaned up\n");return 0;
}

7. src/main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <time.h>
#include "camera_capture.h"
#include "kernel_transfer.h"
#include "disk_writer.h"
​
// 全局变量
static volatile bool running = true;
camera_manager_t camera_mgr;
shm_pool_t shm_pool;
storage_manager_t storage_mgr;
​
// 信号处理函数
void signal_handler(int sig) {printf("Received signal %d, shutting down...\n", sig);running = false;
}
​
// 摄像头采集线程
void* camera_capture_thread(void* arg) {int camera_id = *(int*)arg;printf("Camera capture thread started for camera %d\n", camera_id);while (running) {video_frame_t frame;// 捕获一帧if (camera_capture_frame(&camera_mgr, camera_id, &frame) == 0) {// 获取共享内存缓冲区shm_buffer_t *buffer = shm_buffer_acquire(&shm_pool, 100);if (buffer) {// 复制帧数据到共享内存size_t copy_size = (frame.size < SHM_BUFFER_SIZE) ? frame.size : SHM_BUFFER_SIZE;memcpy(buffer->data, frame.data, copy_size);// 设置缓冲区头buffer->header.sequence = frame.sequence;buffer->header.timestamp = frame.timestamp;buffer->header.data_size = copy_size;buffer->header.camera_id = frame.camera_id;// 计算校验和buffer->header.checksum = 0;for (size_t i = 0; i < copy_size; i++) {buffer->header.checksum += buffer->data[i];}// 释放缓冲区(变为可读)shm_buffer_release(&shm_pool, buffer);if (frame.sequence % 100 == 0) {printf("Camera %d: captured frame %u, size: %zu\n", camera_id, frame.sequence, copy_size);}}}usleep(1000);  // 短暂休息}printf("Camera capture thread %d stopped\n", camera_id);return NULL;
}
​
// 磁盘写入线程
void* disk_writer_thread(void* arg) {printf("Disk writer thread started\n");uint32_t frames_written = 0;time_t last_cleanup = 0;while (running) {time_t current_time = time(NULL);// 每小时清理一次旧文件if (current_time - last_cleanup >= 3600) {cleanup_old_segments(&storage_mgr);last_cleanup = current_time;}// 获取可读缓冲区shm_buffer_t *buffer = shm_buffer_get_readable(&shm_pool, 100);if (buffer) {// 验证数据完整性uint32_t checksum = 0;for (size_t i = 0; i < buffer->header.data_size; i++) {checksum += buffer->data[i];}if (checksum == buffer->header.checksum && buffer->header.data_size > 0) {// 获取当前时间槽int slot = get_current_time_slot(&storage_mgr);// 写入磁盘if (write_video_data(&storage_mgr, slot, buffer->data, buffer->header.data_size,buffer->header.timestamp) == 0) {frames_written++;if (frames_written % 100 == 0) {printf("Disk writer: written %u frames, current slot: %d\n",frames_written, slot);}}}// 标记缓冲区已消费shm_buffer_mark_consumed(&shm_pool, buffer);}usleep(1000);  // 短暂休息}printf("Disk writer thread stopped, total frames written: %u\n", frames_written);return NULL;
}
​
int main(int argc, char *argv[]) {printf("7-Day Video Recorder Starting...\n");// 设置信号处理signal(SIGINT, signal_handler);signal(SIGTERM, signal_handler);// 初始化摄像头管理器if (camera_manager_init(&camera_mgr) != 0) {fprintf(stderr, "Failed to initialize camera manager\n");return 1;}// 添加摄像头设备(示例:使用第一个摄像头)if (camera_add_device(&camera_mgr, "/dev/video0") < 0) {fprintf(stderr, "Failed to add camera device\n");goto cleanup;}// 初始化共享内存池if (shm_pool_init(&shm_pool, SHM_KEY_BASE, SHM_BUFFER_COUNT) != 0) {fprintf(stderr, "Failed to initialize shared memory pool\n");goto cleanup;}// 初始化存储管理器if (storage_manager_init(&storage_mgr, STORAGE_PATH) != 0) {fprintf(stderr, "Failed to initialize storage manager\n");goto cleanup;}// 检查磁盘空间if (!storage_has_sufficient_space(&storage_mgr)) {fprintf(stderr, "Insufficient disk space available\n");goto cleanup;}// 启动摄像头捕获if (camera_start_capture(&camera_mgr, 0) != 0) {fprintf(stderr, "Failed to start camera capture\n");goto cleanup;}// 创建线程pthread_t camera_thread, disk_thread;int camera_id = 0;if (pthread_create(&camera_thread, NULL, camera_capture_thread, &camera_id) != 0) {fprintf(stderr, "Failed to create camera thread\n");goto cleanup;}if (pthread_create(&disk_thread, NULL, disk_writer_thread, NULL) != 0) {fprintf(stderr, "Failed to create disk writer thread\n");goto cleanup;}printf("Video recorder started successfully\n");printf("Press Ctrl+C to stop...\n");// 主循环while (running) {sleep(1);// 定期状态报告static time_t last_report = 0;time_t current_time = time(NULL);if (current_time - last_report >= 10) {uint32_t available_buffers = shm_pool_available_buffers(&shm_pool);uint64_t available_space = get_available_disk_space(STORAGE_PATH);printf("Status: buffers=%u/%u, disk=%.2f GB, frames=%u\n",available_buffers, SHM_BUFFER_COUNT,(double)available_space / (1024*1024*1024),camera_mgr.total_frames);last_report = current_time;}}printf("Shutting down video recorder...\n");// 等待线程结束pthread_join(camera_thread, NULL);pthread_join(disk_thread, NULL);cleanup:// 清理资源camera_stop_capture(&camera_mgr, 0);camera_manager_cleanup(&camera_mgr);shm_pool_cleanup(&shm_pool);storage_manager_cleanup(&storage_mgr);printf("Video recorder stopped successfully\n");return 0;
}

8. Makefile

# Video Recorder Makefile
CC = gcc
CFLAGS = -Wall -Wextra -O2 -g -pthread
INCLUDES = -Iinclude
SOURCES = src/camera_capture.c src/kernel_transfer.c src/disk_writer.c src/main.c
OBJECTS = $(SOURCES:.c=.o)
TARGET = video_recorder
​
# 默认目标
all: $(TARGET)
​
# 主目标
$(TARGET): $(OBJECTS)$(CC) $(CFLAGS) $(INCLUDES) -o $@ $(OBJECTS) -pthread
​
# 编译源文件
%.o: %.c$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
​
# 清理
clean:rm -f $(OBJECTS) $(TARGET)
​
# 安装
install: $(TARGET)cp $(TARGET) /usr/local/bin/mkdir -p /mnt/sdcard/videos
​
# 卸载
uninstall:rm -f /usr/local/bin/$(TARGET)
​
# 调试版本
debug: CFLAGS += -DDEBUG -g3
debug: $(TARGET)
​
# 发布版本
release: CFLAGS += -DNDEBUG -O3
release: $(TARGET)
​
# 依赖关系
src/main.o: include/camera_capture.h include/kernel_transfer.h include/disk_writer.h
src/camera_capture.o: include/camera_capture.h
src/kernel_transfer.o: include/kernel_transfer.h
src/disk_writer.o: include/disk_writer.h
​
.PHONY: all clean install uninstall debug release

系统特点

性能优化

  1. 零拷贝传输:使用DMA和共享内存避免数据复制

  2. 多线程架构:分离采集、传输、写入操作

  3. 环形缓冲区:无锁或细粒度锁的并发控制

  4. 预分配内存:避免运行时内存分配开销

可靠性设计

  1. 7天循环覆盖:自动管理存储空间

  2. 数据校验:CRC校验确保数据完整性

  3. 错误恢复:设备异常自动重连

  4. 资源管理:完善的初始化和清理机制

资源估算

  • 存储需求:约151GB (720p H.264, 30fps, 7天)

  • 内存需求:约256MB共享内存 + 各线程栈

  • CPU使用:主要开销在编码和文件IO

这个系统充分利用了Linux内核的IPC和DMA特性,实现了高性能的7天循环视频录制解决方案。

http://www.dtcms.com/a/536048.html

相关文章:

  • Arguments: ls-remote --tags --heads git://github.com/adobe-webplatform/eve.git
  • Glide 图片缓存:异步更新 + 动画支持 + 自定义目录+自定义刷新时效
  • SWAT模型应用
  • 界面控件DevExpress WPF v25.1 - 官宣支持Avalonia XPF
  • HarmonyOS应用日志HiLog:从基础使用到高级调试技术
  • 系统架构设计师备考第55天——数据库设计融合物联网层次架构案例分析
  • 加查网站建设乌海seo
  • 北京金港建设股份有限公司网站wordpress缓存清理
  • Deepseek大模型结合Chrome搜索爬取2025AI投资趋势数据
  • 基于 ComfyUI 的 Stable Diffusion 本地部署与使用教程(Windows + CUDA12.0)
  • HTTPS 端口,443 之外的那些坑与排查实战
  • Stable Diffusion 短视频制作算力需求与优化策略研究
  • ComfyUI本地部署Stable Diffusion:核心组件(Python、PyTorch、CUDA)版本与显卡配置全指南
  • Adobe Pro DC裁剪PDF流程介绍
  • 如何编写 Chrome 插件(Chrome Extension)
  • 怎样做网站吸引人深圳网站seo优化排名公司
  • 比wordpress更好的网站程序外贸高端网站建设
  • Qt QML Q_DECLARE_METATYPE宏的作用浅解
  • PyTorch 中 Tensor 交换维度(transpose、permute、view)详解
  • WebGL低端设备优化全攻略
  • 网站顶部素材校园文创产品设计
  • 无界微前端学习和使用
  • FPGA DDR3实战(十一):基于DDR3的高速故障录播系统(三)—— 地址管理与故障定位机制
  • 自定义协议、序列化与守护进程:构建可靠后端服务
  • 【FPGA】时序逻辑计数器——Verilog实现
  • 示范专业网站建设wordpress 添加二级
  • 【LeetCode】88. 合并两个有序数组
  • Redis键过期策略深度剖析:惰性删除与定期删除的完美平衡
  • 【紧急救援】MySQL root密码忘记?一键重置脚本和全平台解决方案
  • Redis Commander:一款基于Web、免费开源的Redis管理工具