在编写一个看似简单的 Qt 应用程序部分,该部分将运行一个子进程并从其标准输出中读取数据时,我偶然发现了一个让我非常困惑的问题。应用程序应该从子进程中读取数据块(原始视频帧)并在它们到达时对其进行处理:
- 启动一个 QProcess
- 收集数据直到有足够的一帧
- 处理帧
- 返回步骤 2
这个想法是使用信号和槽来实现处理循环——这在我下面提供的简单、精简的示例中可能看起来很愚蠢,但在原始应用程序的框架内似乎完全合理。所以我们开始:
app::app() {
process.start("cat /dev/zero");
buffer = new char[frameLength];
connect(this, SIGNAL(wantNewFrame()), SLOT(readFrame()), Qt::QueuedConnection);
connect(this, SIGNAL(frameReady()), SLOT(frameHandler()), Qt::QueuedConnection);
emit wantNewFrame();
}
我从这里开始一个简单的过程 ( cat /dev/zero
),这样我们就可以确信它不会耗尽数据。我还做了两个连接:一个在需要帧时开始读取,另一个在帧到达时调用数据处理函数。请注意,这个简单的示例在单个线程中运行,因此将连接设为排队类型以避免无限递归。该wantNewFrame()
信号启动第一帧的采集;它在控件返回事件循环时得到处理。
bool app::readFrame() {
qint64 bytesNeeded = frameLength;
qint64 bytesRead = 0;
char* ptr = buffer;
while (bytesNeeded > 0) {
process.waitForReadyRead();
bytesRead = process.read(ptr, bytesNeeded);
if (bytesRead == -1) {
qDebug() << "process state" << process.state();
qDebug() << "process error" << process.error();
qDebug() << "QIODevice error" << process.errorString();
QCoreApplication::quit();
break;
}
ptr += bytesRead;
bytesNeeded -= bytesRead;
}
if (bytesNeeded == 0) {
emit frameReady();
return true;
} else
return false;
}
读取帧:基本上,我只是在数据到达时将其填充到缓冲区中。最后的frameReady()
信号表明帧已准备好,进而导致数据处理功能运行。
void app::frameHandler() {
static qint64 frameno = 0;
qDebug() << "frame" << frameno++;
emit wantNewFrame();
}
一个简单的数据处理器:它只计算帧数。完成后,它发出wantNewFrame()
重新开始读取周期。
就是这个。为了完整起见,我还将在此处发布头文件和 main()。
应用程序.h:
#include <QDebug>
#include <QCoreApplication>
#include <QProcess>
class app : public QObject
{
Q_OBJECT
public:
app();
~app() { delete[] buffer; }
signals:
void wantNewFrame();
void frameReady();
public slots:
bool readFrame();
void frameHandler();
private:
static const quint64 frameLength = 614400;
QProcess process;
char* buffer;
};
主.cpp:
#include "app.h"
int main(int argc, char** argv)
{
QCoreApplication coreapp(argc, argv);
app foo;
return coreapp.exec();
}
现在是奇怪的部分。该程序可以很好地处理随机数量的帧(我见过从 15 到 1000 多个),但最终停止并抱怨 QProcess 崩溃了:
$ ./app
frame 1
...
frame 245
frame 246
frame 247
process state 0
process error 1
QIODevice error "Process crashed"
进程状态 0 表示“未运行”,进程错误 1 表示“崩溃”。我对其进行了调查,发现子进程收到了一个 SIGPIPE——即父进程已经关闭了它的管道。但我完全不知道发生这种情况的地点和原因。还有人吗?