8 Star 40 Fork 23

ZVision / UVCCapture

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
WebcamWindow.cpp 20.68 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
/*
* Copyright (c) 2020-2022 https://gitee.com/fsfzp888/UVCCapture
* All rights reserved
*/
#include <QComboBox>
#include <QCoreApplication>
#include <QDir>
#include <QFileDialog>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QImage>
#include <QLabel>
#include <QLineEdit>
#include <QPainter>
#include <QPushButton>
#include <QSizePolicy>
#include <QSpinBox>
#include <QSplitter>
#include <QStatusBar>
#include <QString>
#include <QTime>
#include <QTimer>
#include <QVBoxLayout>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream>
#include "ImageFormats.h"
#include "Logger.h"
#include "VideoCapture.h"
#include "VideoDevice.h"
#include "WebcamWindow.h"
WebcamWindow::WebcamWindow(QWidget *parent)
: QMainWindow(parent),
m_stillThread(this),
m_stillWorker(this),
m_viewport(new QLabel),
m_frameMutex(),
m_frame(),
m_controlLayout(new QVBoxLayout),
m_controlGroup(new QGroupBox),
m_windowLayout(new QHBoxLayout),
m_windowGroup(new QGroupBox),
m_startButton(new QPushButton(tr("Turn On"))),
m_stopButton(new QPushButton(tr("Turn Off"))),
m_captureButton(new QPushButton(tr("Capture"))),
m_captureThreeButton(new QPushButton(tr("Capture Three"))),
m_startRecordVideoButton(new QPushButton(tr("Start Record Video"))),
m_stopRecordVideoButton(new QPushButton(tr("Stop Record Video"))),
m_devicesLabel(new QLabel(tr("Devices"))),
m_devices(new QComboBox),
m_resolutionsLabel(new QLabel(tr("Resolutions"))),
m_resolutions(new QComboBox),
m_directoryLabel(new QLabel(tr("Output Path"))),
m_directory(new QLineEdit),
m_browserButton(new QPushButton(tr("Browser"))),
m_nameLabel(new QLabel(tr("name"))),
m_name(new QLineEdit),
m_timeRangeLabel(new QLabel(tr("Hardware trigger video time range(ms)"))),
m_timeRange(new QSpinBox),
m_browserDirectoryLayout(new QHBoxLayout),
m_devicesGroup(new QGroupBox),
m_devicesLayout(new QVBoxLayout),
m_vsplitter(new QSplitter),
m_videoCapture(nullptr),
m_appDirPath(QDir::currentPath()),
m_settings("Z Vision Tech", "UVCCapture"),
m_textSpeaker(new QTextToSpeech(this)),
m_handleCount(0),
m_photoCount(0),
m_stillPhotoCount(0),
m_isCapturing(false),
m_isStop(true),
m_isRecordingVideo(false)
{
setWindowTitle(tr("Webcam"));
setWindowFlags(this->windowFlags() | Qt::MaximizeUsingFullscreenGeometryHint);
m_appDirPath = m_settings.value("mainwindow/directory", m_appDirPath).toString();
m_directory->setText(m_appDirPath);
m_directory->setReadOnly(true);
m_directory->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_devicesLayout->addWidget(m_devicesLabel);
m_devicesLayout->addWidget(m_devices);
m_devicesLayout->addWidget(m_resolutionsLabel);
m_devicesLayout->addWidget(m_resolutions);
m_browserDirectoryLayout->addWidget(m_directory, 3);
m_browserDirectoryLayout->addWidget(m_browserButton, 1);
m_devicesLayout->addWidget(m_directoryLabel);
m_devicesLayout->addLayout(m_browserDirectoryLayout);
m_devicesLayout->addWidget(m_nameLabel);
m_devicesLayout->addWidget(m_name);
m_timeRange->setRange(500, 3500);
m_timeRange->setSingleStep(100);
m_timeRange->setValue(1800);
m_devicesLayout->addWidget(m_timeRangeLabel);
m_devicesLayout->addWidget(m_timeRange);
m_devicesGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_devicesGroup->setLayout(m_devicesLayout);
m_controlLayout->addWidget(m_devicesGroup);
m_controlLayout->addWidget(m_vsplitter);
m_controlLayout->addWidget(m_captureButton);
m_controlLayout->addWidget(m_captureThreeButton);
m_controlLayout->addWidget(m_startRecordVideoButton);
m_controlLayout->addWidget(m_stopRecordVideoButton);
m_controlLayout->addWidget(m_startButton);
m_controlLayout->addWidget(m_stopButton);
m_controlGroup->setLayout(m_controlLayout);
m_controlGroup->setMinimumWidth(300);
m_controlGroup->setMaximumWidth(300);
m_stopButton->setEnabled(false);
m_captureButton->setEnabled(false);
m_captureThreeButton->setEnabled(false);
m_startRecordVideoButton->setEnabled(false);
m_stopRecordVideoButton->setEnabled(false);
m_viewport->setMinimumSize(640, 480);
m_viewport->setBackgroundRole(QPalette::Base);
m_viewport->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
m_viewport->setScaledContents(true);
m_windowLayout->addWidget(m_viewport);
m_windowLayout->addWidget(m_controlGroup);
// m_windowLayout->setSizeConstraint(QLayout::SetFixedSize);
m_windowGroup->setLayout(m_windowLayout);
setCentralWidget(m_windowGroup);
m_statusBar = statusBar();
m_videoCapture = new VideoCapture([this](unsigned char *data, int len, VideoDevice *device) { processFrame(data, len, device); },
[this](unsigned char *, int, VideoDevice *) {
LOG_INFO("hardware trigger capture still image");
if (!m_isStop && m_isCapturing)
{
//processStillFrame(data, len, device);
incThreeCaptureCnt();
}
});
auto devicesNames = m_videoCapture->getDevicesNames();
for (auto &deviceName : devicesNames)
{
QString name = QString::fromWCharArray(deviceName.c_str());
m_devices->addItem(name);
}
auto deviceResolutions = m_videoCapture->getActiveDeviceResolutions();
for (auto &deviceResolution : deviceResolutions)
{
QString resolution = QString::fromStdString(deviceResolution);
m_resolutions->addItem(resolution);
}
//if (deviceResolutions.size() > 1)
//{
// m_resolutions->setCurrentIndex(1);
//}
connect(m_resolutions, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
static_cast<void (WebcamWindow::*)(int)>(&WebcamWindow::changeResolution));
connect(m_devices, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
static_cast<void (WebcamWindow::*)(int)>(&WebcamWindow::changeDevice));
connect(m_startButton, &QPushButton::released, this, &WebcamWindow::startCapture);
connect(m_stopButton, &QPushButton::released, this, &WebcamWindow::stopCapture);
connect(m_captureButton, &QPushButton::released, this, &WebcamWindow::incCaptureCnt);
connect(m_captureThreeButton, &QPushButton::released, this, &WebcamWindow::incThreeCaptureCnt);
connect(m_startRecordVideoButton, &QPushButton::released, this, &WebcamWindow::startRecordVideo);
connect(m_stopRecordVideoButton, &QPushButton::released, this, &WebcamWindow::stopRecordVideo);
connect(m_browserButton, &QPushButton::clicked, this, &WebcamWindow::browse);
connect(this, SIGNAL(beginRecordVideo()), this, SLOT(onClickStartRecordVideoButton()));
connect(this, SIGNAL(finishRecordVideo()), this, SLOT(handleFinishRecordVideo()));
connect(this, SIGNAL(sendStatusBarMessage(QString)), this, SLOT(showStatusBarMessage(QString)));
connect(this, SIGNAL(sendSpeakMessage(QString)), this, SLOT(speakMessage(QString)));
m_stillWorker.moveToThread(&m_stillThread);
connect(&m_stillWorker, SIGNAL(beginRecordVideo()), this, SLOT(startRecordVideo()));
connect(&m_stillWorker, SIGNAL(finishRecordVideo()), this, SLOT(handleFinishRecordVideo()));
connect(&m_stillWorker, SIGNAL(sendStatusBarMessage(QString)), this, SLOT(showStatusBarMessage(QString)));
connect(&m_stillWorker, SIGNAL(sendSpeakMessage(QString)), this, SLOT(speakMessage(QString)));
connect(&m_stillWorker, SIGNAL(trackExtraImage(int)), this, SLOT(incStillCaptureCnt(int)));
connect(this, SIGNAL(sendHandleStillImageMessage(QImage &)), &m_stillWorker, SLOT(postImage(QImage &)), Qt::BlockingQueuedConnection);
m_stillThread.start();
if (m_textSpeaker->state() == QTextToSpeech::Ready)
{
m_textSpeaker->setLocale(QLocale::Chinese);
m_textSpeaker->setRate(0.0);
m_textSpeaker->setPitch(1.0);
m_textSpeaker->setVolume(1.0);
}
else
{
LOG_ERROR(tr("TTS engine not installed").toLatin1());
}
m_timer.start();
}
WebcamWindow::~WebcamWindow()
{
m_stillThread.terminate();
m_devices->blockSignals(true);
m_resolutions->blockSignals(true);
m_videoCapture->stopCapture();
delete m_videoCapture;
}
void WebcamWindow::processStillFrame(const unsigned char *data, int len, VideoDevice *device)
{
if (!device || data == nullptr || len <= 0)
{
return;
}
VideoDevice::Properties prop = device->getCurrentProperties();
long width = prop.width;
long height = prop.height;
m_makeQImage = getQImageMaker(prop.pixelFormat);
QImage newFrame(m_makeQImage(data, len, width, height));
emit sendHandleStillImageMessage(newFrame);
}
static std::string getTimeString(bool bLocal = true, bool bIncludeMS = true)
{
auto tNow = std::chrono::system_clock::now();
// auto tmNow = std::chrono::system_clock::to_time_t(tNow);
auto tSeconds = std::chrono::duration_cast<std::chrono::seconds>(tNow.time_since_epoch());
auto secNow = tSeconds.count();
tm tmNow;
if (bLocal)
{
localtime_s(&tmNow, &secNow);
}
else
{
gmtime_s(&tmNow, &secNow);
}
std::ostringstream oss;
oss << std::put_time(&tmNow, "%Y-%m-%d %H %M %S");
if (bIncludeMS)
{
auto tMilli = std::chrono::duration_cast<std::chrono::milliseconds>(tNow.time_since_epoch());
auto ms = tMilli - tSeconds;
oss << " " << std::setfill('0') << std::setw(3) << ms.count();
}
return oss.str();
}
static void writeTextToImage(QImage &img, const QString &text)
{
QPixmap pix = QPixmap::fromImage(img);
QPainter painter(&pix);
painter.begin(&pix);
painter.setPen(Qt::black);
QFont font = painter.font();
font.setPixelSize(18 * img.width() / 640);
font.setFamily("Microsoft YaHei");
font.setBold(true);
painter.setFont(font);
painter.drawText(pix.rect(), Qt::AlignBottom | Qt::AlignRight, text);
img = pix.toImage();
}
void WebcamWindow::speakMessage(QString msg)
{
if (m_textSpeaker->state() == QTextToSpeech::Ready)
{
QString name = getUserName();
m_textSpeaker->say(name);
m_textSpeaker->say(msg);
}
else
{
LOG_ERROR("Text to speech engine not in ready state!");
}
}
void WebcamWindow::showStatusBarMessage(QString msg)
{
m_statusBar->showMessage(msg, 1500);
}
void WebcamWindow::writeQImageToFile(QImage &img)
{
QString name = m_name->text();
if (name.isEmpty())
{
name = tr("unknown");
}
QString app_dir = m_appDirPath;
QDir dir;
if (!dir.exists(app_dir))
{
dir.mkpath(app_dir);
}
auto pt = getTimeString();
std::stringstream ss;
ss << pt << ".jpg";
std::string filename;
while (!ss.eof())
{
std::string res;
ss >> res;
filename += res;
}
app_dir = app_dir + "/" + name;
if (!dir.exists(app_dir))
{
dir.mkpath(app_dir);
}
app_dir = app_dir + "/" + filename.c_str();
QString watermark = name + " " + pt.c_str();
writeTextToImage(img, watermark);
img.save(app_dir, "JPG");
app_dir = app_dir + tr(" has already beed saved.");
emit sendStatusBarMessage(app_dir);
}
void WebcamWindow::processFrame(const unsigned char *data, int len, VideoDevice *device)
{
if (!device || data == nullptr || len <= 0)
{
return;
}
VideoDevice::Properties prop = device->getCurrentProperties();
long width = prop.width;
long height = prop.height;
m_makeQImage = getQImageMaker(prop.pixelFormat);
QImage newFrame(m_makeQImage(data, len, width, height));
m_videoMutex.lock();
if (m_isRecordingVideo)
{
if (isJPEGFormat(prop.pixelFormat))
{
m_aviWriter.writeFrame((const char *)data, len, 0);
}
else
{
QByteArray ba;
ConvertToJPEGBuf(newFrame, ba);
m_aviWriter.writeFrame(ba.data(), ba.size(), 0);
}
}
m_videoMutex.unlock();
m_frameMutex.lock();
if (m_photoCount)
{
writeQImageToFile(newFrame);
--m_photoCount;
}
if (m_stillPhotoCount)
{
writeQImageToFile(newFrame);
--m_stillPhotoCount;
}
long vp_width = m_viewport->width();
long vp_height = m_viewport->height();
// m_frame = newFrame.mirrored(device->getCurrentProperties().isFlippedHorizontal, device->getCurrentProperties().isFlippedVertical ||
// m_isFlipped);
m_frame = newFrame.scaled(vp_width, vp_height);
m_frameMutex.unlock();
QMetaObject::invokeMethod(this, "presentFrame", Qt::QueuedConnection);
}
void WebcamWindow::resizeEvent(QResizeEvent *ev)
{
m_frameMutex.lock();
QMainWindow::resizeEvent(ev);
m_frameMutex.unlock();
}
void WebcamWindow::incCaptureCntCustom(int cnt)
{
m_frameMutex.lock();
m_photoCount += cnt;
m_frameMutex.unlock();
}
void WebcamWindow::incStillCaptureCnt(int cnt)
{
m_frameMutex.lock();
m_stillPhotoCount += cnt;
m_frameMutex.unlock();
}
void WebcamWindow::incCaptureCnt()
{
emit sendSpeakMessage(tr("save one photos"));
incCaptureCntCustom(1);
}
void WebcamWindow::incThreeCaptureCnt()
{
emit sendSpeakMessage(tr("save three photos"));
incCaptureCntCustom(1);
incStillCaptureCnt(2);
}
void WebcamWindow::presentFrame()
{
m_frameMutex.lock();
m_viewport->setPixmap(QPixmap::fromImage(m_frame));
m_frameMutex.unlock();
// adjustSize();
m_viewport->repaint();
}
void WebcamWindow::changeResolution(int resolutionNum)
{
bool wasCapturing = m_isCapturing;
stopCapture();
m_videoCapture->changeActiveDeviceResolution(resolutionNum);
// adjustSize();
if (wasCapturing)
{
startCapture();
}
}
void WebcamWindow::changeDevice(int deviceNum)
{
bool wasCapturing = m_isCapturing;
stopCapture();
m_videoCapture->changeActiveDevice(deviceNum);
m_resolutions->clear();
auto deviceResolutions = m_videoCapture->getActiveDeviceResolutions();
for (auto &deviceResolution : deviceResolutions)
{
QString resolution = QString::fromStdString(deviceResolution);
m_resolutions->addItem(resolution);
}
if (wasCapturing)
{
startCapture();
}
}
void WebcamWindow::onClockStopRecordVideoButton()
{
handleFinishRecordVideo();
emit sendStatusBarMessage(tr("Video finish record."));
emit sendSpeakMessage(tr("finish record video"));
}
void WebcamWindow::handleFinishRecordVideo()
{
// signal may be emit multiple times
m_videoMutex.lock();
if (m_isRecordingVideo)
{
m_aviWriter.close();
m_isRecordingVideo = false;
m_startRecordVideoButton->setEnabled(true);
m_stopRecordVideoButton->setEnabled(false);
LOG_INFO("Close AVI video file");
}
m_videoMutex.unlock();
}
void WebcamWindow::startCapture()
{
m_isStop = false;
m_startButton->setEnabled(false);
m_stopButton->setEnabled(true);
m_captureButton->setEnabled(true);
m_captureThreeButton->setEnabled(true);
m_startRecordVideoButton->setEnabled(true);
m_stopRecordVideoButton->setEnabled(false);
if (m_videoCapture->startCapture())
{
m_isCapturing = true;
}
}
void WebcamWindow::stopCapture()
{
m_isStop = true;
m_startButton->setEnabled(true);
m_stopButton->setEnabled(false);
m_captureButton->setEnabled(false);
m_captureThreeButton->setEnabled(false);
m_startRecordVideoButton->setEnabled(false);
m_stopRecordVideoButton->setEnabled(false);
if (m_videoCapture->stopCapture())
{
m_isCapturing = false;
}
m_frame.fill(Qt::GlobalColor::white);
presentFrame();
if (m_isRecordingVideo)
{
emit finishRecordVideo();
}
}
QString str2qstr(const std::string str)
{
return QString::fromLocal8Bit(str.data());
}
std::string qstr2str(const QString qstr)
{
QByteArray cdata = qstr.toLocal8Bit();
return std::string(cdata);
}
void WebcamWindow::onClickStartRecordVideoButton()
{
startRecordVideo();
emit sendSpeakMessage(tr("start record video"));
}
void WebcamWindow::startRecordVideo()
{
m_startRecordVideoButton->setEnabled(false);
m_stopRecordVideoButton->setEnabled(true);
QString name = m_name->text();
if (name.isEmpty())
{
name = tr("unknown");
}
QString app_dir = m_appDirPath;
QDir dir;
if (!dir.exists(app_dir))
{
dir.mkpath(app_dir);
}
auto pt = getTimeString();
std::stringstream ss;
ss << pt << ".avi";
std::string filename;
while (!ss.eof())
{
std::string res;
ss >> res;
filename += res;
}
app_dir = app_dir + "/" + name;
if (!dir.exists(app_dir))
{
dir.mkpath(app_dir);
}
app_dir = app_dir + "/" + filename.c_str();
auto device = m_videoCapture->getActiveDevice();
VideoDevice::Properties prop = device->getCurrentProperties();
long width = prop.width;
long height = prop.height;
filename = qstr2str(app_dir);
if (!m_aviWriter.open(filename.c_str()))
{
LOG_ERROR("Fail to open AVI file %s to write", app_dir.toStdString().c_str());
m_startRecordVideoButton->setEnabled(true);
return;
}
LOG_INFO("Open AVI video file %s", filename.c_str());
double real_fps = m_fps;
if (real_fps < 10)
{
real_fps = 10.0;
}
m_aviWriter.setVideo(width, height, real_fps, "MJPG");
m_isRecordingVideo = true;
app_dir = app_dir + tr(" start recording.");
emit sendStatusBarMessage(app_dir);
QString speak_msg = tr(" start recording.");
emit sendSpeakMessage(speak_msg);
}
void WebcamWindow::stopRecordVideo()
{
emit finishRecordVideo();
QString speak_msg = tr("Video finish record.");
emit sendSpeakMessage(speak_msg);
emit sendStatusBarMessage(speak_msg);
}
void WebcamWindow::browse()
{
QString directory = QFileDialog::getExistingDirectory(this, tr("Get output directory"), QDir::currentPath());
if (!directory.isEmpty())
{
m_appDirPath = directory;
m_directory->setText(directory);
m_settings.setValue("mainwindow/directory", m_appDirPath);
}
}
QString WebcamWindow::getUserName() const
{
return m_name->text();
}
int WebcamWindow::getTimeRange() const
{
return m_timeRange->value();
}
StillImageWorker::StillImageWorker(WebcamWindow *win) : m_win(win) {}
StillImageWorker::~StillImageWorker() noexcept {}
void StillImageWorker::postImage(QImage &img)
{
m_stillImageQueueMtx.lock();
m_stillImageQueue.push_back(std::move(img));
m_stillImageQueueMtx.unlock();
QTimer::singleShot(m_win->getTimeRange(), this, SLOT(handleStillImageQueue()));
}
void StillImageWorker::handleStillImageQueue()
{
m_stillImageQueueMtx.lock();
if (m_stillImageQueue.size())
{
bool is_need_record_video = false;
if (m_stillImageQueue.size() > 1)
{
is_need_record_video = true;
}
// only save the first image
if (is_need_record_video)
{
if (m_win->isRecordingVideo())
{
emit finishRecordVideo();
emit sendSpeakMessage(tr("finish record video"));
}
else
{
emit beginRecordVideo();
}
}
else
{
QImage newFrame = std::move(m_stillImageQueue[0]);
writeQImageToFile(newFrame);
emit sendSpeakMessage(tr("save three photos"));
emit trackExtraImage(2);
}
m_stillImageQueue.clear();
}
m_stillImageQueueMtx.unlock();
}
void StillImageWorker::writeQImageToFile(QImage &img)
{
QString name = m_win->getUserName();
if (name.isEmpty())
{
name = tr("unknown");
}
QString app_dir = m_win->getAppDirPath();
QDir dir;
if (!dir.exists(app_dir))
{
dir.mkpath(app_dir);
}
auto pt = getTimeString();
std::stringstream ss;
ss << pt << ".jpg";
std::string filename;
while (!ss.eof())
{
std::string res;
ss >> res;
filename += res;
}
app_dir = app_dir + "/" + name;
if (!dir.exists(app_dir))
{
dir.mkpath(app_dir);
}
app_dir = app_dir + "/" + filename.c_str();
QString watermark = name + " " + pt.c_str();
writeTextToImage(img, watermark);
img.save(app_dir, "JPG");
app_dir = app_dir + tr(" has already beed saved.");
emit sendStatusBarMessage(app_dir);
}
C++
1
https://gitee.com/fsfzp888/UVCCapture.git
git@gitee.com:fsfzp888/UVCCapture.git
fsfzp888
UVCCapture
UVCCapture
master

搜索帮助