Git Product home page Git Product logo

obs-ptz's Introduction

Pan Tilt Zoom (PTZ) Controls for OBS Studio

build status

This is a plugin for controlling PTZ Cameras from OBS studio.

This plugin adds a new control dock window that can be used to control pan, tilt, zoom camera directly from the OBS Studio main window. It also tracks the current active scenes to automatically select the correct camera for control and can be automated by adding PTZ Actions sources to trigger camera actions when scenes change.

PTZ Controls Screenshot

PTZ Controls Screenshot

Features:

  • Pan, Tilt, and Zoom controls
  • Auto and manual focus
  • Hotkeys control
  • Joystick control
  • Save and recall camera presets
  • Control any number of cameras
  • Auto select active camera based on active scene
  • Control camera power
  • Whitebalance control
  • Supported protocols
    • VISCA
    • VISCA over UDP (Sony protocol)
    • VISCA over TCP (PTZ Optics and others)
    • Pelco-P
    • Pelco-D
    • ONVIF (experimental)

OBS project resource page

#obsptz on Twitter

User Guide

Installation

Go to the releases page to find the latest binary release for your platform. Binaries are created for Windows (x64), MacOS (Arm, x86_64, and Universal), and Ubuntu Linux 22.04 (x86_64). Download the package for your platform and install it. If you need support for a different platform (e.g. Linux Arm) then you'll need to follow the building from source instructions below.

OBS PTZ Releases

Configuration

To show the controls dock, in the Docks menu select PTZ Controls. The PTZ Controls window should appear. You can drag the window to any side of the OBS Studio main window to dock it into place, or just leave it floating.

Initially no PTZ cameras will be configured. To add a camera, click on the gear icon at the bottom of the dock window, or in the Tools menu select PTZ Controls. The PTZ Settings window will appear.

The Settings window has three tabs, General, Cameras and About. The General tab has settings that affect every camera. The Cameras tab is where you add and remove cameras, and change individual camera settings. The About tab give some details about the plugin and what version is installed.

Adding a Camera

The camera controller only handles the PTZ control of a video source. You should add the camera's video source in OBS before adding PTZ control to the plugin.

To add a camera, select the Cameras tab and click the + button at the bottom of the window. It will expand to a list of camera control connections that are available. Select the control protocol that is used by your camera. A new camera instance will be added to the list above. Click on the new camera and the camera settings will appear on the right hand side of the window. You'll need to enter the camera connection details, either the network address or serial port used for control. Click the Apply button to connect to the camera.

Finally associate the camera with an OBS source by using the Source combo box. This lets the plugin automatically select the right camera for control when the preview or program scene changes in OBS.

Removing a camera

To remove a camera, select the camera in the settings dialog can click the - button in the toolbar.

Controlling Cameras

Cameras are controlled with the arrow buttons in the control dock. To move a camera, select it in the list of cameras (bottom left of the control dock) and then click the control buttons above. The arrow buttons will pan/tilt the camera, The magnifing glass buttons will zoom in and out, and the small/large buttons will chagne the focus. You can also toggle autofocus on and off with the AF button and trigger a one-touch refocus action.

Presets are listed on the right hand side of the dock. Presets can be saved, recalled, and renamed from the dock window.

To save a preset, right click on the preset that you want to change and select Save Preset. Similarly, to rename a preset, right click and select Rename Preset, or select Clear Preset to reset the name back to default.

Double click to recall a preset.

Joystick Control

To enable joystick control, select the Joystick Control check box on the general tab of the settings dialog. All of the connected joysticks will be shown in the list box. Click on the joystick that you want to use for camera control. The mapping of controls to PTZ actions is shown in the settings dialog to the right of the joystick list.

Advanced Features

Block Moves on Live Camera

In Studio mode, camera adjustments are usually set up with the source visible in the Preview scene before being transitioned over to the live Program scene. Manual camera movements are avoided on the Program scene because they can be quite abrupt and unpleasant to watch.

OBS PTZ can by default block out manual moves of cameras visible in Program. To enable this feature, check the Lockout live PTZ moves in studio mode checkbox in the Setting dialog About tab.

With the feature enabled the pan, tilt, zoom and preset controls will be disabled for any camera visible in Program, preventing live moves. If you need to override the block and do a live movement anyway then you can temporarily override the block by clicking the lock icon in the toolbar.

Debugging data

The plugin can generate a large amount of debug data with all the protocol messages sent to and received by the cameras. Debug logs appear in the main obs-studio log, but are disable by default. To enable verbose debug logs, select Enable debug logging in the settings dialog.

Build Instructions

Linux

git clone https://github.com/glikely/obs-ptz
mkdir obs-ptz/build
cd obs-ptz/build
cmake ..
make

Copy or symlink obs-ptz.so into the OBS plugins directory. Typically /usr/lib/obs-plugins or /usr/lib64/obs-plugins

sudo cp obs-ptz/rundir/RelWithDebugInfo/obs-plugins/64bit/obs-ptz.so /usr/lib/obs-plugins/

Debian 11 Bullseye

In Debian 11 Bullseys you can use the development package libobs-dev to build the plugin instead of building obs-studio from source. Do the following on Debian to get a working build environment:

sudo apt build-dep obs-studio
sudo apt install libobs-dev libqt5serialport5-dev
git clone https://github.com/glikely/obs-ptz
mkdir obs-ptz/build
cd obs-ptz/build
cmake ..
make

Windows

To simplify development it helps to include MSBuild, 7-Zip, and Inno Setup Compiler in your default path (Search for 'Edit the System Environment Variables' in the Windows search bar).

This project contains a helper script for building the plugin that assumes that obs-studio and obs-ptz share the same top level directory. It also assumes that Qt5 is installed under c:\Qt\. If you have Qt5 installed or obs-studio checked out somewhere else then you'll need to modify the winbuild and winrun scripts.

