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);
#endif2. 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);
#endif3. 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);
#endif4. 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
系统特点
性能优化
零拷贝传输:使用DMA和共享内存避免数据复制
多线程架构:分离采集、传输、写入操作
环形缓冲区:无锁或细粒度锁的并发控制
预分配内存:避免运行时内存分配开销
可靠性设计
7天循环覆盖:自动管理存储空间
数据校验:CRC校验确保数据完整性
错误恢复:设备异常自动重连
资源管理:完善的初始化和清理机制
资源估算
存储需求:约151GB (720p H.264, 30fps, 7天)
内存需求:约256MB共享内存 + 各线程栈
CPU使用:主要开销在编码和文件IO
这个系统充分利用了Linux内核的IPC和DMA特性,实现了高性能的7天循环视频录制解决方案。
