agsh / onvif

ONVIF node.js implementation
http://agsh.github.io/onvif/
MIT License
696 stars 237 forks source link

set video encoder configuration #283

Open RotemDoar opened 1 year ago

RotemDoar commented 1 year ago

My company is using Onvif to control IP cameras. We are trying to set the encoder of the cameras by using this library. We have a problem that after using the setVideoEncoderConfiguration function, the encoder of the cameras has not changed.

For example -

We trying to set the encoder of this camera to H264 (the current encoder is H265). At first we use the function - get profiles, and we got -

{
  '$': { Profile: 'Main', GovLength: 100, token: 'VideoEncode_token_1' },
  name: 'VideoEncode_1',
  useCount: 1,
  encoding: 'H265',
  resolution: { width: 1920, height: 1080 },
  rateControl: {
    '$': { ConstantBitRate: true },
    frameRateLimit: 25,
    bitrateLimit: 1536
  },
  multicast: {
    address: { type: 'IPv4', IPv4Address: '239.0.0.0' },
    port: 50554,
    TTL: 1,
    autoStart: false
  },
  quality: 3
}

and then, we are trying to set video encoder configuration. we are executing -

var Cam = require('onvif').Cam;

const cam_obj = new Cam({
    hostname: "192.168.1.55",
    username: 'admin',
    password: '123456',
    port: 80,
    timeout: 5000
    },
    async (err) => {
            if (err) {
                console.log(`load failed`)
                return
            }
            cam_obj.setVideoEncoderConfiguration({
                token: 'VideoEncode_token_1',
                encoding: 'H264',

            },(err,data)=>{
                if(err){
                    console.log(err)
                }
                else{
                    console.log(data)
                }
            })

        }
    );

We got no errors, but the encoder of the camera is still H265. Moreover we tried to log the return data of the xml by adding logs here - (after line 446 in media.js file)

}, function(err, data, xml) {
            console.log(data)
            console.log(xml)
            if (err || linerase(data).setVideoEncoderConfigurationResponse !== '') {
                return callback.call(this, linerase(data).setVideoEncoderConfigurationResponse !== ''
                    ? new Error('Wrong `SetVideoEncoderConfiguration` response')
                    : err, data, xml);
            }
            //get new encoding settings from device
            this.getVideoEncoderConfiguration(options.token || options.$.token, callback); 

We got -

[ { setVideoEncoderConfigurationResponse: [ '' ] } ]
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope                                                        ><SOAP-ENV:Header></SOAP-ENV:Header><SOAP-ENV:Body><trt:SetVideoEncoderConfigurationResponse></trt:SetVideoEncoderConfigurationResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

any ideas what is wrong?

RogerHardiman commented 1 year ago

It is possible this will not work. The original ONVIF API for video codec changes was called "the Media API", I'll call it "Media1"). It supported JPEG, MPEG-4 and H264 Then along came the Media2 API which is the API that also adds H265 support.

Currently this library implements lots of Media1, but only enough of Media2 for what I needed on a project. In the code, the functions to set the Video Codec only talk to the Media(1) API. It is possible that your camera does not know what to do when you make a change (via a Media1 API) to an ONVIF Profile that contains a Media2 value (ie H265)

The fix may be to change in libs\media.js around line 391 and change the code so it checks if the camera supports Media2, and if it does, generate the Media2 XML with the Media2 namespaces. You can see examples of handling Media1 and Media2 around line 1052 with GetStreamUri

RogerHardiman commented 1 year ago

You may be lucky and find the XML for the Media2 setVideoEncoderConfiguration is exactly the same as in Media1, so it is just Namespace changes. But on some of the other things, the API was different

RotemDoar commented 1 year ago

ok I understand. so you mean that I cam execute the set video configuration like this -

var Cam = require('onvif').Cam;
const cam_obj = new Cam({
    hostname: "192.168.1.55",
    username: 'admin',
    password: '123456',
    port: 80,
    timeout: 5000
    },
    async (err) => {
            if (err) {
                console.log(`load failed`)
                return
            }
            cam_obj._request({
                        service: 'media2',
                        body: cam_obj._envelopeHeader() +
                        '<SetVideoEncoderConfiguration xmlns="http://www.onvif.org/ver20/media/wsdl">' +
                            '<Configuration token = "' + 'VideoEncode_token_1' + '">' +
                            ('<Encoding xmlns="http://www.onvif.org/ver10/schema">' + 'H264' + '</Encoding>') +
                            '</Configuration>' +
                            '<ForcePersistence>true</ForcePersistence>' +
                        '</SetVideoEncoderConfiguration>' +
                        cam_obj._envelopeFooter()
                    }, function(err, data, xml) {
                        if(err){
                            console.log(err)
                        }
                        else{
                            console.log(data)
                        }
                    })

        }
    );

Is there any more configuration I should add?