Mauricio
Mauricio

Reputation: 31

C# .NET Framework 4.52 Zoom/Focus for PELCO Camera via ONVIF

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:

  1. Following the implementation algorithm described in this answer: ONVIF: what is the command to focus in /out?

  2. Added this Controller class to my project https://www.codeproject.com/Tips/1192709/ONVIF-PTZ-Control-in-Csharp

  3. 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

  4. 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:

  1. (Not my code, my comments) from the Controller.cs referenced, here is how services are accessed:
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;
  1. In the Controller class linked above, I have added the following:
//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

Answers (1)

Mauricio
Mauricio

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

Related Questions