リアルタイム ストリーム

リアルタイムのタイムスタンプ

MediaPipe 電卓グラフは、インタラクティブなアプリケーションの動画フレームまたは音声フレームのストリームを処理するためによく使用されます。MediaPipe フレームワークでは、単調に増加するタイムスタンプを連続するパケットに割り当てる必要があります。慣例により、リアルタイムの計算とグラフでは、各フレームの記録時間または表示時間をタイムスタンプとして使用し、各タイムスタンプは Jan/1/1970:00:00:00 からのマイクロ秒数を示します。これにより、さまざまな送信元からのパケットをグローバルに一貫した順序で処理できます。

リアルタイムのスケジュール設定

通常、各計算ツールは、特定のタイムスタンプの入力パケットがすべて使用可能になるとすぐに実行されます。これは通常、計算ツールが前のフレームの処理を完了し、入力を生成する各計算ツールが現在のフレームの処理を完了したときに発生します。MediaPipe スケジューラは、これらの条件が満たされるとすぐに各計算ツールを呼び出します。詳細については、同期をご覧ください。

タイムスタンプの範囲

計算ツールが特定のタイムスタンプの出力パケットを生成しない場合、代わりにそのタイムスタンプのパケットが生成されないことを示す「タイムスタンプ バウンド」を出力できます。この表示は、そのタイムスタンプの特定のストリームにパケットが到着していなくても、ダウンストリームの計算をそのタイムスタンプで実行できるようにするために必要です。これは、インタラクティブ アプリケーションのリアルタイム グラフで特に重要です。インタラクティブなアプリケーションの場合は、各計算ツールができるだけ早く処理を開始することが重要です。

次のようなグラフについて考えてみましょう。

node {
   calculator: "A"
   input_stream: "alpha_in"
   output_stream: "alpha"
}
node {
   calculator: "B"
   input_stream: "alpha"
   input_stream: "foo"
   output_stream: "beta"
}

タイムスタンプ T で、ノード A が出力ストリーム alpha でパケットを送信していないとします。ノード B はタイムスタンプ Tfoo でパケットを取得し、タイムスタンプ Talpha でパケットを待機しています。Aalpha のタイムスタンプ範囲の更新を B に送信しない場合、B はパケットが alpha に到着するまで待機し続けます。その間、foo のパケットキューでは TT+1 などにパケットが蓄積されます。

ストリームにパケットを出力するために、計算ツールで API 関数 CalculatorContext::OutputsOutputStream::Add を使用します。代わりにストリームにタイムスタンプの範囲を出力するには、計算ツールで API 関数 CalculatorContext::OutputsCalculatorContext::SetNextTimestampBound を使用します。指定された境界は、指定された出力ストリームの次のパケットで許容される最小のタイムスタンプです。パケットが出力されない場合、カルキュレータは通常、次のような処理を行います。

cc->Outputs().Tag("output_frame").SetNextTimestampBound(
  cc->InputTimestamp().NextAllowedInStream());

関数 Timestamp::NextAllowedInStream は、連続したタイムスタンプを返します。例: Timestamp(1).NextAllowedInStream() == Timestamp(2)

タイムスタンプ境界の伝播

リアルタイムのグラフで使用する計算ツールでは、ダウンストリームの計算ツールを迅速にスケジュールできるように、入力タイムスタンプの範囲に基づいて出力タイムスタンプの範囲を定義する必要があります。一般的なパターンは、計算ツールで入力パケットと同じタイムスタンプを持つパケットを出力することです。この場合、Calculator::Process を呼び出すたびにパケットを出力するだけで、出力タイムスタンプの範囲を定義できます。

ただし、計算ツールでは、出力タイムスタンプについてこの一般的なパターンに従う必要はありません。単調に増加する出力タイムスタンプを選択するだけで済みます。そのため、特定の計算ツールでは、タイムスタンプの範囲を明示的に計算する必要があります。MediaPipe には、各計算ツールの適切なタイムスタンプ範囲を計算するためのツールがいくつか用意されています。