Both 32 and 64 bit versions of the plugin will be built if you've built both version of OBS Studio. Edit the script if you only want to build one version.

  • Build OBS Studio using instructions on OBS-Studio Wiki: https://obsproject.com/wiki/Install-Instructions
  • Clone this repository into a working directory
  • Run scripts\winbuild.cmd to build the plugin
  • Run scripts\winrun32.cmd or scripts\winrun64.cmd to run OBS either the 32bit or 64bit version of the plugin

From Powershell:

git clone https://github.com/glikely/obs-ptz
cd obs-ptz
.\scripts\winbuild.cmd
.\scripts\winrun64.cmd

Use the scripts\winbuild-rel.cmd script to build the release version of the plugin as a zip file and installer. You'll first need to build a RelWithDebInfo version of OBS Studio before building the release plugin.

MacOS

  • Install Homebrew
  • Install Packages
$ brew install packages
git clone https://github.com/glikely/obs-ptz
cd obs-ptz
.github/scripts/build-macos.sh
.github/scripts/package-macos.sh

Using Qt5 instead of Qt6

obs-studio has moved on to Qt6 for official builds, but some packagers are still using Qt5. RPMFusion on Fedora 37 for example. obs-ptz will still happly build against Qt6, but if obs-studio is using Qt5 then the plugin will make it crash at startup with the following cryptic error:

info: [obs-ptz] plugin loaded successfully (version 0.14.1)
QWidget: Must construct a QApplication before a QWidget

You need to install the Qt5 development packages and tell obs-ptz to use Qt5 instead:

sudo yum install qt5-qtbase-devel qt5-qtbase-private-devel \
                 qt5-qtsvg-devel qt5-qtwayland-devel       \
		 qt5-qtx11extras-devel qt5-qtserialport-devel
git clone https://github.com/glikely/obs-ptz
mkdir obs-ptz/build
cd obs-ptz/build
cmake -DQT_VERSION=5 ..
make

Contributing

Contributions welcome! You can submit changes as GitHub pull requests. Or email patches to me at mailto:[email protected]

See CONTRIBUTING.md for details.

Acknowledgements

Thank you to everyone who has contributed to his project, either with filing issues, asking questions, or contributing to the code. All code and documentation contributors are listed in AUTHORS.

Joystick support uses the QJoystick library.

And finally, thank you to everyone who contributes to the Free and Open Source Software that this project is built upon, including OBS Studio, Qt, Simple DirectMedia Layer (SDL), Linux, and countless libraries and tools.

obs-ptz's People

Contributors

cg2121 avatar dattrax avatar exeldro avatar fenrirthviti avatar glikely avatar jonata avatar luukverhagen avatar norihiro avatar palakis avatar patthemav avatar quartzo avatar rytoex avatar tt2468 avatar tytan652 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

obs-ptz's Issues

Program max speed for presets on Sony BRC-300

This is not a pressing issue, but when you tell the Sony BRC-300 to recall a preset, it goes rather quickly to the preset. Apparently, there is a way to set the speed at which the camera moves to a preset if you're using the Sony RM-B300 controller. I'm guessing there is a way to send it through the VISCA interface as well. Here's the appropriate section in the controller manual. Is it possible to implement this in your software as well?

image

Enhancement: Camera IP Address Discovery Feature

Use the ONVIF protocol (not sure if VISCA over IP has such a feature) to auto discover the IP Address of the cameras on a network. If possible, it would be good to select the network if possible as some PCs might have 2 or more networks configured or multiple LAN cards. Sometimes the Autodiscovery feature runs on the wrong network and doesn't detect the desired cameras.

PTZ control with Visca TCP not persisting as expected

I am finding that in some cases the PTZ control using Visca TCP, in this case a PTZ optics camera, is not persisting as expected.
A colleague reported that the camera was not being controlled by the presets or manual controls last Saturday. This was a day after I had it working and labeled. Before I was able to confirm that we had a major storm without a power outage.
I recreated the camera and labels this morning and then now 4 hours later, presets and manual control were no longer working.
I double clicked on the camera and opened the setting panel to confirm the correct IP was there. It looked fine. Closed that panel and attempted to control again and manual and preset control resumed working.

Adding Pelco-D

Now we have added Pelco-P, we should also add Pelco-D. If you haven't implemented it when I'm back from my holidays, I will pick up this Issue.

Only partially works with Sony BRC-300 Camera

I am attempting to use this plugin with a Sony BRC-300 camera. The plugin detects the camera, but the only four directional buttons that respond are the diagonal ones (up and left, down and left, up and right, down and right), and the home button. When you press any of these, they don't go just a bit, they go all the way until the camera reaches it's limits, although the speed control does affect the speed at which it travels. Any of the other buttons throw a VISCA error in the log file.

I am using the precompiled Windows download that is available through the OBS website, on Windows 10.

