This package works with a variety of ONVIF compatible devices allowing for IP Cameras and NVRs (network video recorders) to be integrated into Dart and Flutter applications. The package includes the ability to control the PTZ (pan-tilt-zoom) movements of a device along with managing presets as well as controlling how video and audio is being streamed from the device. Review the documentation below to get more details on available features.
- Dart Implementation of ONVIF IP Camera Client
- Table of contents
- Getting Started
- Lower level requests
- Onvif cli (Onvif at the command prompt)
- Supported Onvif Operations
- Tested Onvif Devices
- New for version 3.0.0-dev.0
- New for version 2.2.x
- New for version 2.1.x
- Onvif specifications and documentation
- Features and bugs
- Possible unexpected behavior
- Known Issues
- Breaking changes
- Contributors
- Contributing
To use this package in your code, first add the dependency to your project:
dependencies:
...
easy_onvif: ^3.0.1+1
If you need additional help getting started with dart, check out these guides.
Import the easy_onvif library.
import 'package:easy_onvif/onvif.dart';
final onvif = await Onvif.connect(
host: [hostname or ip address],
username: [username],
password: [password]);
It's important to understand that all Onvif commands have an associated Access Policy
assigned to them based on the table below:
Administrator | Operator | User | Anonymous | |
---|---|---|---|---|
PRE_AUTH | X | X | X | X |
READ_SYSTEM | X | X | X | |
READ_SYSTEM_SENSITIVE | X | X | ||
READ_SYSTEM_SECRET | X | |||
WRITE_SYSTEM | X | |||
UNRECOVERABLE | X | |||
READ_MEDIA | X | X | X | |
ACTUATE | X | X |
You can find the command and it's Access Policy
in the API reference or in the spec PDF (links in the table below). As shown in the above table, a user with Administrator
access can use all Onvif commands. It is recommended that you authenticate with an Administrator
while using this package to avoid unexpected errors when sending your device different Onvif commands.
Through the deviceManagement
operations you can get information about the connected device.
var deviceInfo = await onvif.deviceManagement.getDeviceInformation(); // Access Class: READ_SYSTEM
print(deviceInfo.model);
Many operations require you to supply a profileToken
which can be retrieved through media
operations.
var profiles = await onvif.media.getProfiles(); // Access Class: READ_MEDIA
profiles.forEach((element) {
print('${element.name} ${element.token}');
});
var profileToken = profiles.first.token;
With the ptz
operations you can get a list of camera presets from the connected device.
var presets = await onvif.ptz.getPresets(profileToken); // ACCESS CLASS: READ_MEDIA
//get a specific preset
var preset = presets[11];
//print the preset values
print(
'preset: ${preset.position.panTilt?.x} ${preset.position.panTilt?.y} ${preset.position.zoom?.x}');
//use the GotoPreset operation to point the camera to the given preset
await onvif.ptz.gotoPreset(profileToken, preset); // ACCESS CLASS: ACTUATE
Be sure to look through the API Reference for information about the parameters required for the supported Onvif operations.
In cases where there is no helper method for a specific Onvif operation, a low-level call can be used to make the request to the device. The code below performs the GetAudioOutputs
Media1 operation. All responses take the form of a hashmap Map<String,dynamic>
.
// code fragment for a sample low level request
//
// build a xml fragment for the specific Onvif operation
Transport.builder.element('GetStatus', nest: () { // ACCESS CLASS: READ_MEDIA
Transport.builder.namespace(Xmlns.tptz);
ReferenceToken(profileToken).buildXml(builder);
});
// using the connected onvif object from the earlier example
final transport = onvif.transport;
// build the soap request envelope and send the request
// since this is a media1 request, send to the media1 endpoint
final responseEnvelope = await transport.securedRequest( // include a Security header in the request
onvif.ptz.uri,
soap.Body(
request: Transport.builder.buildFragment(),
));
print(responseEnvelope.body.response);
A command line interface for controlling an Onvif device with cli commands
Install using dart pub
:
dart pub global activate easy_onvif
Install using brew
:
brew tap faithoflifedev/easy_onvif
brew install onvif
Run the following command to see help:
onvif --help
Result,
A command line interface for controlling Onvif compliant devices
Usage: onvif <command> [arguments]
Global options:
-h, --help Print this usage information.
--config-file (defaults to "/Users/chris/.onvif/credentials.json")
--log-level [all, debug, info, warning, error, off (default)]
Available commands:
authorize Generate an authentication file for an Onvif device.
debug Generate a debug bundle for an Onvif device.
device-management Device management commands.
imaging Imaging commands.
media1 Media ver10 commands.
media2 Media ver20 commands.
probe Probe/device discovery command.
ptz PTZ commands.
recordings Recordings commands.
replay Replay commands.
search Search commands.
version Display the package name and version.
Run "onvif help <command>" for more information about a command.
Please see the cli documentation README.md for more detailed usage information.
Onvif Operation | Dart Method | Dart Return Type | Test |
---|---|---|---|
CreateUsers | createUsers | Future<bool> |
[x] |
DeleteUsers | deleteUsers | Future<bool> |
[x] |
GetCapabilities | getCapabilities | Future<Capabilities> |
[x] |
GetDeviceInformation | getDeviceInformation | Future<GetDeviceInformationResponse> |
[x] |
GetDiscoveryMode | getDiscoveryMode | Future<String> |
[x] |
GetDNS | getDNS | Future<DnsInformation> |
[x] |
GetEndpointReference | getEndpointReference | Future<Map<String, dynamic>> |
[ ] |
GetHostname | getHostname | Future<HostnameInformation> |
[x] |
GetNetworkProtocols | getNetworkProtocols | Future<List<NetworkProtocol>> |
[x] |
GetNTP | getNtp | Future<NtpInformation> |
[x] |
GetServiceCapabilities | getServiceCapabilities | Future<DeviceServiceCapabilities> |
[x] |
GetServices | getServices | Future<List<Service>> |
[x] |
GetStorageConfiguration | getStorageConfiguration | Future<StorageConfiguration> |
[ ] |
GetStorageConfigurations | getStorageConfigurations | Future<List<StorageConfiguration>> |
[ ] |
GetSystemDateAndTime | getSystemDateAndTime | Future<SystemDateAndTime> |
[x] |
GetSystemUris | getSystemUris | Future<GetSystemUrisResponse> |
[x] |
GetSystemLog | getSystemLog | Future<SystemInformation> |
[ ] |
GetSystemSupportInformation | getSystemSupportInformation | Future<SystemInformation> |
[ ] |
GetUsers | getUsers | Future<List<User>> |
[x] |
SystemReboot | systemReboot | Future<String> |
[ ] |
Onvif Operation | Dart Method | Dart Return Type | Test |
---|---|---|---|
GetCurrentPreset | getCurrentPreset | Future<ImagingPreset> |
[ ] |
GetPresets | getPresets | Future<List<ImagingPreset>> |
[ ] |
GetServiceCapabilities | getServiceCapabilities | Future<Capabilities> |
[ ] |
GetStatus | getStatus | Future<Status> |
[ ] |
SetCurrentPreset | setCurrentPreset | Future<bool> |
[ ] |
Onvif Operation | Dart Method | Dart Return Type | Test |
---|---|---|---|
GetAudioSources | getAudioSources | Future<List<AudioSource>> |
[x] |
GetMetadataConfiguration | getMetadataConfiguration | Future<MetadataConfiguration> |
[x] |
GetMetadataConfigurations | getMetadataConfigurations | Future<List<MetadataConfiguration>> |
[x] |
GetProfile | getProfile | Future<Profile> |
[x] |
GetProfiles | getProfiles | Future<List<Profile>?> |
[x] |
GetServiceCapabilities | getServiceCapabilities | Future<Capabilities1> |
[x] |
GetSnapshotUri | getSnapshotUri | Future<MediaUri> |
[x] |
GetStreamUri | getStreamUri | Future<MediaUri> |
[x] |
GetVideoSources | getVideoSources | Future<VideoSources> |
[x] |
StartMulticastStreaming | startMulticastStreaming | Future<bool> |
[x] |
StopMulticastStreaming | stopMulticastStreaming | Future<bool> |
[x] |
Onvif Operation | Dart Method | Dart Return Type | Test |
---|---|---|---|
GetMetadataConfigurationOptions | getMetadataConfigurationOptions | Future<MetadataConfigurationOptions> |
[x] |
GetMetadataConfigurations | getMetadataConfigurations | Future<List<MetadataConfiguration>> |
[x] |
GetProfiles | getProfiles | Future<List<MediaProfile>?> |
[x] |
GetServiceCapabilities | getServiceCapabilities | Future<Capabilities2> |
[x] |
GetSnapshotUri | getSnapshotUri | Future<String> |
[x] |
GetStreamUri | getStreamUri | Future<String> |
[x] |
GetVideoEncoderInstances | getVideoEncoderInstances | Future<Info> |
[ ] |
GetVideoSourceConfigurationOptions | getVideoSourceConfigurationOptions | Future<VideoSourceConfigurationOptions> |
[x] |
GetVideoEncoderConfigurations | getVideoEncoderConfigurations | Future<List<VideoEncoder2Configuration>> |
[ ] |
StartMulticastStreaming | startMulticastStreaming | Future<bool> |
[x] |
StopMulticastStreaming | stopMulticastStreaming | Future<bool> |
[x] |
Onvif Operation | Dart Method | Dart Return Type | Test |
---|---|---|---|
AbsoluteMove | absoluteMove | Future<bool> |
[x] |
ContinuousMove | continuousMove | Future<bool> |
[x] |
GetCompatibleConfigurations | getCompatibleConfigurations | Future<List<PtzConfiguration>> |
[x] |
GetConfiguration | getConfiguration | Future<PtzConfiguration> |
[x] |
GetConfigurationOptions | getConfigurationOptions | Future<PtzConfigurationOptions> |
[x] |
GetConfigurations | getConfigurations | Future<List<PtzConfiguration>> |
[x] |
GetPresets | getPresets | Future<List<Preset>> |
[x] |
GetPresetTour | getPresetTour | Future<PresetTour> |
[ ] |
GetPresetTours | getPresetTours | Future<List<PresetTours>> |
[ ] |
GetServiceCapabilities | getServiceCapabilities | Future<Capabilities> |
[x] |
GetStatus | getStatus | Future<PtzStatus> |
[x] |
GotoHomePosition | gotoHomePosition | Future<bool> |
[x] |
GotoPreset | gotoPreset | Future<bool> |
[x] |
RelativeMove | relativeMove | Future<bool> |
[x] |
RemovePreset | removePreset | Future<bool> |
[x] |
SetHomePosition | setHomePosition | Future<bool> |
[x] |
SetPreset | setPreset | Future<String> |
[x] |
Stop | stop | Future<bool> |
[x] |
Onvif Operation | Dart Method | Return Type |
---|---|---|
N/A | move | Future<void> |
N/A | moveDown | Future<void> |
N/A | moveLeft | Future<void> |
N/A | moveRight | Future<void> |
N/A | moveUp | Future<void> |
N/A | zoomIn | Future<void> |
N/A | zoomOut | Future<void> |
N/A | getCurrentPreset | Future<Preset?> |
Onvif Operation | Dart Method | Dart Return Type | Test |
---|---|---|---|
CreateRecording | createRecording | Future<String> |
[ ] |
CreateRecordingJob | createRecordingJob | Future<CreateRecordingJobResponse> |
[ ] |
DeleteRecording | deleteRecording | Future<bool> |
[ ] |
DeleteRecordingJob | deleteRecordingJo | Future<bool> |
[ ] |
GetRecordingJobs | getRecordingJobs | Future<List<GetRecordingJobsResponseItem>> |
[ ] |
GetRecordingJobState | getRecordingJobState | Future<List<RecordingJobStateInformation>> |
[ ] |
GetRecordingOptions | getRecordingOptions | Future<List<RecordingOptions>> |
[ ] |
GetRecordings | getRecordings | Future<List<GetRecordingsResponseItem>> |
[ ] |
GetServiceCapabilities | getServiceCapabilities | Future<Capabilities> |
[ ] |
setRecordingJobMode | setRecordingJobMode | Future<bool> |
[ ] |
Onvif Operation | Dart Method | Dart Return Type | Test |
---|---|---|---|
GetReplayConfiguration | getReplayConfiguration | Future<ReplayConfiguration> |
[ ] |
GetReplayUri | getReplayUri | Future<String> |
[ ] |
GetServiceCapabilities | getServiceCapabilities | Future<Capabilities> |
[ ] |
SetReplayConfiguration | setReplayConfiguration | Future<bool> |
[ ] |
Onvif Operation | Dart Method | Dart Return Type | Test |
---|---|---|---|
FindRecordings | findRecordings | Future<String> |
[ ] |
GetRecordingSearchResults | getRecordingSearchResults | Future<List<FindRecordingResult>> |
[ ] |
GetRecordingInformation | getRecordingInformation | Future<RecordingInformation> |
[ ] |
GetRecordingSummary | getRecordingSummary | Future<RecordingSummary> |
[ ] |
The values returned by the Onvif API GetDeviceInformation
call.
Manufacturer | Model | Firmware Version | Known Issue |
---|---|---|---|
Happytimesoft | IPCamera | limited capabilities | |
ONVIF | ENP1A14-IR/25X | commands not implemented¹ | |
D-Link Corporation | DCS-6511 | commands not implemented² | |
[empty] | GX728MF-IR28 | commands not implemented³ | |
LOREX | LNB4421SB | testing in progress | |
ONVIF_IPNC | IPG-8150PSS | 1.3.0-20210203CN-PT | commands not implemented⁴ |
SUNBA | Performance-Series | IPC-B2202.3.73.C06290.NB.230628 | commands not implemented⁵ |
¹ The ENP1A14-IR/25X does not support the following commands:
- Recordings
CreateRecording
DeleteRecording
² The DCS-6511 does not support the following commands:
- Device Management:
GetServices
GetServiceCapabilities
- Media1:
GetMetadataConfiguration
GetProfile
GetServiceCapabilities
- Media2:
- is not supported by this device
- PTZ:
- not tested
³ The GX728MF-IR28 does not support the following commands:
- Device Management:
GetSystemUris
- Media1:
GetMetadataConfiguration
GetProfile
GetStreamUri
StartMulticastStreaming
StopMulticastStreaming
- Media2:
GetStreamUri
StartMulticastStreaming
StopMulticastStreaming
- PTZ:
- not supported
⁴ The IPG-8150PSS does not support the following commands:
- Device Management:
GetStorageConfigurations
GetStorageConfiguration
GetEndpointReference
GetSystemSupportInformation
- PTZ
GetPresetTours
GetPresetTour
GotoHomePosition
- supported but not functioningSetHomePosition
- supported but not functioning
⁵ The SUNBA does not support the following commands:
- Device Management:
CreateUsers
DeleteUsers
GetStorageConfigurations
GetStorageConfiguration
GetSystemUris
- Media1:
GetStreamUri
- but it works in Media2
- PTZ:
GotoHomePosition
- No Home PositionRelativeMove
- work-around available with themove
helperSetHomePosition
Bug fixes for a number of small bugs that got introduced through adding some experimental features in some of the previous releases. The main issues identified and resolved where in the areas of Authentication Issue #58, PTZ Issue #54 and the Media2 module (no issue reported). In addition all currently supported SOAP requests now have unit tests to confirm that the package continues to create the requests properly as the code evolves. Finally, there have been extensive code revisions to help with the long term maintenance of the package. As part of this a number of method signatures have changed as well as some of the objects in the object model have been renamed. For the most part these changes are in the lower level interfaces within the package and should not affect many users.
A new cli
command has been added that will hopefully ease in debugging issues with this library when it comes to the vast variety of Onvif devices that are out in the wild. Once the cli
utility has been installed and authorized per the quick start instructions, the command onvif debug
will create a debug folder with a debug.txt
and debug.zip
that can be added to an issue to help to debug and resolve that issue.
- Support for Media2 Onvif operations
- Support for Recording Onvif operations (experimental)
- Support for Replay Onvif operations (experimental)
// defaults to `MixedProfile` a special case object that has the fields for both
// a media1 `Profile` and a media2 `MediaProfile`.
var profiles = await onvif.media.getProfiles();
// determine the media level supported
print(onvif.media.mediaSupportLevel.name);
// alternatively, make an explicit call to the Media1 operation
var profiles1 = await onvif.media.media1.getProfiles();
// or, make an explicit call to the Media2 operation
var profiles2 = await onvif.media.media2.getProfiles();
https://www.onvif.org/profiles/specifications/
Please file feature requests and bugs with the issue tracker.
The helper method move
in the PTZ module will fallback to AbsoluteMove
if RelativeMove
is not supported by the device. The move
command is not currently aware of the device bounds , so using the command to change the x
position of the device hen the device has already at max x
will have no affect. The same would go for the y
position and for zoom
.
Github Issue #45, when using v2.1.2+13 (and likely earlier) on Windows 11 (and probably 10) the following error will the displayed when attempting to use the Multicast.probe
method.
Unhandled exception:
SocketException: Failed to create datagram socket (OS Error: The requested address is not valid in its context., errno = 10049), address = 239.255.255.250, port = 3702
This is due to a bug in the underlying Dart SDK #53477.
With the help of Viper-Bit (who has supplied a c++ workaround that uses Dart FFI) and Add00 (building and testing the c++ code on Windows 11) version 2.1.3 and up of the package now uses Dart FFI to resolve this issue on the Windows platform. For Flutter Windows applications there is a supplied discovery.dll
file that must be placed in the assets
folder and referenced in the pubspec.yaml
, for Windows Dart (cli) apps the discovery.dll
defaults to the folder derived as join(Directory.current.path, 'bin', 'discovery.dll')
. Alternatively, cli apps on Windows can use the ONVIF_DISCOVERY_DLL
environment variable to override the default path for instance:
$env:ONVIF_DISCOVERY_DLL="example/flutter_model/assets/discovery.dll"
dart run bin/onvif.dart probe list-devices
The discovery.dll
file can be found in the bin folder of the project on GitHub.
There have been extensive code revisions to help with the long term maintenance of the package. As part of this a number of method signatures have changed as well as some of the objects in the object model have been renamed. For the most part these changes are in the lower level interfaces within the package and should not affect too many users.
For this release a number of Common
classes where given name changes to better reflect the Onvif spec. Since these classes are used fairly deep in the API it's unlikely that if will affect many users. In addition in the current release these changes are limits to the PTZ
module, so if the changes cause issue, it would only be in this section of the code.
There is probably a number of breaking changes in this version since some method signatures have changed. Since a large portion of the code base was rebuilt from scratch tracking all the changes wasn't a priority. Sorry for any inconvenience.
Any help from the open-source community is always welcome and needed:
- Found an issue?
- Please fill a bug report with details.
- Need a feature?
- Open a feature request with use cases.
- Are you using and liking the project?
- Promote the project: create an article or post about it
- Make a donation
- Do you have a project that uses this package
- let's cross promote, let me know and I'll add a link to your project
- Are you a developer?
- Fix a bug and send a pull request.
- Implement a new feature.
- Improve the Unit Tests.
- Have you already helped in any way?
- Many thanks from me, the contributors and everybody that uses this project!
If you donate 1 hour of your time, you can contribute a lot, because others will do the same, just be part and start with your 1 hour.