2

サブプロセスを実行し、標準出力からデータを読み取る Qt アプリケーションの一見単純な部分をコーディングしているときに、私は本当に困惑する問題に出くわしました。アプリケーションは、サブプロセスからデータのブロック (生のビデオ フレーム) を読み取り、到着時に処理する必要があります。

  1. QProcess を開始する
  2. 1 フレーム分になるまでデータを収集する
  3. フレームを加工する
  4. ステップ 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) を開始して、データが不足しないことを確信できるようにします。また、2 つの接続を作成します。1 つはフレームが必要なときに読み取りを開始し、もう 1 つはフレームの到着時にデータ処理関数を呼び出します。この単純な例は単一のスレッドで実行されるため、無限再帰を回避するために接続がキュー タイプになることに注意してください。この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();
}

そして今、奇妙な部分です。このプログラムはランダムな数のフレームを問題なく処理しますが (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 を受け取っていることがわかりました。つまり、親プロセスがそのパイプを閉じていたのです。しかし、私はこれがどこで、なぜ起こるのか全くわかりません。他の人はいますか?

4

1 に答える 1