Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 70 additions & 32 deletions examples/camera_streaming/camera_streaming.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/

/* LIBC/STL */
#include <array>
#include <cmath>
#include <cstdint>
#include <cstring>
Expand All @@ -32,7 +33,6 @@
#include <boost/accumulators/statistics/extended_p_square.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/median.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/sum.hpp>
Expand Down Expand Up @@ -60,6 +60,28 @@ using namespace fpsdk::common::video;

// ---------------------------------------------------------------------------------------------------------------------

// Statistics
struct Stats
{
// clang-format off
static constexpr std::array<double, 4> PROB = {{ 0.5, 0.68, 0.95, 0.997 }};
using Accumulator = boost::accumulators::accumulator_set<double, boost::accumulators::stats<
boost::accumulators::tag::count,
boost::accumulators::tag::mean,
boost::accumulators::tag::min,
boost::accumulators::tag::max,
boost::accumulators::tag::sum,
boost::accumulators::tag::variance,
boost::accumulators::tag::extended_p_square>>;
Accumulator lat { boost::accumulators::extended_p_square_probabilities = PROB }; // Latency receiving the data
Accumulator size { boost::accumulators::extended_p_square_probabilities = PROB }; // Size of the data
Accumulator exp { boost::accumulators::extended_p_square_probabilities = PROB }; // Exposure duration
Accumulator dec { boost::accumulators::extended_p_square_probabilities = PROB }; // Time decoding video
// clang-format on
};

// ---------------------------------------------------------------------------------------------------------------------

int main(int argc, char** argv)
{
#ifndef NDEBUG
Expand All @@ -82,7 +104,8 @@ int main(int argc, char** argv)
"1 for encoded video)\n"
"Examples:\n"
" camera_streaming 10.0.2.1 CAM1 HIRES_VID 1\n"
" camera_streaming 10.0.2.1 CAM1 HIRES_IMG 10");
" camera_streaming 10.0.2.1 CAM1 HIRES_IMG 10\n"
" timeout -s SIGINT 60 camera_streaming ...");
return EXIT_FAILURE;
}

Expand Down Expand Up @@ -115,28 +138,24 @@ int main(int argc, char** argv)
VideoFrameDecoderPtr decoder;

// Statistics
using Accumulator = boost::accumulators::accumulator_set<double,
boost::accumulators::stats<boost::accumulators::tag::count, boost::accumulators::tag::mean,
boost::accumulators::tag::median, boost::accumulators::tag::min, boost::accumulators::tag::max,
boost::accumulators::tag::sum, boost::accumulators::tag::variance>>;
Accumulator acc_lat; // Latency receiving the data
Accumulator acc_size; // Size of the data
Accumulator acc_dec; // Latency decoding video
Stats stats;

// Stream data until we get SIGINT (CTRL-c)
SigIntHelper sigint;
CamData data;
std::size_t n_frames = 0;
char info[1000];
bool ok = true;
TicToc tt_str;
const auto t_start = Time::FromClockRealtime();
while (ok && !sigint.ShouldAbort() && stream->NextFrame(data)) {
// We'll calculate the latency from the time we received the data (now) and the data reference time. Note that
// this requires the computer that runs this program being timesynced with the sensor (and the sensor being
// timesynced, too, ideally with GNSS). Any error in timesync of the sensor or the computer affects the measured
// latency (could be to the better or to the worse).
// latency (could be to the better or to the worse). Also, we'll use the end of exposure as the reference time
// instead of the middle of exposure.
const auto t_recv = Time::FromClockRealtime();
const auto t_data = Time::FromPosixNs(data.ts_);
const auto t_expo = Duration::FromNSec(data.dt_);
const auto t_data = Time::FromPosixNs(data.ts_) + (t_expo * 0.5);
n_frames++;

// Generic info
Expand All @@ -145,13 +164,16 @@ int main(int argc, char** argv)
n_frames, data.valid_ ? "valid" : "invalid", CamIdToStr(data.cam_id_), CamDataTypeToStr(data.type_),
CamDataFmtToStr(data.fmt_), CamDataFrmToStr(data.frm_), data.data_.size(), t_data.StrIsoTime(3).c_str(),
Duration::FromNSec(data.dt_).GetSec() * 1e3);
acc_size((double)data.data_.size() / 1024.0); // [KiB]

// Update statistics
stats.size((double)data.data_.size() / 1024.0); // [KiB]
stats.exp((double)t_expo.GetSec() * 1e3); // [ms]

// Latency
if (data.valid_) {
const double lat = (t_recv - t_data).GetSec() * 1e3; // [ms]
len += std::snprintf(&info[len], sizeof(info) - len, " -- latency: %+5.1f", lat);
acc_lat(lat);
stats.lat(lat);
}

// Decode video
Expand All @@ -174,36 +196,52 @@ int main(int argc, char** argv)

