Skip to content
Merged
Show file tree
Hide file tree
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
10 changes: 10 additions & 0 deletions src/assets/org.deepin.camera.encode.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@
"description": "Enable 8k preview",
"permissions": "readwrite",
"visibility": "private"
},
"deviceBlacklist": {
"value": [],
"serial": 0,
"flags": ["global"],
"name": "device blacklist",
"name[zh_CN]": "设备黑名单",
"description": "Set device blacklist, format: vid,pid,name",
"permissions": "readwrite",
"visibility": "private"
}
}
}
6 changes: 6 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ int main(int argc, char *argv[])
set_enable_8k_preview(enable ? 1 : 0);
}

if (dconfig && dconfig->isValid() && dconfig->keyList().contains("deviceBlacklist")) {
QStringList deviceBlacklist = dconfig->value("deviceBlacklist").toStringList();
qInfo() << "device blacklist:" << deviceBlacklist;
DataManager::instance()->setDeviceBlacklist(deviceBlacklist);
}

if (!libVaDriverName.isEmpty()) {
qputenv("LIBVA_DRIVER_NAME", libVaDriverName.toLocal8Bit());
}
Expand Down
48 changes: 48 additions & 0 deletions src/src/basepub/datamanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,54 @@ bool DataManager::encExists(){
return m_H264EncoderExists;
}

void DataManager::setDeviceBlacklist(const QStringList &blacklist)
{
// 无论是否为空,都先清空现有黑名单
m_deviceBlacklistSet.clear();
if (blacklist.isEmpty()) {
qInfo() << "Empty blacklist provided";
return;
}

// 每一条黑名单项的格式都是 vid,pid,name
// 其中vid和pid都是4位十六进制数,name是设备名称
// 后续匹配操作的时候,我们会忽略大小写
for (const QString &item : blacklist) {
QStringList parts = item.split(",");
if (parts.size() != 3) {
qWarning() << "Drop blacklist item(format error):" << item;
continue;
}

// 验证VID和PID是否为4位十六进制数
static const QRegularExpression hexPattern("^[0-9a-fA-F]{4}$");
if (!hexPattern.match(parts[0]).hasMatch() || !hexPattern.match(parts[1]).hasMatch()) {
qWarning() << "Drop blacklist item(invalid VID/PID):" << item;
continue;
}

// 验证设备名称不为空,且长度不超过100个字符
if (parts[2].trimmed().isEmpty() || parts[2].size() > 100) {
qWarning() << "Drop blacklist item(empty device name OR too long):" << item;
continue;
}

qInfo() << "Add blacklist item:" << item;
m_deviceBlacklistSet.insert(item.toLower());
}
};

bool DataManager::isDeviceValid(const QString &vid, const QString &pid, const QString &name)
{
// 参数验证
if (vid.isEmpty() || pid.isEmpty() || name.isEmpty()) {
return true; // 空参数视为有效,避免误判
}

QString key = vid.toLower() + "," + pid.toLower() + "," + name.toLower();
return !m_deviceBlacklistSet.contains(key);
}