Here is the log file:
21:07:39.879: CPU Name: Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz
21:07:39.879: CPU Speed: 2492MHz
21:07:39.879: Physical Cores: 2, Logical Cores: 4
21:07:39.879: Physical Memory: 8075MB Total, 3007MB Free
21:07:39.879: Windows Version: 10.0 Build 19043 (release: 2009; revision: 1165; 64-bit)
21:07:39.879: Running as administrator: false
21:07:39.879: Aero is Enabled (Aero is always on for windows 8 and above)
21:07:39.880: Windows 10 Gaming Features:
21:07:39.880: Game DVR: On
21:07:39.883: Sec. Software Status:
21:07:39.888: Norton 360: enabled (AV)
21:07:39.888: Microsoft Defender Antivirus: disabled (AV)
21:07:39.889: Norton 360: enabled (FW)
21:07:39.889: Windows Firewall: disabled (FW)
21:07:39.889: Current Date/Time: 2021-09-09, 21:07:39
21:07:39.889: Browser Hardware Acceleration: true
21:07:39.889: Portable mode: false
21:07:40.444: OBS 27.0.1 (64-bit, windows)
21:07:40.444: ---------------------------------
21:07:40.479: ---------------------------------
21:07:40.479: audio settings reset:
21:07:40.479: samples per sec: 48000
21:07:40.479: speakers: 2
21:07:40.482: ---------------------------------
21:07:40.482: Initializing D3D11...
21:07:40.482: Available Video Adapters:
21:07:40.485: Adapter 0: Intel(R) HD Graphics 3000
21:07:40.485: Dedicated VRAM: 33554432
21:07:40.485: Shared VRAM: 1711276032
21:07:40.485: PCI ID: 8086:126
21:07:40.486: Driver Version: 9.17.10.4459
21:07:40.486: output 0: pos={0, 0}, size={1366, 768}, attached=true, refresh=60, name=
21:07:40.493: Loading up D3D11 on adapter Intel(R) HD Graphics 3000 (0)
21:07:40.499: D3D11 loaded successfully, feature level used: a100
21:07:40.499: DXGI increase maximum frame latency success
21:07:41.539: ---------------------------------
21:07:41.539: video settings reset:
21:07:41.539: base resolution: 720x480
21:07:41.539: output resolution: 720x480
21:07:41.539: downscale filter: Bicubic
21:07:41.539: fps: 30/1
21:07:41.539: format: NV12
21:07:41.539: YUV mode: 709/Partial
21:07:41.540: NV12 texture support enabled
21:07:41.541: Audio monitoring device:
21:07:41.541: name: Default
21:07:41.541: id: default
21:07:41.546: ---------------------------------
21:07:41.547: Skipping module '../../obs-plugins/64bit/chrome_elf.dll', not an OBS plugin
21:07:41.557: [CoreAudio encoder]: CoreAudio AAC encoder not installed on the system or couldn't be loaded
21:07:41.558: Failed to load 'en-US' text for module: 'decklink-captions.dll'
21:07:41.560: Failed to load 'en-US' text for module: 'decklink-ouput-ui.dll'
21:07:41.638: [AMF] Unable to load 'amfrt64.dll', error code 126.
21:07:41.643: [AMF] AMF Test failed due to one or more errors.
21:07:41.643: Failed to initialize module 'enc-amf.dll'
21:07:41.659: Skipping module '../../obs-plugins/64bit/libcef.dll', not an OBS plugin
21:07:41.674: Skipping module '../../obs-plugins/64bit/libEGL.dll', not an OBS plugin
21:07:41.676: Skipping module '../../obs-plugins/64bit/libGLESv2.dll', not an OBS plugin
21:07:41.688: [obs-browser]: Version 2.14.3
21:07:41.688: [obs-browser]: CEF Version 75.1.16+g16a67c4+chromium-75.0.3770.100
21:07:41.697: [noise suppress: Nvidia RTX denoiser disabled, redistributable not found]
21:07:42.477: Failed to load 'en-US' text for module: 'ptz-controls.dll'
21:07:42.477: PTZ Controls plugin v0.7.0
21:07:42.640: gamepads found 0
21:07:42.640: ViscaUART::get_interface():735: Looking for UART object COM4
21:07:42.640: ViscaUART::get_interface():738: Creating new VISCA object COM4
21:07:42.640: VISCA Unable to open UART COM4
21:07:42.640: VISCA Unable to open UART COM4
21:07:42.643: Skipping module '../../obs-plugins/64bit/Qt5Gamepad.dll', not an OBS plugin
21:07:42.644: Skipping module '../../obs-plugins/64bit/Qt5SerialPort.dll', not an OBS plugin
21:07:42.656: VLC found, VLC video source enabled
21:07:42.672: A DeckLink iterator could not be created. The DeckLink drivers may not be installed
21:07:42.677: No blackmagic support
21:07:42.693: ---------------------------------
21:07:42.694: Loaded Modules:
21:07:42.694: win-wasapi.dll
21:07:42.694: win-mf.dll
21:07:42.694: win-dshow.dll
21:07:42.694: win-decklink.dll
21:07:42.694: win-capture.dll
21:07:42.694: vlc-video.dll
21:07:42.694: text-freetype2.dll
21:07:42.694: rtmp-services.dll
21:07:42.694: ptz-controls.dll
21:07:42.694: obs-x264.dll
21:07:42.694: obs-vst.dll
21:07:42.694: obs-transitions.dll
21:07:42.694: obs-text.dll
21:07:42.694: obs-qsv11.dll
21:07:42.694: obs-outputs.dll
21:07:42.694: obs-filters.dll
21:07:42.694: obs-ffmpeg.dll
21:07:42.694: obs-browser.dll
21:07:42.694: image-source.dll
21:07:42.694: frontend-tools.dll
21:07:42.694: enc-amf.dll
21:07:42.694: decklink-ouput-ui.dll
21:07:42.694: decklink-captions.dll
21:07:42.694: coreaudio-encoder.dll
21:07:42.694: ---------------------------------
21:07:42.695: ==== Startup complete ===============================================
21:07:42.702: All scene data cleared
21:07:42.702: ------------------------------------------------
21:07:43.017: WASAPI: Device 'Speakers (2- High Definition Audio Device)' [48000 Hz] initialized
21:07:43.017: [Loaded global audio device]: 'Desktop Audio'
21:07:43.085: WASAPI: Device 'Microphone (2- High Definition Audio Device)' [44100 Hz] initialized
21:07:43.085: [Loaded global audio device]: 'Mic/Aux'
21:07:43.088: [WASAPISource::WASAPISource] Device '{0.0.1.00000000}.{4aa749c4-6fe1-40d5-8935-37b4801fefb6}' not found. Waiting for device
21:07:43.102: Switched to scene 'Scene'
21:07:43.102: ------------------------------------------------
21:07:43.102: Loaded scenes:
21:07:43.102: - scene 'Scene':
21:07:43.102: - source: 'Video Capture Device' (dshow_input)
21:07:43.103: - source: 'Audio Input Capture' (wasapi_input_capture)
21:07:43.103: - source: 'HDMI' (dshow_input)
21:07:43.103: - source: 'SVIDEO' (dshow_input)
21:07:43.103: ------------------------------------------------
21:07:43.115: adding 42 milliseconds of audio buffering, total audio buffering is now 42 milliseconds (source: Mic/Aux)
21:07:43.115:
21:07:43.299: SVIDEO: data.GetDevice failed
21:07:43.299: SVIDEO: Video configuration failed
21:07:43.323: HDMI: data.GetDevice failed
21:07:43.323: HDMI: Video configuration failed
21:07:43.353: Video Capture Device: data.GetDevice failed
21:07:43.353: Video Capture Device: Video configuration failed
21:09:14.981: gamepads found 0
21:09:55.844: ViscaUART::get_interface():735: Looking for UART object COM5
21:09:55.844: ViscaUART::get_interface():738: Creating new VISCA object COM5
21:09:55.911: PTZUARTWrapper::send():91: COM5 --> 88:30:01:ff
21:09:55.940: ViscaUART::receive_datagram():691: VISCA <-- 88:30:02:ff
21:09:55.940: VISCA Interface COM5: 1 camera found
21:09:55.940: PTZUARTWrapper::send():91: COM5 --> 88:01:00:01:ff
21:09:55.940: PTZUARTWrapper::send():91: COM5 --> 81:09:00:02:ff
21:09:55.979: ViscaUART::receive_datagram():691: VISCA <-- 90:50:00:01:04:0f:01:12:02:ff
21:09:55.979: PTZUARTWrapper::send():91: COM5 --> 81:09:00:02:ff
21:09:55.993: ViscaUART::receive_datagram():691: VISCA <-- 88:01:00:01:ff
21:09:56.008: ViscaUART::receive_datagram():691: VISCA <-- 90:50:00:01:04:0f:01:12:02:ff
21:09:56.009: PTZUARTWrapper::send():91: COM5 --> 81:09:06:12:ff
21:09:56.039: ViscaUART::receive_datagram():691: VISCA <-- 90:50:00:00:00:00:00:00:00:00:00:ff
21:09:56.039: PTZUARTWrapper::send():91: COM5 --> 81:09:7e:7e:00:ff
21:09:56.094: ViscaUART::receive_datagram():691: VISCA <-- 90:50:00:00:00:00:00:00:01:00:00:00:00:03:00:ff
21:09:56.094: PTZUARTWrapper::send():91: COM5 --> 81:09:7e:7e:01:ff
21:09:56.144: ViscaUART::receive_datagram():691: VISCA <-- 90:50:0b:0b:0c:03:00:08:00:00:06:11:07:17:07:ff
21:09:56.145: PTZUARTWrapper::send():91: COM5 --> 81:09:7e:7e:02:ff
21:09:56.194: ViscaUART::receive_datagram():691: VISCA <-- 90:50:01:00:00:00:00:00:00:00:00:00:18:00:00:ff
21:09:56.194: PTZUARTWrapper::send():91: COM5 --> 81:09:7e:7e:03:ff
21:09:56.244: ViscaUART::receive_datagram():691: VISCA <-- 90:50:00:00:00:05:00:05:08:08:00:00:00:00:00:ff
21:09:56.244: PTZUARTWrapper::send():91: COM5 --> 81:09:7e:7e:04:ff
21:09:56.271: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:09:56.271: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:09:56.271: PTZUARTWrapper::send():91: COM5 --> 81:09:7e:7e:05:ff
21:09:56.301: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:09:56.301: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:06.650: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:06.650: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:00:ff
21:10:06.698: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:06.698: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:06.698: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:10:06.698: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:10:08.412: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:0c:00:02:03:ff
21:10:08.435: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:08.436: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.222: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:10.245: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.245: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.314: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:0c:00:02:03:ff
21:10:10.337: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.337: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.357: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:10.379: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.379: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.448: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:0c:00:02:03:ff
21:10:10.471: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.471: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.533: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:10.559: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.559: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.588: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:0c:00:02:03:ff
21:10:10.611: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.611: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.655: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:10.678: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.679: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.739: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:0c:00:02:03:ff
21:10:10.763: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.763: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.812: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:10.834: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.834: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.898: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:0c:00:02:03:ff
21:10:10.922: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.922: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:10.961: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:10.984: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:10.984: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:11.035: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:0c:00:02:03:ff
21:10:11.059: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:11.060: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:11.110: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:11.134: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:11.134: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:14.702: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:00:02:03:ff
21:10:14.726: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:14.726: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:17.020: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:17.042: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:17.042: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:17.662: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:14:02:01:ff
21:10:17.684: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:10:17.695: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:10:18.368: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:18.389: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:18.389: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:22.724: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:14:01:02:ff
21:10:22.745: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:10:22.753: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:10:22.895: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:22.920: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:22.921: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:30.157: PTZUARTWrapper::send():91: COM5 --> 81:01:06:04:ff
21:10:30.177: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:10:33.690: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:10:34.701: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:00:02:03:ff
21:10:34.722: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:34.722: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:34.899: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:34.920: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:34.920: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:35.137: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:00:02:03:ff
21:10:35.162: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:35.162: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:35.396: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:35.418: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:35.418: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:35.508: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:00:02:03:ff
21:10:35.532: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:35.532: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:35.583: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:35.607: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:35.607: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:35.657: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:00:02:03:ff
21:10:35.681: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:35.681: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:35.749: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:35.771: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:35.771: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:36.640: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:00:01:03:ff
21:10:36.667: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:36.667: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:36.725: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:36.748: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:36.748: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:36.850: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:00:01:03:ff
21:10:36.873: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:36.873: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:36.949: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:36.971: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:36.971: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:37.024: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:00:01:03:ff
21:10:37.052: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:37.052: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:37.111: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:37.134: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:37.135: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:38.020: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:14:03:02:ff
21:10:38.049: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:38.049: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:38.104: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:38.130: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:38.130: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:38.577: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:14:03:02:ff
21:10:38.604: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:38.604: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:38.679: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:38.705: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:38.705: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:38.763: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:14:03:02:ff
21:10:38.788: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:38.788: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:38.838: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:38.863: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:38.863: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:38.912: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:14:03:02:ff
21:10:38.937: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:38.937: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:38.999: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:39.023: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:39.023: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:39.785: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:14:03:01:ff
21:10:39.809: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:39.810: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:39.845: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:39.869: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:39.869: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:39.944: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:14:03:01:ff
21:10:39.969: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:39.969: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:39.995: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:40.019: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:40.019: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:40.094: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:14:03:01:ff
21:10:40.118: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:40.118: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:40.168: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:40.195: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:40.195: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:41.834: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:14:02:02:ff
21:10:41.859: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:10:41.869: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:10:41.918: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:41.946: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:41.946: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:47.394: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:18:14:01:01:ff
21:10:47.418: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:10:47.429: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:10:47.493: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:10:47.517: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:10:47.517: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:10:54.296: PTZUARTWrapper::send():91: COM5 --> 81:01:06:04:ff
21:10:54.318: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:10:57.783: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:03.476: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:04:04:02:02:ff
21:11:03.500: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:03.512: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:03.562: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:11:03.592: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:03.592: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:06.767: PTZUARTWrapper::send():91: COM5 --> 81:01:06:04:ff
21:11:06.793: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:07.195: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:09.039: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:04:00:02:03:ff
21:11:09.064: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:09.064: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:09.127: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:11:09.152: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:09.152: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:09.485: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:04:00:02:03:ff
21:11:09.512: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:09.512: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:09.560: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:11:09.586: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:09.586: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:09.647: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:04:00:02:03:ff
21:11:09.676: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:09.676: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:09.734: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:11:09.763: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:09.763: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:09.833: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:04:00:02:03:ff
21:11:09.859: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:09.859: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:09.907: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:11:09.932: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:09.933: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:09.994: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:04:00:02:03:ff
21:11:10.021: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:10.021: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:10.063: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:11:10.089: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:10.089: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:11.128: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:21:ff
21:11:11.170: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:11.183: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:11.200: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:00:ff
21:11:11.237: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:11.257: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:11.485: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:21:ff
21:11:11.521: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:11.540: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:11.597: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:00:ff
21:11:11.637: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:11.655: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:11.675: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:21:ff
21:11:11.704: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:11.722: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:12.231: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:00:ff
21:11:12.271: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:12.284: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:12.404: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:21:ff
21:11:12.438: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:12.454: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:12.520: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:00:ff
21:11:12.555: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:12.570: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:15.575: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:25:ff
21:11:15.608: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:15.621: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:15.673: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:00:ff
21:11:15.708: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:15.724: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:15.898: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:25:ff
21:11:15.942: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:15.956: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:17.362: PTZUARTWrapper::send():91: COM5 --> 81:01:04:07:00:ff
21:11:17.393: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:17.411: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:25.958: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:10:02:02:ff
21:11:25.993: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:26.006: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:11:26.027: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:11:26.055: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:11:26.055: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:11:30.540: PTZUARTWrapper::send():91: COM5 --> 81:01:06:04:ff
21:11:30.644: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:11:32.981: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:12:32.801: ViscaUART::get_interface():735: Looking for UART object COM5
21:12:34.882: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:10:02:02:ff
21:12:34.909: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:12:34.930: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:12:35.025: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:12:35.054: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:35.054: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:36.195: PTZUARTWrapper::send():91: COM5 --> 81:01:06:04:ff
21:12:36.300: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:12:37.370: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:12:38.522: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:00:02:03:ff
21:12:38.550: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:38.550: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:39.512: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:12:39.546: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:39.546: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:39.623: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:00:02:03:ff
21:12:39.663: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:39.663: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:39.698: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:12:39.734: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:39.734: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:39.777: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:00:02:03:ff
21:12:39.808: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:39.808: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:39.860: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:12:39.887: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:39.887: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:39.922: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:00:02:03:ff
21:12:39.958: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:39.959: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:39.993: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:12:40.022: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:40.022: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:40.075: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:00:02:03:ff
21:12:40.104: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:40.104: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:40.135: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:12:40.168: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:40.168: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:40.220: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:00:02:03:ff
21:12:40.253: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:40.254: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:40.291: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:12:40.320: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:40.321: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:46.119: gamepads found 0
21:12:48.907: ViscaUART::get_interface():735: Looking for UART object COM5
21:12:51.294: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:13:10:02:01:ff
21:12:51.326: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:12:51.342: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff
21:12:51.443: PTZUARTWrapper::send():91: COM5 --> 81:01:06:01:00:00:03:03:ff
21:12:51.477: ViscaUART::receive_datagram():691: VISCA <-- 90:60:02:ff
21:12:51.477: PTZVisca::receive():547: VISCA PTZ received error: 90:60:02:ff
21:12:52.399: PTZUARTWrapper::send():91: COM5 --> 81:01:06:04:ff
21:12:52.506: ViscaUART::receive_datagram():691: VISCA <-- 90:41:ff
21:12:53.482: ViscaUART::receive_datagram():691: VISCA <-- 90:51:ff

