1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include <array>
26#include <cmath>
27#include <cstdint>
28#include <cstring>
29#include <string>
30
31
32#include <boost/accumulators/accumulators.hpp>
33#include <boost/accumulators/statistics/count.hpp>
34#include <boost/accumulators/statistics/extended_p_square.hpp>
35#include <boost/accumulators/statistics/max.hpp>
36#include <boost/accumulators/statistics/mean.hpp>
37#include <boost/accumulators/statistics/min.hpp>
38#include <boost/accumulators/statistics/stats.hpp>
39#include <boost/accumulators/statistics/sum.hpp>
40#include <boost/accumulators/statistics/variance.hpp>
41
42
50
51
52
53
54
61
62
63
64
65struct Stats
66{
67
68 static constexpr std::array<double, 4> PROB = {{ 0.5, 0.68, 0.95, 0.997 }};
69 using Accumulator = boost::accumulators::accumulator_set<double, boost::accumulators::stats<
70 boost::accumulators::tag::count,
71 boost::accumulators::tag::mean,
72 boost::accumulators::tag::min,
73 boost::accumulators::tag::max,
74 boost::accumulators::tag::sum,
75 boost::accumulators::tag::variance,
76 boost::accumulators::tag::extended_p_square>>;
77 Accumulator lat { boost::accumulators::extended_p_square_probabilities = PROB };
78 Accumulator size { boost::accumulators::extended_p_square_probabilities = PROB };
79 Accumulator exp { boost::accumulators::extended_p_square_probabilities = PROB };
80#if FPSDK_USE_FFMPEG
81 Accumulator dec { boost::accumulators::extended_p_square_probabilities = PROB };
82#endif
83
84};
85
86
87
88
90{
91
92 public:
93 Opts() :
94 ProgramOptions("camera_streaming", { { 's', true, "sensor" }, { 'n', true, "camera" },
95 { 'd', true, "data" }, { 'r', true, "rate" }, { 'c', false, "stdout" } })
96 {};
97
98
99 std::string sensor_;
100 CamId camera_ = CamId::UNSPECIFIED;
102 int rate_ = 1;
103 bool stdout_ = false;
104
105
106 void PrintHelp() override final
107 {
108
109 std::fputs(
110 "\n"
111 "PBx-A1 camera streaming example\n"
112 "\n"
113 "Usage:\n"
114 "\n"
115 " camera_streaming -s <sensor> -c <camera> -d <data> [-r <rate>] [-c]\n"
116 "\n", stdout);
117 std::fputs(COMMON_FLAGS_HELP, stdout);
118 std::fputs(
119 "\n"
120 " -s, --sensor -- Hostname or IP address of sensor\n"
121 " -n, --camera -- Which camera to use (CAM1, CAM2, ...)\n"
122 " -d, --data -- Which data to stream (HIRES_VID, LORES_IMG, etc.)\n"
123 " -r, --rate -- Throttling rate (default 1, >1 only for _IMG streams)\n"
124 " -c, --stdout -- Pipe raw image/video data to stdout (careful!)\n"
125 "\n"
126 "Example:\n"
127 "\n"
128 " camera_streaming -s 10.0.2.1 -n CAM1 -d HIRES_VID\n"
129 "\n"
130 " camera_streaming -s 10.0.2.1 -n CAM1 -d LORES_IMG\n"
131 "\n"
132 " timeout -s SIGINT 60 camera_streaming ...\n"
133 "\n"
134 " camera_streaming -s 10.0.2.1 -n CAM1 -d HIRES_VID -c | ffmpeg -i - -c copy out.mp4\n"
135 "\n"
136 " camera_streaming -s 10.0.2.1 -n CAM1 -d HIRES_VID -q -c | \\\n"
137 " mpv --cache-secs=0 --profile=low-latency --demuxer-readahead-secs=0 --untimed --cache-pause=no -\n"
138 " camera_streaming -s 10.0.2.1 -n CAM1 -d HIRES_VID -q -c | \\\n"
139 " ffplay -fflags nobuffer -flags low_delay -framedrop -probesize 32 -analyzeduration 0 -\n"
140 "\n"
141 "\n", stdout);
142
143 }
144
145 bool HandleOption(const Option& option, const std::string& argument) final
146 {
147 bool ok = true;
148 switch (option.flag) {
149 case 's': sensor_ = argument; break;
150 case 'n': camera_ =
CamIdFromStrOr(argument.c_str(), CamId::UNSPECIFIED);
break;
153 case 'c': stdout_ = true; break;
154 default: ok = false; break;
155 }
156 return ok;
157 }
158
159 bool CheckOptions(const std::vector<std::string>& args) final
160 {
161 bool ok = true;
162 INFO(
"sensor = %s", sensor_.c_str());
165 INFO(
"rate = %d", rate_);
167 if (!args.empty() || sensor_.empty() || (camera_ == CamId::UNSPECIFIED) ||
168 (data_ == CamDataType::UNSPECIFIED) || (rate_ < 1)) {
169 ok = false;
170 }
171 return ok;
172 }
173};
174
175
176
177int main(int argc, char** argv)
178{
179#ifndef NDEBUG
181#endif
182
183#if !FPSDK_USE_FFMPEG
184 WARNING(
"!!!!! This app is not compiled with FFmpeg support !!!!!");
185#endif
186
187
188
189
190
191
192
193 Opts opts;
194 if (!opts.LoadFromArgv(argc, argv)) {
195 return false;
196 }
197 opts.LogVersion();
198
200
201
202 auto stream =
CreateCamStream({
"cam", opts.sensor_, opts.camera_, opts.data_, opts.rate_ });
203 if (!stream) {
204 return EXIT_FAILURE;
205 }
206
207
208 if (!stream->Connect()) {
209 return EXIT_FAILURE;
210 }
211
212#if FPSDK_USE_FFMPEG
213
215#endif
216
217
218 Stats stats;
219
220
223 std::size_t n_frames = 0;
224 char info[1000];
225 bool ok = true;
227 while (ok && !sigint.
ShouldAbort() && stream->NextFrame(data)) {
228
229
230
231
232
236 n_frames++;
237
238
239 std::size_t len = std::snprintf(info, sizeof(info),
240 "Frame %6" PRIuMAX ": %06" PRIuMAX " %-7s %-5s %-10s %-9s %" PRIu32 "x%" PRIu32 " %-7s %6" PRIuMAX " %s %4.1f",
245
246
247 stats.size((
double)data.
data_.size() / 1024.0);
248 stats.exp((double)t_expo.GetSec() * 1e3);
249
250
252 const double lat = (t_recv - t_data).GetSec() * 1e3;
253 len += std::snprintf(&info[len], sizeof(info) - len, " -- latency: %+5.1f", lat);
254 stats.lat(lat);
255 }
256
257
258
259
260
261 if (opts.stdout_) {
262 write(fileno(stdout), data.
data_.data(), data.
data_.size());
263 }
264
265#if FPSDK_USE_FFMPEG
266
268
271 }
272 if (decoder) {
274 const auto img = decoder->DecodeFrame(data.
data_);
275
276
277 if (img) {
278 const auto lat = tt.
Toc().
GetSec() * 1e3;
279 len += std::snprintf(&info[len], sizeof(info) - len, " -- decode: %+5.1f", lat);
280 stats.dec(lat);
281 } else {
282 len += std::snprintf(&info[len], sizeof(info) - len, " -- DECODE FAIL");
283 continue;
284 }
285
286
287
288
289 }
290 }
291#endif
292
294 }
296 const auto t_str = (t_end - t_start).GetSec();
297
298 stream->Disconnect();
299
300
301 NOTICE(
"Streamed %s %s %s %d for %.0fs (%" PRIuMAX
" frames)", opts.sensor_.c_str(),
CamIdToStr(opts.camera_),
302 CamDataTypeToStr(opts.data_), opts.rate_, t_str, boost::accumulators::count(stats.size));
303 if (boost::accumulators::count(stats.lat) > 10) {
304 INFO(
"Latency receiving data [ms]: mean %4.1f (std %4.1f) min/0.5/0.68/0.95/0.997/max %4.1f %4.1f %4.1f %4.1f %4.1f %4.1f",
305 boost::accumulators::mean(stats.lat), std::sqrt(boost::accumulators::variance(stats.lat)),
306 boost::accumulators::min(stats.lat), boost::accumulators::extended_p_square(stats.lat)[0],
307 boost::accumulators::extended_p_square(stats.lat)[1], boost::accumulators::extended_p_square(stats.lat)[2],
308 boost::accumulators::extended_p_square(stats.lat)[3], boost::accumulators::max(stats.lat));
309 }
310#if FPSDK_USE_FFMPEG
311 if (boost::accumulators::count(stats.dec) > 10) {
312 INFO(
"Latency decoding video [ms]: mean %4.1f (std %4.1f) min/0.5/0.68/0.95/0.997/max %4.1f %4.1f %4.1f %4.1f %4.1f %4.1f",
313 boost::accumulators::mean(stats.dec), std::sqrt(boost::accumulators::variance(stats.dec)),
314 boost::accumulators::min(stats.dec), boost::accumulators::extended_p_square(stats.dec)[0],
315 boost::accumulators::extended_p_square(stats.dec)[1], boost::accumulators::extended_p_square(stats.dec)[2],
316 boost::accumulators::extended_p_square(stats.dec)[3], boost::accumulators::max(stats.dec));
317 }
318#endif
319 if (boost::accumulators::count(stats.exp) > 10) {
320 INFO(
"Exposure duration [ms]: mean %4.1f (std %4.1f) min/0.5/0.68/0.95/0.997/max %4.1f %4.1f %4.1f %4.1f %4.1f %4.1f",
321 boost::accumulators::mean(stats.exp), std::sqrt(boost::accumulators::variance(stats.exp)),
322 boost::accumulators::min(stats.exp), boost::accumulators::extended_p_square(stats.exp)[0],
323 boost::accumulators::extended_p_square(stats.exp)[1], boost::accumulators::extended_p_square(stats.exp)[2],
324 boost::accumulators::extended_p_square(stats.exp)[3], boost::accumulators::max(stats.exp));
325 }
326 if ((boost::accumulators::count(stats.size) > 10) && (t_str > 1.0)) {
327 INFO(
"Data size per frame [KiB]: mean %4.0f (std %4.0f) min/0.5/0.68/0.95/0.997/max %4.0f %4.0f %4.0f %4.0f %4.0f %4.0f",
328 boost::accumulators::mean(stats.size), std::sqrt(boost::accumulators::variance(stats.size)),
329 boost::accumulators::min(stats.size), boost::accumulators::extended_p_square(stats.size)[0],
330 boost::accumulators::extended_p_square(stats.size)[1],
331 boost::accumulators::extended_p_square(stats.size)[2],
332 boost::accumulators::extended_p_square(stats.size)[3], boost::accumulators::max(stats.size));
333 const double avg = boost::accumulators::sum(stats.size) / t_str;
334
335 INFO(
"Average data rate [KiB/s]: %.0f (~%.1f%%GigE)", avg, avg / 120500.0 * 1e2);
336 }
337
338 return ok ? EXIT_SUCCESS : EXIT_FAILURE;
339}
340
341
Fixposition SDK: Utilities for apps.
Fixposition SDK: Camera types and utilities.
Helper to catch SIGINT (CTRL-c).
bool ShouldAbort()
Check if signal was raised and we should abort.
Helper to print a strack trace on SIGSEGV and SIGABRT.
static Duration FromNSec(const int64_t nsec)
Make Duration from nanoseconds.
double GetSec(const int prec=9) const
Get duration as seconds.
Helper to measure wallclock time.
Duration Toc(const bool reset=false)
Get elapsed wallclock time.
static Time FromPosixNs(const uint64_t posix_ns)
From POSIX nanoseconds (POSIX).
static Time FromClockRealtime()
From system clock current (now) system time (CLOCK_REALTIME).
Fixposition SDK: Logging.
#define NOTICE(...)
Print a notice message.
#define WARNING(...)
Print a warning message.
#define INFO(...)
Print a info message.
Camera types and utilities.
const char * CamIdToStr(const CamId camid)
Stringify camera ID enum.
const char * CamDataFmtToStr(const CamDataFmt fmt)
Stringify camera data format enum.
CamId CamIdFromStrOr(const char *str, const CamId def)
Convert camera ID string to enum.
const char * CamDataTypeToStr(const CamDataType type)
Stringify camera data type enum.
CamDataType
Camera data type (type of data).
const char * CamDataFrmToStr(const CamDataFrm frm)
Stringify camera data frame type enum.
@ H265_NAL
H.265 NAL (HEVC) (video/hevc).
@ I_FRAME
I-frame (= Horizon camsys MC_H26[45]_NALU_TYPE_I).
CamStreamPtr CreateCamStream(const CamStreamParams ¶ms)
Create a camera stream instance.
CamDataType CamDataTypeFromStrOr(const char *str, const CamDataType def)
Convert camera data type string to enum.
constexpr const char * ToStr(const bool value)
Stringify value (bool).
bool StrToValue(const std::string &str, int8_t &value)
Convert string to value (int8_t).
@ RGB24
Packed RGB 8:8:8, 24bpp, RGBRGB... (= FFmpeg AV_PIX_FMT_RGB24).
@ H265
H.265 High Efficiency Video Coding (HEVC).
std::unique_ptr< VideoFrameDecoder > VideoFrameDecoderPtr
Pointer to a VideoFrameDecoder instance, see CreateVideoFrameDecoder().
VideoFrameDecoderPtr CreateVideoFrameDecoder(const VideoDecoderParams ¶ms)
Create a video frame decoder.
Fixposition SDK: String utilities.
Camera data from CamStream (or from FPL, see fpl::CamData).
uint64_t ts_
Camera image time (middle of exposure) [CLOCK_REALTIME ns] (0 = invalid).
CamId cam_id_
= meta_.cam_id_ as enum (if value in range)
uint32_t dt_
Exposure time (duration) [ns] (0 = not available).
CamDataFmt fmt_
= meta_.fmt_ as enum (if value in range)
uint64_t seq_
Sequence number.
CamDataType type_
= meta_.type_ as enum (if value in range)
bool valid_
Data valid, successfully extracted from message.
uint32_t height_
Height of the fram [px].
std::vector< uint8_t > data_
Image/frame data, contents depends on fmt_ etc.
uint32_t width_
Width of the frame [px].
CamDataFrm frm_
= meta_.frm_ as enum (if value in range)
Fixposition SDK: Time utilities.
Fixposition SDK: Common types and type helpers.
Fixposition SDK: Video frame decoding.