在编写Qt应用程序的一个看似简单的部分时,它会运行一个子进程并从其标准输出中读取数据,但我偶然发现了一个让我感到困惑的问题。应用程序应该从子进程读取数据块(原始视频帧)并在它们到达时处理它们:
这个想法是使用信号和插槽来实现处理循环 - 这在我在下面提供的简单的,简化的示例中可能看起来很愚蠢,但在原始应用程序的框架内似乎完全合理。所以我们走了:
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()。
app.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;
};
main.cpp中:
#include "app.h"
int main(int argc, char** argv)
{
QCoreApplication coreapp(argc, argv);
app foo;
return coreapp.exec();
}
现在是奇怪的部分。这个程序处理一个随机数量的帧就好了(我已经看到了从十五到一千多的任何东西),但最终停止并抱怨QProcess崩溃了:
$ ./app
frame 1
...
frame 245
frame 246
frame 247
process state 0
process error 1
QIODevice error "Process crashed"
进程状态0表示“未运行”,进程错误1表示“崩溃”。我调查了它,发现子进程收到了一个SIGPIPE - 即父进程关闭了它的管道。但我绝对没有关于这种情况发生的原因和原因。还有其他人吗?
答案 0 :(得分:0)
代码有点奇怪(不使用readyRead
信号,而是依赖于延迟信号/插槽)。正如您在讨论中指出的那样,您已经看到thread on the qt-interest ML我询问过类似问题的地方。我刚刚意识到我当时也使用了QueuedConnection
。我无法解释为什么它是错的 - 在我看来,排队的信号“应该有效”。盲目的是,Qt的实现使用的invokeMethod
以某种方式与您的信号传递竞争,以便在Qt有机会处理数据之前清空读取缓冲区。这意味着Qt将最终读取零字节并(正确地)将其解释为EOF
,从而关闭管道。
我找不到引用的“Qt任务217111”了,但是他们的Jira中有一些关于waitForReadyRead
无法按照用户期望工作的报告,请参阅例如QTBUG-9529
我会把它带到Qt的“兴趣”邮件列表中,并且不要使用waitFor...
系列方法。我同意他们的文件值得更新。