DataManager *DataManager::instance()
{
if (m_dataManager == nullptr) {
Expand Down
12 changes: 12 additions & 0 deletions src/src/basepub/datamanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@ class DataManager: public QObject
* @return
*/
bool isEnable8kPreview() const { return m_enable8kPreview; };

/**
* @brief 设置Camera设备黑名单
* @param blacklist
*/
void setDeviceBlacklist(const QStringList &blacklist);
/**
* @brief 检查当前设备是否有效
* @return
*/
bool isDeviceValid(const QString &vid, const QString &pid, const QString &name);
private:
DataManager();
static DataManager *m_dataManager;
Expand All @@ -200,5 +211,6 @@ class DataManager: public QObject
bool m_isPreviewNoDelay = false; // 是否预览无延迟
bool m_enableUsbGroup = false; // 是否启用USB摄像头分组
bool m_enable8kPreview = false; // 是否启用8K预览
QSet<QString> m_deviceBlacklistSet; // 设备黑名单
};
#endif // DATAMANAGER_H
5 changes: 4 additions & 1 deletion src/src/devnummonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ void DevNumMonitor::startCheck()

void DevNumMonitor::timeOutSlot()
{
check_device_list_events(get_v4l2_device_handler());
// 检查设备列表变化事件
if (check_device_list_events(get_v4l2_device_handler())) {
emit deviceListChanged();
}

if (get_device_list()->num_devices <= 1) {
emit seltBtnStateDisable();
Expand Down
5 changes: 5 additions & 0 deletions src/src/devnummonitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ class DevNumMonitor: public QObject
*/
void existDevice();

/**
* @brief deviceListChanged 相机设备列表改变信号
*/
void deviceListChanged();

protected:
/**
* @brief run 运行
Expand Down
6 changes: 6 additions & 0 deletions src/src/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,7 @@ void CMainWindow::loadAfterShow()
if(DataManager::instance()->encodeEnv() != QCamera_Env) {
connect(m_devnumMonitor, SIGNAL(existDevice()), m_videoPre, SLOT(onRestartDevices()));//重启设备
connect(m_devnumMonitor, SIGNAL(noDeviceFound()), m_videoPre, SLOT(onRestartDevices()));//重启设备
connect(m_devnumMonitor, SIGNAL(deviceListChanged()), m_videoPre, SLOT(updateValidDevices())); // 更新可用设备列表
} else if (DataManager::instance()->encodeEnv() == QCamera_Env) {
initCameraConnection();
}
Expand Down Expand Up @@ -2037,6 +2038,11 @@ void CMainWindow::onLocalTimeChanged()

void CMainWindow::setSelBtnShow()
{
// 有效设备个数小于等于1,不显示切换按钮
if (m_videoPre->getValidDeviceNum() <= 1) {
return;
}

m_bSwitchCameraShowEnable = true;
if (m_cameraSwitchBtn->isHidden()) {
showChildWidget();
Expand Down
160 changes: 115 additions & 45 deletions src/src/videowidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,18 @@ void videowidget::delayInit()
m_flashLabel->hide();

QString device = dc::Settings::get().getBackOption("device").toString();
//启动视频
switchCamera(device.toStdString().c_str(), "");
// 启动视频预览
// 如果配置的设备无效,切换到第一个有效的设备
// 如果有效的设备也不存在,走默认流程
updateValidDevices();
if (!isDeviceValidByDevice(device)) {
QString validDevice = getFirstValidDevice();
qWarning() << "INVALID device from config:" << device << ", found first valid device:" << validDevice;
switchCamera(validDevice.toStdString().c_str(), "");
} else {
qInfo() << "VALID device from config:" << device;
switchCamera(device.toStdString().c_str(), ""); // 走默认逻辑
}

QObject::connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged,
this, &videowidget::onThemeTypeChanged);
Expand Down Expand Up @@ -1135,38 +1145,25 @@ void videowidget::onChangeDev()
groupNum = getUSBCameraGroup(devlist, vGroupData);
qInfo() << __func__ << "groupNum:" << groupNum;
}
if (groupNum == 1) {
if (devlist->num_devices == 2) {
for (int i = 0 ; i < devlist->num_devices; i++) {
const char *curDev = devlist->list_devices[i].device;
if (str != curDev) {
if (E_OK == switchCamera(curDev, devlist->list_devices[i].name)) {
break;
}
}
}
// 如果摄像头设备个数为0,分组情况就不用考虑了,直接显示无摄像头提示
if (devlist->num_devices == 0) {
DataManager::instance()->setdevStatus(NOCAM);
showNocam();
} else if (groupNum == 0) {
switchCamera("", ""); // 无有效设备,但是有设备,走默认逻辑
} else if (groupNum == 1) {
if (m_validDevices.empty()) {
switchCamera("", ""); // 无有效设备,但是有设备,走默认逻辑
} else {
if (devlist->num_devices == 0) {
DataManager::instance()->setdevStatus(NOCAM);
showNocam();
}

for (int i = 0 ; i < devlist->num_devices; i++) {
const char *curDev = devlist->list_devices[i].device;
if (str == curDev) {
if (i == devlist->num_devices - 1) {
switchCamera(devlist->list_devices[0].device, devlist->list_devices[0].name);
break;
} else {
switchCamera(devlist->list_devices[i + 1].device, devlist->list_devices[i + 1].name);
break;
}
}

if (str.isEmpty()) {
switchCamera(devlist->list_devices[0].device, devlist->list_devices[0].name);
break;
}
int idx = getValidDeviceIndexByDevice(str);
if (idx == -1 || idx >= m_validDevices.size() - 1) {
// 获取第一个有效设备,直接切换到该设备
const ValidDevice &dev = m_validDevices[0];
switchCamera(dev.getDevice().toStdString().c_str(), dev.getName().toStdString().c_str());
} else {
// 切换到当前设备的下一个有效设备
const ValidDevice &dev = m_validDevices[idx + 1];
switchCamera(dev.getDevice().toStdString().c_str(), dev.getName().toStdString().c_str());
}
}
} else {
Expand All @@ -1180,14 +1177,11 @@ void videowidget::onChangeDev()
}
}
} else {
if (devlist->num_devices == 0) {
DataManager::instance()->setdevStatus(NOCAM);
showNocam();
}

bool found = false; // 标记是否找到当前设备
for (int i = 0 ; i < vGroupData.count(); i++) {
const char *curDev = vGroupData[i].second[0]->device;
if (str == curDev) {
found = true;
if (i == vGroupData.count() - 1) {
switchCamera(vGroupData[0].second[0]->device, vGroupData[0].second[0]->name);
break;
Expand All @@ -1196,11 +1190,10 @@ void videowidget::onChangeDev()
break;
}
}

if (str.isEmpty()) {
switchCamera(vGroupData[0].second[0]->device, vGroupData[0].second[0]->name);
break;
}
}
if (!found) {
// 未找到当前设备,切换到第一个设备
switchCamera(vGroupData[0].second[0]->device, vGroupData[0].second[0]->name);
}
}
}
Expand Down Expand Up @@ -1257,12 +1250,16 @@ int videowidget::getUSBCameraGroup(v4l2_device_list_t *devlist, QVector<QPair<QS
// 来自xiwo分支,根据location进行分组
// 收到建议使用 QMap<QString, QVector<v4l2_dev_sys_data_t *>> 来存储分组数据,但我们担心影响现有代码逻辑,
// 所以暂时保留 QVector<QPair<QString, QVector<v4l2_dev_sys_data_t *>>> 来存储分组数据。
if (devlist == nullptr) {
if (devlist == nullptr || devlist->list_devices == nullptr || devlist->num_devices == 0) {
qWarning() << __func__ << "devlist is NULL!";
return 0;
}

for (int i = 0 ; i < devlist->num_devices; i++) {
for (int i = 0; i < devlist->num_devices; i++) {
if (!isDeviceValidByDevice(devlist->list_devices[i].device)) {
continue; // 无效设备不参与分组
}

QString location = QString(devlist->list_devices[i].location);

int j = 0;
Expand Down Expand Up @@ -1756,6 +1753,79 @@ void videowidget::onFilterDisplayChanged(int bDisplay)
m_imgPrcThread->setFilterGroupState(bDisplay);
}

QString videowidget::getFirstValidDevice()
{
// 加锁,确保线程安全
QReadLocker locker(&m_mutexValidDevices);
if (m_validDevices.isEmpty()) {
qWarning() << __func__ << "no valid device!";
return "";
}

// 返回第一个有效相机设备的设备节点路径
const ValidDevice &dev = m_validDevices.first();
qInfo() << __func__ << dev.getVid() << dev.getPid() << dev.getName() << dev.getDevice();
return dev.getDevice();
}

void videowidget::updateValidDevices()
{
qInfo() << __func__;
// 加锁,确保线程安全
QWriteLocker locker(&m_mutexValidDevices);
m_validDevices.clear(); // 清空有效相机设备列表

v4l2_device_list_t *devList = get_device_list();
if (!devList) {
qWarning() << __func__ << "get device list FAILED!";
return;
}

v4l2_dev_sys_data_t *v4l2_devices = devList->list_devices;
if (!v4l2_devices) {
qWarning() << __func__ << "get device list FAILED!";
return;
}

for (int i = 0; i < devList->num_devices; i++) {
// 获取设备的VID和PID,不足4位用0填充
QString vid = formatDeviceId(v4l2_devices[i].vendor);
QString pid = formatDeviceId(v4l2_devices[i].product);
if (DataManager::instance()->isDeviceValid(vid, pid, v4l2_devices[i].name)) {
qInfo() << __func__ << "found valid device:" << vid << pid << v4l2_devices[i].name << v4l2_devices[i].device;
// 添加有效相机设备
m_validDevices.push_back(ValidDevice(vid, pid, v4l2_devices[i].name, v4l2_devices[i].device));
continue;
}

qInfo() << __func__ << "found invalid device(blacklist):" << vid << pid << v4l2_devices[i].name << v4l2_devices[i].device;
}
}

bool videowidget::isDeviceValidByDevice(const QString &device)
{
return getValidDeviceIndexByDevice(device) != -1;
}

int videowidget::getValidDeviceIndexByDevice(const QString &device)
{
// 加锁,确保线程安全
QReadLocker locker(&m_mutexValidDevices);
for (int i = 0; i < m_validDevices.size(); i++) {
if (m_validDevices[i].getDevice() == device) {
return i;
}
}
return -1;
}

int videowidget::getValidDeviceNum()
{
// 加锁,确保线程安全
QReadLocker locker(&m_mutexValidDevices);
return m_validDevices.size();
}

videowidget::~videowidget()
{
m_imgPrcThread->stop();
Expand Down
Loading
Loading