Matching PTZ device to scene by name does not scale

Active PTZ device is selected by matching the scene name, but this does not scale and does not let a camera be matched to multiple scenes. To fix this, cameras should track specific sources instead of scenes

Additional button to trigger a white balance adjustement

A "White Balance" button is very much needed to adjust the white balance of the cameras for different lighting conditions.
The mix of outdoor light from the windows and the indoor lights varies at different times of the day, hence a white balance is necessary on a regular basis.

Suggestion: Options to invert the PTZ directions

By adding options on the "PTZ Device Settings" to invert pan, invert tilt, (and or zoom).
This will help some cheap PTZ cameras that doesn't have enough options to configure their orientations and other PTZ cameras that are installed inverted in the ceiling.

PTZ Controls not loading

I copied the 4 files from the Zip to the plugins/64bit folder and relaunched OBS. I don't see any PTZ control options in the View or View Docks menu.
Here's the OBS Log

16:33:47.505: CPU Name: Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
16:33:47.505: CPU Speed: 2712MHz
16:33:47.505: Physical Cores: 2, Logical Cores: 4
16:33:47.505: Physical Memory: 8096MB Total, 4039MB Free
16:33:47.505: Windows Version: 10.0 Build 19042 (release: 2009; revision: 1083; 64-bit)
16:33:47.505: Running as administrator: false
16:33:47.505: Aero is Enabled (Aero is always on for windows 8 and above)
16:33:47.506: Windows 10 Gaming Features:
16:33:47.506: Game Bar: Off
16:33:47.506: Game DVR: Off
16:33:47.506: Game DVR Background Recording: Off
16:33:47.509: Sec. Software Status:
16:33:47.514: Webroot SecureAnywhere: enabled (AV)
16:33:47.514: Microsoft Defender Antivirus: disabled (AV)
16:33:47.514: Windows Firewall: enabled (FW)
16:33:47.515: Current Date/Time: 2021-07-15, 16:33:47
16:33:47.515: Browser Hardware Acceleration: true
16:33:47.515: Portable mode: false
16:33:47.915: OBS 27.0.1 (64-bit, windows)
16:33:47.915: ---------------------------------
16:33:47.945: ---------------------------------
16:33:47.945: audio settings reset:
16:33:47.945: samples per sec: 48000
16:33:47.945: speakers: 2
16:33:47.946: ---------------------------------
16:33:47.946: Initializing D3D11...
16:33:47.946: Available Video Adapters:
16:33:47.949: Adapter 0: Intel(R) HD Graphics 620
16:33:47.949: Dedicated VRAM: 134217728
16:33:47.949: Shared VRAM: 4244981760
16:33:47.949: PCI ID: 8086:5916
16:33:47.950: Driver Version: 23.20.16.4973
16:33:47.950: output 0: pos={0, 0}, size={1920, 1080}, attached=true, refresh=60, name=
16:33:47.953: Loading up D3D11 on adapter Intel(R) HD Graphics 620 (0)
16:33:47.973: D3D11 loaded successfully, feature level used: b000
16:33:47.973: DXGI increase maximum frame latency success
16:33:48.497: ---------------------------------
16:33:48.497: video settings reset:
16:33:48.497: base resolution: 1920x1080
16:33:48.497: output resolution: 1920x1080
16:33:48.497: downscale filter: Bicubic
16:33:48.497: fps: 30/1
16:33:48.497: format: NV12
16:33:48.497: YUV mode: 709/Partial
16:33:48.497: NV12 texture support not available
16:33:48.498: Audio monitoring device:
16:33:48.498: name: Speakers (CEntrance MicPort Pro)
16:33:48.498: id: {0.0.0.00000000}.{274ab247-0b7e-4c70-bb82-05efc92b7e8e}
16:33:48.502: ---------------------------------
16:33:48.503: Skipping module '../../obs-plugins/64bit/chrome_elf.dll', not an OBS plugin
16:33:48.505: Skipping module '../../obs-plugins/64bit/concrt140.dll', not an OBS plugin
16:33:48.509: [CoreAudio encoder]: CoreAudio AAC encoder not installed on the system or couldn't be loaded
16:33:48.510: Failed to load 'en-US' text for module: 'decklink-captions.dll'
16:33:48.512: Failed to load 'en-US' text for module: 'decklink-ouput-ui.dll'
16:33:48.978: [AMF] Unable to load 'amfrt64.dll', error code 126.
16:33:48.982: [AMF] AMF Test failed due to one or more errors.
16:33:48.982: Failed to initialize module 'enc-amf.dll'
16:33:48.997: Skipping module '../../obs-plugins/64bit/libcef.dll', not an OBS plugin
16:33:49.008: Skipping module '../../obs-plugins/64bit/libEGL.dll', not an OBS plugin
16:33:49.009: Skipping module '../../obs-plugins/64bit/libGLESv2.dll', not an OBS plugin
16:33:49.015: Skipping module '../../obs-plugins/64bit/msvcp140.dll', not an OBS plugin
16:33:49.018: [obs-browser]: Version 2.14.3
16:33:49.018: [obs-browser]: CEF Version 75.1.16+g16a67c4+chromium-75.0.3770.100
16:33:49.024: [noise suppress: Nvidia RTX denoiser disabled, redistributable not found]
16:33:49.026: [obs-ndi] hello ! (version 4.9.0)
16:33:49.026: [obs-ndi] Trying 'C:\Program Files\NewTek\NDI 4 Runtime\v4'
16:33:49.027: [obs-ndi] Found NDI library at 'C:/Program Files/NewTek/NDI 4 Runtime/v4/Processing.NDI.Lib.x64.dll'
16:33:49.032: [obs-ndi] NDI runtime loaded successfully
16:33:49.044: [obs-ndi] NDI library initialized successfully (NDI SDK WIN64 06:20:19 Apr 1 2020 4.5.1.0)
16:33:49.087: Skipping module '../../obs-plugins/64bit/Qt5Network.dll', not an OBS plugin
16:33:49.089: Module '../../obs-plugins/64bit/Qt5Network.dll' not loaded
16:33:49.093: [Source Dock] loaded version 0.1.1
16:33:49.095: Failed to load 'en-US' text for module: 'StreamDeckPlugin.dll'
16:33:49.106: Skipping module '../../obs-plugins/64bit/ucrtbase.dll', not an OBS plugin
16:33:49.107: Skipping module '../../obs-plugins/64bit/vccorlib140.dll', not an OBS plugin
16:33:49.109: Skipping module '../../obs-plugins/64bit/vcruntime140.dll', not an OBS plugin
16:33:49.114: VLC found, VLC video source enabled
16:33:49.124: A DeckLink iterator could not be created. The DeckLink drivers may not be installed
16:33:49.126: No blackmagic support
16:33:49.139: ---------------------------------
16:33:49.139: Loaded Modules:
16:33:49.139: win-wasapi.dll
16:33:49.139: win-mf.dll
16:33:49.139: win-dshow.dll
16:33:49.139: win-decklink.dll
16:33:49.139: win-capture.dll
16:33:49.139: vlc-video.dll
16:33:49.139: text-freetype2.dll
16:33:49.139: StreamDeckPlugin.dll
16:33:49.139: source-dock.dll
16:33:49.139: rtmp-services.dll
16:33:49.139: obs-x264.dll
16:33:49.139: obs-vst.dll
16:33:49.139: obs-transitions.dll
16:33:49.139: obs-text.dll
16:33:49.139: obs-qsv11.dll
16:33:49.139: obs-outputs.dll
16:33:49.139: obs-ndi.dll
16:33:49.139: obs-filters.dll
16:33:49.139: obs-ffmpeg.dll
16:33:49.139: obs-browser.dll
16:33:49.139: image-source.dll
16:33:49.139: frontend-tools.dll
16:33:49.139: enc-amf.dll
16:33:49.139: decklink-ouput-ui.dll
16:33:49.139: decklink-captions.dll
16:33:49.139: coreaudio-encoder.dll
16:33:49.139: ---------------------------------
16:33:49.140: ==== Startup complete ===============================================
16:33:49.179: Switched to Preview/Program mode
16:33:49.179: ------------------------------------------------
16:33:49.187: All scene data cleared
16:33:49.187: ------------------------------------------------
16:33:49.238: [WASAPISource::WASAPISource] Device '{0.0.0.00000000}.{800788e8-e5ba-4681-9b45-3f8da1978cbf}' not found. Waiting for device
16:33:49.239: [Loaded global audio device]: 'Desktop Audio'
16:33:49.240: [WASAPISource::WASAPISource] Device '{0.0.1.00000000}.{8464afd4-7a46-498f-b0ab-02d7672486cc}' not found. Waiting for device
16:33:49.240: [Loaded global audio device]: 'Mic/Aux'
16:33:49.240: - filter: 'Compressor' (compressor_filter)
16:33:49.240: - filter: 'Expander' (expander_filter)
16:33:49.249: [obs-ndi] started A/V threads for source 'HP-LAPTOP (AMD Radeon HD 6470M 1)'
16:33:49.251: [obs-ndi] A/V thread for 'NDI™ Source' started
16:33:49.295: Switched to scene 'Cam 2 PTZ'
16:33:49.353: ------------------------------------------------
16:33:49.353: Loaded scenes:
16:33:49.353: - scene 'Cam 1 Fixed':
16:33:49.353: - source: 'Fixed Cam' (dshow_input)
16:33:49.353: - scene 'Cam 2 PTZ':
16:33:49.353: - source: 'PTZ Cam' (dshow_input)
16:33:49.353: - scene 'NDI Scene':
16:33:49.353: - source: 'NDI™ Source' (ndi_source)
16:33:49.353: ------------------------------------------------
16:33:49.458: Fixed Cam: data.GetDevice failed
16:33:49.458: Fixed Cam: Video configuration failed
16:33:49.492: ---------------------------------
16:33:49.492: [DShow Device: 'PTZ Cam'] settings updated:
16:33:49.492: video device: MEI CV610-U3-V2
16:33:49.492: video path: \?\usb#vid_2cb3&pid_0201&mi_00#7&e990382&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
16:33:49.492: resolution: 1280x720
16:33:49.492: flip: 0
16:33:49.492: fps: 30.00 (interval: 333333)
16:33:49.492: format: YUY2
16:33:49.808: using video device audio: no
16:33:49.808: audio device: Digital Audio Interface (MEI CV610-U3-V2)
16:33:49.808: sample rate: 44100
16:33:49.808: channels: 2
16:33:49.808: audio type: Capture
16:36:48.974: adding 640 milliseconds of audio buffering, total audio buffering is now 640 milliseconds (source: PTZ Cam)
16:36:48.974:

