QtのカメラをAndroidで動作させる





Qtはすでにモバイルアプリケーションを開発するのに適した環境ですが、いくつかのポイントは未完成のままです。 そのため、たとえば、カメラを使用して標準的な例を実行しようとすると、Windowsシステムでは動作しますが、Androidでは動作しません。 同時に、Qml経由でカメラを使用する例は非常に機能しています。 つまり、Androidでカメラを操作することは実装されていますが、カメラへの完全なアクセスはありません。 そして、ビデオストリームに自由にアクセスできるようにする場合はどうでしょうか。



QtMultimediaモジュールのソースコードを調べると、カメラの操作に制限がある理由は松葉杖を隠す必要があることが明らかになりました。 また、OpenGLを介してハードウェア出力を提供するには、これらの松葉杖をインストールする必要がありました。 それでも、カメラのビデオストリームへのフルアクセスを提供することは可能です。



説明を始める前に、個別の写真を取得するために以下に説明するすべてを行う必要はないことを警告する価値があります。 Qmlを介してカメラを使用し、その上にコンポーネントを書き込むだけで、個々のフレームをキャプチャできます。 しかし、どのように、それはここに書かれています



すべてをゼロから記述しないために、「Camera Example」と呼ばれるQtの例(スクリーンショットにあります)を使用し、機能させます。 画像を表示するには、QCameraViewfinderクラスのオブジェクトを使用します。 代わりに独自に作成します。 また、出力にはOpenGLを使用する必要があります。



メディアオブジェクトから受信したフレームのカスタム出力クラスを作成するために、Qtは、対話が行われる仮想関数を含む抽象クラスQAbstractVideoSurfaceを準備します。 フレームに基づいて独自のクラスを作成します。このクラスはフレームの受信を担当し、CameraSurfaceと呼びます。 また、QOpenGLWidgetから継承されたCameraSurfaceWidgetクラスがフレーム出力を担当します。 これらの2つのクラスを組み合わせることが可能ですが、QAbstractVideoSurfaceとQOpenGLWidgetから継承する場合、QObjectクラスからの二重継承が発生します。 そして、あなたはそれをすることはできません。



以下の実装コード全体を見ることができますが、ここでは重要な点について簡単に説明します。 念のため、 ここで QAbstractVideoSurfaceクラスを操作する方法について詳しく知ることができます



関数bool CameraSurface :: present(const QVideoFrame&frame)で新しいフレームを取得します。 フレームパラメータは、ビデオストリームの非常に新しいフレームです。 カメラから取得できるデータは、配列の形式(WindowsまたはSymbianで発生)またはテクスチャの形式(Androidで)になります。 また、テクスチャがある場合は、すぐに読み取らないでください。 frame.handle()を呼び出すと、テクスチャインデックスを取得しているだけだと思う​​かもしれませんが、実際には、同時にストリームのOpenGLコンテキストに基づいたリソースの初期化がトリッキーです。 ただし、この関数はスレッドではなく呼び出されるため、このOpenGLコンテキストはここでは機能しません。 そして、関数宣言のconstキーワードにだまされないようにしましょう。内部のデータは暗黙のうちに可変とマークされます。 フレームをコピーして、描画時にデータを読み取るだけです。



しかし、それだけではありません。 カメラにリンクする場合、CameraSurfaceには非表示の「GLContext」プロパティがあり、OpenGLコンテキストをそこに書き込むことが期待されています。 そして、CameraSurfaceオブジェクトのストリームでこれをより良くするために、つまり、Qtシグナルとスロットの機能を通してスロット呼び出しを使用します。 そして、プロパティオブジェクト「_q_GLThreadCallback」を介して、「GLContext」のエントリに関するイベントを送信します。 そして、このイベントにはQEvent :: Userコードが必要です。 理論的には、これはカスタムタイプのイベントですが、これらの松葉杖についてはまったく知らないはずなので、気にしないでください。 一般に、Windowsではすべてがアクションなしで機能しますが、Androidでこれを行わないと、カメラはフレームの送信を開始しません。



つまり、描画コードは次のようになります。



void CameraSurfaceWidget::paintGL() { if (!_surface->isActive()) {//       ,  _surface->scheduleOpenGLContextUpdate();//     OpenGL QObject* glThreadCallback = (_surface->property("_q_GLThreadCallback")).value<QObject*>();//  , , //     ? if (glThreadCallback) { QEvent event(QEvent::User);//    glThreadCallback->event(&event);//   } //       . , ,     . } else { QVideoFrame& frame = _surface->frame(); //  } }
      
      





その結果、AndroidでストリームとWindowsライクなインターフェイスを処理することができます。 ところで、フレームバッファオブジェクトとglReadPixelsを使用して、フレームテクスチャからデータを引き出すことができます(glGetTexImageはOpenGL ESにはありません)。 そして、これがこれを行う唯一の方法ではありません。 QVideoProbeを介してフレームを受信することはできますが、大幅に遅れるため、すべてがプロセッサで処理されているようです。 そのため、一般的には忘れておく方が良いでしょう。



奇妙なQt
最後にもう1つ奇妙なことがあります。 フレーム形式がFormat_RGB32の場合、カラーチャネルはBG Rの順序になります。形式がFormat_BGR32の場合、RG Bになります。Qtで何かが混同されます。



修正された例をこちらからダウンロードできます。



All Articles