0

I'd like to get started using Delphi to access and control IP-cameras with the Onvif SOAP protocol.

However, I do not understand how to actually perform the calls from Delphi. I've imported the following WDSL:

http://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl

and a unit was generated in Delphi. But how do I perform the calls towards the camera? Am I supposed to use this generated together with THTTPRIO somehow? How to I specify the IP-address of the camera I have (currently an Axis Q1755 camera).

If someone could get me started by pointing me in the correct direction I would be very grateful.

TomRay74
  • 513
  • 6
  • 17
  • To specifiy the P address: see http://stackoverflow.com/questions/3596868/onvif-how-to-form-the-device-web-service-address-from-the-ip-address-of-an-nvt – mjn Sep 29 '14 at 06:46

2 Answers2

1

If you used Delphi's WSDL importer it will have generated the necessary classes to represent any request fro the SOAP webservice and its corresponding response.

It will also have generated a wrapper, which is what you will use to actually do the calls. Usually it will have a few methods like following:

function mySoapMethod(myRequest: TmyRequestType): TMyResponse;

To do a call you basically do following:

  1. Get a wrapper instance reference. There should be a method in the unit called something similar to GetWrapper.
  2. If necessary, you create an instance for the request type and set all its properties. Mind you, it may be there is no request class as such if the SOAP method just needs a few basic types as parameters. Take also in account that any sufficiently complicated request type might implicate that you will need to create instances of objects and assign them as properties to the request.
  3. Use the corresponding wrapper method to send the request (or setthe parameters if it is a simple call).
  4. Receive the response object and act on it as needed.

In pseudocode it would be as following:

myWrapper := GetMyWrapper();
myRequest := TMyRequest.Create;
//set myRequest properties
myResponse := myWrapper.mySoapMethod(myRequest);
//do whatever you need with the response

About the wrapper

The wrapper will be an implementation of the IInvokable interface with some added methods. Actually it should have one method for each SOAP method you can call.

Usually its interface declaration will be something like this:

TmyWrapper = interface(IInvokable)
['...'] //GUID here
  function oneMethod(...): oneMethodResponse;
  function anotherMethod(...): anotherMethodResponse;
end;

function getMyWrapper(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): TmyWrapper ;

And the function's implementation will look like this:

function GetMyWrapper(UseWSDL: Boolean; Addr: string; HTTPRIO: THTTPRIO): TmyWrapper;
const
  defWSDL = 'http://<soap service IP and port>/<soap service name>?wsdl';
  defURL  = 'http://<soap service IP and port>/<soap service name>';
  defSvc  = '<default service name>';
  defPrt  = '<default service port>';
var
  RIO: THTTPRIO;
begin
  Result := nil;
  if (Addr = '') then
  begin
    if UseWSDL then
      Addr := defWSDL
    else
      Addr := defURL;
  end;
  if HTTPRIO = nil then
    RIO := THTTPRIO.Create(nil)
  else
    RIO := HTTPRIO;
  try
    Result := (RIO as TmyWrapper );
    if UseWSDL then
    begin
      RIO.WSDLLocation := Addr;
      RIO.Service := defSvc;
      RIO.Port := defPrt;
    end else
      RIO.URL := Addr;
  finally
    if (Result = nil) and (HTTPRIO = nil) then
      RIO.Free;
  end;
end;

Some side notes:

  • You can inject an HTTPRIO instance to the wrapper when you get its instance by using the getXYZWrapper method. This can help you for example to set another SOAP URL or to inspect the XML generated.
  • Sometimes Delphi generates the XML to be sent in a slightly different way as the SOAP expects it. If you think it should work but does not, inspect the XML generated and compare it to what it should be. You can use the HTTPRIO object's onBeforeExecute event to modify the XMLbefore sending it.
  • I you need it, you can also use the onAfterExecute method of the HTTPRIO object to inspect the XML response.
Guillem Vicens
  • 3,936
  • 30
  • 44
  • Thank you for the detailed response. Unfortunately, I have no Wrapper function in the generated unit that looks like the one you suggested. I have a bunch of TRemotable classes and some enums and Interfaces. But how do I wire these up with the HTTPRIO object to call my camera? – TomRay74 Sep 29 '14 at 05:39
  • @TomRay74, sorry, it was late yesterday and I wrote this answer out of my head. I'll update it to give more detail on the wrapper. – Guillem Vicens Sep 29 '14 at 06:26
0

you can use my imported WSDLs by Delphi XE7

Media : Media.WSDL

Imaging : Imaging.wsdl

device management: devicemgmt.wsdl


usually imported WSDLs have a function such as

function GetMySoap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): MySoap;

but it not exists in onvif imported wsdl.

