-
Notifications
You must be signed in to change notification settings - Fork 2
DRM_DeviceController
DeviceController 是 utilsCore DRM 模块的核心类,提供全局唯一的 DRM 设备管理功能,负责设备初始化、资源获取、热插拔处理和平面资源管理。
- 全局唯一的 DRM 设备管理器
- 设备初始化和资源查询
- 热插拔事件处理
- 平面资源管理和查询
- DRM 显示输出的设备管理
- HDMI 热插拔监控
- 可用平面资源的查询
- 显示模式的获取和配置
- 依赖:
drm内核子系统,libdrm库 - 被依赖:
DrmLayer,PlanesCompositor,DmaBuffer
DeviceController 是 DRM 设备的单例管理器,提供:
- DRM 设备的打开和初始化
- 资源(connector/crtc/encoder/plane)的查询
- 热插拔事件的监听和处理
- 平面资源的缓存和查询
- 单例模式: 全局只允许一个 DeviceController 实例
- RAII: 使用 FdWrapper 自动管理设备文件描述符
- 回调机制: 通过回调通知热插拔事件
static DrmDevicePtr create(const std::string& path = "/dev/dri/card0");参数说明:
-
path(输入): DRM 设备路径,默认/dev/dri/card0
返回值:
- 成功: 返回全局唯一的
DrmDevicePtr(DeviceController 的智能指针) - 失败: 返回
nullptr
所有权归属:
- 返回的
shared_ptr持有 DeviceController 的所有权 - 全局单例,多次调用返回相同的实例
注意事项:
- 单例限制: 全局只允许一个实例,多次调用返回相同指针
-
dumb buffer 支持: 必须检查
DRM_CAP_DUMB_BUFFER -
原子模式: 必须设置
DRM_CLIENT_CAP_ATOMIC - 路径验证: 确保 path 指向有效的 DRM 设备
使用例程:
// 使用默认路径
auto controller = DeviceController::create();
if (!controller) {
fprintf(stderr, "Failed to create DRM controller\n");
return -1;
}
// 使用自定义路径
auto controller2 = DeviceController::create("/dev/dri/card1");int get() const;参数说明: 无
返回值: DRM 设备的文件描述符
所有权归属: 只读访问,不转移所有权
注意事项:
- 返回的 fd 由 FdWrapper 管理,不要手动关闭
- fd 可以用于直接调用 DRM ioctl
void registerResourceCallback(
const ResourceCallback& preRefreshCallback,
const ResourceCallback& postRefreshCallback
);参数说明:
-
preRefreshCallback(输入): 热插拔刷新前回调 -
postRefreshCallback(输入): 热插拔刷新后回调
返回值: 无
所有权归属: 回调函数由 DeviceController 持有,调用者不负责释放
注意事项:
-
回调时机:
-
preRefreshCallback: 热插拔事件发生前,通知释放资源 -
postRefreshCallback: 热插拔事件处理后,通知重新获取资源
-
- 延迟处理: 热插拔事件有 600ms 延迟等待系统稳定
- 线程安全: 回调在热插拔线程中调用,注意线程安全
使用例程:
auto controller = DeviceController::create();
controller->registerResourceCallback(
[]() {
// 释放资源
printf("Pre-refresh: Release resources\n");
},
[]() {
// 重新获取资源
printf("Post-refresh: Reacquire resources\n");
}
);void getPossiblePlane(
int plane_type,
uint32_t format,
std::vector<uint32_t>& out_ids
);参数说明:
-
plane_type(输入): 平面类型 (DRM_PLANE_TYPE_OVERLAY/PRIMARY/CURSOR) -
format(输入): 像素格式 (如DRM_FORMAT_ARGB8888) -
out_ids(输出): 符合条件的 plane ID 列表
返回值: 无,结果写入 out_ids
所有权归属: out_ids 由调用者提供并持有
注意事项:
-
平面类型:
-
DRM_PLANE_TYPE_OVERLAY: 覆盖层(通常用于UI) -
DRM_PLANE_TYPE_PRIMARY: 主层(通常用于视频) -
DRM_PLANE_TYPE_CURSOR: 光标层
-
- 格式匹配: 只返回支持指定格式的 plane
- CRTC 支持: plane 必须支持当前的 CRTC
使用例程:
auto controller = DeviceController::create();
// 查询支持 ARGB8888 的 overlay planes
std::vector<uint32_t> overlay_planes;
controller->getPossiblePlane(DRM_PLANE_TYPE_OVERLAY,
DRM_FORMAT_ARGB8888,
overlay_planes);
for (uint32_t plane_id : overlay_planes) {
printf("Overlay plane: %u\n", plane_id);
}PlanesCachePtr getPlaneById(uint32_t id);参数说明:
-
id(输入): plane ID
返回值:
- 成功: 返回
PlanesCachePtr(平面的缓存信息) - 失败: 返回
nullptr
所有权归属: 返回的 shared_ptr 持有平面信息的所有权
注意事项:
- 返回的指针指向 DeviceController 内部缓存
- 平面信息包括格式列表、plane 详细信息等
std::shared_ptr<drmModeRes> refreshResources();参数说明: 无
返回值:
- 成功: 返回
drmModeRes智能指针 - 失败: 返回
nullptr
所有权归属: 返回的 shared_ptr 使用自定义 Deleter 自动释放
注意事项:
- 重新查询所有 connector、encoder、crtc 资源
- 热插拔后必须调用此方法更新资源
- 使用
res_mutex保护并发访问
SharedDev& refreshAllDevices();参数说明: 无
返回值: 设备列表的引用
所有权归属: 只读访问,不转移所有权
注意事项:
- 查询所有 connector 并配置 connector-crtc 绑定
- 使用
devMutex保护并发访问 - 热插拔后必须调用此方法
size_t refreshPlane(uint32_t crtc_id);参数说明:
-
crtc_id(输入): CRTC ID
返回值:
- 成功: 返回匹配的 plane 数量
- 失败: 返回 0
所有权归属: 只读访问,不转移所有权
注意事项:
- 查询所有支持指定 CRTC 的 plane
- 更新
planesCache_缓存 - 使用
cacheMutex保护并发访问
uint32_t getPropertyId(int fd, drmModeObjectPropertiesPtr props, const char *name);参数说明:
-
fd(输入): DRM 文件描述符 -
props(输入): DRM 对象属性集合 -
name(输入): 属性名称
返回值:
- 成功: 返回属性ID
- 失败: 返回 0
所有权归属: 只读访问,不转移所有权
注意事项:
- 用于查询 DRM 对象的属性ID
- 通常用于原子提交时设置属性
SharedDev& getDevices();参数说明: 无
返回值: 设备组合列表的引用
所有权归属: 只读访问,不转移所有权
注意事项:
- 返回当前所有可用的设备组合 (connector-crtc 绑定)
- 使用
devMutex保护并发访问 - 热插拔后需要调用
refreshAllDevices()更新列表
std::shared_ptr<drmModeRes> getResources() const;参数说明: 无
返回值:
- 成功: 返回
drmModeRes智能指针 - 失败: 返回
nullptr
所有权归属: 返回的 shared_ptr 使用自定义 Deleter 自动释放
注意事项:
- 返回 DRM 资源信息 (connector/encoder/crtc)
- 使用
resMutex保护并发访问 - 热插拔后需要调用
refreshResources()更新资源
std::shared_ptr<drmModePlaneRes> getPlaneResources() const;参数说明: 无
返回值:
- 成功: 返回
drmModePlaneRes智能指针 - 失败: 返回
nullptr
所有权归属: 返回的 shared_ptr 使用自定义 Deleter 自动释放
注意事项:
- 返回 DRM 平面资源信息
- 使用
resMutex保护并发访问 - 热插拔后需要调用
refreshResources()更新资源
| 资源 | 拥有者 | 释放方式 | 线程保护 |
|---|---|---|---|
| DRM FD | FdWrapper |
~FdWrapper() → close()
|
内部同步 |
| resources_ | DeviceController | drmModeFreeResources() |
res_mutex |
| planeResources_ | DeviceController | drmModeFreePlaneResources() |
res_mutex |
| devices_ | DeviceController | 析构时自动释放 | devMutex |
| planesCache_ | DeviceController | 析构时自动释放 | cacheMutex |
| 回调函数 | DeviceController | 析构时自动释放 | - |
创建流:
DeviceController::create() → 全局单例 (DrmDev::fd_ptr)
回调流:
注册回调 → DeviceController 持有回调 → 热插拔时调用
- fd_mutex: 保护 DRM 文件描述符访问
- res_mutex: 保护 resources_ 和 planeResources_
- dev_mutex: 保护 devices_ 列表
- cache_mutex: 保护 planesCache_ 缓存
-
refreshResources(),refreshAllDevices(),refreshPlane()是线程安全的 - 回调函数在热插拔线程中调用,注意线程安全
- 不要在回调中执行耗时操作
// 创建设备控制器
DrmDev::fd_ptr = DeviceController::create("/dev/dri/card0");
// 获取设备组合
std::vector<DevPtr>& devices = DrmDev::fd_ptr->getDevices();
if (devices.empty()){
std::cout << "[DrmDev] Get no devices." << std::endl;
return;
}
// 取出第一个可用设备组合
DevPtr dev = devices[0];
if (!dev) {
std::cout << "Failed to get usable device." << std::endl;
return;
}
std::cout << "Connector ID: " << dev->connector_id << ", CRTC ID: " << dev->crtc_id
<< ", Resolution: " << dev->width << "x" << dev->height << "\n";
// 获取所有在指定CRTC上的Plane
DrmDev::fd_ptr->refreshPlane(dev->crtc_id);
// 初始化 id 列表
std::vector<uint32_t> usablePrimaryPlaneIds;
std::vector<uint32_t> usableOverlayPlaneIds;
// 获取指定类型并且支持目标格式的 Plane
DrmDev::fd_ptr->getPossiblePlane(DRM_PLANE_TYPE_PRIMARY, DRM_FORMAT_BGRA8888, usablePrimaryPlaneIds);
DrmDev::fd_ptr->getPossiblePlane(DRM_PLANE_TYPE_OVERLAY, DRM_FORMAT_NV12, usableOverlayPlaneIds);
// 检查是否有可以ID
if (usablePrimaryPlaneIds.empty() || usableOverlayPlaneIds.empty()) {
std::cout << "Some plane do not matched.\n";
return;
}
// 初始化 layer
std::unique_ptr<DrmLayer> primaryLayer = std::make_unique(std::vector<DmaBufferPtr>(), 2);
std::unique_ptr<DrmLayer> overLayer = std::make_unique(std::vector<DmaBufferPtr>(), 2);
// 配置属性
DrmLayer::LayerProperties OverLayerProps{
// NV12
.plane_id_ = usableOverlayPlaneIds[0],
.crtc_id_ = dev->crtc_id,
// 源图像区域
// src_* 使用左移 16
.srcX_ = fx(0),
.srcY_ = fx(0),
.srcwidth_ = fx(autoWidth),
.srcheight_ = fx(autoHeight),
// 显示图像区域
// crtc_* 不使用左移
.crtcX_ = 0,
.crtcY_ = 0,
// 自动缩放
.crtcwidth_ = dev->width,
.crtcheight_ = dev->height,
.zOrder_ = 0
};
// 配置属性
DrmLayer::LayerProperties PrimaryLayerProps = OverLayerProps;
PrimaryLayerProps.plane_id_ = usablePrimaryPlaneIds[0];
PrimaryLayerProps.zOrder_ = 1;
// 初始化layer
primaryLayer->setProperty(PrimaryLayerProps);
overLayer->setProperty(OverLayerProps);
// 将layer添加到合成器
std::unique_ptr<PlanesCompositor> compositor = std::move(PlanesCompositor::create());
compositor->addLayer(primaryLayer);
compositor->addLayer(overLayer);
std::cout << "Layer initialized.\n"; DrmDev::fd_ptr = DeviceController::create("/dev/dri/card0");
// 热插拔回调会自动触发
// 在回调中:
// 1. 释放旧的 plane 和 framebuffer
// 2. 刷新资源
DrmDev::fd_ptr->registerResourceCallback(
[]() {
// 释放资源
printf("Pre-refresh: Release resources\n");
},
[]() {
// 重新获取资源
printf("Post-refresh: Reacquire resources\n");
}
);
// 3. 重新创建 plane 和 framebuffer
// 4. 重新绑定回调- 单例模式: 全局只允许一个 DeviceController 实例
- 热插拔延迟: 热插拔事件处理有 600ms 延迟
- 回调线程: 回调在热插拔线程中调用,注意线程安全
-
CRTC 管理: 防止重复分配 CRTC,使用
crtcStatu跟踪 -
资源刷新: 热插拔后必须调用
refreshAllDevices()和refreshPlane() -
平面查询:
getPossiblePlane()会检查格式和 CRTC 支持 - 线程安全: 使用多个 mutex 保护不同资源
- DrmLayer - DRM 图层管理
- PlanesCompositor - DRM 平面合成
- DmaBuffer - DMA-BUF 缓冲区
- README - DRM 模块总览
主页
API 文档
DMA 模块
DRM 模块
- DRM 模块总览
- DeviceController - DRM 设备控制器
- DrmLayer - DRM 图层管理
- PlanesCompositor - DRM 平面合成器
- DrmBpp - DRM 格式定义
NET 模块
- NET 模块总览
- TcpServer - TCP 服务器
- SocketConnection - Socket 连接管理
- CommandHandler - 命令处理器
- DataPacket - 数据包
V4L2 模块
- V4L2 模块总览
- CameraController - V4L2 摄像头控制器
- Frame - V4L2 帧数据结构
- FormatTool - V4L2 格式工具
- Exception - V4L2 异常类
V4L2Param 模块
- V4L2Param 模块总览
- ParamControl - 参数控制
- ParamLogger - 参数日志
- ParamProcessor - 参数处理器
RGA 模块
- RGA 模块总览
- RgaConverter - RGA 转换器
- RgaProcessor - RGA 处理器
- FormatTool - RGA 格式工具
MPP 模块
- MPP 模块总览
- EncoderContext - 编码器上下文
- EncoderCore - 编码器核心
- JpegEncoder - JPEG 编码器
- StreamWriter - 流写入器
- MppResourceGuard - MPP 资源守护
- FileTools - 文件工具
- FormatTool - 格式工具
Sys 模块
- Sys 模块总览
- CpuMonitor - CPU 监控器
- MemoryMonitor - 内存监控器
- Base - 基础类
Mouse 模块
- Mouse 模块总览
- Watcher - 鼠标监视器
Utils 模块
- Utils 模块总览
- AsyncThreadPool - 异步线程池
- ConcurrentQueue - 并发队列
- FdWrapper - 文件描述符包装器
- FenceWatcher - 围栏监视器
- FixedSizePool - 固定大小对象池
- Logger - 日志记录器
- ObjectsPool - 对象池
- OrderedQueue - 有序队列
- ProgressBar - 进度条
- SafeQueue - 安全队列
- SharedBufferState - 共享缓冲区状态
- SimpleVariant - 简单变体类型
- ThreadPauser - 线程暂停器
- ThreadUtils - 线程工具
- Types - 类型定义
- UdevMonitor - Udev 监视器