CameraControl 技术架构说明


1. 项目定位与整体思路

CameraControl 核心目标是:

  • 用 EDSDK.dll(native)实现 相机连接/会话、拍照、下载、属性读写、LiveView、对焦/测光等
  • 用一个 WinForms UI 把这些能力串起来(你现在看到的 Camera Control 界面)
  • 用"命令队列 + 事件回调 + 观察者通知"把 UI 与相机操作解耦

后面要做照相亭软件,直接以它为基底是很合理的:先复用"相机控制域模型 + 命令执行框架 + 事件回调框架",再把 UI 替换成照相亭控制层/远端控制层。


2. 代码模块分层(从上到下)

2.1 UI 层(WinForms)

  • MainWindow / RemoteCapture / CameraSetting / DateTimeZoneSetting / RecordFuncCardSetting / Progress
    这些是窗口与对话框。
  • Property/*.cs:大量自定义控件(ComboBox/TrackBar/Label/Button),用于把某个"相机属性"映射为 UI 控件。

关键点:UI 不直接调用 EDSDK,而是发 ActionEvent,让 Controller 去排队执行命令。


2.2 控制层(Controller)

  • CameraController : ActionListener 是核心入口

    它持有:

    • _model(相机状态/数据)
    • _processor(命令队列执行器)

启动流程里,Controller 先启动命令线程,再投递 OpenSession、GetProperty 等初始化命令:

UI 触发动作后,Controller 会把动作翻译成命令并 Post 到队列,例如 TakePicture、StartEvf、DownloadEvf 等:

另外它也提供 "下载整卡/删整卡/格式化" 这种高层入口:


2.3 命令层(Command Pattern)

Command/*.cs 基本都是 一个命令类 = 一个相机操作,例如:

  • TakePictureCommand:发送快门命令并处理 busy/error,成功后把 canDownloadImage = true

  • DownloadCommand:对单个 directoryItem 执行 EdsCreateFileStream → EdsDownload → EdsDownloadComplete

  • DownloadAllFilesCommand:遍历卷(Volume)下的 DCIM / XFVC(轮流切换),统计图片列表,再批量下载

这套命令模式非常适合你做"薄封装":完全可以保留命令层,把 UI 事件换成"照相亭业务指令"(拍照、预览、倒计时、打印、上传AI...)。


2.4 模型层(CameraModel)

CameraModel 存相机句柄、属性缓存、状态位(如 isEvfEnable、canDownloadImage 等),并提供 NotifyObservers 做事件广播。

(项目里很多命令完成后会 _model.NotifyObservers(e),UI 再根据事件刷新界面。)

TakePictureCommand 设置 canDownloadImage,是 "拍照后允许下载"的一个 gating:


2.5 事件与观察者(Observer / Listener / Delegates)

  • Observer.cs:观察者接口/基类(用于 UI 或其他组件订阅模型事件)
  • CameraEvent / CameraEventListener:相机事件封装(错误、下载开始/完成、busy 等)
  • ActionEvent / ActionListener / ActionSource:UI 动作事件体系(典型 Java 风格,C# 用类封装"命令+参数")

典型路径是:

UI控件 → 发 ActionEvent(Command.xxx, arg)CameraController.ActionPerformedCommandProcessor.PostCommandCommand.ExecuteCameraModel.NotifyObservers(CameraEvent)UI刷新


2.6 Native 互操作层(薄封装 / PInvoke)

EDSDK.cs 是你要的"薄封装"雏形:

它用 [DllImport("EDSDK.dll")] 暴露 EDSDK API,例如 EdsSetPropertyData / EdsCreateFileStream / EdsCreateMemoryStream 等。

并且做了一些 Marshal 辅助(例如 ManualWBData 的序列化/反序列化):

你后面做照相亭时,最佳实践就是把 EDSDK.cs 这种 P/Invoke 层严格独立为一个程序集 (比如 CameraSdk.Native),上层只用"强类型 C# API",不要到处散落 DllImport。


3. 关键运行时流程

3.1 启动与会话

Controller.Run() 启动命令线程并投递 OpenSession:

OpenSessionCommand 里有一段很重要:它在会话开始前通过一串 EdsSetPropertyData(... 0x01000000, magicKey, ..., PropID_XXX) "启用私有属性"(TempStatus、RollingPitching、MovieParam、Aspect...等)。

这与官方文档里"在 open session 前做 enable property 才能用某些扩展属性"的说明是一致的:

3.2 拍照与"为什么照片不在 SD 卡里"

观察到的现象:电脑能立刻拿到新照片,但相机回放看不到。这在 EDSDK 控制里最常见原因就是:

  • SaveTo(保存目标)被设置为 Host(电脑)或 Host+Camera
  • 如果是 Host,照片不会写入卡(或写入被延迟/不生成),相机回放自然看不到

也搜到 PropID_SaveTo 的常量存在(你之前的查找结果),说明这个 Sample 具备控制 SaveTo 的入口(是否在你这版代码的 StillImage 流程里设置,要看 OpenSession / RemoteShooting 的完整逻辑;你这次上传的片段里 OpenSession 主要在做"enable private properties",还没直接看到 SaveTo 的设置代码)。

但可以确定的是:下载逻辑确实是把文件写到"运行目录(dirItemInfo.szFileName)对应的路径"(注意这里是直接用文件名创建文件流,并不是选一个固定下载目录):

如果你从 UI 里点击 "SD 卡下载",弹窗 2/2 一闪而过但找不到文件,常见原因就是:
文件被下载到了程序当前工作目录(例如 bin\x64\Debug\ 下),而不是你以为的 Pictures/Downloads。

✅ 可以立刻验证:在 DownloadCommandEdsCreateFileStream(dirItemInfo.szFileName, ...) 这一行打断点,看 dirItemInfo.szFileName 和进程当前目录(Environment.CurrentDirectory)到底是什么。

这比猜下载到哪更快。


4. LiveView(EVF)是什么?StartEvf / DownloadEvf / EndEvf / DriveLens / DoEvfAF / ClickAF 的含义

Canon EDSDK 里把 LiveView 叫 EVF(Electronic View Finder) 数据流控制。

4.1 StartEvf(开启 LiveView 输出到电脑)

核心是把相机的 EVF 输出设备切到 "PC"(有的相机也支持同时输出到 LCD+PC)。官方文档示例也强调"start LiveView 后,会通过 property change 触发下载 EVF 图像"的流程:

4.2 DownloadEvf(拉取一帧 LiveView)

本质是循环调用:

  • 创建 MemoryStream(或复用缓冲)
  • EdsCreateEvfImageRef(stream)
  • EdsDownloadEvfImage(camera, evfImage)
  • 解析 evfImage 中的可视区域、对焦框信息、JPEG 帧数据,然后画到 UI(你的工程里是 EvfPictureBox

(你上传的 pdf 示例里明确出现了 EdsCreateMemoryStream / EdsCreateEvfImageRef / EdsGetPropertyData(...Evf_VisibleRect...) 的套路)

4.3 EndEvf(退出 LiveView)

Controller 里在 EndEvf 前还会强制把 EVF AF 关掉:

这属于"退出 LiveView 的安全收尾",避免相机处在 AF 状态导致后续命令失败。

4.4 DriveLens(手动驱动镜头对焦)

DriveLensCommand 一般会调用 EVF 模式下的 "驱动镜头近/远一步"的命令(Near1/2/3、Far1/2/3)。

Controller 明确把 Focus Near/Far 按钮映射到 DriveLensCommand:

4.5 DoEvfAF(让相机在 LiveView 下执行 AF)

Controller 把 EVF_AF_ON / EVF_AF_OFF 映射到 DoEvfAFCommand:

4.6 ClickAF(点选对焦点)/ ClickWB(点白平衡)

这是高级交互:你在 LiveView 画面上点一下,程序把坐标转换成相机需要的坐标系/Rect,然后发命令让相机在那个点 AF 或做 WB 采样。

Controller 里 ClickAF / ClickWB 的分支非常清晰:


5. 这个工程的"技术路线总结"(一句话版)

WinForms UI + Controller(ActionEvent)+ Command Queue(串行化相机操作)+ CameraModel(状态缓存)+ Observer(事件回调驱动 UI 刷新)+ EDSDK P/Invoke(薄封装)

这正是想要的"照相亭控制层"理想底座:

  • EDSDK 操作必须串行、要处理 busy/retry ------ 命令队列很关键
  • 拍照/下载/LiveView 都是异步事件驱动 ------ Observer 很关键
  • UI/业务一定会变 ------ Controller/Command/Model 的解耦让你以后替换 UI 成本极低

6. 要做 AI Photo Booth:MVP 技术栈设想

给的约束是:

  • MVP:iPad/手机作为控制端
  • 相机控制(Canon R 系列)目前最稳妥还是 Windows 主控 + EDSDK(你已经验证官方 demo 可跑)

因此 MVP 我建议你把系统拆成两块:

6.1 Booth Host(Windows 迷你主机 / 工控机)

  • 运行基于 CameraControl 抽出来的 Camera Service
  • 对外提供 HTTP/WebSocket API(拍照、LiveView帧、状态、设置参数、下载路径、AI上传、打印...)
  • 内部继续复用:CommandProcessor + Commands + Model + EventListener

6.2 Control Client(iPad/手机)

  • Web(H5/PWA) 做控制端最快(浏览器即用,iPad/Android 都 OK)
  • 控制端通过 WebSocket 看 LiveView(低延迟)+ 通过 HTTP 发指令(可靠)

这样你不用纠结 Flutter/Qt 先上哪个:MVP 先把 "跨端控制" 用 Web 解决,Host 端继续 .NET/C# 做相机控制最省风险。

相关推荐
一起搞IT吧2 小时前
相机Camera日志实例分析之十二:相机Camx【萌拍后置zoom拍照】单帧流程日志详解
android·c++·数码相机·智能手机
helloworddm2 小时前
防止应用多开-WPF
服务器·架构·c#
qq7422349844 小时前
大模型技术全景与核心概念解析:从基础原理到AI智能体架构
人工智能·python·架构
Godspeed Zhao4 小时前
现代智能车机系统2——EEA架构(1)
架构
一起搞IT吧4 小时前
三方相机问题分析十一:【手电筒回调异常】手电筒打开3档时,达到档位控制温度,手电筒二级界面中档位为0
android·图像处理·数码相机
catchadmin5 小时前
CatchAdmin 2025 年终总结 模块化架构的进化之路
架构·php·开源软件
乾元5 小时前
Network-as-Code:把 HCIE / CCIE 实验脚本转为企业级 CI 工程化流程
运维·网络·人工智能·安全·web安全·ai·架构
Aaron15885 小时前
三种主流接收机架构(超外差、零中频、射频直采)对比及发展趋势浅析
c语言·人工智能·算法·fpga开发·架构·硬件架构·信号处理
Wilson Chen5 小时前
从“手搓”到云原生:某 B2B 平台服装 AI 搜索架构演进实战
人工智能·云原生·架构