9-5 ステータスコールバックと画像スナップショット機能の例

プログラムをもっとスマートに:映像ステータスのリアルタイム把握とワンクリックスナップショット!

チャプター 10-5 へようこそ!これまでの実践では、キャプチャーカードの映像をウィンドウに表示させることに成功しました。しかし、実際のオーディオ・ビデオ開発は往々にしてさらに複雑です。ユーザーが突然ケーブルを抜いてしまったらどうすればよいでしょうか?信号の解像度が急に変更された場合、プログラムはどうやってそれを検知するのでしょうか?あるいは、素晴らしい瞬間をスナップショットとして保存したい場合はどうすればよいでしょうか?

本章では、NexVDO SDK の2つの実用的な機能、「Callback( ステータスコールバック )」と「Snapshot( 画像スナップショット )」を解禁します。まずは、今日完成させる最終的な成果を見てみましょう:


© Blender Foundation | Big Buck Bunny | CC BY 3.0

ウィンドウの下部に情報バーを追加し、現在の信号ステータス( 解像度、FPS、)をリアルタイムで表示できるようにします。さらに、2つの便利なスナップショットボタンも追加します。準備はいいですか?それでは始めましょう!

コアAPIの理解 - コールバック関数 ( Callback )

コードを書き始める前に、今日の主役を紹介しましょう:

コールバック関数は、プログラム内に配置した「情報員」のようなものです。特定のイベントが発生すると、その情報員が自発的にあなたへ報告してくれます。

NexVDO SDK は、大きく分けて2種類のコールバック(Callback)を提供しています

1. イベントコールバック ( Event Callback ) : 「受動的トリガー」メカニズムを採用しており、特定のイベントが発生した時(例:解像度の変更、信号の切断、信号の未検出など)にのみシステムから呼び出されます。



2. データコールバック ( Data Callback ) : 「リアルタイム供給」を担当します。一度起動すると、基盤となるレイヤーからキャプチャされた生の映像/音声データを、途切れることなくリアルタイムでメインプログラムに提供し続けます。

取扱説明書を確認すると、各コールバック機能には、非常によく似た2つの API 名が含まれていることに気づくでしょう。例えば:

• QCAP_REGISTER_FORMAT_CHANGED_CALLBACK
• PF_FORMAT_CHANGED_CALLBACK

この2つは一体何が違うのでしょうか?簡単に言えば、1つは「連絡先の登録」であり、もう1つは「情報員が報告してくるフォーマット」です。

1. 連絡先の登録 : QCAP_REGISTER_... ( 登録関数 )

この関数は あなた自身が自発的に呼び出す ものです。その役割は、警備室に行って「信号を見かけたら、この番号に電話してください」と登録するのと同じです。必要なのは3つの基本的なパラメータだけです:



⚠️ 開発のヒント:登録タイミングの鉄則! ぜひ覚えておいてください:すべてのコールバックは、 必ず QCAP_CREATE の後に登録する必要があります。デバイスが作成される前に、焦って情報員を配置しないでくださいね!


2. 情報員の報告フォーマット:PF_... ( コールバック関数の定義 )

これは、あなたが実際にコード内で実装する関数です。先ほど登録したイベントが発生すると、SDKが自動的にこの関数をトリガーし 

、豊富な低レベル情報をあなたに提供してくれます!

今日は、最もよく使われる3つの「イベント情報員」に焦点を当てます。以下は、それぞれのトリガーされるタイミングと、もたらされる情報パラメータです:

イベントコールバック - 映像フォーマットの変更

• 登録 API :QCAP_REGISTER_FORMAT_CHANGED_CALLBACK
• トリガーのタイミング : システムが信号を正常に検出した時、または映像の入力フォーマットが変更された時に、この関数が呼び出されます。
• 返される情報パラメータ ( PF_FORMAT_CHANGED_CALLBACK ) :この関数は非常に詳細な映像および音声パラメータを返します。そのパラメータから必要な情報を直接取得し、画面に表示させることができます:

イベントコールバック - 有効な信号なし

• 登録 API QCAP_REGISTER_NO_SIGNAL_DETECTED_CALLBACK
• トリガーのタイミング : デバイスが有効な指定信号を一切検出できない時に、このコールバックがトリガーされます。
• 返される情報パラメータ ( PF_NO_SIGNAL_DETECTED_CALLBACK ) :

イベントコールバック - 信号の切断(取り外し)

• 登録 API :QCAP_REGISTER_SIGNAL_REMOVED_CALLBACK
• トリガーのタイミング : デバイスが正常に動作している最中に、何らかの突発的な理由(例:ユーザーが誤って HDMI/SDI ケーブルを抜いてしまったなど)によって信号が突然失われた時に、この関数が即座に起動します。
• 返される情報パラメータ ( PF_SIGNAL_REMOVED_CALLBACK ) :

データコールバック - 映像データの取得

• 登録 API:QCAP_REGISTER_VIDEO_PREVIEW_CALLBACK
• トリガーのタイミング : デバイスが各映像フレームをキャプチャするたびにこの関数が即座にトリガーされ、キャプチャされたばかりの生の映像データがあなたに渡されます。
• 返される情報パラメータ ( PF_VIDEO_PREVIEW_CALLBACK ):

ヒント : この映像コールバック関数には、非常に実用的な隠しテクニックがあります。システムから渡された pFrameBuffer が NULL (空)であり、かつ nFrameBufferLen が 0 の場合、これは現在のキャプチャーカードに「信号が接続されていない」ことを意味します!この特性を利用して、フールプルーフ(エラーハンドリング処理)を行うことができます。

データコールバック - 音声データの取得

• 登録 API :QCAP_REGISTER_AUDIO_PREVIEW_CALLBACK
• トリガーのタイミング : デバイスが音声をキャプチャした時にこの関数がトリガーされ、生の音声データを提供します。
• 返される情報パラメータ ( PF_AUDIO_PREVIEW_CALLBACK ) :

ヒント : 映像のコールバックと同様に、音声の pFrameBuffer が NULL であり、かつ nFrameBufferLen が 0 の場合も、現在信号が入力されていないことを意味します。

コアAPIの理解 - 画像スナップショット ( Snapshot )

キャプチャーカードの映像を無事に画面へ表示できたら、次に最もよく使われる機能は間違いなく「スナップショットの保存」です!対応する関数を呼び出すだけで、BMP、JPG、PNG、TIF、さらには医療用のDICOM(DCM)フォーマットまで簡単に出力できます。

スナップショットの前提条件 : すべてのスナップショットAPIは、画像を正常に取得するために「デバイスが正常に接続され、かつ映像信号の入力がある」状態になってから呼び出す必要があります。


SDKは「圧縮するかどうか」と「クロッピング(切り抜き)に対応するかどうか」に基づき、スナップショット機能を以下の4つのコアAPIに分かりやすく分類しています。ニーズに合わせて自由に選択できます:

基本スナップショットAPI:全画面保存

画面全体をシンプルに保存したいだけであれば、以下の2つの最も基本的なAPIを使用できます:

• QCAP_SNAPSHOT_BMP :非圧縮の生画像をキャプチャします(BMP、DCMに対応)。
• QCAP_SNAPSHOT_JPG :容量を節約するために圧縮画像をキャプチャします(JPG、PNG、TIF、DCMに対応)。

これら2つのAPIのコアパラメータは以下の通りです:

概念の整理:非同期 (Asynchronous) と同期 (Synchronous) とは?

先ほどのパラメータの中で、SDKは「UIのフリーズ(カクつき)」を解決するために bIsAsync というパラメータを設計しています:

• 非同期モード ( Asynchronous ) : TRUE に設定(デフォルト)。メインプログラムがスナップショットのコマンドを出した後、すぐに実行を継続し、バックグラウンドでひそかにファイルを保存します。スナップショットによってUIがフリーズすることは絶対にありません!
• 同期モード ( Synchronous ) : FALSE に設定。メインプログラムはファイルの保存が完了するまで立ち止まって待機しなければならず、完了してから次に進みます。

推奨 : スナップショット処理をシルクのようにスムーズにするため、デフォルトの TRUE を維持することを強くお勧めします!

高度なスナップショット API:部分的なクロッピング( 切り抜き )とスケーリング( 縮尺 )に対応

より高度な機能を使用したい場合は、末尾に _EX が付く API(例:  QCAP_SNAPSHOT_JPG_EX ) を使用できます。これにより、キャプチャ時に「画像のクロッピング」と「スケーリング」を直接完了させることができます。


© Blender Foundation | Big Buck Bunny | CC BY 3.0

基本的な保存パスに加えて、 _EX  バージョンの API には画像を正確に制御するための6つの重要な幾何学的パラメータが追加されています:

UI 表示インターフェースの構築

ここでは、チャプター 9-4 ですでに作成した キャプチャ表示機能プロジェクト をそのまま引き継ぎます。開発のワークフローをよりスムーズにするため、必要な UI コンポーネントを一度すべて配置してから、C++ コードのロジック処理に集中します。Qt Creator の左側にあるプロジェクトディレクトリで、 mainwindow.ui をダブルクリックしてデザインモード ( Design Mode ) に入ります:



• 情報表示エリアの作成 ( Callback 機能に対応 )
    
    まず、Callback の情報員のために専用の「放送用スピーカー」を用意します。これは、基盤レイヤーから報告される映像の解像度、FPS、または切断時の警告をリアルタイムで表示するためのものです。

左側のツールバーの Widgets Box から、 QLabel を画面の空白部分にドラッグ&ドロップします。
    
    右下のプロパティエディタ ( Property Editor ) で objectName プロパティを見つけ、それを m_info に変更します。これが、後でコード内で呼び出すための専用コードネームになります!



• スナップショットボタンの作成 ( Snapshot 機能に対応 )
    
    次に、ユーザーがいつでもスナップショット機能をトリガーできるように、2つの物理的なボタンを提供します。
    
    左側のツールバーの Buttons カテゴリから、2つの QPushButtons を画面上にドラッグ&ドロップします。
    
   右下のプロパティエディタで text  プロパティを見つけるか(またはボタンを直接ダブルクリックして)、これら2つのボタンの表示テキストをそれぞれ SNAPSHOT BMPSNAPSHOT JPG に変更します。

素晴らしい!これで私たちの「開発キャンバス」の準備が整いました。情報を受信するための専用ラベルブロックがあり、押すことができる2つの物理的なボタンがあります。

すべて整いました。次に mainwindow.cpp に切り替えてください。いよいよこれらの UI コンポーネントに魂を吹き込み、基盤となる API と連携させていきます!

コアコードの記述

キャンバスの準備ができたら、Qt Creator をコードモード ( Code Mode ) に切り替えてください。  mainwindow.hmainwindow.cpp を開いて実装していきましょう!

Callback 情報報告メカニズムの実装

• UI 更新関数 ( setInfoText ) の作成
    
    Callback は基盤となる SDK によってトリガーされるため、先ほど作成した m_info ラベルに情報をスムーズに表示するには、仲介役となる関数が必要です。まず  mainwindow.h を開き、 public セクションでこの関数を宣言してください:



続いて、 mainwindow.cpp を開いてこの関数を実装します:



• Callback 情報員のアクションの定義 ( PF_... )
    
    次に、 mainwindow.cpp  の上部( MainWindow コンストラクタの前)に、コールバック関数の定義を記述します。
    
   ここで、先ほど言及した pUserData のテクニックを活用し、それを MainWindow にキャスト(型変換)します。これにより、書き立ての setInfoText 関数を呼び出して画面を更新することができます:





• 情報員をライフサイクルに登録する ( QCAP_REGISTER_... )
    
   タスクを定義したら、それらをデバイスにバインド( 関連付け )することを忘れないでください。 MainWindow  コンストラクタ内の  QCAP_CREATE の後に、これら5つの Callback を順番に登録してください:

    // ... Omitted (QCAP_CREATE device creation) ...

    // Sequentially register the five major Callbacks, remember to pass 'this' as the last 
    QCAP_REGISTER_FORMAT_CHANGED_CALLBACK(m_pDevice, on_format_changed_cb, this);
    QCAP_REGISTER_NO_SIGNAL_DETECTED_CALLBACK(m_pDevice, on_no_signal_detected_cb, this);
    QCAP_REGISTER_SIGNAL_REMOVED_CALLBACK(m_pDevice, on_signal_removed_cb, this);
    QCAP_REGISTER_VIDEO_PREVIEW_CALLBACK(m_pDevice, on_video_preview_cb, this);
    QCAP_REGISTER_AUDIO_PREVIEW_CALLBACK(m_pDevice, on_audio_preview_cb, this);

    // ... Omitted (QCAP_RUN start device) ...


⚠️ マインダー :  本章の QCAP_REGISTER_... は、すべて QCAP_CREATE の後、かつ QCAP_RUN の前に記述する必要があります!また、最後のパラメータには必ず this を渡してください。

スナップショットボタンのコードを書き続ける前に、先ほどの努力の成果をテストしてみましょう。Qt Creator の左下にある 緑色の三角形の再生ボタン ( Run ) をクリックして、現在のプロジェクトをビルドおよび実行します。

ウィンドウがポップアップし、キャプチャーカードが映像信号を正常に受信したら、画面の左下に視線を移してみてください。先ほど配置した空白の QLabel が、基盤となる情報員から報告されたリアルタイム情報を正確に表示していることに驚くはずです!


