Skip to content

cam.getSnapshotUri() not working with public camera id #336

Open

Description

@JooZef315

I am connecting to a camera with its public Ip as it is not on my local network,

      const cam = new Cam(
        {
          username: "xx",
          password: "xx",
          hostname: "x.x.x.x",
          port: x,
        },
        async function (err) {
          if (err) {
            console.error("Failed to connect to the camera:", err.message || err);
            return reject(new Error(`Connection failed: ${err.message || err}`));
          }         
          await cam.getSnapshotUri(
            { stream: "RTP-Multicast", protocol: "RTSP" },
            function (err, data) {
              console.log(data);
            }
          );
    }
  );

data returns
{ uri: undefined }

what did i miss? or isn't it possible to take a snapshot with that connection?

Activity

  1. RogerHardiman commented on Oct 16, 2024

    @RogerHardiman
    Collaborator

    Spotted a few eroors in the code.
    a) You only need Stream (Multicast) and Protocol (RTSP) for Get StreamUri(). You do not need them for GetSnapshotUri

    b) The await on getStreamuri does nothing. The library is a "function with callback" type of library, unless you pass the library through Promisify and then you can use await and would not have a callback.
    If you were trying to wait for it to finish, and then resolve a promise, you'd call resolve after console.log(data)

    this may not explain why it fails though

  2. JooZef315 commented on Oct 16, 2024

    @JooZef315
    Author

    Spotted a few eroors in the code. a) You only need Stream (Multicast) and Protocol (RTSP) for Get StreamUri(). You do not need them for GetSnapshotUri

    b) The await on getStreamuri does nothing. The library is a "function with callback" type of library, unless you pass the library through Promisify and then you can use await and would not have a callback. If you were trying to wait for it to finish, and then resolve a promise, you'd call resolve after console.log(data)

    this may not explain why it fails though

    thank you for replying @RogerHardiman,

    refactored it to this, but still the same error

    cam.getSnapshotUri(
    { profileToken: cam.activeSource.profileToken },
    function (err, data) {
      console.log(data);
    }
    );
  3. NdiayeeCheikh commented on Apr 14, 2025

    @NdiayeeCheikh

    You can use an api according to your framework, i'm using nextjs by the way.
    Inside the api , just do a soap request :

    import DigestClient from 'digest-fetch';
    import { parseStringPromise } from 'xml2js'; // Utilisation de xml2js pour parser le XML

    export async function POST(req) {
    try {
    const body = await req.json();
    const ip = body.ip;
    const profileToken = body.profileList[0];
    console.log('IP:', ip);
    console.log('ProfileToken:', profileToken);

    if (!ip || !profileToken) {
      return new Response(
        JSON.stringify({ error: 'Adresse IP ou ProfileToken manquants' }),
        {
          status: 400,
          headers: { 'Content-Type': 'application/json' },
        }
      );
    }
    
    const url = `http://${ip}:80/onvif/device_service`;
    const username = 'xx';
    const password = 'xx';
    
    const headers = {
      'Content-Type': 'application/soap+xml; charset=utf-8',
      'SOAPAction': '"http://www.onvif.org/ver10/media/wsdl/GetSnapshotUri"',
    };
    
    const soapBody = `
      <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:wsdl="http://www.onvif.org/ver10/media/wsdl">
        <soap:Header/>
        <soap:Body>
          <wsdl:GetSnapshotUri>
            <wsdl:ProfileToken>${profileToken}</wsdl:ProfileToken>
          </wsdl:GetSnapshotUri>
        </soap:Body>
      </soap:Envelope>
    `;
    
    const client = new DigestClient(username, password);
    const response = await client.fetch(url, {
      method: 'POST',
      headers,
      body: soapBody,
    });
    
    const xml = await response.text();
    
    const result = await parseStringPromise(xml, { explicitArray: false });
    const snapshotUri = result['env:Envelope']['env:Body']['trt:GetSnapshotUriResponse']['trt:MediaUri']['tt:Uri'];
    console.log('SnapshotUri:', snapshotUri);
    return new Response(
      JSON.stringify({ snapshotUri }),
      {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      }
    );
    

    } catch (err) {
    return new Response(JSON.stringify({ error: err.message }), {
    status: 500,
    headers: { 'Content-Type': 'application/json' },
    });
    }
    }

  4. RogerHardiman commented on Sep 30, 2026

    @RogerHardiman
    Collaborator

    Could I have remote acccess to the camera - (I realise this was 2 years ago) please
    It is also possible that this is a camera that does not implement cam.getSnapshotUri().
    That ONVIF API is only needed in Profile T devices. If it is an older ONVIF Device (Profile s) then it may not be available for your user login.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions