从零构建 Windows 自定义相机媒体源:SimpleMediaSource 示例深度解析与部署实战

发布时间:2026/9/27 10:43:27
从零构建 Windows 自定义相机媒体源:SimpleMediaSource 示例深度解析与部署实战 示例工程【免费下载链接】Windows-driver-samplesThis repo contains driver samples prepared for use with Microsoft Visual Studio and the Windows Driver Kit (WDK). It contains both Universal Windows Driver and desktop-only driver samples.项目地址https://gitcode.com/gh_mirrors/wi/Windows-driver-samples点击查看免费下载本指南以 Windows-driver-samples 仓库中的 SimpleMediaSource 示例 为蓝本系统讲解如何编写一个自定义媒体源Custom Media SourceCOM 组件与配套UMDF 相机驱动安装包并将其安装为系统级相机设备、通过 Windows 相机应用Camera App直接采集实时画面。读完本文你将掌握 Frame Server 自定义媒体源的整体架构、IMFMediaSourceEx 媒体源与 IMFMediaStream2 流的关键实现、INF 驱动包的相机注册方式以及devgen/pnputil的完整部署验证流程。示例构成两个项目、一条完整的相机链路SimpleMediaSource 示例位于仓库 general/SimpleMediaSource 目录由解决方案 SimpleMediaSource.sln 组织两个独立项目分别对应自定义媒体源架构中的媒体源组件与驱动包两层项目目录类型职责MediaSourcegeneral/SimpleMediaSource/MediaSourceCOM DLL实现自定义媒体源IMFMediaSourceEx、流IMFMediaStream2与帧生成器负责产出视频帧SimpleMediaSourceDrivergeneral/SimpleMediaSource/SimpleMediaSourceDriverUMDF 2 驱动包作为系统相机设备的载体通过 INF 将媒体源 DLL 注册到 Frame Server 并暴露为相机该示例的核心价值在于演示了一条完整链路UMDF 驱动把 COM 媒体源挂接为相机设备 → Frame Server 发现并激活媒体源 → 媒体源按需生成帧 → 相机应用消费画面。由于媒体源不依赖任何真实硬件其生成的视频内容是一条随时间滚动的黑白渐变grayscale gradient因此它也是验证和调试自定义相机管线的最小可运行参考。工作原理Frame Server 如何把 COM 媒体源变成相机从设备到媒体源的桥接传统相机驱动如 AVStream miniport在内核态产出帧而 SimpleMediaSource 的做法完全不同驱动层只负责枚举设备、建立接口真正的帧生产在用户态的 COM DLL 中完成。其桥接机制可从 SimpleMediaSourceDriver.inf 中清晰读出[SimpleMediaSource.NT.Interfaces] AddInterface %KSCATEGORY_VIDEO_CAMERA%, %CustomCaptureSource.ReferenceString%, CustomCaptureSourceInterface AddInterface %KSCATEGORY_VIDEO%, %CustomCaptureSource.ReferenceString%, CustomCaptureSourceInterface AddInterface %KSCATEGORY_CAPTURE%, %CustomCaptureSource.ReferenceString%, CustomCaptureSourceInterface [CustomCaptureSourceInterface.AddReg] HKR,,CLSID,,%ProxyVCap.CLSID% HKR,,CustomCaptureSourceClsid,,%CustomCaptureSource.CLSID% HKR,,FriendlyName,,%CustomCaptureSource.Desc%这段配置做了三件事为设备声明三个 KS 类别接口KSCATEGORY_VIDEO_CAMERA{E5323777-F976-4f5b-9B55-B94699C46E44}即现代相机设备归属的类别、KSCATEGORY_VIDEO{6994AD05-93EF-11D0-A3CC-00A0C9223196}与KSCATEGORY_CAPTURE{65E8773D-8F56-11D0-A3B9-00A0C9223196}使设备在相机分类下可见通过CustomCaptureSourceClsid把接口指向媒体源 COM 类的 CLSID{9812588D-5CE9-4E4C-ABC1-049138D10DCE}Frame Server 据此定位并激活媒体源 DLL通过HKR,,CLSID,,%ProxyVCap.CLSID%挂接代理{17CCA71B-ECD7-11D0-B908-00A0C9223196}确保媒体源在管线中以标准方式被调用。COM 组件的系统注册媒体源 DLL 本身也通过 INF 的[CustomCaptureSource.ComRegistration]段完成系统级 COM 注册[CustomCaptureSource.ComRegistration] HKCR,CLSID\%CustomCaptureSource.CLSID%,,,%CustomCaptureSource.Desc% HKCR,CLSID\%CustomCaptureSource.CLSID%\InprocServer32,,%REG_EXPAND_SZ%,%13%\SimpleMediaSource.dll HKCR,CLSID\%CustomCaptureSource.CLSID%\InprocServer32,ThreadingModel,,Both其中%13%是 INF 对%SystemRoot%\System32\DriverStore\FileRepository\inf 包\的代号即SimpleMediaSource.dll随驱动一起被复制到 DriverStore 后以InprocServer32进程内服务器、ThreadingModelBoth形式注册供 Frame Server 进程加载。媒体源在何时被激活媒体源并非始终驻留而是由 Frame Server 按需激活。激活动作由 SimpleMediaSourceActivate.cpp 中的ActivateObject完成IFACEMETHODIMP SimpleMediaSourceActivate::ActivateObject(REFIID riid, void** ppv) { ... m_spSimpleMediaSrc winrt::make_selfwinrt::WindowsSample::implementation::SimpleMediaSource(); RETURN_IF_FAILED(m_spSimpleMediaSrc-Initialize()); RETURN_IF_FAILED(m_spSimpleMediaSrc-QueryInterface(riid, ppv)); return S_OK; }该对象实现IMFActivate以及作为其基类的IMFAttributes的全部属性方法扮演工厂角色当相机应用发起采集时Frame Server 通过 CLSID 实例化SimpleMediaSourceActivate调用ActivateObject创建并初始化真正的SimpleMediaSource再以所需接口riid交还调用方。媒体源核心实现从类声明看接口契约SimpleMediaSource.h 以一行winrt::implements声明了整个媒体源实现的接口面这是理解其能力边界的最佳入口struct SimpleMediaSource : winrt::implementsSimpleMediaSource, IMFMediaSourceEx, IMFGetService, IKsControl, IMFSampleAllocatorControl接口分工如下IMFMediaSourceEx媒体源主体负责生命周期Start/Stop/Pause/Shutdown、事件经IMFMediaEventGenerator的BeginGetEvent/GetEvent/QueueEvent、呈现描述符CreatePresentationDescriptor、特性GetCharacteristics以及扩展的属性查询GetSourceAttributes/GetStreamAttributes和 D3D 管理SetD3DManagerIMFGetService向管线提供服务查询入口示例中不暴露任何服务一律返回MF_E_UNSUPPORTED_SERVICEIKsControl让媒体源能响应内核流KS属性请求示例实现了一个自定义的颜色模式属性集IMFSampleAllocatorControl与 Frame Server 协商采样器分配策略示例声明使用由上层提供的分配器MFSampleAllocatorUsage_UsesProvidedAllocator。源的状态机与事件队列从源码结构看SimpleMediaSource维护了一个四态状态机SourceState::Invalid / Stopped / Started / Shutdown所有公开方法都先加winrt::slim_mutex m_Lock再通过_CheckShutdownRequiresLock校验关闭状态。事件机制统一委托给MFCreateEventQueue创建的IMFMediaEventQueue例如Start成功后投递MESourceStartedStop后投递MESourceStopped流启动时投递MEStreamStarted。值得注意的是GetEvent的实现刻意不在锁内调用事件队列见 SimpleMediaSource.cpp 中GetEvent的注释因为该方法可能无限阻塞这是媒体源编写中典型的锁与阻塞折中。Start流的选中、启动与事件投递Start方法SimpleMediaSource.cpp演示了媒体源启动的标准流程校验传入的IMFPresentationDescriptor流数量必须与内部一致、至少选中一条流见_ValidatePresentationDescriptor取系统时间作为起始时间戳InitPropVariantFromInt64(MFGetSystemTime(), ...)遍历呈现描述符中的每条流若被选中则更新内部描述符SelectStream、将流置为运行态SetStreamState(MF_STREAM_STATE_RUNNING)、调用流的Start并按该流此前是否处于选中状态投递MEUpdatedStream或MENewStream事件最后统一投递MESourceStarted事件。由于示例是单流NUM_STREAMS 1代码注释明确说明多流场景需要遍历流列表为每条选中的流逐一投递上述事件。传感器配置文件Sensor Profile媒体源通过_CreateSourceAttributes将传感器配置文件集合挂到源属性上MF_DEVICEMFT_SENSORPROFILE_COLLECTION定义了两个 profile// Legacy profile 是必选项保证非 profile 感知的应用也能降级工作 MFCreateSensorProfile(KSCAMERAPROFILE_Legacy, 0, nullptr, profile); profile-AddProfileFilter(STREAM_ID, L((RES;FRT30,1;SUT))); // 高帧率 profile 仅允许 60fps MFCreateSensorProfile(KSCAMERAPROFILE_HighFrameRate, 0, nullptr, profile); profile-AddProfileFilter(STREAM_ID, L((RES;FRT60,1;SUT)));profile 过滤字符串中的RES分辨率、FRT帧率、SUTsubtype 类型是相机 profile 的标准匹配键1表示该键属于必选匹配MatchRequired。从源码结构看这类 profile 声明是媒体源向系统宣告自身能力边界如是否支持高帧率的方式Legacy profile 被注释为 mandatory以确保旧应用仍可用。流实现SimpleMediaStream 与帧的产出媒体类型定义每条流由 SimpleMediaStream实现IMFMediaStream2表示。在 SimpleMediaStream.cpp 的Initialize中示例为流定义了两种媒体类型NV12MFVideoFormat_NV12640×480、30 fps、渐进式扫描、平均比特率约 221 Mbps640 × 1.5 × 480 × 8 × 30RGB32MFVideoFormat_RGB32同尺寸同帧率、平均比特率约 294 Mbps640 × 480 × 4 × 8 × 30。两种类型均设置MF_MT_ALL_SAMPLES_INDEPENDENT TRUE帧间独立便于随机访问。随后调用MFCreateStreamDescriptor创建流描述符默认选中 NV12SetCurrentMediaType(mediaTypeList[0])并将流类别PINNAME_VIDEO_CAPTURE、流 ID 及帧源类型MFFrameSourceTypes_Color写入流属性。RequestSample采样器、锁缓冲与帧填充Frame Server 对媒体源发起取样时会调用流的RequestSample。其核心流程SimpleMediaStream.cpp如下RETURN_IF_FAILED(m_spSampleAllocator-AllocateSample(sample)); // 从分配器取出采样 RETURN_IF_FAILED(sample-GetBufferByIndex(0, outputBuffer)); // 取得 0 号缓冲 RETURN_IF_FAILED(outputBuffer-QueryInterface(IID_PPV_ARGS(buffer2D))); // 转成 2D 缓冲 RETURN_IF_FAILED(buffer2D-Lock2DSize(MF2DBuffer_LockFlags_Write, pbuf, pitch, bufferStart, bufferLength)); RETURN_IF_FAILED(m_spFrameGenerator-CreateFrame(pbuf, bufferLength, pitch, m_rgbMask)); // 填帧 RETURN_IF_FAILED(buffer2D-Unlock2D()); RETURN_IF_FAILED(sample-SetSampleTime(MFGetSystemTime())); // 时间戳 RETURN_IF_FAILED(sample-SetSampleDuration(333333)); // 时长 ~ 1/30s (100ns 单位)这里有两个值得注意的实现事实分配器来源Start()中根据m_allocatorUsage分支——若声明MFSampleAllocatorUsage_UsesProvidedAllocator本示例采用则必须由上层Frame Server在启动前通过SetDefaultAllocator注入分配器否则返回E_POINTER若声明为自建则调用MFCreateVideoSampleAllocatorEx自行创建并通过InitializeSampleAllocator(10, spMediaType.get())预分配 10 个采样请求令牌若RequestSample携带pToken则通过MFSampleExtension_Token属性挂到采样上用于异步取样场景的完成通知。完成填帧后MEMediaSample事件携带采样对象被投递到流事件队列由 Frame Server 转交给管线下游。颜色模式自定义 KS 属性媒体源通过IKsControl::KsProperty实现了一个自定义属性集PROPSETID_SIMPLEMEDIASOURCE_CUSTOMCONTROLGUID{0CE2EF73-4800-4F53-9B8E-8C06790FC0C7}属性 IDKSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE值为 0。其读写行为在 SimpleMediaSource.cpp 的KsProperty中定义属性集不匹配或 ID 越界时返回HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND)——源码注释明确指出这是为了模仿 AVStream miniport 未注册 KS 处理程序时的标准行为空缓冲查询pPropertyData NULL ulDataLength 0返回ERROR_MORE_DATA并给出所需缓冲大小符合IKsControl::KsProperty的查询约定实际读写时将ColorMode字段RGB 掩码同步到每条流写操作调用m_streamList[i]-SetRGBMask(...)读操作返回m_streamList[0]-GetRGBMask()。颜色掩码常量定义在 SimpleMediaSource.h#define KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_GRAYSCALE 0x00FFFFFFL #define KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_RED 0x00FF0000L #define KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_GREEN 0x0000FF00L #define KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_BLUE 0x000000FFL流默认的m_rgbMask为蓝色KSPROPERTY_SIMPLEMEDIASOURCE_CUSTOMCONTROL_COLORMODE_BLUE。这一设计展示了一条通用的扩展路径自定义媒体源可以像真实内核态相机驱动一样通过 KS 属性对外暴露设备控制项。帧生成器滚动黑白渐变的实现原理SimpleFrameGenerator 是一个独立的纯 C 类负责把原始像素数据写入 2D 缓冲。Initialize校验媒体类型仅接受 RGB32 与 NV12否则返回MF_E_UNSUPPORTED_FORMAT并记录分辨率与子类型。CreateFrame按子类型分流RGB32 直接调用_CreateRGB32FrameNV12 则先在临时缓冲中生成 RGB32 帧再经RGB32ToNV12Frame做像素格式转换。渐变效果的核心在_CreateRGB32FrameLONGLONG curSysTimeInS MFGetSystemTime() / (MFTIME)10000000; int offset curSysTimeInS % height; for (unsigned int r 0; r height; r) { uint32_t* p (uint32_t*)(pBuf (r * pitch)); for (unsigned int c 0; c width; c) { BYTE gray (BYTE)r offset; *p ((uint32_t)gray 16 | (uint32_t)gray 8 | (uint32_t)gray) rgbMask; p; } }即每一行的灰度值由行号 当前秒数取模决定从而形成随秒数整体滚动的纵向灰度渐变最终像素值再与rgbMask按位与实现灰阶、红、绿、蓝四种显示模式。NV12 转换路径在 SimpleFrameGenerator.cpp 中实现了标准 BT.601 系数换算RGB24ToYUY2/RGB24ToY并以 2×2 像素块为单位完成 Y 平面与 UV 平面的交错采样。UMDF 驱动包最简设备载体驱动包项目遵循标准 UMDF 2 模板项目文件与文件分工见 ReadMe.txtDriver.c 负责DriverEntry创建 WDF 驱动对象、注册EvtDeviceAdd与清理回调、初始化 WPP 跟踪Device.c 与 Queue.c 分别处理设备与 IO 队列。设备被注册为根枚举root-enumerated的假设备硬件 ID 为root\SimpleMediaSource见 INF 的[Standard.NT$ARCH$.10.0...17134]段因此无需真实硬件即可加载。INF 中有两个对相机场景至关重要的配置[SimpleMediaSource.NT.Wdf] UmdfServiceSimpleMediaSource, SimpleMediaSource_Install UmdfServiceOrderSimpleMediaSource UmdfKernelModeClientPolicyAllowKernelModeClients ; Required to allow ksthunk.sys to load above us as a Camera driver.其中UmdfKernelModeClientPolicyAllowKernelModeClients的注释明确说明了必要性允许内核模式客户端ksthunk.sys挂载其上这是设备能被识别为相机驱动的前提。同时[Version] 段声明ClassCamera、ClassGuid{ca3e7ab9-b4c3-4ae6-8251-579ef933890f}、PnpLockdown1驱动包同时复制SimpleMediaSourceDriver.dllUMDF 服务二进制与SimpleMediaSource.dll媒体源 COM 组件到 DriverStore。构建与安装部署完整操作步骤第 1 步构建解决方案用 Visual Studio Windows Driver KitWDK打开并构建 SimpleMediaSource.sln。要求目标系统满足 INF 中10.0...17134Windows 10 1803 及更高的版本约束。第 2 步定位输出目录构建完成后输出目录形如Windows-driver-samples\general\SimpleMediaSource\x64\Release驱动包位于其中名为SimpleMediaSourceDriver的子目录。部署前必须确认该目录包含以下 4 个文件SimpleMediaSource.dll—— 自定义媒体源 COM 组件simplemediasourcedriver.cat—— 驱动签名目录文件SimpleMediaSourceDriver.dll—— UMDF 驱动服务二进制SimpleMediaSourceDriver.inf—— 安装描述文件。第 3 步部署驱动包依次执行两条命令需管理员权限的命令行devgen /add /bus ROOT /hardwareid root\SimpleMediaSource pnputil /add-driver SimpleMediaSourceDriver.inf /install第一条devgen在根总线ROOT上创建一个硬件 ID 为root\SimpleMediaSource的虚拟设备节点用于触发 PnP 枚举第二条pnputil将驱动包加入 DriverStore 并立即为匹配设备安装。第 4 步验证设备是否出现打开设备管理器Device Manager在Camera相机类别下定位名为SimpleMediaSource Capture Source的设备。设备描述字符串来自 INF 的[Strings]段SimpleMediaSource.DeviceDesc SimpleMediaSource Capture Source。若设备未出现检查%windir%\inf\setupapi.dev.log中的安装日志。此外devgen命令会输出设备的实例 IDInstanceID可用其作为pnputil入参查询设备状态pnputil /enum-devices /instanceid InstanceID of SimpleMediaSource /deviceids /services /stack /drivers该命令可一次查看设备的硬件 ID、服务、驱动栈与驱动信息是定位设备已创建但驱动未加载类问题的直接手段。第 5 步打开相机应用验证取流打开Microsoft 相机应用Camera App如有必要切换摄像头直到画面来自 SimpleMediaSource。此时应看到一条滚动的黑白渐变scrolling black and white gradient——这是帧生成器实时产出帧的直接证据说明从设备枚举、媒体源激活到帧投递的整条管线已经打通。调试要点与常见问题设备不出现优先查看setupapi.dev.log中的错误段同时用pnputil /enum-devices /instanceid ...确认设备节点与驱动栈。根枚举设备不需要硬件若devgen未成功创建节点后续安装无从谈起。相机应用看不到设备检查 INF 中三个 KS 接口类别KSCATEGORY_VIDEO_CAMERA/KSCATEGORY_VIDEO/KSCATEGORY_CAPTURE与CustomCaptureSourceClsid是否正确写入注册表确认SimpleMediaSource.dll已随驱动复制到 DriverStore 且 COM 注册段生效HKCR\CLSID\{9812588D-5CE9-4E4C-ABC1-049138D10DCE}。启动/取流失败验证流分配器约定——本示例声明MFSampleAllocatorUsage_UsesProvidedAllocator若上层未调用SetDefaultAllocator注入分配器Start()会返回E_POINTER见 SimpleMediaStream.cpp 中Start的校验逻辑。驱动未作为相机加载确认 INF 的[SimpleMediaSource.NT.Wdf]段保留UmdfKernelModeClientPolicyAllowKernelModeClients否则 ksthunk.sys 无法挂载其上。扩展方向从单流示例到真实产品从源码结构可以推断出若干明确的扩展路径多流支持NUM_STREAMS常量SimpleMediaSource.h 中定义为 1可直接改为更大值Initialize中的创建循环、Start/Stop中的遍历逻辑已按流数组编写多流场景下需为每条选中流投递MENewStream/MEUpdatedStream事件源码注释已注明。更多媒体类型在 SimpleMediaStream.cpp 的mediaTypeList中追加类型并同步扩展SimpleFrameGenerator的CreateFrame分支当前仅支持 RGB32 与 NV12。自定义设备控制仿照PROPSETID_SIMPLEMEDIASOURCE_CUSTOMCONTROL与KsProperty的实现新增 KS 属性集即可向应用层暴露更多硬件控制项。接入真实数据源将SimpleFrameGenerator::CreateFrame替换为真实传感器/渲染源的填帧逻辑即完成从演示渐变到真实相机的蜕变。小结SimpleMediaSource 用最精简的代码展示了 Windows Frame Server 自定义媒体源的完整形态UMDF 驱动负责设备枚举与相机类别注册COM 媒体源通过IMFMediaSourceEx、IMFMediaStream2、IKsControl、IMFSampleAllocatorControl等接口完成状态管理、事件投递、帧生产与设备控制。本文所有安装步骤、INF 配置与接口行为均直接取自仓库中的 README.md、SimpleMediaSourceDriver.inf 及 MediaSource/SimpleMediaSourceDriver 目录下的源码可作为开发自定义相机媒体源时的可运行参考起点。赞分享示例工程【免费下载链接】Windows-driver-samplesThis repo contains driver samples prepared for use with Microsoft Visual Studio and the Windows Driver Kit (WDK). It contains both Universal Windows Driver and desktop-only driver samples.项目地址https://gitcode.com/gh_mirrors/wi/Windows-driver-samples点击查看免费下载相关推荐UWP 自定义媒体传输控件实战解析 Windows-universal-samples 的 XamlCustomMediaTransportControls 示例UWP 自定义媒体传输控件实战解析 Windows universal samples 的 XamlCustomMediaTransportControls示例工程Bob终极Neovim版本管理器完整指南Bob终极Neovim版本管理器完整指南 Bob是一款跨平台且易于使用的Neovim版本管理器让你能够直接从命令行轻松切换不同版本的Neovim。无论你是N开发工具QGIS OAuth2 预定义配置示例深度解析从 JSON 样例到大规模部署实战QGIS OAuth2 预定义配置示例深度解析从 JSON 样例到大规模部署实战 本指南以 QGIS 仓库中的 OAuth2 认证方法示例配置文档 src/GIS桌面应用数据可视化后端上一篇Magisk终极指南解决Android Root与系统定制的五大核心难题下一篇10种融合模式详解OpenReel Video混合模式正片叠底、滤色、叠加创意玩法创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询