in extensions/usb-camera/GetUSBCamera.cpp [95:313]
void GetUSBCamera::onSchedule(core::ProcessContext *context, core::ProcessSessionFactory *session_factory) {
std::lock_guard<std::recursive_mutex> lock(*dev_access_mtx_);
double default_fps = 1;
double target_fps = default_fps;
std::string conf_fps_str;
context->getProperty("FPS", conf_fps_str);
if (conf_fps_str.empty()) {
logger_->log_info("FPS property was not set; using default %f", default_fps);
} else {
try {
target_fps = std::stod(conf_fps_str);
} catch (std::invalid_argument &e) {
logger_->log_error("Could not parse configured FPS value (will use default %f): %s", default_fps, conf_fps_str);
}
}
uint16_t target_width = 0;
uint16_t target_height = 0;
std::string conf_width_str;
context->getProperty("Width", conf_width_str);
if (!conf_width_str.empty()) {
auto target_width_ul = std::stoul(conf_width_str);
if (target_width_ul > UINT16_MAX) {
logger_->log_error("Configured target width %s is out of range", conf_width_str);
} else {
target_width = static_cast<uint16_t>(target_width_ul);
}
logger_->log_info("Using configured target width: %i", target_width);
}
std::string conf_height_str;
context->getProperty("Height", conf_height_str);
if (!conf_height_str.empty()) {
auto target_height_ul = std::stoul(conf_height_str);
if (target_height_ul > UINT16_MAX) {
logger_->log_error("Configured target height %s is out of range", conf_height_str);
} else {
target_height = static_cast<uint16_t>(target_height_ul);
}
logger_->log_info("Using configured target height: %i", target_height);
}
std::string conf_format_str;
context->getProperty("Format", conf_format_str);
int usb_vendor_id;
std::string conf_vendor_id;
context->getProperty("USB Vendor ID", conf_vendor_id);
std::stringstream(conf_vendor_id) >> std::hex >> usb_vendor_id;
logger_->log_info("Using USB Vendor ID: %x", usb_vendor_id);
int usb_product_id;
std::string conf_product_id;
context->getProperty("USB Product ID", conf_product_id);
std::stringstream(conf_product_id) >> std::hex >> usb_product_id;
logger_->log_info("Using USB Product ID: %x", usb_product_id);
const char *usb_serial_no = nullptr;
std::string conf_serial;
context->getProperty("USB Serial No.", conf_serial);
if (!conf_serial.empty()) {
usb_serial_no = conf_serial.c_str();
logger_->log_info("Using USB Serial No.: %s", conf_serial);
}
cleanupUvc();
logger_->log_info("Beginning to capture frames from USB camera");
uvc_stream_ctrl_t ctrl{};
uvc_error_t res;
res = uvc_init(&ctx_, nullptr);
if (res < 0) {
logger_->log_error("Failed to initialize UVC service context");
ctx_ = nullptr;
return;
}
logger_->log_debug("UVC initialized");
// Locate device
res = uvc_find_device(ctx_, &dev_, usb_vendor_id, usb_product_id, usb_serial_no);
if (res < 0) {
logger_->log_error("Unable to find device: %s", uvc_strerror(res));
dev_ = nullptr;
} else {
logger_->log_info("Device found");
// Open the device
res = uvc_open(dev_, &devh_);
if (res < 0) {
logger_->log_error("Unable to open device: %s", uvc_strerror(res));
devh_ = nullptr;
} else {
logger_->log_info("Device opened");
// Iterate resolutions & framerates >= context fps, or nearest
uint16_t width = 0;
uint16_t height = 0;
uint32_t max_size = 0;
uint32_t fps = 0;
double min_diff = -1;
double current_diff = -1;
double current_width_diff = -1;
double current_height_diff = -1;
for (auto fmt_desc = uvc_get_format_descs(devh_); fmt_desc; fmt_desc = fmt_desc->next) {
uvc_frame_desc_t *frame_desc;
switch (fmt_desc->bDescriptorSubtype) {
case UVC_VS_FORMAT_UNCOMPRESSED:
case UVC_VS_FORMAT_FRAME_BASED:
for (frame_desc = fmt_desc->frame_descs; frame_desc; frame_desc = frame_desc->next) {
uint32_t frame_fps = 10000000 / frame_desc->dwDefaultFrameInterval;
logger_->log_info("Discovered supported format %ix%i @ %i",
frame_desc->wWidth,
frame_desc->wHeight,
frame_fps);
if (target_height > 0 && target_width > 0) {
if (frame_fps >= target_fps) {
current_width_diff = abs(frame_desc->wWidth - target_width) / static_cast<double>(target_width);
logger_->log_debug("Current frame format width difference is %f", current_width_diff);
current_height_diff = abs(frame_desc->wHeight - target_height) / static_cast<double>(target_height);
logger_->log_debug("Current frame format height difference is %f", current_height_diff);
current_diff = (current_width_diff + current_height_diff) / 2;
logger_->log_debug("Current frame format difference is %f", current_diff);
if (min_diff < 0 || current_diff < min_diff) {
logger_->log_info("Format %ix%i @ %i is now closest to target",
frame_desc->wWidth,
frame_desc->wHeight,
frame_fps);
width = frame_desc->wWidth;
height = frame_desc->wHeight;
max_size = frame_desc->dwMaxVideoFrameBufferSize;
fps = frame_fps;
min_diff = current_diff;
}
}
} else {
if (frame_desc->dwMaxVideoFrameBufferSize > max_size && frame_fps >= target_fps) {
width = frame_desc->wWidth;
height = frame_desc->wHeight;
max_size = frame_desc->dwMaxVideoFrameBufferSize;
fps = frame_fps;
}
}
}
break;
case UVC_VS_FORMAT_MJPEG:
logger_->log_info("Skipping MJPEG frame formats");
break;
default:logger_->log_warn("Found unknown format");
}
}
if (fps == 0) {
logger_->log_error("Could not find suitable frame format from device. "
"Try changing configuration (lower FPS) or device.");
return;
}
logger_->log_info("Negotiating stream profile (looking for %dx%d @ %d)", width, height, fps);
res = uvc_get_stream_ctrl_format_size(devh_, &ctrl, UVC_FRAME_FORMAT_UNCOMPRESSED, width, height, fps);
if (res < 0) {
logger_->log_error("Failed to find a matching stream profile: %s", uvc_strerror(res));
} else {
cb_data_.session_factory = session_factory;
if (frame_buffer_ != nullptr) {
uvc_free_frame(frame_buffer_);
}
frame_buffer_ = uvc_allocate_frame(width * height * 3);
if (!frame_buffer_) {
printf("unable to allocate bgr frame!");
logger_->log_error("Unable to allocate RGB frame");
return;
}
cb_data_.frame_buffer = frame_buffer_;
cb_data_.context = context;
cb_data_.png_write_mtx = png_write_mtx_;
cb_data_.dev_access_mtx = dev_access_mtx_;
cb_data_.logger = logger_;
cb_data_.format = conf_format_str;
cb_data_.device_width = width;
cb_data_.device_height = height;
cb_data_.device_fps = fps;
cb_data_.target_fps = target_fps;
cb_data_.last_frame_time = std::chrono::steady_clock::time_point();
res = uvc_start_streaming(devh_, &ctrl, onFrame, &cb_data_, 0);
if (res < 0) {
logger_->log_error("Unable to start streaming: %s", uvc_strerror(res));
} else {
logger_->log_info("Streaming...");
// Enable auto-exposure
uvc_set_ae_mode(devh_, 1);
}
}
}
}
}