Add support for the NDI PTZ protocol

My stuff is working fine but I am wondering if another protocol you could support is NDI. From what I understand it also wraps PTZ and also handles discovery.

It might be a nice way to go for simple set up. Just a suggestion.

Standalone version

A standalone version of this to use with other programs would be great. And please have it still do PTZ over serial!

VISCA stop code is not sent if camera doesn't respond

I use this plugin on linux and thank you for this project.
There are some problems, when I press an arrow and release it, it does not always send the 03:03:ff.
The problem happens every time, I have to click on all the other arrows randomly so that it sends the standby code and the PTZ stops to move.

Do you have a solution ?

model name unknown

this is just to give you the model og my camera, so the model number / vendor list can be updated

my model is a Birddog p100

image

Feature request: Add controller-local presets

Some cameras (e.g., Cisco PrecisionHD) don't expose preset save/recall VISCA commands. To use presets with these cameras requires saving the pan/tilt/zoom/focus settings in the controller configuration file, and sending them back to the camera when the preset is recalled.

The plugin isn't currently able to handle controller-local presets. It requires new functionality to query the camera for its current position and store it when the "save preset" option is selected, and to use that data when recalling a preset. It also requires functionality to detect whether or not the camera provides preset commands, or if the controller has to manage them.

VISCA over IP support

