J’ai étoffé mon système de vidéosurveillance avec maintenant trois caméras extérieures au total, c’est ma configuration a priori définitive. Comme j’étais plutôt satisfait de la caméra Annke C500 I51DL avec une bonne compatibilité sous Linux j’en ai prise une deuxième, pour la dernière caméra c’est toujours une Annke, un modèle dôme commandable à distance WPT500 I51CP qui marche également parfaitement sous Linux.


Pour finaliser mon dispositif, je souhaite également placer une caméra à l’intérieur. Sans vouloir y mettre le prix j’ai testé une caméra Annke Crater Pro I81CG qui m’a coûté guère plus d’une vingtaine d’euros.

Il faudra installer le logiciel Myannke sous un mobile sous Android et pas n’importe quelle version, mais la version v10.6.1.2306271600 qui fournit une fonctionnalité Onvif qu’on activera. La caméra est ensuite accessible avec une adresse rtsp qui va ressembler à ça
rtsp://admin:password@192.168.2.134:554/live/1jfiegbrziwpq_p3_FVGPXEBUISWI
Sauf que la chaîne de caractère à la fin varie régulièrement. J’ai cherché à identifier une adresse rtsp fixe et de fil en aiguille ça m’a permis de découvrir le protocole Onvif. Onvif pour Open Network Video Interface Forum est un protocole de communication pour les périphériques connectés en réseau IP destinés à la vidéosurveillance (caméra, enregistreur). Sous Linux il existe une librairie libonvif qui fournit quelques utilitaires en ligne bien utiles. La commande onvif-util -a va lister les périphériques visibles sur le réseau :
Found 5 cameras on interface 192.168.2.14
192.168.2.152 IPCAM(IPCAM)
192.168.2.113 localhost(I51DL)
192.168.2.139 GZ(IPC IPC-model)
192.168.2.111 localhost(I51DL)
192.168.2.134 IPCAM(IPCAM)
Elle permet également de se connecter à une caméra onvif-util -u ‘admin’ -p ‘password’ 192.168.2.113 voilà le résultat
found host: 192.168.2.113
successfully connected to host
name: I51DL
serial: I51DL20240529AAWRFF0235068
I51DL>
Il suffit ensuite de taper help pour avoir accès des commandes diverses pour visualiser et modifier la configuration. La commande onvif-cli -u ‘admin’ -a ‘password’ –host ‘192.168.2.134’ permettra également d’obtenir des informations en ligne de commande, voici un exemple au prompt ONVIF >>> avec quelques commandes:
ONVIF >>> cmd media GetVideoSources
True: [OrderedDict([('Framerate', 25.0), ('Resolution', OrderedDict([('Width', 1280), ('Height', 720)])), ('Imaging', None), ('Extension', None), ('token', 'video_source_token'), ('_attr_1', OrderedDict())])]
ONVIF >>> cmd devicemgmt GetHostname
True: OrderedDict([('FromDHCP', False), ('Name', 'IPCAM'), ('Extension', None), ('_attr_1', None)])
ONVIF >>> cmd devicemgmt GetDeviceInformation
True: OrderedDict([('Manufacturer', 'IPCAM'), ('Model', 'IPC'), ('FirmwareVersion', 'v5.3.1.1811211102'), ('SerialNumber', 'gm8135_v2'), ('HardwareId', 'v4.8.1.1708141702')])
ONVIF >>> cmd media GetProfiles
True: [OrderedDict([('Name', 'MainProfile'), ('VideoSourceConfiguration', OrderedDict([('Name', 'video_source_cfg'), ('UseCount', 2), ('SourceToken', 'video_source_token'), ('Bounds', OrderedDict([('x', 0), ('y', 0), ('width', 2560), ('height', 1440)])), ('_value_1', None), ('Extension', None), ('token', 'video_source_cfg_token'), ('ViewMode', None), ('_attr_1', OrderedDict())])), ('AudioSourceConfiguration', OrderedDict([('Name', 'audio_source_cfg'), ('UseCount', 2), ('SourceToken', 'audio_source_token'), ('_value_1', None), ('token', 'audio_source_cfg_token'), ('_attr_1', OrderedDict())])), ('VideoEncoderConfiguration', OrderedDict([('Name', 'main_video_encoder_cfg'), ('UseCount', 1), ('Encoding', 'H264'), ('Resolution', OrderedDict([('Width', 2560), ('Height', 1440)])), ('Quality', 30.0), ('RateControl', OrderedDict([('FrameRateLimit', 15), ('EncodingInterval', 50), ('BitrateLimit', 2097)])), ('MPEG4', None), ('H264', OrderedDict([('GovLength', 45), ('H264Profile', 'Baseline')])), ('Multicast', OrderedDict([('Address', OrderedDict([('Type', 'IPv4'), ('IPv4Address', '0.0.0.0'), ('IPv6Address', None)])), ('Port', 0), ('TTL', 3), ('AutoStart', False), ('_value_1', None), ('_attr_1', None)])), ('SessionTimeout', datetime.timedelta(microseconds=10000)), ('_value_1', None), ('token', 'main_video_encoder_cfg_token'), ('GuaranteedFrameRate', None), ('_attr_1', OrderedDict())])), ('AudioEncoderConfiguration', OrderedDict([('Name', 'AAC_audio_encoder_cfg'), ('UseCount', 2), ('Encoding', 'AAC'), ('Bitrate', 128), ('SampleRate', 16), ('Multicast', OrderedDict([('Address', OrderedDict([('Type', 'IPv4'), ('IPv4Address', '0.0.0.0'), ('IPv6Address', None)])), ('Port', 0), ('TTL', 120), ('AutoStart', False), ('_value_1', None), ('_attr_1', None)])), ('SessionTimeout', datetime.timedelta(seconds=720)), ('_value_1', None), ('token', 'AAC_audio_encoder_cfg_token'), ('_attr_1', OrderedDict())])), ('VideoAnalyticsConfiguration', OrderedDict([('Name', 'analytics_cfg'), ('UseCount', 2), ('AnalyticsEngineConfiguration', OrderedDict([('AnalyticsModule', [OrderedDict([('Parameters', OrderedDict([('SimpleItem', [OrderedDict([('Name', 'Sensitivity'), ('Value', '4')])]), ('ElementItem', [OrderedDict([('_value_1', ), ('Name', 'Layout')])]), ('Extension', None), ('_attr_1', None)])), ('Name', 'tt:CellMotionEngine'), ('Type', 'tt:CellMotionEngine')]), OrderedDict([('Parameters', OrderedDict([('SimpleItem', [OrderedDict([('Name', 'Sensitivity'), ('Value', '4')])]), ('ElementItem', [OrderedDict([('_value_1', ), ('Name', 'Field')]), OrderedDict([('_value_1', ), ('Name', 'Transform')])]), ('Extension', None), ('_attr_1', None)])), ('Name', 'tt:TamperEngine'), ('Type', 'tt:TamperEngine')])]), ('Extension', None), ('_attr_1', None)])), ('RuleEngineConfiguration', OrderedDict([('Rule', [OrderedDict([('Parameters', OrderedDict([('SimpleItem', [OrderedDict([('Name', 'ActiveCells'), ('Value', 'zwA')]), OrderedDict([('Name', 'AlarmOffDelay'), ('Value', '1000')]), OrderedDict([('Name', 'AlarmOnDelay'), ('Value', '1000')]), OrderedDict([('Name', 'MinCount'), ('Value', '4')])]), ('ElementItem', []), ('Extension', None), ('_attr_1', None)])), ('Name', 'tt:CellMotionDetector'), ('Type', 'tt:CellMotionDetector')]), OrderedDict([('Parameters', OrderedDict([('SimpleItem', []), ('ElementItem', [OrderedDict([('_value_1', ), ('Name', 'Field')])]), ('Extension', None), ('_attr_1', None)])), ('Name', 'tt:TamperDetector'), ('Type', 'tt:TamperDetector')])]), ('Extension', None), ('_attr_1', None)])), ('_value_1', None), ('token', 'analytics_cfg_token'), ('_attr_1', OrderedDict())])), ('PTZConfiguration', OrderedDict([('Name', 'ptzcfg'), ('UseCount', 0), ('NodeToken', 'ptzNodeToken'), ('DefaultAbsolutePantTiltPositionSpace', None), ('DefaultAbsoluteZoomPositionSpace', None), ('DefaultRelativePanTiltTranslationSpace', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/TranslationGenericSpace'), ('DefaultRelativeZoomTranslationSpace', None), ('DefaultContinuousPanTiltVelocitySpace', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace'), ('DefaultContinuousZoomVelocitySpace', None), ('DefaultPTZSpeed', OrderedDict([('PanTilt', OrderedDict([('x', 1.0), ('y', 1.0), ('space', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace')])), ('Zoom', None)])), ('DefaultPTZTimeout', datetime.timedelta(seconds=60)), ('PanTiltLimits', OrderedDict([('Range', OrderedDict([('URI', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace'), ('XRange', OrderedDict([('Min', -1.0), ('Max', 1.0)])), ('YRange', OrderedDict([('Min', -1.0), ('Max', 1.0)]))]))])), ('ZoomLimits', None), ('Extension', None), ('token', 'ptzToken'), ('MoveRamp', None), ('PresetRamp', None), ('PresetTourRamp', None), ('_attr_1', OrderedDict())])), ('MetadataConfiguration', None), ('Extension', None), ('token', 'MainProfileToken'), ('fixed', True), ('_attr_1', OrderedDict())]), OrderedDict([('Name', 'SubProfile'), ('VideoSourceConfiguration', OrderedDict([('Name', 'video_source_cfg'), ('UseCount', 2), ('SourceToken', 'video_source_token'), ('Bounds', OrderedDict([('x', 0), ('y', 0), ('width', 640), ('height', 360)])), ('_value_1', None), ('Extension', None), ('token', 'video_source_cfg_token'), ('ViewMode', None), ('_attr_1', OrderedDict())])), ('AudioSourceConfiguration', OrderedDict([('Name', 'audio_source_cfg'), ('UseCount', 2), ('SourceToken', 'audio_source_token'), ('_value_1', None), ('token', 'audio_source_cfg_token'), ('_attr_1', OrderedDict())])), ('VideoEncoderConfiguration', OrderedDict([('Name', 'sub_video_encoder_cfg'), ('UseCount', 1), ('Encoding', 'H264'), ('Resolution', OrderedDict([('Width', 640), ('Height', 360)])), ('Quality', 30.0), ('RateControl', OrderedDict([('FrameRateLimit', 15), ('EncodingInterval', 50), ('BitrateLimit', 1024)])), ('MPEG4', None), ('H264', OrderedDict([('GovLength', 60), ('H264Profile', 'Baseline')])), ('Multicast', OrderedDict([('Address', OrderedDict([('Type', 'IPv4'), ('IPv4Address', '0.0.0.0'), ('IPv6Address', None)])), ('Port', 0), ('TTL', 3), ('AutoStart', False), ('_value_1', None), ('_attr_1', None)])), ('SessionTimeout', datetime.timedelta(microseconds=10000)), ('_value_1', None), ('token', 'sub_video_encoder_cfg_token'), ('GuaranteedFrameRate', None), ('_attr_1', OrderedDict())])), ('AudioEncoderConfiguration', OrderedDict([('Name', 'AAC_audio_encoder_cfg'), ('UseCount', 2), ('Encoding', 'AAC'), ('Bitrate', 128), ('SampleRate', 16), ('Multicast', OrderedDict([('Address', OrderedDict([('Type', 'IPv4'), ('IPv4Address', '0.0.0.0'), ('IPv6Address', None)])), ('Port', 0), ('TTL', 120), ('AutoStart', False), ('_value_1', None), ('_attr_1', None)])), ('SessionTimeout', datetime.timedelta(seconds=720)), ('_value_1', None), ('token', 'AAC_audio_encoder_cfg_token'), ('_attr_1', OrderedDict())])), ('VideoAnalyticsConfiguration', OrderedDict([('Name', 'analytics_cfg'), ('UseCount', 2), ('AnalyticsEngineConfiguration', OrderedDict([('AnalyticsModule', [OrderedDict([('Parameters', OrderedDict([('SimpleItem', [OrderedDict([('Name', 'Sensitivity'), ('Value', '4')])]), ('ElementItem', [OrderedDict([('_value_1', ), ('Name', 'Layout')])]), ('Extension', None), ('_attr_1', None)])), ('Name', 'tt:CellMotionEngine'), ('Type', 'tt:CellMotionEngine')]), OrderedDict([('Parameters', OrderedDict([('SimpleItem', [OrderedDict([('Name', 'Sensitivity'), ('Value', '4')])]), ('ElementItem', [OrderedDict([('_value_1', ), ('Name', 'Field')]), OrderedDict([('_value_1', ), ('Name', 'Transform')])]), ('Extension', None), ('_attr_1', None)])), ('Name', 'tt:TamperEngine'), ('Type', 'tt:TamperEngine')])]), ('Extension', None), ('_attr_1', None)])), ('RuleEngineConfiguration', OrderedDict([('Rule', [OrderedDict([('Parameters', OrderedDict([('SimpleItem', [OrderedDict([('Name', 'ActiveCells'), ('Value', 'zwA')]), OrderedDict([('Name', 'AlarmOffDelay'), ('Value', '1000')]), OrderedDict([('Name', 'AlarmOnDelay'), ('Value', '1000')]), OrderedDict([('Name', 'MinCount'), ('Value', '4')])]), ('ElementItem', []), ('Extension', None), ('_attr_1', None)])), ('Name', 'tt:CellMotionDetector'), ('Type', 'tt:CellMotionDetector')]), OrderedDict([('Parameters', OrderedDict([('SimpleItem', []), ('ElementItem', [OrderedDict([('_value_1', ), ('Name', 'Field')])]), ('Extension', None), ('_attr_1', None)])), ('Name', 'tt:TamperDetector'), ('Type', 'tt:TamperDetector')])]), ('Extension', None), ('_attr_1', None)])), ('_value_1', None), ('token', 'analytics_cfg_token'), ('_attr_1', OrderedDict())])), ('PTZConfiguration', OrderedDict([('Name', 'ptzcfg'), ('UseCount', 0), ('NodeToken', 'ptzNodeToken'), ('DefaultAbsolutePantTiltPositionSpace', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace'), ('DefaultAbsoluteZoomPositionSpace', 'http://www.onvif.org/ver10/tptz/ZoomSpaces/PositionGenericSpace'), ('DefaultRelativePanTiltTranslationSpace', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/TranslationGenericSpace'), ('DefaultRelativeZoomTranslationSpace', 'http://www.onvif.org/ver10/tptz/ZoomSpaces/TranslationGenericSpace'), ('DefaultContinuousPanTiltVelocitySpace', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/VelocityGenericSpace'), ('DefaultContinuousZoomVelocitySpace', 'http://www.onvif.org/ver10/tptz/ZoomSpaces/VelocityGenericSpace'), ('DefaultPTZSpeed', OrderedDict([('PanTilt', OrderedDict([('x', 1.0), ('y', 1.0), ('space', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/GenericSpeedSpace')])), ('Zoom', OrderedDict([('x', 1.0), ('space', 'http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace')]))])), ('DefaultPTZTimeout', datetime.timedelta(seconds=60)), ('PanTiltLimits', OrderedDict([('Range', OrderedDict([('URI', 'http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace'), ('XRange', OrderedDict([('Min', -1.0), ('Max', 1.0)])), ('YRange', OrderedDict([('Min', -1.0), ('Max', 1.0)]))]))])), ('ZoomLimits', OrderedDict([('Range', OrderedDict([('URI', 'http://www.onvif.org/ver10/tptz/ZoomSpaces/ZoomGenericSpeedSpace'), ('XRange', OrderedDict([('Min', -1.0), ('Max', 1.0)]))]))])), ('Extension', None), ('token', 'ptzToken'), ('MoveRamp', None), ('PresetRamp', None), ('PresetTourRamp', None), ('_attr_1', OrderedDict())])), ('MetadataConfiguration', None), ('Extension', None), ('token', 'SubProfileToken'), ('fixed', True), ('_attr_1', OrderedDict())])]
ONVIF >>>
Ca donne un résultat pas forcément très lisible. Pour avoir la liste des commandes possibles, il faudra se référer aux spécifications Onvif. On y retrouve une rubrique sur les interfaces réseau, on y retrouve par exemple la rubrique media évoquée plus haut au prompt ONVIF, c’est là qu’on peut rajouter les commandes avec la syntaxe cmd media commande. L’autre rubrique utile est device appelée avec la commande cmd devicemgmt commande.
Autre moyen d’obtenir des informations de la caméra avec la commande curl, grâce à ce script appelé onvif-list :
#!/bin/bash curl --anyauth --digest -u "admin" --location 'http://192.168.2.134:80/onvif/device_service' \ --header 'Content-Type: application/xml' \ --data '<?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:trt="http://www.onvif.org/ver10/media/wsdl"> <soap:Body> <trt:GetProfiles/> </soap:Body> </soap:Envelope>'
Cela va générer un fichier xml qu’on va remettre en forme grâce aux commandes successives :
onvif-list > result.xml xmllint --format result.xml > onvif-result.xml
Cela va donner un fichier xml bien formaté et bien plus lisible que la version qu’on a obtenu plus haut. Il en reste pas moins que cette découverte de Onvif ne m’a pas permis de découvrir une adresse rtsp fixe pour ma caméra Crater Pro, je pense que je vais devoir écrire un programme qui fera l’interface avec zoneminder pour avoir une adresse fixe.
Côté logiciel j’ai enfin réussi à compiler zoneminder, j’utilise donc maintenant la dernière version stable 1.36.35. En revanche j’ai encore beaucoup de soucis pour trouver les bons réglages, de temps à autre zoneminder et ZMES le serveur d’identification, explosent la charge CPU+RAM et font littéralement tomber mon serveur, ce n’est pas acceptable. Par exemple, lorsqu’il pleut, j’ai un afflux d’évènements non désirés qui polluent le système. Je continue donc à tâtonner pour trouver les réglages idéaux, il me reste également à voir comment piloter sous zoneminder la caméra dôme WPT500.
Néanmoins j’ai mis à jour ma page sur la vidéosurveillance sur mon site Funix qu’on peut retrouver ici.