4. 关闭摄像头设备
任务说明
本案例实现Camera HAL层设备关闭流程:通过close()函数释放已打开的设备资源(包括内存映射、请求队列、硬件连接),并将设备状态置为关闭。该函数对应HAL接口中的close_device()回调,是摄像头设备生命周期管理的收尾环节。
核心实现
// camera_hal_device.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#define CAMERA_DEVICE_PATH "/dev/video0"
#define CAMERA_BUFFER_COUNT 4
#define CAMERA_BUFFER_SIZE (1920 * 1080 * 2) // YUYV格式
typedef struct {
int fd; // 设备文件描述符
void *buffers[4]; // mmap缓冲区地址
size_t buffer_lengths[4]; // 各缓冲区长度
int buffer_count; // 已映射缓冲区数量
int is_streaming; // 是否处于流式传输状态
int is_initialized; // 设备是否已初始化
} camera_device_t;
// 停止视频流采集
static int camera_stop_streaming(camera_device_t *dev) {
if (!dev || !dev->is_streaming) {
return 0;
}
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(dev->fd, VIDIOC_STREAMOFF, &type) < 0) {
perror("VIDIOC_STREAMOFF failed");
return -1;
}
dev->is_streaming = 0;
printf("Video streaming stopped\n");
return 0;
}
// 释放mmap缓冲区
static void camera_release_buffers(camera_device_t *dev) {
if (!dev) {
return;
}
for (int i = 0; i < dev->buffer_count; i++) {
if (dev->buffers[i] != MAP_FAILED && dev->buffers[i] != NULL) {
munmap(dev->buffers[i], dev->buffer_lengths[i]);
dev->buffers[i] = NULL;
dev->buffer_lengths[i] = 0;
printf("Buffer %d unmapped\n", i);
}
}
dev->buffer_count = 0;
}
// 关闭摄像头设备(HAL层close接口)
int camera_device_close(camera_device_t *dev) {
if (!dev) {
return -EINVAL;
}
if (!dev->is_initialized) {
printf("Device already closed\n");
free(dev);
return 0;
}
printf("Closing camera device...\n");
// 1. 停止流式传输(若正在采集)
if (camera_stop_streaming(dev) < 0) {
// 即使停止失败也继续执行资源释放
fprintf(stderr, "Warning: failed to stop streaming\n");
}
// 2. 释放mmap缓冲区
camera_release_buffers(dev);
// 3. 关闭设备文件描述符
if (dev->fd >= 0) {
close(dev->fd);
dev->fd = -1;
printf("Device fd closed\n");
}
// 4. 标记设备为未初始化
dev->is_initialized = 0;
// 5. 释放设备结构体内存
free(dev);
printf("Camera device closed successfully\n");
return 0;
}
// 辅助函数:打开设备(用于测试close的对称性)
camera_device_t *camera_device_open(void) {
camera_device_t *dev = calloc(1, sizeof(camera_device_t));
if (!dev) {
return NULL;
}
dev->fd = open(CAMERA_DEVICE_PATH, O_RDWR);
if (dev->fd < 0) {
perror("Failed to open camera device");
free(dev);
return NULL;
}
// 实际项目中此处会完成:查询能力、设置格式、请求缓冲区、mmap映射
// 本示例仅模拟初始化状态,便于验证close流程
dev->is_initialized = 1;
dev->is_streaming = 0;
dev->buffer_count = 0;
printf("Camera device opened (fd=%d)\n", dev->fd);
return dev;
}
// 测试入口
int main(void) {
// 打开设备
camera_device_t *dev = camera_device_open();
if (!dev) {
return -1;
}
// 执行关闭操作
int ret = camera_device_close(dev);
if (ret == 0) {
printf("Test PASSED: device closed successfully\n");
} else {
printf("Test FAILED: close returned %d\n", ret);
}
return ret;
}
关键设计说明
camera_device_close()按以下顺序完成资源释放:
- 停止流式传输:若设备正在采集视频流,先发送
VIDIOC_STREAMOFF停止硬件采集,避免关闭过程中仍有DMA操作访问缓冲区。 - 释放mmap缓冲区:调用
munmap()解除用户空间与内核缓冲区的映射关系,防止内存泄漏。 - 关闭文件描述符:通过
close()释放设备节点占用的文件描述符,断开与内核驱动的连接。 - 释放结构体内存:使用
free()回收HAL层维护的设备上下文。
函数通过is_initialized标志防止重复关闭,并在每个步骤输出日志便于跟踪执行路径。
编译与验证
# 编译(需Linux内核头文件支持V4L2定义)
gcc -o camera_close_test camera_hal_device.c -I/usr/include
# 运行测试(需root权限访问/dev/video0)
sudo ./camera_close_test
预期输出(在设备节点存在且可访问时):
Camera device opened (fd=3)
Closing camera device...
Device fd closed
Camera device closed successfully
Test PASSED: device closed successfully
判定标准:
- 程序退出码为0,且打印
Test PASSED; - 若设备节点不存在,
open()失败返回错误信息,测试终止; - 若设备正在采集时关闭,日志中应出现
Video streaming stopped; - 使用
valgrind --leak-check=full ./camera_close_test检查无内存泄漏。