I think VISCA over IP doesn’t need any explanation.

Any way we can help bring support for this essential feature?

Control pan and tilt speed

Looking at the code, I noticed that the zoom method takes the zoom parameter and the zoom speed parameter, and it works perfectly. However, it seems that pan and tilt do not have this parameter. Am I mistaken or not? If not, is there any camera limitation for implementing this? If not again, is there a possibility that implementation will happen in the future?

plugin not loading in (distro or latest) OBS on Ubuntu linux

After building the shared object file:

build.log

I've tried placing it everywhere suggested by various OBS forum posts:

/usr/lib/obs-plugins

/usr/share/obs/obs-plugins/

$HOME/.config/obs-studio/plugins/

but OBS does not load the plugin. I've checked that file permissions on the .so object are the same as other libraries that OBS loads, I've run strace to see if it looks in those directories (it does),

strace.log

I've double checked that I have all build dependencies. I updated OBS from the version that is in the Ubuntu repositories to the official stable OBS repository.

I'm just stumped.

2021-07-03 07-41-15.txt

Buttons for unsupported functions should be disable

Suggestion : if a camera can only can scan (H) and (V) could the other directional controls be greyed out
Also make home button cause camera go to home position if you have not already done this.

Thanks

Make size of gamepad adjustable

The gamepad of the ptz controls dock is - too small - on a 4K Monitor to be operated easily.
It would be very much appreciated if the ratio between the different sections of the gamepad could be adjusted according to the individual needs. Either by dragging the border between the sections or by entering a ratio factor in the settings dialogue.
Thanks for consideration!

