Reputation: 31
I'm new to programming and this is my first app with ONVIF. I have a basic Windows forms app to control a fixed camera, that will stream over LAN to a Win10 PC that cannot be upgraded from .NET Framework 4.52. I'm currently receiving/recording RTSP video stream with VLC.
How do I implement zoom and focus functions to consume these PTZ services?
I need to have some basic zoom in/out buttons and probably couple the logic for the focal length/focus to adjust accordingly.
I have tried:
Following the implementation algorithm described in this answer: ONVIF: what is the command to focus in /out?
Added this Controller class to my project https://www.codeproject.com/Tips/1192709/ONVIF-PTZ-Control-in-Csharp
Using the specification, doesn't give me but some generic description I can't fully understand onvif.org/specs/srv/ptz/ONVIF-PTZ-Service-Spec-v221.pdf
Using the WSDL filed I used VS2019 to add the service reference for: device, media1, media2, ptz services. I also find it more useful for a quick reference than the specification PDF. http://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl
My approach:
var messageElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.CreateVersion(
EnvelopeVersion.Soap12, AddressingVersion.None)
};
HttpTransportBindingElement httpBinding = new HttpTransportBindingElement()
{
AuthenticationScheme = AuthenticationSchemes.Digest
};
CustomBinding bind = new CustomBinding(messageElement, httpBinding);
mediaClient = new MediaClient(bind,
new EndpointAddress($"http://{cameraAddress}/onvif/Media"));
mediaClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.UserName = userName;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.Password = password;
ptzClient = new PTZClient(bind,
new EndpointAddress($"http://{cameraAddress}/onvif/PTZ"));
ptzClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;
ptzClient.ClientCredentials.HttpDigest.ClientCredential.UserName = userName;
ptzClient.ClientCredentials.HttpDigest.ClientCredential.Password = password;
var profs = mediaClient.GetProfiles();//TODO: Inspect profiles returned
profile = mediaClient.GetProfile(profs[0].token);
options = ptzClient.GetConfigurationOptions(configs[0].token);
velocity = new MRML_PELCO_EXF1230_7N_Camera_Control_v1.OnvifPTZService.PTZSpeed()
{
PanTilt = new MRML_PELCO_EXF1230_7N_Camera_Control_v1.OnvifPTZService.Vector2D()
{
x = 0,
y = 0,
space = options.Spaces.ContinuousPanTiltVelocitySpace[0].URI,
},
Zoom = new MRML_PELCO_EXF1230_7N_Camera_Control_v1.OnvifPTZService.Vector1D()
{
x = 0,
//Inspect this space var:
//space = options.Spaces.ContinuousZoomVelocitySpace[0].URI, // mm/s
//specifies the change per second of the focal length
space = options.Spaces.RelativeZoomTranslationSpace[0].URI
//See page 40 of ONVIF-PTZ-Service-Spec
}
};
if (relative)
{
timer = new Timer(TimerInterval);
timer.Elapsed += Timer_Elapsed;
velocity.PanTilt.space = options.Spaces.RelativePanTiltTranslationSpace[0].URI;
panDistance = (options.Spaces.RelativePanTiltTranslationSpace[0].XRange.Max -
options.Spaces.RelativePanTiltTranslationSpace[0].XRange.Min) / PanIncrements;
tiltDistance = (options.Spaces.RelativePanTiltTranslationSpace[0].YRange.Max -
options.Spaces.RelativePanTiltTranslationSpace[0].YRange.Min) / TiltIncrements;
}
vector = new PTZVector()
{
PanTilt = new OnvifPTZService.Vector2D()
{
x = 0,
y = 0,
space = options.Spaces.RelativePanTiltTranslationSpace[0].URI
}
};
result = initialised = true;
//I realize this should be cleaned up but as a novice, I'm more worried about it working soon
public void zoomIn()
{
if(initialised)
{
//It is unclear if these variables were initialized correctly
translation = new PTZVector()
{ //THIS IS INCORRECT:
//Zoom = options.Spaces.RelativeZoomTranslationSpace[0].XRange.Min
};
// Actions to be performed
//Input: ReferenceToken ProfileToken, PTZVector Translation, PTZSpeed Speed
ptzClient.RelativeMove(profile.token, translation, speed);
//Other variables passed here are declared & initialized in the Controller.cs file
}
}
The issues: I'm running out of time and need to have zoom/focus working like yesterday. I will comment at some point on the specific errors outputted. For now I put this in front of expert eyes that could potentially point out my silly rookie mistakes.
p.s. I should say the camera is in a remote location so I need to gather knowledge, write anything I can, upload to a memory card and then go try in this location with slow internet on my phone as a backup i.e. the PC running the app has no internet. So yeah, not ideal.
Upvotes: 1
Views: 1235
Reputation: 31
In case anyone needs it, here is what ended up working for me:
//Here is what's needed to connect to all WS clients
HttpTransportBindingElement httpTransport = new HttpTransportBindingElement();
httpTransport.AuthenticationScheme = System.Net.AuthenticationSchemes.Digest;//ONVIF Device Manager uses Basic
httpTransport.MaxReceivedMessageSize = Int32.MaxValue;//100L * 1024L * 1024L
httpTransport.MaxBufferSize = Int32.MaxValue;
httpTransport.ProxyAddress = null;
httpTransport.BypassProxyOnLocal = true;
httpTransport.UseDefaultWebProxy = false;
httpTransport.TransferMode = TransferMode.StreamedResponse;
//Custom Binding:
binding = new CustomBinding(new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, System.Text.Encoding.UTF8), httpTransport);
binding.CloseTimeout = TimeSpan.FromSeconds(30.0);
binding.OpenTimeout = TimeSpan.FromSeconds(30.0);
binding.SendTimeout = TimeSpan.FromMinutes(10.0);
binding.ReceiveTimeout = TimeSpan.FromMinutes(3.0);
//Here is how to connect to the deviceClient
deviceClient = new DeviceClient(binding, new EndpointAddress(deviceUri.ToString()));
deviceClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
deviceClient.ClientCredentials.HttpDigest.ClientCredential.UserName = user;
deviceClient.ClientCredentials.HttpDigest.ClientCredential.Password = pw;
//Here is how to connect to the mediaClient (required for PTZ)
var messageElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.CreateVersion(
EnvelopeVersion.Soap12, AddressingVersion.None)
};
HttpTransportBindingElement httpBinding = new HttpTransportBindingElement()
{
AuthenticationScheme = AuthenticationSchemes.Digest
};
CustomBinding bind = new CustomBinding(messageElement, httpBinding);
mediaClient = new MediaClient(bind,
new EndpointAddress($"http://{cameraAddress}/onvif/media_service"));
mediaClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.UserName = cameraUser;
mediaClient.ClientCredentials.HttpDigest.ClientCredential.Password = cameraPassword;
var profiles = mediaClient.GetProfiles();//TODO: INSPECT ALL PROFILES AVAILABLE
mediaProfile = mediaClient.GetProfile(profiles[0].token);
//Here is how to connect to PTZ Client
ptzClient = new PTZClient(bind,
new EndpointAddress($"http://{cameraAddress}/onvif/ptz_service"));
ptzClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;
ptzClient.ClientCredentials.HttpDigest.ClientCredential.UserName = cameraUser;
ptzClient.ClientCredentials.HttpDigest.ClientCredential.Password = cameraPassword;
var compatibleConfigs = ptzClient.GetCompatibleConfigurations(mediaProfile.token);//TODO: INSPECT ALL CONFIGS AVAILABLE profiles[0].token
var ptzNodes = ptzClient.GetNodes();//TODO: INSPECT ALL NODES AVAILABLE
var ptzConfig = ptzClient.GetConfiguration(compatibleConfigs[0].token);
ptzOptions = ptzClient.GetConfigurationOptions(compatibleConfigs[0].token);
//Now For the MaxZoom for my PELCO camera (sure there are better ways...)
ptzTranslation = new OnvifPTZService.PTZVector()
{
Zoom = new OnvifPTZService.Vector1D()
{
x = .085F,
space = ptzOptions.Spaces.RelativeZoomTranslationSpace[0].URI
}
};
ptzSpeed = new OnvifPTZService.PTZSpeed()
{
Zoom = new OnvifPTZService.Vector1D()
{
x = .01F,
space = ptzOptions.Spaces.RelativeZoomTranslationSpace[0].URI
}
};
ptzClient.RelativeMove(mediaProfile.token, ptzTranslation, ptzSpeed);
//Now For the minZoom for my PELCO camera (i.e. back to starting not-zoomed state)
ptzTranslation = new OnvifPTZService.PTZVector()
{
Zoom = new OnvifPTZService.Vector1D()
{
x = -1,
space = ptzOptions.Spaces.RelativeZoomTranslationSpace[0].URI
}
};
ptzSpeed = new OnvifPTZService.PTZSpeed()
{
Zoom = new OnvifPTZService.Vector1D()
{
x = -1,
space = ptzOptions.Spaces.RelativeZoomTranslationSpace[0].URI
}
};
ptzClient.RelativeMove(mediaProfile.token, ptzTranslation, ptzSpeed);
//Incremental zoomin
ptzTranslation = new OnvifPTZService.PTZVector()
{
Zoom = new OnvifPTZService.Vector1D()
{
x = .0075F,
space = ptzOptions.Spaces.RelativeZoomTranslationSpace[0].URI
}
};
ptzSpeed = new OnvifPTZService.PTZSpeed()
{
Zoom = new OnvifPTZService.Vector1D()
{
x = .09F,
space = ptzOptions.Spaces.RelativeZoomTranslationSpace[0].URI
}
};
ptzClient.RelativeMove(mediaProfile.token, ptzTranslation, ptzSpeed);
//Incremental zoomout
ptzTranslation = new OnvifPTZService.PTZVector()
{
Zoom = new OnvifPTZService.Vector1D()
{
x = -.0075F,
space = ptzOptions.Spaces.RelativeZoomTranslationSpace[0].URI
}
};
ptzSpeed = new OnvifPTZService.PTZSpeed()
{
Zoom = new OnvifPTZService.Vector1D()
{
x = 0.09F,
space = ptzOptions.Spaces.RelativeZoomTranslationSpace[0].URI
}
};
ptzClient.RelativeMove(mediaProfile.token, ptzTranslation, ptzSpeed);
//Note you can adjust the values passed to x to increase/decrease the zoom jump. I'm still learning so please comment if you have information for others implementing this.
Upvotes: 2