27 #define PERROR(x) LERROR("In file " << JEVOIS_ENGINE_CONFIG_FILE << ':' << linenum << ": " << x)
30 std::string jevois::VideoMapping::path()
const
32 return JEVOIS_MODULE_PATH
"/" + vendor +
'/' + modulename;
36 std::string jevois::VideoMapping::sopath(
bool delete_old_versions)
const
38 if (ispython)
return JEVOIS_MODULE_PATH
"/" + vendor +
'/' + modulename +
'/' + modulename +
".py";
41 std::filesystem::path
const dir = JEVOIS_MODULE_PATH
"/" + vendor +
'/' + modulename;
42 std::filesystem::path
const stem = modulename +
".so";
45 for (
auto const & entry : std::filesystem::directory_iterator(dir))
46 if (entry.path().stem() == stem)
47 try { ver = std::max(ver, std::stoi(entry.path().extension().string().substr(1))); }
52 std::filesystem::path
const latest = (dir / stem).
string() +
'.' +
std::to_string(ver);
54 if (delete_old_versions)
55 for (
auto const & entry : std::filesystem::directory_iterator(dir))
56 if (entry.path().stem() == stem && entry.path() != latest)
57 std::filesystem::remove(entry.path());
59 return latest.string();
62 return (dir / stem).string();
66 std::string jevois::VideoMapping::srcpath()
const
68 if (ispython)
return JEVOIS_MODULE_PATH
"/" + vendor +
'/' + modulename +
'/' + modulename +
".py";
69 else return JEVOIS_MODULE_PATH
"/" + vendor +
'/' + modulename +
'/' + modulename +
".C";
73 std::string jevois::VideoMapping::cmakepath()
const
75 return JEVOIS_MODULE_PATH
"/" + vendor +
'/' + modulename +
"/CMakeLists.txt";
79 std::string jevois::VideoMapping::modinfopath()
const
81 return JEVOIS_MODULE_PATH
"/" + vendor +
'/' + modulename +
"/modinfo.html";
85 unsigned int jevois::VideoMapping::osize()
const
89 unsigned int jevois::VideoMapping::csize()
const
93 unsigned int jevois::VideoMapping::c2size()
const
97 float jevois::VideoMapping::uvcToFps(
unsigned int interval)
100 return float(1000000000U / interval) * 0.01F;
104 unsigned int jevois::VideoMapping::fpsToUvc(
float fps)
106 return (
unsigned int)(10000000.0F / fps + 0.499F);
110 float jevois::VideoMapping::v4l2ToFps(
struct v4l2_fract
const & interval)
113 return float(interval.denominator * 100U / interval.numerator) * 0.01F;
117 struct v4l2_fract
jevois::VideoMapping::fpsToV4l2(float fps)
119 return { 100U, (
unsigned int)(fps * 100.0
F) };
123 std::string jevois::VideoMapping::ostr()
const
125 std::ostringstream ss;
126 ss <<
jevois::fccstr(ofmt) <<
' ' << ow <<
'x' << oh <<
" @ " << ofps <<
"fps";
131 std::string jevois::VideoMapping::cstr()
const
133 std::ostringstream ss;
134 ss <<
jevois::fccstr(cfmt) <<
' ' << cw <<
'x' << ch <<
" @ " << cfps <<
"fps";
139 std::string jevois::VideoMapping::c2str()
const
141 std::ostringstream ss;
142 ss <<
jevois::fccstr(c2fmt) <<
' ' << c2w <<
'x' << c2h <<
" @ " << cfps <<
"fps";
147 std::string jevois::VideoMapping::cstrall()
const
149 std::string ret = cstr();
150 if (crop == jevois::CropType::CropScale) ret +=
" + " + c2str();
155 std::string jevois::VideoMapping::str()
const
157 std::ostringstream ss;
159 ss <<
"OUT: " << ostr() <<
" CAM: " << cstr();
160 if (crop == jevois::CropType::CropScale) ss <<
" CAM2: " << c2str();
162 ss <<
" MOD: " << vendor <<
':' << modulename <<
' ' << (ispython ?
"Python" :
"C++");
168 std::string jevois::VideoMapping::menustr()
const
170 std::ostringstream ss;
172 ss << modulename << (ispython ?
" (Py)" :
" (C++)");
173 ss <<
" CAM: " << cstr();
174 if (crop == jevois::CropType::CropScale) ss <<
" + " << c2str();
180 std::string jevois::VideoMapping::menustr2()
const
182 std::ostringstream ss;
184 ss << modulename << (ispython ?
" (Py)" :
" (C++)");
185 ss <<
" CAM: " << cstr();
186 if (crop == jevois::CropType::CropScale) ss <<
" + " << c2str();
191 case 0: ss <<
", OUT: None (headless)";
break;
192 default: ss <<
", OUT: " << ostr() <<
' ';
199 bool jevois::VideoMapping::hasSameSpecsAs(VideoMapping
const & other)
const
201 return (ofmt == other.ofmt && ow == other.ow && oh == other.oh && std::abs(ofps - other.ofps) < 0.01F &&
202 cfmt == other.cfmt && cw == other.cw && ch == other.ch && std::abs(cfps - other.cfps) < 0.01F &&
203 crop == other.crop &&
204 (crop != jevois::CropType::CropScale ||
205 (c2fmt == other.c2fmt && c2w == other.c2w && c2h == other.c2h && std::abs(cfps - other.cfps) < 0.01F)));
209 bool jevois::VideoMapping::isSameAs(VideoMapping
const & other)
const
211 return (hasSameSpecsAs(other) && wdr == other.wdr && vendor == other.vendor && modulename == other.modulename &&
212 ispython == other.ispython);
216 std::ostream & jevois::operator<<(std::ostream & out, jevois::VideoMapping
const & m)
218 out <<
jevois::fccstr(m.ofmt) <<
' ' << m.ow <<
' ' << m.oh <<
' ' << m.ofps <<
' ';
220 if (m.wdr != jevois::WDRtype::Linear)
225 case jevois::CropType::Scale:
228 out << m.crop <<
':';
break;
229 case jevois::CropType::CropScale:
230 out << m.crop <<
'=' <<
jevois::fccstr(m.c2fmt) <<
'@' << m.c2w <<
'x' << m.c2h <<
':';
break;
233 out <<
jevois::fccstr(m.cfmt) <<
' ' << m.cw <<
' ' << m.ch <<
' ' << m.cfps <<
' '
234 << m.vendor <<
' ' << m.modulename;
241 int parse_relative_dim(std::string
const & str,
int c)
243 if (str.empty())
throw std::range_error(
"Invalid empty output width");
244 if (str[0] ==
'+')
return c + std::stoi(str.substr(1));
245 else if (str[0] ==
'-')
return c - std::stoi(str.substr(1));
246 return std::stoi(str);
249 void parse_cam_format(std::string
const & str,
unsigned int & fmt, jevois::WDRtype & wdr, jevois::CropType & crop,
250 unsigned int & c2fmt,
unsigned int & c2w,
unsigned int & c2h)
253 wdr = jevois::WDRtype::Linear;
254 crop = jevois::CropType::Scale;
258 if (tok.empty())
throw std::range_error(
"Empty camera format is not allowed");
260 for (std::string & t : tok)
263 try { wdr = jevois::from_string<jevois::WDRtype>(t);
continue; }
catch (...) { }
267 if (ttok.empty())
throw std::range_error(
"Invalid empty camera format modifier: " + t);
271 crop = jevois::from_string<jevois::CropType>(ttok[0]);
276 case jevois::CropType::Scale:
if (ttok.size() == 1)
continue;
break;
277 case jevois::CropType::CropScale:
278 if (ttok.size() == 4)
281 c2w = std::stoi(ttok[2]);
282 c2h = std::stoi(ttok[3]);
288 throw std::range_error(
"Invalid camera format modifier [" + t +
289 "] - must be Linear|DOL or Crop|Scale|CropScale=FCC@WxH");
295 std::istream & jevois::operator>>(std::istream & in, jevois::VideoMapping & m)
297 std::string of, cf, ows, ohs;
298 in >> of >> ows >> ohs >> m.ofps >> cf >> m.cw >> m.ch >> m.cfps >> m.vendor >> m.modulename;
302 m.ow = parse_relative_dim(ows, m.cw);
303 m.oh = parse_relative_dim(ohs, m.ch);
308 parse_cam_format(cf, m.cfmt, m.wdr, m.crop, m.c2fmt, m.c2w, m.c2h);
316 std::vector<jevois::VideoMapping> jevois::loadVideoMappings(jevois::CameraSensor s,
size_t & defidx,
bool checkso,
319 std::ifstream ifs(JEVOIS_ENGINE_CONFIG_FILE);
320 if (ifs.is_open() ==
false)
LFATAL(
"Could not open [" << JEVOIS_ENGINE_CONFIG_FILE <<
']');
321 return jevois::videoMappingsFromStream(s, ifs, defidx, checkso, hasgui);
325 std::vector<jevois::VideoMapping> jevois::videoMappingsFromStream(jevois::CameraSensor s, std::istream & is,
326 size_t & defidx,
bool checkso,
bool hasgui)
329 std::vector<jevois::VideoMapping> mappings;
330 jevois::VideoMapping defmapping = { };
332 for (std::string line; std::getline(is, line); ++linenum)
335 if (tok.empty())
continue;
336 if (tok.size() == 1 && tok[0].empty())
continue;
337 if (tok[0][0] ==
'#')
continue;
338 if (tok.size() < 10) {
PERROR(
"Found " << tok.size() <<
" tokens instead of >= 10 -- SKIPPING");
continue; }
340 jevois::VideoMapping m;
344 m.ofps = std::stof(tok[3]);
346 parse_cam_format(tok[4], m.cfmt, m.wdr, m.crop, m.c2fmt, m.c2w, m.c2h);
347 m.cw = std::stoi(tok[5]);
348 m.ch = std::stoi(tok[6]);
349 m.cfps = std::stof(tok[7]);
351 m.ow = parse_relative_dim(tok[1], m.cw);
352 m.oh = parse_relative_dim(tok[2], m.ch);
354 catch (std::exception
const & e) {
PERROR(
"Skipping entry because of parsing error: " << e.what());
continue; }
355 catch (...) {
PERROR(
"Skipping entry because of parsing errors");
continue; }
358 m.modulename = tok[9];
361 try { m.setModuleType(); }
365 {
PERROR(
"No .so|.py found for " << m.vendor <<
'/' << m.modulename <<
" -- SKIPPING.");
continue; }
370 {
PERROR(
"Camera video format [" << m.cstr() <<
"] not supported by sensor -- SKIPPING.");
continue; }
374 {
PERROR(
"Graphical user interface not available or disabled -- SKIPPING");
continue; }
379 {
PERROR(
"GUI output only supported on JeVois-Pro -- SKIPPING");
continue; }
381 #ifndef JEVOIS_PLATFORM
384 {
PERROR(
"Crop or Crop+Scale camera input only supported on JeVois-Pro platform -- SKIPPING");
continue; }
385 #endif // JEVOIS_PLATFORM
393 if (defmapping.cfmt == 0) { defmapping = m;
LINFO(
"Default in videomappings.cfg is " << m.str()); }
394 if (tok.size() > 11 && tok[11][0] !=
'#')
PERROR(
"Extra garbage after 11th token ignored");
396 else if (tok[10][0] !=
'#')
PERROR(
"Extra garbage after 10th token ignored");
399 mappings.push_back(m);
404 std::sort(mappings.begin(), mappings.end(),
405 [=](jevois::VideoMapping
const & a, jevois::VideoMapping
const & b)
408 if (a.ofmt < b.ofmt) return true;
409 if (a.ofmt == b.ofmt) {
410 if (a.ow > b.ow) return true;
412 if (a.oh > b.oh) return true;
414 if (a.ofps > b.ofps) return true;
415 if (std::abs(a.ofps - b.ofps) < 0.01F) {
419 if (a.ofmt != 0 && a.ofmt != JEVOISPRO_FMT_GUI)
420 PERROR(
"WARNING: Two modes have identical output format: " << a.ostr());
423 if (a.cfmt < b.cfmt) return true;
424 if (a.cfmt == b.cfmt) {
425 if (a.cw > b.cw) return true;
427 if (a.ch > b.ch) return true;
429 if (a.cfps > b.cfps) return true;
444 if (checkso ==
false) { defidx = 0;
return mappings; }
447 if (mappings.empty() || mappings.back().ofmt == 0 || mappings.back().ofmt ==
JEVOISPRO_FMT_GUI)
449 PERROR(
"No valid video mapping with UVC output found -- INSERTING A DEFAULT ONE");
450 jevois::VideoMapping m;
451 m.ofmt = V4L2_PIX_FMT_YUYV; m.ow = 640; m.oh = 480; m.ofps = 30.0F;
452 m.cfmt = V4L2_PIX_FMT_YUYV; m.cw = 640; m.ch = 480; m.cfps = 30.0F;
453 m.vendor =
"JeVois"; m.modulename =
"PassThrough"; m.ispython =
false;
457 mappings.push_back(m);
464 auto a = mappings.begin(), b = a + 1;
465 while (b != mappings.end())
468 if (a->isSameAs(*b)) { b = mappings.erase(b);
continue; }
470 else if (b->ofmt != 0 && b->ofmt !=
JEVOISPRO_FMT_GUI && a->ofmt == b->ofmt && a->ow == b->ow && a->oh == b->oh)
472 if (std::abs(a->ofps - b->ofps) < 0.01F) b->ofps -= 1.0F;
473 else if (b->ofps > a->ofps) b->ofps = a->ofps - 1.0F;
480 if (defmapping.cfmt == 0)
482 LERROR(
"No default video mapping provided, using first one with UVC output");
483 for (
size_t i = 0; i < mappings.size(); ++i)
if (mappings[i].ofmt) { defidx = i;
break; }
489 for (
size_t i = 0; i < mappings.size(); ++i)
if (mappings[i].isSameAs(defmapping)) { defidx = i;
break; }
496 unsigned int ofmt = ~0U, ow = ~0U, oh = ~0U, iformat = 0, iframe = 0;
497 for (jevois::VideoMapping & m : mappings)
499 if (m.ofmt == 0 || m.ofmt ==
JEVOISPRO_FMT_GUI) { m.uvcformat = 0; m.uvcframe = 0;
LDEBUG(m.str());
continue; }
500 if (m.ofmt != ofmt) { ofmt = m.ofmt; ow = ~0U; oh = ~0U; ++iformat; iframe = 0; }
501 if (m.ow != ow || m.oh != oh) { ow = m.ow; oh = m.oh; ++iframe; }
502 m.uvcformat = iformat; m.uvcframe = iframe;
510 bool jevois::VideoMapping::match(
unsigned int oformat,
unsigned int owidth,
unsigned int oheight,
511 float oframespersec)
const
513 if (ofmt == oformat && ow == owidth && oh == oheight && (std::abs(ofps - oframespersec) < 0.1
F))
return true;
518 void jevois::VideoMapping::setModuleType()
522 std::string sopa = sopath();
523 std::ifstream testifs(sopa);
524 if (testifs.is_open() ==
false)
527 ispython =
true; sopa = sopath();
528 std::ifstream testifs2(sopa);
529 if (testifs2.is_open() ==
false)
throw std::runtime_error(
"Could not open module file " + sopa +
"|.so");