Possibility add more presets

Currently, there is a limit of 10 presets. Is there a possibility to add more presets per configuration?

Thk for all

Enhancement: Hotkeys for Moving Diagonally

As of now, it is not yet configurable to assign a hotkey for the diagonal movement... Zoom hotkeys can simultaneously work with the pan and tilt.. To save hotkeys, maybe its possible to move the camera diagonally by pressing both the pan and tilt hotkeys

unable to edit ip address in windows

Cant select camera in the setting menu to edit the ip address, however if you add a new camera and then remove the camera it will select the camera above it.

VISCA over IP - UDP vs TCP/IP

Hi @glikely,

I have been talking to one of the engineers for a camera that had problems with VISCA over IP to see if there is a problem with the firmware. He ran a few tests and showed he got the same responses via VISCA over IP as he did via Serial. The thing was, he said you need to use TCP/IP sockets and not UDP. I checked the commit to add VISCA over IP to this project, it looks like the project is using UDP sockets. More reliable handling of queries might be achieve by using TCP/IP (or make this selectable as some MFG might need UDP).

Sreial:
WeChat Image_20210729222941
WeChat Image_20210729222922

TCP/IP:
WeChat Image_20210729223952
WeChat Image_20210729223442

So maybe we can add the port type to the plugin?

Great work on this needed OBS tool though as well as the integration into Face-Tracker is great for the PTZ cameras.

