Network streaming
The dv-processing library an easy to integrate solution to stream data over network TCP connections with optional
encryption for security or through local sockets. This achieved by using client-server paradigm implemented using two
classes: dv::io::NetworkWriter
and dv::io::NetworkReader
. The
dv::io::NetworkWriter
acts as a server, supports multiple concurrent clients, and streams a single data
type stream to the clients. The dv::io::NetworkReader
connects to a server and is able to receive the
streamed data.
This tutorial will introduce basic usage of these classes to stream event data using a network writer and receive the same data using network reader.
Network streaming server
The following sample implements a network streaming server using dv::io::NetworkWriter
, that streams
periodic event data, that is synthesized in software:
1#include <dv-processing/data/generate.hpp>
2#include <dv-processing/io/network_writer.hpp>
3
4int main() {
5 using namespace std::chrono_literals;
6
7 // Define image space resolution dimensions
8 const cv::Size resolution(200, 200);
9
10 // Define output event stream with valid resolution
11 const dv::io::Stream stream = dv::io::Stream::EventStream(0, "events", "TEST_DATA", resolution);
12
13 // Initiate the server, needs stream definition to initiate
14 dv::io::NetworkWriter server("0.0.0.0", 10101, stream);
15
16 // Print the ready state of the server
17 std::cout << "Waiting for connections..." << std::endl;
18
19 // Stream interval defines the packet frequency for this sample
20 const dv::Duration streamInterval = 10ms;
21
22 // Starting coordinates of the rectangle data that is going to be sent out in this sample
23 cv::Point2i offset(0, 0);
24
25 // Rectangle size in pixels
26 const cv::Point2i rectSize(20, 20);
27
28 // A boolean variable used to define movement direction of the rectangle
29 bool direction = true;
30
31 // Run indefinitely
32 while (true) {
33 // Do not produce output if there are no connected clients
34 if (server.getClientCount() > 0) {
35 // Generate the rectangle at given offset position
36 const dv::EventStore events = dv::data::generate::eventRectangle(dv::now(), offset, offset + rectSize);
37
38 // Increase or decrease the position coordinates depending on the "direction" boolean
39 offset = direction ? offset + cv::Point2i(1, 1) : offset - cv::Point2i(1, 1);
40
41 // Check if the rectangle coordinates reaches borders of the image
42 if (offset.x == 0 || offset.y == 0 || offset.x + rectSize.x == resolution.width
43 || offset.y + rectSize.y == resolution.height) {
44 // Reverse the motion direction
45 direction = !direction;
46 }
47
48 // Send it out to clients
49 server.writeEvents(events);
50 }
51
52 // Sleep the application for the streaming interval duration
53 std::this_thread::sleep_for(streamInterval);
54 }
55
56 return EXIT_SUCCESS;
57}
1import dv_processing as dv
2from datetime import timedelta
3import time
4
5# Define image space resolution dimensions
6resolution = (200, 200)
7
8# Define output event stream with valid resolution
9stream = dv.io.Stream.EventStream(0, "events", "TEST_DATA", resolution)
10
11# Initiate the server, needs stream definition to initiate
12server = dv.io.NetworkWriter("0.0.0.0", 10101, stream)
13
14# Print the ready state of the server
15print("Waiting for connections...")
16
17# Stream interval defines the packet frequency for this sample
18streamInterval = timedelta(milliseconds=10)
19
20# Starting coordinates of the rectangle data that is going to be sent out in this sample
21offset = (0, 0)
22
23# Rectangle size in pixels
24rectSize = (20, 20)
25
26# A boolean variable used to define movement direction of the rectangle
27direction = True
28
29# Run indefinitely
30while True:
31 # Do not produce output if there are no connected clients
32 if server.getClientCount() > 0:
33 # Generate the rectangle at given offset position
34 events = dv.data.generate.eventRectangle(dv.now(), offset, (offset[0] + rectSize[0], offset[1] + rectSize[1]))
35
36 # Increase or decrease the position coordinates depending on the "direction" boolean
37 offset = (offset[0] + 1, offset[1] + 1) if direction else (offset[0] - 1, offset[1] - 1)
38
39 # Check if the rectangle coordinates reaches borders of the image
40 if offset[0] == 0 or offset[1] == 0 or offset[0] + rectSize[0] == resolution[0] or offset[1] + rectSize[1] == \
41 resolution[1]:
42 # Reverse the motion direction
43 direction = not direction
44
45 # Send it out to clients
46 server.writeEvents(events)
47
48 # Sleep the application for the streaming interval duration
49 time.sleep(streamInterval.total_seconds())
Compiling and running the sample above will run an event streaming server, which is accessible on port 10101 on all available network devices. The instance will wait for a client to connect, next chapter will introduce a minimal client application to receive and visualize the streamed data.
Network streaming client
The following code sample provide a basic usage of dv::io::NetworkReader
to receive network streamed data.
1#include <dv-processing/io/network_reader.hpp>
2#include <dv-processing/visualization/event_visualizer.hpp>
3
4#include <opencv2/highgui.hpp>
5
6int main() {
7 using namespace std::chrono_literals;
8
9 // Initiate the client connection to the same port and localhost loopback address
10 dv::io::NetworkReader client("127.0.0.1", 10101);
11
12 // Validate that this client is connected to an event data stream
13 if (!client.isEventStreamAvailable()) {
14 throw dv::exceptions::RuntimeError("Server does not provide event data!");
15 }
16
17 // Initialize the event visualizer with server reported sensor resolution
18 dv::visualization::EventVisualizer visualizer(client.getEventResolution().value());
19
20 // Create a preview window to show the visualized events
21 cv::namedWindow("Preview", cv::WINDOW_NORMAL);
22
23 // Declare an event stream slicer to synchronized event data packets
24 dv::EventStreamSlicer slicer;
25
26 // Perform visualization every 10 milliseconds, which should match the server publishing frequency
27 slicer.doEveryTimeInterval(10ms, [&visualizer](const dv::EventStore &events) {
28 // Display preview image
29 cv::imshow("Preview", visualizer.generateImage(events));
30
31 // Short sleep, if user clicks escape key (code 27), exit the application
32 if (cv::waitKey(2) == 27) {
33 exit(0);
34 }
35 });
36
37 // While client is connected
38 while (client.isRunning()) {
39 // Read the event data, validate, and feed into the slicer
40 if (const auto events = client.getNextEventBatch(); events.has_value()) {
41 slicer.accept(*events);
42 }
43 }
44
45 return EXIT_SUCCESS;
46}
1import dv_processing as dv
2import cv2 as cv
3from datetime import timedelta
4
5# Initiate the client connection to the same port and localhost loopback address
6client = dv.io.NetworkReader("127.0.0.1", 10101)
7
8# Validate that this client is connected to an event data stream
9if not client.isEventStreamAvailable():
10 raise RuntimeError("Server does not provide event data!")
11
12# Initialize the event visualizer with server reported sensor resolution
13visualizer = dv.visualization.EventVisualizer(client.getEventResolution())
14
15# Create a preview window to show the visualized events
16cv.namedWindow("Preview", cv.WINDOW_NORMAL)
17
18# Declare an event stream slicer to synchronized event data packets
19slicer = dv.EventStreamSlicer()
20
21
22# Callback method to show the generated event visualization
23def show_preview(events: dv.EventStore):
24 # Display preview image
25 cv.imshow("Preview", visualizer.generateImage(events))
26
27 # Short sleep, if user clicks escape key (code 27), exit the application
28 if cv.waitKey(2) == 27:
29 exit(0)
30
31
32# Perform visualization every 10 milliseconds, which should match the server publishing frequency
33slicer.doEveryTimeInterval(timedelta(milliseconds=10), show_preview)
34
35# While client is connected
36while True:
37 # Read the event data
38 events = client.getNextEventBatch()
39
40 # Validate the data and feed into the slicer
41 if events is not None:
42 slicer.accept(events)
This code sample connects to the port 10101 on local loopback network, receives event data stream and uses
dv::visualization::EventVisualizer
to generate a preview image of the event data.
Running the samples
We can now combine both samples to stream the synthetic events and visualize the output. First, run the server sample application, when it prints “Waiting for connections…”, launch the second client application, it should show the preview window with a small rectangle moving from corner to corner.