patrickmichalina / onvif-rx

📹 Communicate with ONVIF devices and cameras in server and browser environments.
MIT License
23 stars 11 forks source link

Onvif Device URLs #52

Open Mudrekh opened 2 years ago

Mudrekh commented 2 years ago

Hi @patrickmichalina

We came accross an issue with certain devices relating to the URLs used to interface with the device. The default url to interface with the device is at http://<IP>/onvif/device_service. While some manufacturers seem to publish the entire ONVIF API on the device_service route, some do not. For example, we were using an ADVIDIA camera that actually only published services on their specific routes. For reference, I believe the actual ONVIF interface guide recommends that you actually call Device.GetCapabilities first to determine what services the device supports as well as get the URLs for different services.

Have you come upon this issue and thought about how you would solve it using the 'Managed' api? Based on the 'Ad Hoc' usage you could probably get around it, but having to supply the configs everytime seems tedius and more error prone.

The ADVIDIA camera we were testing with returns this data for Capabilities. Using the XAddr's supplied for each service returns correctly, where as calls made to services not on the default URL result in 'Optional Operation Not Supported'

{
  "Capabilities": {
    "Analytics": {
      "XAddr": "http://192.168.1.91/onvif/Analytics",
      "RuleSupport": true,
      "AnalyticsModuleSupport": true
    },
    "Device": {
      "XAddr": "http://192.168.1.91/onvif/device_service",
      "Network": {
        "IPFilter": true,
        "ZeroConfiguration": true,
        "IPVersion6": true,
        "DynDNS": true,
        "Extension": {
          "Dot11Configuration": false,
          "Extension": {
            "DHCPv6": true,
            "Dot1XConfigurations": "0"
          }
        }
      },
      "System": {
        "DiscoveryResolve": false,
        "DiscoveryBye": true,
        "RemoteDiscovery": false,
        "SystemBackup": false,
        "SystemLogging": true,
        "FirmwareUpgrade": true,
        "SupportedVersions": [
          {
            "Major": "16",
            "Minor": "12"
          },
          {
            "Major": "2",
            "Minor": "60"
          },
          {
            "Major": "2",
            "Minor": "40"
          },
          {
            "Major": "2",
            "Minor": "20"
          },
          {
            "Major": "2",
            "Minor": "10"
          },
          {
            "Major": "2",
            "Minor": "0"
          }
        ],
        "Extension": {
          "HttpFirmwareUpgrade": true,
          "HttpSystemBackup": false,
          "HttpSystemLogging": false,
          "HttpSupportInformation": false
        }
      },
      "IO": {
        "InputConnectors": "0",
        "RelayOutputs": "0",
        "Extension": {
          "Auxiliary": false,
          "AuxiliaryCommands": "nothing",
          "Extension": {}
        }
      },
      "Security": {
        "TLS1.1": true,
        "TLS1.2": true,
        "OnboardKeyGeneration": false,
        "AccessPolicyConfig": false,
        "X.509Token": false,
        "SAMLToken": false,
        "KerberosToken": false,
        "RELToken": false,
        "Extension": {
          "TLS1.0": true,
          "Extension": {
            "Dot1X": false,
            "SupportedEAPMethod": "0",
            "RemoteUserHandling": false
          }
        }
      }
    },
    "Events": {
      "XAddr": "http://192.168.1.91/onvif/Events",
      "WSSubscriptionPolicySupport": true,
      "WSPullPointSupport": true,
      "WSPausableSubscriptionManagerInterfaceSupport": false
    },
    "Imaging": {
      "XAddr": "http://192.168.1.91/onvif/Imaging"
    },
    "Media": {
      "XAddr": "http://192.168.1.91/onvif/Media",
      "StreamingCapabilities": {
        "RTPMulticast": true,
        "RTP_TCP": true,
        "RTP_RTSP_TCP": true
      },
      "Extension": {
        "ProfileCapabilities": {
          "MaximumNumberOfProfiles": "10"
        }
      }
    },
    "Extension": {
      "extCapabilities": {
        "XAddr": "http://192.168.1.91/onvif/onvif_ext",
        "IOInputSupport": false,
        "PrivacyMaskSupport": true,
        "PTZ3DZoomSupport": false,
        "PTZPatternSupport": true,
        "Language": "2"
      },
      "DeviceIO": {
        "XAddr": "http://192.168.1.91/onvif/DeviceIO",
        "VideoSources": "1",
        "VideoOutputs": "0",
        "AudioSources": "0",
        "AudioOutputs": "0",
        "RelayOutputs": "0"
      },
      "Recording": {
        "XAddr": "http://192.168.1.91/onvif/Recording",
        "ReceiverSource": false,
        "MediaProfileSource": true,
        "DynamicRecordings": false,
        "DynamicTracks": false,
        "MaxStringLength": "64"
      },
      "Search": {
        "XAddr": "http://192.168.1.91/onvif/SearchRecording",
        "MetadataSearch": false
      },
      "Replay": {
        "XAddr": "http://192.168.1.91/onvif/Replay"
      }
    }
  }
}
patrickmichalina commented 2 years ago

I am reviewing this and will get back to you thanks.

Mudrekh commented 2 years ago

@patrickmichalina Did you have a chance to review this further? If it helps, I'm currently using a snippet like this to 'override' certain URLs so that they work.

const debug = require('debug')('onvif-rx');
const _ = require('lodash');

const services = [
  'Event',
  'Media',
  'Analytics',
  'Display',
  'Imaging',
  'Provisioning',
  'PTZ',
  'Receiver',
  'Recording',
  'Replay',
  'Search',
  'AdvancedSecurity'
];

const OnvifRxCamera = {
  create({ ip, port, username, password }) {
    const rxcam = onvifrx.createManagedDeviceInNode({
      username,
      password,
      deviceUrl: `http://${ip}:${port || 80}/onvif/device_service`
    });

    rxcam.connect = async function () {
      const { Capabilities } = await this.api.Device.GetCapabilities();
      debug(ip, port, Capabilities);
      services.forEach((service) => {
        const { XAddr } = Capabilities[service] || Capabilities[`${service}s`] || {};
        if (!XAddr) return;

        const conf = _.omit(rxcam.api.Device.config, ['deviceUrl']);
        const url = new URL(XAddr);
        conf.deviceUrl = `http://${ip}:${port || 80}${url.pathname}`;
        debug(ip, port, service, conf.deviceUrl);
        rxcam.api[service].config = conf;
      });
    };
    return rxcam;
  }
};
module.exports = OnvifRxCamera;
patrickmichalina commented 2 years ago

@Mudrekh Kinda. I was actually just rewriting the library, but it hasn't been a priority at the moment.

patrickmichalina commented 2 years ago

@Mudrekh I remember I needed to do this from the start but was being a little lazy and didn't get around to it because the Amcrest cameras I was testing against were happy using the default.

Mudrekh commented 2 years ago

@patrickmichalina OK, no problem. For now the snippet I have works, but would would love to have a built-in solution.