1. SetNextTimestampBound() を使用すると、出力ストリームのタイムスタンプの範囲 t + 1 を指定できます。

cc->Outputs.Tag("OUT").SetNextTimestampBound(t.NextAllowedInStream());

または、タイムスタンプ t を持つ空のパケットを生成して、タイムスタンプ バウンド t + 1 を指定することもできます。

cc->Outputs.Tag("OUT").Add(Packet(), t);

入力ストリームのタイムスタンプ境界は、入力ストリームのパケットまたは空のパケットによって示されます。

Timestamp bound = cc->Inputs().Tag("IN").Value().Timestamp();

2. TimestampOffset() を指定すると、入力ストリームから出力ストリームにタイムスタンプにバインドされたタイムスタンプを自動的にコピーできます。

cc->SetTimestampOffset(0);

この設定には、タイムスタンプ境界のみが到着し、Calculator::Process が呼び出されない場合でも、タイムスタンプ境界が自動的に伝播されるという利点があります。

3. ProcessTimestampBounds() を使用すると、新しい「清算タイムスタンプ」ごとに Calculator::Process を呼び出すために指定できます。ここで、「清算タイムスタンプ」は、現在のタイムスタンプ境界よりも下の新しい最も大きいタイムスタンプです。ProcessTimestampBounds() を使用しない場合、Calculator::Process は 1 つ以上の到着パケットとともにのみ呼び出されます。

cc->SetProcessTimestampBounds(true);

この設定により、計算ツールは、入力タイムスタンプのみが更新される場合でも、独自のタイムスタンプ境界の計算と伝播を実行できます。TimestampOffset() の効果を複製するために使用できますが、他の要素を考慮したタイムスタンプ バウンドの計算にも使用できます。

たとえば、SetTimestampOffset(0) を複製するには、電卓で次のことができます。

absl::Status Open(CalculatorContext* cc) {
  cc->SetProcessTimestampBounds(true);
}

absl::Status Process(CalculatorContext* cc) {
  cc->Outputs.Tag("OUT").SetNextTimestampBound(
      cc->InputTimestamp().NextAllowedInStream());
}

計算ツール::オープン、計算::クローズのスケジュール

Calculator::Open は、必要な入力サイドパケットがすべて生成されたときに呼び出されます。入力サイドパケットは、包含アプリケーションまたはグラフ内の「サイドパケット計算ツール」によって提供されます。サイドパケットは、API の CalculatorGraph::InitializeCalculatorGraph::StartRun を使用してグラフ外部から指定できます。サイドパケットは、CalculatorGraphConfig::OutputSidePacketsOutputSidePacket::Set を使用してグラフ内の計算ツールで指定できます。

Calculator::Close は、すべての入力ストリームがクローズされるか、タイムスタンプ バウンド Timestamp::Done に到達して Done になったときに呼び出されます。

注: グラフが保留中の計算ツールの実行をすべて終了して Done になった場合、一部のストリームが Done になる前に、MediaPipe は残りの Calculator::Close の呼び出しを呼び出し、すべての計算ツールが最終出力を生成できるようにします。

TimestampOffset を使用すると、Calculator::Close に影響します。SetTimestampOffset(0) を指定する計算ツールでは、設計上、すべての入力ストリームが Timestamp::Done に達したときに、すべての出力ストリームが Timestamp::Done に達したため、それ以上の出力が不可能であることを示します。これにより、このような計算ツールでは Calculator::Close の最中にパケットが出力されなくなります。計算ツールで Calculator::Close 中にサマリー パケットを生成する必要がある場合、Calculator::Process では、Calculator::Close 中に少なくとも 1 つのタイムスタンプ(Timestamp::Max など)が使用できるように、タイムスタンプ境界を指定する必要があります。つまり、このような計算ツールは通常、SetTimestampOffset(0) に依存できず、代わりに SetNextTimestampBounds() を使用してタイムスタンプの範囲を明示的に指定する必要があります。