add this function to your project instance, Media2 is name of IInvokable class

function GetWSSoap(UseWSDL: Boolean=System.False; Addr: string=''; HTTPRIO: THTTPRIO = nil): Media2;
const
  defWSDL = 'http://192.168.1.1/onvif/Media?WSDL';
  defURL  = 'http://192.168.1.1/onvif/Media';
  defSvc  = 'Media';
  defPrt  = '80';
var
  RIO: THTTPRIO;
begin
  Result := nil;
  if (Addr = '') then
  begin
    if UseWSDL then
      Addr := defWSDL
    else
      Addr := defURL;
  end;
  if HTTPRIO = nil then
    RIO := THTTPRIO.Create(nil)
  else
    RIO := HTTPRIO;
  try
    Result := (RIO as Media2);
    if UseWSDL then
    begin
      RIO.WSDLLocation := Addr;
      RIO.Service := defSvc;
      RIO.Port := defPrt;
    end else
      RIO.URL := Addr;
  finally
    if (Result = nil) and (HTTPRIO = nil) then
      RIO.Free;
  end;
end;

another Solution , get some general information with send a http requst Sample request GetProfiles is name of methods

<?xml version="1.0"?> <soap:Envelope 
xmlns:soap="http://www.w3.org/2003/05/soap-envelope" 
xmlns:wsdl="http://www.onvif.org/ver10/media/wsdl">
<soap:Header>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1"> 
<UsernameToken> 
<Username>%s</Username> 
<Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">%s</Password> 
<Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">%s</Nonce> 
<Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">%s</Created> 
</UsernameToken> 
</Security> 
</soap:Header>
<soap:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
<GetProfiles xmlns="http://www.onvif.org/ver10/media/wsdl" /> 
</soap:Body> 
</soap:Envelope>

button Code :

ip : http://192.168.1.1

url : onvif/media

  GetONVIFPasswordDigest(ed_user.text, ED_pass.text, PasswordDigest, Nonce, Created);
  User_Pass := Format(M_Request, [UserName, PasswordDigest, Nonce, Created]);
  ONVIFRequest(ED_IP.Text + ED_URL.Text,User_Pass,Result);

password maker Procedures :

procedure GetONVIFPasswordDigest(const UserName, Password: String; Var PasswordDigest, Nonce, Created: String);
Var
  i: Integer;
  raw_nonce, bnonce, digest , S_In: TBytes;
  raw_digest: TBytes;
  DT : TDateTime;
begin
  SetLength(raw_nonce, 20);
  for i := 0 to High(raw_nonce) do
    raw_nonce[i] := Random(256);
  bnonce := TNetEncoding.Base64.Encode(raw_nonce);
  Nonce := BytesToString(bnonce);
  DT := Now();
  Created := GetONVIFDateTime(DT);
  S_In := raw_nonce + StringToBytes(Created) + StringToBytes(Password);
  raw_digest := SHA1(S_In);
  digest := TNetEncoding.Base64.Encode(raw_digest);
  PasswordDigest := BytesToString(digest);
end;

get data procedure :

procedure ONVIFRequest(const Addr: String; const InStream, OutStream: TStringStream);overload;
Var
  idhtp1: TIdHTTP;
  Uri: TIdURI;
begin
  idhtp1 := TIdHTTP.Create;
  Uri := TIdURI.Create(Addr);
  try
    With idhtp1 do
    begin
      AllowCookies := True;
      HandleRedirects := True;
      Request.Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
      Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
      Request.Host := '';
      Request.Connection := '';
      Request.Accept := '';
      Request.UserAgent := '';

      Request.CustomHeaders.Clear;
      Request.ContentType := 'text/xml;charset=utf-8';
      Request.CustomHeaders.Add('Host: ' + Uri.Host);
      //-------------------
      request.username := ed_user.text;
      request.password := ed_pass.text;
      HTTPOptions := [hoInProcessAuth,hoForceEncodeParams,hoNoProtocolErrorException, hoWantProtocolErrorContent];
      //-------------------

      ProtocolVersion := pv1_1;
      Post(Addr, InStream, OutStream);
    end;
  finally
    Uri.Free;
    idhtp1.Free;
  end;
end;
procedure ONVIFRequest(const Addr, Request: String; Var Answer: String);overload;
Var
  InStream, OutStream: TStringStream;
begin
  InStream := TStringStream.Create(Request);
  OutStream := TStringStream.Create;
  try
    ONVIFRequest(Addr, InStream, OutStream);
    Answer := OutStream.DataString;
  finally
    InStream.Free;
    OutStream.Free;
  end;
end;

You can also find a better sample here

https://github.com/Laex/Delphi-ONVIF