Delphi中通过ONVIF接收IP摄像机事件的实现方案咨询
解决方案:Delphi下实现ONVIF事件订阅与接收
一、原生SOAP调用实现核心逻辑
ONVIF基于SOAP协议,Delphi自带SOAP工具链,可自行实现事件订阅流程:
- 第一步,获取目标摄像机的ONVIF WSDL文件,通常设备服务入口为
http://[相机IP]/onvif/device_service,事件服务入口为/onvif/events - 用Delphi的WSDL Importer(路径:Tools -> WSDL Importer)导入事件服务WSDL,自动生成SOAP客户端单元
- 实现WS-Security鉴权:在
THTTPRio的OnBeforeExecute事件中,添加包含用户名密码的Digest认证头,符合ONVIF的鉴权标准 - 选择订阅模式:
- 回调模式:搭建本地HTTP服务(推荐用
TIdHTTPServer),调用Subscribe方法指定回调地址,摄像机触发事件时会主动推送SOAP消息到该地址 - 拉取模式:调用
CreatePullPointSubscription创建拉取订阅,定时调用PullMessages主动获取事件数据
- 回调模式:搭建本地HTTP服务(推荐用
- 解析事件:从SOAP响应中提取事件类型,越线检测对应
RuleEngine/LineCrossing、移动侦测对应RuleEngine/MotionDetection,根据业务需求处理告警逻辑
二、第三方组件简化开发
如果不想从头封装SOAP逻辑,可选用成熟的Delphi ONVIF组件:
- ONVIF Delphi Component Suite:封装了设备发现、事件订阅、媒体流管理等全量ONVIF功能,通过组件的
Subscribe方法即可完成事件订阅,触发时通过组件回调接收告警 - 自定义Indy封装:基于Indy的HTTP组件手动构造SOAP请求,适合需要高度定制协议细节的场景
三、与TVideograbber的整合方案
结合你已在用的TVideograbber实现联动:
- 事件触发时,利用
TVideograbber.Overlay功能在视频画面上叠加告警标识,比如绘制红框、告警文字 - 触发时刻调用
TVideograbber.SaveFrameToBitmap保存当前帧,关联事件日志展示 - 将事件信息写入界面日志列表,实现视频与告警的同步追踪
关键代码示例
1. 拉取模式订阅事件(基于自动生成的SOAP客户端)
var EventService: EventPortType; PullPoint: PullPointSubscription; MsgList: NotificationMessageList; I: Integer; begin // 初始化事件服务客户端 EventService := GetEventPortType(false, 'http://192.168.1.100/onvif/events', nil); // 添加WS-Security鉴权 (EventService as THTTPRio).OnBeforeExecute := procedure(const MethodName: string; SOAPRequest: TStream) var SecHeader: TSecurityHeader; begin SecHeader := TSecurityHeader.Create; try SecHeader.UsernameToken.Username := 'admin'; SecHeader.UsernameToken.Password := 'your_password'; SecHeader.UsernameToken.PasswordType := 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest'; // 注意:实际需按ONVIF标准生成Nonce和Created时间戳,此处为简化示例 (EventService as THTTPRio).SOAPHeaders.Send(SecHeader); finally SecHeader.Free; end; end; // 创建拉取订阅 PullPoint := EventService.CreatePullPointSubscription('', '', 60); // 定时拉取消息(建议用TTimer触发,间隔不超过订阅超时时间) MsgList := EventService.PullMessages(PullPoint.SubscriptionReference.Address.Value, 10, 60); // 解析事件 if Assigned(MsgList.NotificationMessage) then begin for I := 0 to Length(MsgList.NotificationMessage) - 1 do begin if MsgList.NotificationMessage[I].Message._Type.Contains('LineCrossing') then begin // 处理越线告警 TVideograbber1.OverlayText('越线检测触发', 10, 10, clRed); end else if MsgList.NotificationMessage[I].Message._Type.Contains('MotionDetection') then begin // 处理移动侦测告警 TVideograbber1.OverlayText('移动侦测触发', 10, 30, clYellow); end; end; end; end;
2. 回调模式本地HTTP服务(基于Indy)
procedure TMainForm.IdHTTPServer1CommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var EventXML: TXMLDocument; EventTypeNode: IXMLNode; begin // 解析摄像机推送的SOAP事件消息 EventXML := TXMLDocument.Create(nil); try EventXML.LoadFromStream(ARequestInfo.PostStream); EventXML.Active := True; // 通过XPath定位事件类型节点(需引入XMLIntf单元) EventTypeNode := EventXML.DocumentElement.SelectNode('//tt:Message/tt:Type'); if Assigned(EventTypeNode) then begin if EventTypeNode.Text.Contains('LineCrossing') then Synchronize(procedure begin TVideograbber1.OverlayText('越线告警', 10, 10, clRed); end) else if EventTypeNode.Text.Contains('MotionDetection') then Synchronize(procedure begin TVideograbber1.OverlayText('移动侦测', 10, 30, clYellow); end); end; // 返回SOAP响应确认 AResponseInfo.ResponseNo := 200; AResponseInfo.ContentText := '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Body><wsnt:SubscribeResponse xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2"><wsnt:SubscriptionReference><wsa:Address xmlns:wsa="http://www.w3.org/2005/08/addressing">http://192.168.1.50:8080/onvif-callback</wsa:Address></wsnt:SubscriptionReference></wsnt:SubscribeResponse></s:Body></s:Envelope>'; AResponseInfo.ContentType := 'application/soap+xml'; finally EventXML.Free; end; end;
内容的提问来源于stack exchange,提问作者Shraga Milon
相关产品推荐
相关产品推荐