-Greg

Bug: "stuck" controls with Focus and VISCA over IP

Love the addition of the Focus! With this version I am starting to see "Stuck" controls again while using the PTZ and Focus controls. I think I saw this back at 0.4.0 or earlier I think when you first introduced the VISCA over IP.

I experienced a little unexpected behavior with the Focus controls. I have been trying to figure out the best way to reproduce it but it is elusive in nature. It seems like at first setup, the initial state of the "Focus" controls are not known. Due to this, altering the focus manually doesn't work until you first select AF. I don't know the best way to set the One-Touch Focus, but currently that doesn't do anything I can see yet when I hit that button in Manual focus mode.

The manual focus is a great addition to the Plugin for those with obstacle in their field of view (Poles, etc.) as well as for simple artistic blur of the screen at times. Love it!

Ability to set IP camera address/ID (not IP)?

Hi,

first of all thanks for this software. I was wondering if it was possible to set the VISCA ID/address for the IP option. I am using a UDP to RS485 converter and have multiple cameras daisy chained to it, only the first camera seems to react.

Thanks again,
Normen

Cisco Precision HD camera and OBS PTZ compatibility

There are some difference in commands that Cisco implemented so Sony VISCA don't works with Cisco cameras (see attached user guide, from page 28). When using PTZ plugin in OBS, only pan and tilt commands works (not Up+Right... just one at a time).
Working:
-pan left
-pan right
-tilt down
-tilt upp
Working different:
-pan left+ tilt up: goes up and to the left to limit position.
-pan left+ tilt down: goes down and to the left to limit position.
-pan right+ tilt up: goes up and to the right to limit position.
-pan right+ tilt down: goes down and to the right to limit position.
-speed slider: when in bottom position, no movements, every other position, max speed.
Don't working:
-home button
-any preset function

Commands Cisco uses according user manual (attached):
Command; Command Packet; Comments
Zoom_Stop; 8x 01 04 07 00 ff ;
Zoom_Tele; 8x 01 04 07 2p ff ; p = speed parameter,
Zoom_Wide; 8x 01 04 07 3p ff ; a (low) to b (high)
Zoom_Direct; 8x 01 04 47 0p 0q 0r 0s ff ; pqrs: zoom position
PT_Stop; 8x 01 06 01 03 03 03 03 ff
PT_Reset; 8x 01 06 05 ff ; Reset pan/tilt to center position. This also re–synchronizes the motors.
PT_Up; 8x 01 06 01 0p 0t 03 01 ff ; p: pan speed , t: tilt speed
PT_Down; 8x 01 06 01 0p 0t 03 02 ff
PT_Left; 8x 01 06 01 0p 0t 01 03 ff
PT_Right; 8x 01 06 01 0p 0t 02 03 ff
PT_UpLeft; 8x 01 06 01 0p 0t 01 01 ff
PT_UpRight; 8x 01 06 01 0p 0t 02 01 ff
PT_DownLeft; 8x 01 06 01 0p 0t 01 02 ff
PT_DownRight; 8x 01 06 01 0p 0t 02 02 ff
PT_Direct; 8x 01 06 02 0p 0t 0q 0r 0s 0u 0v 0w 0x 0y ff ; p: max pan speed, t: max tilt speed, qrsu: pan position, vwxy: tilt position
PTZF_Direct; 8x 01 06 20 0p 0q 0r 0s 0t 0u 0v 0w 0x 0y 0z 0g 0h 0i 0j 0k ff ; Sets all motors in one operation.
pqrs: pan, tuvw: tilt, xyzg: zoom, hijk: focus, Attempts to linearize movement for pan and tilt. The focus value will not be used if the camera is in continuous autofocus mode. NOTE: Never route this message through Sony cameras

precisionhd_1080p-720p_camera_user_guide.pdf

PTZ Action hidden issue

When the PTZ Action is hidden it will work as well. However I hope it will only work when showing.

Bug: Recalling presets doesn't update autofocus state

A preset includes whether or not autofocus is active, but the UI is not updated when a preset is recalled because it doesn't ask the camera for an update of it's state.

This needs to be fixed by sending an inquiry after the preset recall has completed.

Linux - Buttons are too big

I don't know if it's the same on Windows, but on Linux, the pad is too big.
How to adjust the size?

Thanks for your help!

Capture du 2021-06-17 01-15-53

Show power indication in PTZ Device List

I am honored that the previous suggestion was accepted by you. Thank you so much!
In addition I would like to add a power indicator, is it possible?

like this:
aa

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.