add linux.
This commit is contained in:
parent
4d2affb862
commit
7c3dbcedfe
@ -29,27 +29,39 @@ VideoPlayer::~VideoPlayer() {
|
|||||||
bool VideoPlayer::open(const std::string &deviceName) {
|
bool VideoPlayer::open(const std::string &deviceName) {
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
m_formatContext = avformat_alloc_context();
|
m_formatContext = avformat_alloc_context();
|
||||||
auto format = av_find_input_format("dshow");
|
#ifdef WIN32
|
||||||
|
constexpr auto format = "dshow";
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "video=" << deviceName;
|
||||||
|
auto device = oss.str();
|
||||||
|
#else
|
||||||
|
constexpr auto format = "v4l2";
|
||||||
|
auto &device = deviceName;
|
||||||
|
#endif
|
||||||
|
auto inputFormat = av_find_input_format(format);
|
||||||
|
|
||||||
AVDictionary *dictionary = nullptr;
|
AVDictionary *dictionary = nullptr;
|
||||||
// ffmpeg -f dshow -list_options true -i video="UVC Camera"
|
// ffmpeg -f dshow -list_options true -i video="UVC Camera"
|
||||||
av_dict_set(&dictionary, "video_size", "800*600", 0);
|
av_dict_set(&dictionary, "video_size", "800*600", 0);
|
||||||
|
|
||||||
std::ostringstream oss;
|
int status = avformat_open_input(&m_formatContext, device.c_str(), inputFormat, &dictionary);
|
||||||
oss << "video=" << deviceName;
|
|
||||||
auto device = oss.str();
|
|
||||||
int status = avformat_open_input(&m_formatContext, "video=UVC Camera", format, &dictionary);
|
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
char message[256] = {0};
|
char message[256] = {0};
|
||||||
av_make_error_string(message, sizeof(message), status);
|
av_make_error_string(message, sizeof(message), status);
|
||||||
LOG(error) << "open device[" << device << "] failed: " << status << "," << message;
|
LOG(error) << "open device[" << device << "] failed: " << status << "," << message;
|
||||||
ret = false;
|
ret = false;
|
||||||
} else {
|
} else {
|
||||||
|
av_dump_format(m_formatContext, 0, device.c_str(), 0);
|
||||||
|
// for (unsigned int i = 0; i < m_formatContext->nb_streams; i++) {
|
||||||
|
// AVStream *stream = m_formatContext->streams[i];
|
||||||
|
// if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||||
|
// std::cout << "当前分辨率: " << stream->codecpar->width << "x" << stream->codecpar->height << std::endl;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
m_exit = false;
|
m_exit = false;
|
||||||
m_thread = std::thread(&VideoPlayer::run, this);
|
m_thread = std::thread(&VideoPlayer::run, this);
|
||||||
}
|
}
|
||||||
av_dict_free(&dictionary);
|
av_dict_free(&dictionary);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,5 @@ FetchContent_MakeAvailable(Kylin)
|
|||||||
add_subdirectory(Peripheral)
|
add_subdirectory(Peripheral)
|
||||||
add_subdirectory(Database)
|
add_subdirectory(Database)
|
||||||
add_subdirectory(Analyser)
|
add_subdirectory(Analyser)
|
||||||
if(WIN32)
|
add_subdirectory(OtaUpdate)
|
||||||
add_subdirectory(OtaUpdate)
|
add_subdirectory(UnitTest)
|
||||||
endif()
|
|
||||||
|
@ -60,7 +60,12 @@ Widget::~Widget() {
|
|||||||
void Widget::start() {
|
void Widget::start() {
|
||||||
m_progressBar->setValue(0);
|
m_progressBar->setValue(0);
|
||||||
auto filePath = m_fileEditor->text();
|
auto filePath = m_fileEditor->text();
|
||||||
if (!std::filesystem::exists(Amass::StringUtility::UTF8ToGBK(filePath.toStdString()))) {
|
#ifdef Q_OS_WIN
|
||||||
|
auto path = Amass::StringUtility::UTF8ToGBK(filePath.toStdString());
|
||||||
|
#else
|
||||||
|
auto path = filePath.toStdString();
|
||||||
|
#endif
|
||||||
|
if (!std::filesystem::exists(path)) {
|
||||||
QMessageBox::warning(this, "升级", "升级文件不存在!");
|
QMessageBox::warning(this, "升级", "升级文件不存在!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -72,7 +77,7 @@ void Widget::start() {
|
|||||||
|
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
setMessage("尝试发现设备......");
|
setMessage("尝试发现设备......");
|
||||||
auto device = discovery->find("UVC Camera", error);
|
auto device = discovery->find(DeviceDiscovery::DeviceName, error);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
QMessageBox::warning(this, "升级", "未检测到模组,请尝试重新插入模组!");
|
QMessageBox::warning(this, "升级", "未检测到模组,请尝试重新插入模组!");
|
||||||
return;
|
return;
|
||||||
|
@ -99,9 +99,10 @@ std::optional<QSerialPortInfo> CdcUpdater::searchDevice() {
|
|||||||
std::optional<QSerialPortInfo> ret = std::nullopt;
|
std::optional<QSerialPortInfo> ret = std::nullopt;
|
||||||
const auto serialPortInfos = QSerialPortInfo::availablePorts();
|
const auto serialPortInfos = QSerialPortInfo::availablePorts();
|
||||||
for (const QSerialPortInfo &portInfo : serialPortInfos) {
|
for (const QSerialPortInfo &portInfo : serialPortInfos) {
|
||||||
LOG(info) << "portName:" << portInfo.portName().toStdString()
|
if ((portInfo.vendorIdentifier() == 0) && (portInfo.productIdentifier() == 0)) continue;
|
||||||
<< ", vendorIdentifier: " << portInfo.vendorIdentifier()
|
LOG(info) << "portName:" << portInfo.portName().toStdString() << ", vendorIdentifier: 0x" << std::hex
|
||||||
<< ", productIdentifier: " << portInfo.productIdentifier();
|
<< portInfo.vendorIdentifier() << ", productIdentifier: 0x" << std::hex
|
||||||
|
<< portInfo.productIdentifier();
|
||||||
if (portInfo.vendorIdentifier() == 0xffff) {
|
if (portInfo.vendorIdentifier() == 0xffff) {
|
||||||
ret = portInfo;
|
ret = portInfo;
|
||||||
break;
|
break;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <boost/scope/scope_exit.hpp>
|
#include <boost/scope/scope_exit.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <mfapi.h>
|
#include <mfapi.h>
|
||||||
#include <mfcaptureengine.h>
|
#include <mfcaptureengine.h>
|
||||||
@ -13,17 +14,9 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct DeviceDiscovery::Device {
|
|
||||||
~Device();
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
Device(IMFMediaSource *source);
|
|
||||||
IMFMediaSource *source = nullptr;
|
|
||||||
IMFSourceReader *reader = nullptr;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void SafeRelease(T **ppT) {
|
void SafeRelease(T **ppT) {
|
||||||
if (*ppT) {
|
if (*ppT) {
|
||||||
@ -210,7 +203,8 @@ std::vector<std::string> DeviceDiscovery::devices() {
|
|||||||
|
|
||||||
struct v4l2_capability cap;
|
struct v4l2_capability cap;
|
||||||
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
|
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
|
||||||
if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
|
if ((strstr(reinterpret_cast<const char *>(cap.card), DeviceName) != nullptr) &&
|
||||||
|
(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
|
||||||
ret.push_back(entry.path().string());
|
ret.push_back(entry.path().string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,6 +213,172 @@ std::vector<std::string> DeviceDiscovery::devices() {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string find_video_device_by_name(const std::string &targetName) {
|
||||||
|
std::string ret;
|
||||||
|
const std::string base_path = "/sys/class/video4linux";
|
||||||
|
for (const auto &entry : std::filesystem::directory_iterator(base_path)) {
|
||||||
|
if (!std::filesystem::is_directory(entry.path())) continue;
|
||||||
|
|
||||||
|
std::string interface;
|
||||||
|
std::ifstream ifs(entry.path().string() + "/device/interface");
|
||||||
|
if (ifs.is_open()) {
|
||||||
|
std::getline(ifs, interface);
|
||||||
|
}
|
||||||
|
if (interface != targetName) continue;
|
||||||
|
|
||||||
|
ifs = std::ifstream(entry.path().string() + "/index");
|
||||||
|
std::string index;
|
||||||
|
if (ifs.is_open()) {
|
||||||
|
std::getline(ifs, index);
|
||||||
|
}
|
||||||
|
if (index != "0") continue;
|
||||||
|
|
||||||
|
ret = "/dev/" + entry.path().filename().string();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<DeviceDiscovery::Device> DeviceDiscovery::find(const std::string &deviceName, std::error_code &error) {
|
||||||
|
auto ret = std::make_shared<Device>();
|
||||||
|
ret->name = find_video_device_by_name(deviceName);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceDiscovery::enterOtaMode(const std::shared_ptr<Device> &device, std::error_code &error) {
|
||||||
|
auto resolutions = deviceResolutions(device);
|
||||||
|
LOG(info) << "device resolutions:";
|
||||||
|
for (auto &[w, h] : resolutions) {
|
||||||
|
LOG(info) << "\t" << w << "*" << h;
|
||||||
|
}
|
||||||
|
int32_t otaSpecificHeight = -1;
|
||||||
|
for (auto &[width, height] : resolutions) {
|
||||||
|
if (width == OtaSpecificWidth) {
|
||||||
|
otaSpecificHeight = height;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (otaSpecificHeight <= 0) {
|
||||||
|
LOG(error) << "cannot find ota specific resolution.";
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
LOG(info) << "found ota specific resolution: " << OtaSpecificWidth << "x" << otaSpecificHeight;
|
||||||
|
}
|
||||||
|
int fd = open(device->name.c_str(), O_RDWR);
|
||||||
|
if (fd <= 0) {
|
||||||
|
LOG(error) << "Failed to open device " << device->name;
|
||||||
|
} else {
|
||||||
|
struct v4l2_format format;
|
||||||
|
memset(&format, 0, sizeof(format));
|
||||||
|
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
format.fmt.pix.width = OtaSpecificWidth;
|
||||||
|
format.fmt.pix.height = otaSpecificHeight;
|
||||||
|
format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
|
||||||
|
format.fmt.pix.field = V4L2_FIELD_INTERLACED;
|
||||||
|
|
||||||
|
if (ioctl(fd, VIDIOC_S_FMT, &format) == -1) {
|
||||||
|
perror("Setting Pixel Format");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
||||||
|
struct v4l2_requestbuffers req;
|
||||||
|
CLEAR(req);
|
||||||
|
req.count = 1;
|
||||||
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
req.memory = V4L2_MEMORY_MMAP;
|
||||||
|
|
||||||
|
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
|
||||||
|
perror("Requesting Buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct v4l2_buffer buf;
|
||||||
|
CLEAR(buf);
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
buf.index = 0;
|
||||||
|
|
||||||
|
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
|
||||||
|
perror("Querying Buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buffer {
|
||||||
|
void *start;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct buffer buffer;
|
||||||
|
buffer.length = buf.length;
|
||||||
|
buffer.start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
|
||||||
|
|
||||||
|
if (buffer.start == MAP_FAILED) {
|
||||||
|
perror("Mapping Buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
|
||||||
|
perror("Queue Buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
|
||||||
|
perror("Start Capture");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
|
||||||
|
perror("Stop Capture");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (munmap(buffer.start, buffer.length) == -1) {
|
||||||
|
perror("Unmapping Buffer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::shared_ptr<Device> &source) {
|
||||||
|
Resolutions ret;
|
||||||
|
int fd = open(source->name.c_str(), O_RDWR);
|
||||||
|
if (fd <= 0) {
|
||||||
|
LOG(error) << "Failed to open device " << source->name;
|
||||||
|
} else {
|
||||||
|
struct v4l2_fmtdesc fmt;
|
||||||
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
fmt.index = 0;
|
||||||
|
|
||||||
|
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == 0) {
|
||||||
|
std::cout << "Pixel Format: " << fmt.description << std::endl;
|
||||||
|
struct v4l2_frmsizeenum frmsize;
|
||||||
|
frmsize.pixel_format = fmt.pixelformat;
|
||||||
|
frmsize.index = 0;
|
||||||
|
|
||||||
|
while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) {
|
||||||
|
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
|
||||||
|
ret.emplace_back(frmsize.discrete.width, frmsize.discrete.height);
|
||||||
|
} else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
|
||||||
|
for (int width = frmsize.stepwise.min_width; width <= frmsize.stepwise.max_width;
|
||||||
|
width += frmsize.stepwise.step_width) {
|
||||||
|
for (int height = frmsize.stepwise.min_height; height <= frmsize.stepwise.max_height;
|
||||||
|
height += frmsize.stepwise.step_height) {
|
||||||
|
ret.emplace_back(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frmsize.index++;
|
||||||
|
}
|
||||||
|
fmt.index++;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DeviceDiscovery::Device::~Device() {
|
DeviceDiscovery::Device::~Device() {
|
||||||
|
@ -10,7 +10,17 @@ class DeviceDiscovery {
|
|||||||
constexpr static int32_t OtaSpecificWidth = 96;
|
constexpr static int32_t OtaSpecificWidth = 96;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Device;
|
constexpr static auto DeviceName = "UVC Camera";
|
||||||
|
struct Device {
|
||||||
|
~Device();
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
Device(IMFMediaSource *source);
|
||||||
|
IMFMediaSource *source = nullptr;
|
||||||
|
IMFSourceReader *reader = nullptr;
|
||||||
|
#else
|
||||||
|
std::string name;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
using Resolution = std::pair<int32_t, int32_t>;
|
using Resolution = std::pair<int32_t, int32_t>;
|
||||||
using Resolutions = std::vector<Resolution>;
|
using Resolutions = std::vector<Resolution>;
|
||||||
|
20
UnitTest/CMakeLists.txt
Normal file
20
UnitTest/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
|
||||||
|
|
||||||
|
# --detect_memory_leak=0 --run_test=MarkdownParserTest,ProcessUtilityTest
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
add_executable(UnitTest main.cpp
|
||||||
|
LinuxDeviceEnums.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(UnitTest
|
||||||
|
PUBLIC LOG_FILTER_LEVEL=2
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(UnitTest
|
||||||
|
PRIVATE Boost::unit_test_framework
|
||||||
|
PRIVATE DataStructure
|
||||||
|
PRIVATE Peripheral
|
||||||
|
PRIVATE Universal
|
||||||
|
)
|
17
UnitTest/LinuxDeviceEnums.cpp
Normal file
17
UnitTest/LinuxDeviceEnums.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "BoostLog.h"
|
||||||
|
#include "DeviceDiscovery.h"
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(EnumDevice) {
|
||||||
|
std::error_code error;
|
||||||
|
DeviceDiscovery discovery;
|
||||||
|
auto device = discovery.find("UVC Camera", error);
|
||||||
|
LOG(info) << device->name;
|
||||||
|
|
||||||
|
auto devices = discovery.devices();
|
||||||
|
for (int i = 0; i < devices.size(); i++) {
|
||||||
|
LOG(info) << "device[" << i << "] " << devices.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
discovery.enterOtaMode(device, error);
|
||||||
|
}
|
2
UnitTest/main.cpp
Normal file
2
UnitTest/main.cpp
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#define BOOST_TEST_MODULE KylinLibraryTest
|
||||||
|
#include "boost/test/included/unit_test.hpp"
|
Loading…
Reference in New Issue
Block a user