#include "HidDevicePortGmsl.hpp"
#include "usb/uvc/ObV4lGmslHostProtocolTypes.hpp"
#include "logger/Logger.hpp"
#include "utils/Utils.hpp"
#include "exception/ObException.hpp"
#include "frame/FrameFactory.hpp"
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/uvcvideo.h>
#include <linux/videodev2.h>
#include <linux/usb/video.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <random>
namespace libobsensor {
const int IMU_FRAME_MAX_NUM = 9; const int IMU_RETRY_READ_NUM = 30;
const int POLL_INTERVAL = 10;
int calculateImuPollInterval(int mImuReadFps) {
int delayTime = 0;
delayTime = (1000 / mImuReadFps) * (IMU_FRAME_MAX_NUM - 4);
LOG_DEBUG("calculateImuPollInterval:{}, mImuReadFps:{}", delayTime, mImuReadFps);
if(delayTime < 5)
delayTime = 5;
return delayTime;
}
int calculateImuPollInterval500HZ(int mImuReadFps) {
int delayTime = 0;
delayTime = (1000 / mImuReadFps) * (IMU_FRAME_MAX_NUM - 1) - 10;
LOG_DEBUG("calculateImuPollInterval500HZ:{}, mImuReadFps:{}", delayTime, mImuReadFps);
if(delayTime < 5)
delayTime = 5;
return delayTime;
}
void imuSleepMs(int mSleepMs) {
std::this_thread::sleep_for(std::chrono::milliseconds(mSleepMs));
}
HidDevicePortGmsl::HidDevicePortGmsl(std::shared_ptr<const USBSourcePortInfo> portInfo) : portInfo_(portInfo), isStreaming_(false), frameQueue_(10) {
imu_fd_ = open(portInfo_->infName.c_str(), O_RDWR);
if(imu_fd_ < 0) {
throw pal_exception(utils::string::to_string() << "HidDevicePortGmsl() openDev failed, errno: " << errno << " " << strerror(errno));
}
LOG_DEBUG("HidDevicePortGmsl() imu_fd_:{}", imu_fd_);
}
HidDevicePortGmsl::~HidDevicePortGmsl() noexcept {
LOG_DEBUG("~HidDevicePortGmsl()");
stopStream();
if(imu_fd_ >= 0) {
close(imu_fd_);
}
imuSleepMs(100); LOG_DEBUG("HidDevicePortGmsl destroyed");
}
std::shared_ptr<const SourcePortInfo> HidDevicePortGmsl::getSourcePortInfo() const {
return portInfo_;
}
void HidDevicePortGmsl::startStream(MutableFrameCallback callback) {
LOG_DEBUG("Try to start stream");
imuRetryReadNum = IMU_RETRY_READ_NUM; imuReadFps = 200; imuPollInterval = 25;
if(isStreaming_) {
throw wrong_api_call_sequence_exception("HidDevicePort::startStream() called while streaming");
}
isStreaming_ = true;
frameQueue_.start(callback);
pollThread_ = std::thread([this]() {
while(true) {
if(!isStreaming_) {
break;
}
if(imuRetryReadNum > 0) {
imuReadFps = getImuFps();
imuPollInterval = (imuReadFps >= 500) ? 5 : calculateImuPollInterval(imuReadFps);
LOG_DEBUG("get current imuReadFps:{}, imuPollInterval:{}ms, imuRetryReadNum:{} ", imuReadFps.load(), imuPollInterval.load(),
imuRetryReadNum.load());
imuRetryReadNum--;
}
imuSleepMs(imuPollInterval);
pollData();
}
});
LOG_DEBUG("HidDevicePortGmsl::startCapture done");
}
void HidDevicePortGmsl::stopStream() {
if(!isStreaming_) {
LOG_WARN("stopStream() called while not streaming");
return;
}
LOG_DEBUG("stopCapture start");
isStreaming_ = false;
if(pollThread_.joinable()) {
pollThread_.join();
}
LOG_DEBUG("release frameQueue");
frameQueue_.flush();
LOG_DEBUG("frameQueue_.flush");
frameQueue_.reset();
LOG_DEBUG("stopCapture done");
}
typedef struct {
uint8_t reportId; uint8_t sampleRate; uint8_t groupLen; uint8_t groupCount; uint32_t reserved; } OBImuHeader;
typedef struct {
int16_t groupId; int16_t accelX;
int16_t accelY;
int16_t accelZ;
int16_t gyroX;
int16_t gyroY;
int16_t gyroZ;
int16_t temperature;
uint32_t timestamp[2];
} OBImuOriginData;
typedef struct {
OBImuHeader imuHeader; OBImuOriginData imuFrameData[IMU_FRAME_MAX_NUM]; uint32_t reserved; } OBImuOrigMsg;
void HidDevicePortGmsl::pollData() {
int groupCount = 0;
auto frame = FrameFactory::createFrame(OB_FRAME_UNKNOWN, OB_FORMAT_UNKNOWN, sizeof(OBImuOrigMsg));
OBImuOrigMsg *imuOrigFrameMsg = reinterpret_cast<OBImuOrigMsg *>(frame->getDataMutable());
memset(imuOrigFrameMsg, 0, sizeof(OBImuOrigMsg));
imuOrigFrameMsg->imuHeader.reportId = 1;
imuOrigFrameMsg->imuHeader.sampleRate = 200;
imuOrigFrameMsg->imuHeader.groupLen = sizeof(OBImuOriginData);
imuOrigFrameMsg->imuHeader.groupCount = 1;
imuOrigFrameMsg->imuHeader.reserved = 0;
imuOrigFrameMsg->reserved = 0;
uint8_t imuFrameMaxSize = sizeof(OBImuOriginData) * (IMU_FRAME_MAX_NUM) + sizeof(i2c_msg_header_t) + sizeof(uint16_t);
auto bufferSize = std::max(sizeof(i2c_msg_t), static_cast<size_t>(imuFrameMaxSize));
std::vector<uint8_t> databuf(bufferSize, 0);
int size = getImuData(databuf.data());
if(size < 0) {
LOG_ERROR("read getImuData failed! ret:{} \n", size);
return;
}
auto *mOriginI2CMsg = reinterpret_cast<i2c_msg_t *>(&databuf[0]);
int mOnceReadLen = mOriginI2CMsg->header.len;
if((mOriginI2CMsg->body.res == 0x00) && (mOnceReadLen > 8)) {
groupCount = (mOnceReadLen - 8) / sizeof(OBImuOriginData);
if(groupCount > 0) {
imuOrigFrameMsg->imuHeader.groupCount = groupCount;
int imuOriginHeaderOffset = sizeof(i2c_msg_header_t) + 2 ;
auto *tmp = reinterpret_cast<imu_origin_data_t *>(&databuf[imuOriginHeaderOffset]);
#if 0#endif
memcpy(imuOrigFrameMsg->imuFrameData, tmp, sizeof(imu_origin_data_t) * groupCount);
#if 0#endif
auto realtime = utils::getNowTimesUs();
frame->setSystemTimeStampUsec(realtime);
frameQueue_.enqueue(frame);
}
else {
LOG_DEBUG("read imu data groupCount is 0 ");
}
}
}
int HidDevicePortGmsl::getImuFps() {
int ret = 0;
uint32_t size = 0;
v4l2_ext_controls ctrls;
v4l2_ext_control ctrl;
memset(&ctrls, 0, sizeof(ctrls));
memset(&ctrl, 0, sizeof(ctrl));
ctrls.ctrl_class = V4L2_CTRL_CLASS_CAMERA;
ctrls.controls = &ctrl;
ctrls.count = 1;
ctrl.id = ORBBEC_CAMERA_CID_GET_IMU_FPS;
ctrl.size = 4;
ctrl.p_u32 = &size;
{
std::unique_lock<std::mutex> lk(mMultiThreadI2CMutex);
ret = xioctlGmsl(imu_fd_, VIDIOC_G_EXT_CTRLS, &ctrls);
}
if(ret < 0) {
LOG_ERROR("{}:{} ioctl failed on getdate errno:{}, strerror:{} \n ", __FILE__, __LINE__, errno, strerror(errno));
return -1;
}
if((size == 0) || (size < 50)) {
size = 200; }
return size;
}
int HidDevicePortGmsl::getImuData(uint8_t *data) {
int ret = 0;
v4l2_ext_controls ctrls;
v4l2_ext_control ctrl;
memset(&ctrls, 0, sizeof(ctrls));
memset(&ctrl, 0, sizeof(ctrl));
ctrls.ctrl_class = V4L2_CTRL_CLASS_CAMERA;
ctrls.controls = &ctrl;
ctrls.count = 1;
ctrl.id = G2R_CAMERA_CID_GET_IMU_DATA;
int size = sizeof(OBImuOriginData) * (IMU_FRAME_MAX_NUM) + sizeof(i2c_msg_header_t) + sizeof(uint16_t);
ctrl.size = size;
ctrl.p_u8 = data;
{
std::unique_lock<std::mutex> lk(mMultiThreadI2CMutex);
ret = xioctlGmsl(imu_fd_, VIDIOC_G_EXT_CTRLS, &ctrls);
}
if(ret < 0) {
LOG_ERROR("{}:{} ioctl failed on getdate :{}\n ", __FILE__, __LINE__, strerror(errno));
return -1;
}
return 0;
}
}