前言
摄像头是很常见的外设,本文分享的是通用QT自带的多媒体模块来操作摄像头,本应用支持选择摄像头,选择摄像头的分辨率,打开/关闭摄像头。
效果图

功能讲解
摄像头下拉框
内容包括3大要素:摄像头序号、摄像头名称、摄像头的VIDPID。相关的代码如下:
void MainWindow::getcamerainfo() {
qDebug() << __FUNCTION__;
ui->comboBox->clear();
cameralist.clear();
// 断开之前的连接,避免重复连接
disconnect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::on_camera_selection_changed);
cameras = QCameraInfo::availableCameras();
if (cameras.isEmpty()) {
qWarning() << "No cameras found.";
QMessageBox::information(this, "提醒", "未检测到摄像头设备");
return;
}
int num = 0;
for (const QCameraInfo &cameraInfo : cameras) {
qDebug() << "Camera deviceName:" << cameraInfo.deviceName();
QString vid = getvidpid("vid_", cameraInfo.deviceName());
QString pid = getvidpid("pid_", cameraInfo.deviceName());
qDebug() << "Camera name:" << cameraInfo.description() << " vid:" << vid << " pid:" << pid;
QString camarastr = QString::number(num) + ". " + cameraInfo.description() + "(" + vid + ":" + pid + ")";
cameralist.append(camarastr);
ui->comboBox->addItem(camarastr);
num++;
}
// 如果有摄像头,默认选择第一个并获取其分辨率
if (ui->comboBox->count() > 0) {
// 手动调用分辨率获取函数,而不是通过信号触发
QTimer::singleShot(100, [this]() {
int index = ui->comboBox->currentIndex();
if (index >= 0 && index < cameras.size()) {
QCameraInfo cameraInfo = cameras.at(index);
updateResolutionComboBox(cameraInfo);
}
});
}
}
在初始化函数中,调用以上函数
分辨率下拉框
QList<QSize> MainWindow::getSupportedResolutions(const QCameraInfo &cameraInfo) {
QList<QSize> resolutions;
// 创建临时摄像头对象获取分辨率
QCamera tempCamera(cameraInfo);
// 使用QEventLoop等待摄像头加载完成
QEventLoop loop;
QTimer timer;
timer.setSingleShot(true);
QObject::connect(&tempCamera, &QCamera::stateChanged, [&](QCamera::State state) {
if (state == QCamera::LoadedState) {
loop.quit();
}
});
QObject::connect(&timer, &QTimer::timeout, [&]() {
if (loop.isRunning()) {
qWarning() << "Camera loading timed out";
loop.quit();
}
});
tempCamera.load();
timer.start(2000); // 2秒超时
loop.exec();
if (tempCamera.state() == QCamera::LoadedState) {
QList<QCameraViewfinderSettings> supportedSettings = tempCamera.supportedViewfinderSettings();
if (supportedSettings.isEmpty()) {
qWarning() << "No supported viewfinder settings for camera:" << cameraInfo.description();
// 备用方法:尝试通过图像捕获获取分辨率
QCameraImageCapture imageCapture(&tempCamera);
QList<QSize> imageResolutions = imageCapture.supportedResolutions();
if (!imageResolutions.isEmpty()) {
resolutions = imageResolutions;
} else {
// 提供一些常见分辨率作为备选
resolutions << QSize(640, 480)
<< QSize(800, 600)
<< QSize(1024, 768)
<< QSize(1280, 720)
<< QSize(1920, 1080);
}
} else {
// 提取分辨率并手动去重(不使用QSet)
for (const QCameraViewfinderSettings &setting : supportedSettings) {
if (setting.resolution().isValid() &&
setting.resolution().width() > 0 &&
setting.resolution().height() > 0) {
// 检查是否已存在相同分辨率
bool alreadyExists = false;
for (const QSize &existing : resolutions) {
if (existing == setting.resolution()) {
alreadyExists = true;
break;
}
}
if (!alreadyExists) {
resolutions.append(setting.resolution());
}
}
}
}
// 按面积排序(从小到大)
std::sort(resolutions.begin(), resolutions.end(), [](const QSize &a, const QSize &b) {
return a.width() * a.height() < b.width() * b.height();
});
} else {
qWarning() << "Failed to load camera for resolution detection:" << cameraInfo.description();
// 提供一些默认分辨率
resolutions << QSize(640, 480)
<< QSize(800, 600)
<< QSize(1024, 768)
<< QSize(1280, 720)
<< QSize(1920, 1080);
}
tempCamera.unload();
return resolutions;
}
void MainWindow::updateResolutionComboBox(const QCameraInfo &cameraInfo) {
qDebug() << "Updating resolution combo box for camera:" << cameraInfo.description();
// 在状态栏显示加载信息
statusBar()->showMessage("正在获取摄像头支持的分辨率...");
// 使用单次定时器异步获取分辨率,避免界面卡顿
QTimer::singleShot(100, [this, cameraInfo]() {
QList<QSize> resolutions = getSupportedResolutions(cameraInfo);
ui->comboBox_resolution->clear();
if (resolutions.isEmpty()) {
ui->comboBox_resolution->addItem("默认分辨率");
statusBar()->showMessage("无法获取分辨率列表,将使用默认分辨率", 3000);
} else {
for (const QSize &resolution : resolutions) {
QString resText = QString("%1 x %2").arg(resolution.width()).arg(resolution.height());
ui->comboBox_resolution->addItem(resText, resolution);
}
statusBar()->showMessage(QString("找到 %1 个支持的分辨率").arg(resolutions.size()), 3000);
}
});
}
void MainWindow::on_camera_selection_changed(int index) {
if (index < 0 || index >= cameras.size()) return;
ui->comboBox_resolution->clear();
// 获取选中的摄像头
QCameraInfo cameraInfo = cameras.at(index);
updateResolutionComboBox(cameraInfo);
}
在初始化函数中引用
// 连接摄像头选择变化信号
connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::on_camera_selection_changed);
打开摄像头
void MainWindow::on_btn_open_clicked() {
if (camera != nullptr) return;
if (cameralist.size() < 1) {
QMessageBox::information(this, "提醒", "摄像头不存在");
return;
}
int cameraNo = ui->comboBox->currentIndex();
QCameraInfo cameraInfo = cameras.at(cameraNo);
qDebug() << "Opening camera" << cameraNo << cameraInfo.description();
// 创建摄像头对象
camera = new QCamera(cameraInfo, this);
if (!camera->isAvailable()) {
qWarning() << "Camera is not available.";
QMessageBox::warning(this, "错误", "摄像头不可用");
delete camera;
camera = nullptr;
return;
}
// 设置分辨率(如果用户选择了特定分辨率)
if (ui->comboBox_resolution->currentIndex() > 0) {
QSize selectedResolution = ui->comboBox_resolution->currentData().toSize();
if (selectedResolution.isValid()) {
QCameraViewfinderSettings settings;
settings.setResolution(selectedResolution);
camera->setViewfinderSettings(settings);
qDebug() << "Set resolution to:" << selectedResolution;
}
}
// 创建视图查找器
viewfinder = new QCameraViewfinder();
//让摄像头画面自适应填充整个widget_video区域
viewfinder->setAspectRatioMode(Qt::IgnoreAspectRatio); // 忽略原始宽高比
viewfinder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
camera->setViewfinder(viewfinder);
// 清除之前的布局
if (ui->widget_video->layout()) {
delete ui->widget_video->layout();
}
// 创建新布局并添加视图查找器
layout = new QVBoxLayout;
layout->addWidget(viewfinder);
ui->widget_video->setLayout(layout);
// 开始预览
camera->start();
status = true;
}
以上通过视图查找器预览摄像头的内容,增加了一下两行代码,让摄像头画面自适应填充整个显示区域:
viewfinder->setAspectRatioMode(Qt::IgnoreAspectRatio); // 忽略原始宽高比
viewfinder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
去掉以上代码,则按照原来的分辨率大小进行显示。
关闭摄像头
void MainWindow::on_btn_close_clicked() {
if (camera == nullptr) return;
camera->stop();
// 清除布局
if (ui->widget_video->layout()) {
QLayoutItem *item;
while ((item = ui->widget_video->layout()->takeAt(0)) != nullptr) {
delete item->widget();
delete item;
}
delete ui->widget_video->layout();
}
delete camera;
camera = nullptr;
status = false;
}
篇尾
QT自带的媒体模块,适合处理一些常用的功能,后续也分享使用opencv方式实现相同的功能。