// Decoding adds latency
const auto lat = tt.Toc().GetSec() * 1e3; // [ms]
len += std::snprintf(&info[len], sizeof(info) - len, " -- decode: %+4.1f", lat);
acc_dec(lat);
len += std::snprintf(&info[len], sizeof(info) - len, " -- decode: %+5.1f", lat);
stats.dec(lat);
}
}

INFO("%s", info);
}
const double t_str = tt_str.Toc().GetSec();
const auto t_end = Time::FromClockRealtime();
const auto t_str = (t_end - t_start).GetSec();

stream->Disconnect();

// Print stats
NOTICE("Streamed %s %s %s %d for %.0fs (%" PRIuMAX " frames)", sensor.c_str(), CamIdToStr(cam_id),
CamDataTypeToStr(type), rate, t_str, boost::accumulators::count(acc_size));
if (boost::accumulators::count(acc_lat) > 10) {
INFO("Latency receiving data [ms]: mean %4.1f (std %4.1f) min/median/max %4.1f %4.1f %4.1f",
boost::accumulators::mean(acc_lat), std::sqrt(boost::accumulators::variance(acc_lat)),
boost::accumulators::min(acc_lat), boost::accumulators::median(acc_lat), boost::accumulators::max(acc_lat));
CamDataTypeToStr(type), rate, t_str, boost::accumulators::count(stats.size));
if (boost::accumulators::count(stats.lat) > 10) { // clang-format off
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", // clang-format on
boost::accumulators::mean(stats.lat), std::sqrt(boost::accumulators::variance(stats.lat)),
boost::accumulators::min(stats.lat), boost::accumulators::extended_p_square(stats.lat)[0],
boost::accumulators::extended_p_square(stats.lat)[1], boost::accumulators::extended_p_square(stats.lat)[2],
boost::accumulators::extended_p_square(stats.lat)[3], boost::accumulators::max(stats.lat));
}
if (boost::accumulators::count(stats.dec) > 10) { // clang-format off
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", // clang-format on
boost::accumulators::mean(stats.dec), std::sqrt(boost::accumulators::variance(stats.dec)),
boost::accumulators::min(stats.dec), boost::accumulators::extended_p_square(stats.dec)[0],
boost::accumulators::extended_p_square(stats.dec)[1], boost::accumulators::extended_p_square(stats.dec)[2],
boost::accumulators::extended_p_square(stats.dec)[3], boost::accumulators::max(stats.dec));
}
if (boost::accumulators::count(acc_dec) > 10) {
INFO("Latency decoding video [ms]: mean %4.1f (std %4.1f) min/median/max %4.1f %4.1f %4.1f",
boost::accumulators::mean(acc_dec), std::sqrt(boost::accumulators::variance(acc_dec)),
boost::accumulators::min(acc_dec), boost::accumulators::median(acc_dec), boost::accumulators::max(acc_dec));
if (boost::accumulators::count(stats.exp) > 10) { // clang-format off
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", // clang-format on
boost::accumulators::mean(stats.exp), std::sqrt(boost::accumulators::variance(stats.exp)),
boost::accumulators::min(stats.exp), boost::accumulators::extended_p_square(stats.exp)[0],
boost::accumulators::extended_p_square(stats.exp)[1], boost::accumulators::extended_p_square(stats.exp)[2],
boost::accumulators::extended_p_square(stats.exp)[3], boost::accumulators::max(stats.exp));
}
if ((boost::accumulators::count(acc_size) > 10) && (t_str > 1.0)) {
INFO("Data size per frame [KiB]: mean %4.0f (std %4.0f) min/median/max %4.0f %4.0f %4.0f",
boost::accumulators::mean(acc_size), std::sqrt(boost::accumulators::variance(acc_size)),
boost::accumulators::min(acc_size), boost::accumulators::median(acc_size),
boost::accumulators::max(acc_size));
INFO("Average data rate [KiB/s]: %.0f", boost::accumulators::sum(acc_size) / t_str);
if ((boost::accumulators::count(stats.size) > 10) && (t_str > 1.0)) { // clang-format off
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", // clang-format on
boost::accumulators::mean(stats.size), std::sqrt(boost::accumulators::variance(stats.size)),
boost::accumulators::min(stats.size), boost::accumulators::extended_p_square(stats.size)[0],
boost::accumulators::extended_p_square(stats.size)[1],
boost::accumulators::extended_p_square(stats.size)[2],
boost::accumulators::extended_p_square(stats.size)[3], boost::accumulators::max(stats.size));
const double avg = boost::accumulators::sum(stats.size) / t_str;
// Assuming GibE link TCP socket ~115MB/s = ~120500 KiB/s
INFO("Average data rate [KiB/s]: %.0f (~%.1f%%GigE)", avg, avg / 120500.0 * 1e2);
}

return ok ? EXIT_SUCCESS : EXIT_FAILURE;
Expand Down
Loading