© Blender Foundation | Big Buck Bunny | CC BY 3.0

スナップショットボタン機能の実装

Callback はすでに順調に動作し始めました。最後のステップは、画面上の2つのスナップショットボタンに魂を吹き込む(命を与える)ことです!

Qt 開発において、ボタンをクリックした後に反応させるには、「シグナルとスロット (Signals and Slots)」メカニズムを使用する必要があります。以下の手順に従って、基盤となる Snapshot API をボタンにバインド(関連付け)しましょう:

• 自動生成コードのトリガー : 一時的に mainwindow.ui のデザイン画面に切り替えてください。先ほど作成した SNAPSHOT BMP ボタンを右クリック ➔ 「Go to slot... (スロットへ移動)」 を選択 ➔
 clicked()を選択します。



• この時、Qt Creator は非常に親切に自動で画面を mainwindow.cpp にジャンプさせ、 on_pushButton_clicked() という名前の空白の関数を用意してくれます! 私たちはこの空白のブロック内に、メーカーが提供する API を呼び出すだけで済みます。

ワクワクしながらスナップショット (Snapshot) ボタンを UI インターフェースに配置した後、まずは立ち止まって一つの問題を考えてみましょう: 「もしユーザーのカメラが全く接続されていなかったり(あるいは途中で抜かれてしまったり)した場合、このタイミングで録画や撮影ボタンを押すと何が起こるでしょうか?」

答えは:プログラムが映像を取得できずにクラッシュ(強制終了)してしまう可能性があります!

この問題を解決するために、 b_IsNoSignal という名前のフールプルーフスイッチ(エラー防止フラグ)を利用できます。その動作ロジックは非常にスマートで、以下の3つのステップに分かれています:

スイッチの宣言と初期化

まず、 mainwindow.hでこの変数を宣言し、プログラムの起動時 ( mainwindow.cpp のコンストラクタ内)に、 デフォルトで true( 現在信号がないことを意味する )に設定 します。プログラムが開いたばかりで、ハードウェアの初期化がまだ完全に終わっていない時は、確かに映像がないからです。

Callback との連動

この変数を、基盤となる「イベントシグナル」に従って自動的にステータスを切り替えるようにします:

• 信号が失われた時 : もしカメラが抜かれたり、信号が取得できなかったりした場合、SDK は
 on_no_signal_detected_cbまたは on_signal_removed_cb をトリガーします。これら2つの Callback 内で、変数を pMainDlg->b_IsNoSignal = true; に戻すことで、「今は映像がありません!」と宣言します。
• 信号が正常に接続された時 : カメラが正常に動作している場合、SDK は
 on_format_changed_cb ( 解像度などのフォーマットを取得したことを意味する)をトリガーします。この時、すぐに変数を pMainDlg->b_IsNoSignal = false; に変更し、「映像の準備が完了しました!」ということを表します。



ボタンに「フールプルーフ(エラー防止)ゲート」を追加する

最後に、スナップショット ( Snapshot ) ボタンのスロット関数に戻り、コア API を実行する前に、厳格なフールプルーフゲートを追加します:

最終確認(動作検証)

これですべてのコードが書き上がりました!Qt Creator の左下にある 緑色の三角形の再生ボタン ( Run ) をもう一度クリックして、プロジェクトをビルドおよび実行してください。

画面に映像がスムーズに表示されたら、思い切って画面下部の SNAPSHOT BMP と   SNAPSHOT JPG ボタンを押してみてください!映像が非常にスムーズで、全くフリーズ(カクつき)がないことに気づくでしょう。

➤ 宝探しタイム:キャプチャした画像はどこに保存された? Qt に触れたばかりの初心者は、保存したばかりの画像を見つけられないことがよくあります。コード内では相対パス( 例: "SNAPSHOT.JPG" )しか記述していないため、画像は Qt プロジェクトのビルド出力フォルダに保存されます。

• ファイルエクスプローラーを開き、プロジェクトのルートディレクトリの1つ上の階層に戻ってください。
•  build-SC710N1QT-Desktop-Debug  という名前のフォルダを探します(名前はプロジェクトやコンパイラの設定によって若干異なります)。
• そのフォルダを開くと、撮影したばかりの .BMP と .JPG のスナップショットファイルがすでにそこにあるのが見えますよ!

ミッション完了(大成功)!NexVDO SDK の Callback と Snapshot の高度な機能の実践を無事に完了したこと、おめでとうございます!


© Blender Foundation | Big Buck Bunny | CC BY 3.0

Copyright © 2026 YUAN High-Tech Development Co., Ltd.
All rights reserved.