libonvif icon indicating copy to clipboard operation
libonvif copied to clipboard

Unable to use the ptz related move API

Open rinjingu opened this issue 9 months ago • 9 comments

I am using the continuousMove and moveStop to control a hikvision PTZ camera, however the control is not success. May I confirm the usage and the functionality of these command?

There is a demo part of usage, onvif_data and onvif_session are inited.

if (x == 0.0 && y == 0.0 && z == 0.0)
        {
            // Stop both pan/tilt and zoom
            auto result_pan_tilt = moveStop(PAN_TILT_STOP, onvif_data);
            auto result_zoom = moveStop(ZOOM_STOP, onvif_data);

            if (result_pan_tilt == 0 && result_zoom == 0)
            {
                return true;
            }
            else
            {
                std::cerr << "Failed to stop PTZ movement" << std::endl;
                std::cerr << "Error: " << onvif_data->last_error << std::endl;
                return false;
            }
        }

and result in

Failed to stop PTZ movement
Error: ter:InvalidArgVal moveStop

Additional log when init:

Found and connected to camera at 172.30.1.98
Camera name: UNKNOWN CAMERA
Serial number: DS-2DC4423IW-D20220704AACHS6K24339421
PTZ Configuration:
PTZ Service URL: POST /onvif/PTZ HTTP/1.1

rinjingu avatar Apr 11 '25 11:04 rinjingu

I think where the issue lies is in trying to run the command twice in one call. The moveStop command is context specific depending on whether the continuousMove command was called for PAN_TILT or ZOOM. This may not be entirely obvious from the code, as there are a couple of layers between the user interface and the command implementation. From the interface, the user is only allowed to initiate either a zoom or an x-y move one at a time. The software determines the STOP flag based on the type of command that was called.

So even though all x, y and z parameters are used for each call, the higher level software limits the combinations to a subset where only one value is allowed to be non-zero when the move command is initiated by clicking on one of the control buttons. When the control button is released, the stop command is issued in a context specific call based on the type of move that was started.

You can find this code in the file ptztab.py that is part of the gui code. There are actually separate calls for stopZoom and stopPanTilt that attempt to provide at least some clarity around this topic.

Thank you so much for reaching out, your interest in the project is greatly appreciated.

sr99622 avatar Apr 11 '25 13:04 sr99622

I have tried with just one move command and still doesn't work for me

rinjingu avatar Apr 13 '25 18:04 rinjingu

The first thing I would look for is that the stop command matches the move command

sr99622 avatar Apr 13 '25 20:04 sr99622

Some code was added to onvif-util.cpp that shows better how this command can work. It turns out that you can actually call stop twice to stop both pan and tilt at one time. The details can be found in this pull request

sr99622 avatar Apr 23 '25 13:04 sr99622

@rinjingu I encountered this issue when working on https://github.com/sr99622/libonvif/pull/116 and other changes. After connecting to the camera you have to first get the camera's information and profile:


initializeSession(onvif_session_);
getActiveNetworkInterfaces(onvif_session_);
getCapabilities(onvif_data);
getDeviceInformation(onvif_data);
getProfileToken(onvif_data_, profile_index);
continuousMove(1, 0, 0, onvif_data);

pele1410 avatar May 27 '25 11:05 pele1410

@pele1410 Regarding #116, after looking at this, it occurred to me that it would be more convenient for users of onvif-util if they didn't have to set the profile themselves when calling this command. We can add profileCheck(onvif_data, args) to each of the move commands pan, zoom and stop so that it will be transparent to the user. This would make the move phrase look as shown below. I can go ahead and add these in to onvif-util.cpp if you like, I didn't want to go and change things suddenly if you are working on something.

else if (args[0] == "move") {
	args.erase(args.begin());
	
	if (args[0] == "pan") {
			if (args.size() > 2) {
				args.erase(args.begin());
				profileCheck(onvif_data, args);
				double const pan_rate = stof(args[0]);
				double const tilt_rate = stof(args[1]);
			
				if (continuousMove(pan_rate, tilt_rate, 0, onvif_data))
				throw std::runtime_error(
							cat("move pan - ", onvif_data->last_error));
			
				std::cout << "  Pan rate " << pan_rate << ", Tilt rate "
				<< tilt_rate << "\n" << std::endl;
			
			} else {
				std::cout << "  Missing value for Pan/Tilt\n" << std::endl;
			}
		
	} else if (args[0] == "zoom") {
			if (args.size() > 1) {
				args.erase(args.begin());
				profileCheck(onvif_data, args);
				double const zoom_rate = stof(args[0]);
			
				if (continuousMove(0, 0, zoom_rate, onvif_data))
				throw std::runtime_error(
							cat("move zoom - ", onvif_data->last_error));
			
				std::cout << "  Zoom rate " << zoom_rate << "\n" << std::endl;
			
			} else {
				std::cout << "  Missing value for Zoom\n" << std::endl;
			}
		
	} else if (args[0] == "stop") {
			// Send stop for both pan/tilt and zoom					
			profileCheck(onvif_data, args);
			if (moveStop(0, onvif_data))
				throw std::runtime_error(
					cat("move stop pan/tilt - ", onvif_data->last_error));
		
			if (moveStop(1, onvif_data))
				throw std::runtime_error(
					cat("move stop zoom - ", onvif_data->last_error));
		
	} else {
			std::cout << "  Unrecognized command \"" << args[0]
				<< "\", type \"help\" to see help\n"
				<< std::endl;
	}
}

sr99622 avatar May 27 '25 16:05 sr99622

I had the same thought as I was writing up the response. I can add that as part of the move commands in my current PR so they get integrated seamlessly. Should be able to tomorrow sometime.

pele1410 avatar May 28 '25 00:05 pele1410

Added the profileCheck to https://github.com/sr99622/libonvif/pull/119

pele1410 avatar Jun 01 '25 10:06 pele1410

@pele1410 thank you for the solution

rinjingu avatar Jun 03 '25 11:06 rinjingu