Strumienie w czasie rzeczywistym

sygnatury czasowe w czasie rzeczywistym;

Wykresy kalkulatora MediaPipe są często wykorzystywane do przetwarzania strumieni ramek wideo lub audio w aplikacjach interaktywnych. Platforma MediaPipe wymaga tylko, aby do kolejnych pakietów były przypisywane monotonicznie rosnące sygnatury czasowe. Zgodnie z konwencją kalkulatory i wykresy w czasie rzeczywistym wykorzystują jako sygnaturę czasową czas rejestracji lub czasu prezentacji każdej klatki, a każda sygnatura czasowa wskazuje mikrosekundy od Jan/1/1970:00:00:00. Dzięki temu pakiety z różnych źródeł mogą być przetwarzane w spójnej globalnie kolejności.

Planowanie w czasie rzeczywistym

Normalnie każdy kalkulator jest uruchamiany w momencie, gdy dostępne są wszystkie pakiety wejściowe dla danej sygnatury czasowej. Zwykle dzieje się tak, gdy kalkulator zakończy przetwarzanie poprzedniej klatki, a każdy kalkulator generujący jego dane wejściowe zakończy przetwarzanie bieżącej klatki. Algorytm szeregowania MediaPipe wywołuje każdy kalkulator, gdy tylko zostaną spełnione te warunki. Więcej informacji znajdziesz w sekcji Synchronizacja.

Granice sygnatury czasowej

Gdy kalkulator nie wygeneruje żadnych pakietów wyjściowych dla danej sygnatury czasowej, może zamiast tego zwrócić „powiązaną sygnaturę czasową” wskazującą, że dla tej sygnatury czasowej nie zostanie wygenerowany żaden pakiet. To oznaczenie jest konieczne, aby kalkulatory pobierania danych mogły działać w danej sygnaturze czasowej, nawet jeśli w przypadku określonych strumieni dla tej sygnatury czasowej nie dotarł do Ciebie żaden pakiet. Jest to szczególnie ważne w przypadku wykresów w czasie rzeczywistym w aplikacjach interaktywnych, gdzie każdy kalkulator musi jak najszybciej rozpocząć przetwarzanie.

Weźmy na przykład wykres podobny do tego:

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

Załóżmy, że w sygnaturze czasowej T węzeł A nie wysyła pakietu w strumieniu wyjściowym alpha. Węzeł B otrzymuje pakiet w komórce foo o sygnaturze czasowej T i czeka na pakiet w strefie czasowej alpha o sygnaturze czasowej T. Jeśli A nie wyśle do B aktualizacji powiązanej z sygnaturą czasową dla zasobu alpha, B będzie czekać na dostarczenie pakietu w ciągu alpha. W międzyczasie kolejka pakietów foo będzie gromadzić pakiety w T, T+1 i tak dalej.

Aby wysłać pakiet w strumieniu, kalkulator używa funkcji interfejsu API CalculatorContext::Outputs i OutputStream::Add. Aby zamiast tego podawać znacznik czasu powiązany w strumieniu, kalkulator może użyć funkcji interfejsu API CalculatorContext::Outputs i CalculatorContext::SetNextTimestampBound. Określona granica to najniższa dozwolona sygnatura czasowa następnego pakietu w określonym strumieniu wyjściowym. Gdy żaden pakiet nie zostanie wygenerowany, kalkulator zazwyczaj wykonuje taką czynność:

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

Funkcja Timestamp::NextAllowedInStream zwraca kolejną sygnaturę czasową. Na przykład: Timestamp(1).NextAllowedInStream() == Timestamp(2).

Propagacja granic sygnatury czasowej

Aby umożliwić szybkie zaplanowanie kalkulatorów danych wejściowych, kalkulatory, które będą używane na wykresach w czasie rzeczywistym, muszą określać granice sygnatur czasowych wyników na podstawie wejściowych progów sygnatury czasowej. Typowym wzorcem jest to, że kalkulatory wyprowadzają pakiety z takimi samymi sygnaturami czasowymi jak pakiety wejściowe. W tym przypadku do określenia granic sygnatury czasowej wyjściowej wystarczy po prostu wystawienie pakietu przy każdym wywołaniu funkcji Calculator::Process.

Kalkulatory nie muszą jednak stosować się do tego wspólnego wzorca dla sygnatur czasowych wyjściowych danych wyjściowych. Muszą jednak wybrać jedno-stopniowo rosnące sygnatury czasowe danych wyjściowych. W związku z tym niektóre kalkulatory muszą wyraźnie obliczać granice sygnatury czasowej. MediaPipe udostępnia kilka narzędzi do obliczania odpowiedniej sygnatury czasowej dla każdego kalkulatora.

1. Funkcji SetNextTimestampBound() można użyć do określenia sygnatury czasowej t + 1 w strumieniu wyjściowym.

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

Możesz też utworzyć pusty pakiet z sygnaturą czasową t, aby określić sygnaturę czasową t + 1.

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

Związana ze sygnaturą czasową strumień danych wejściowych jest określona przez pakiet lub pusty pakiet w strumieniu wejściowym.

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

2. Można określić funkcję TimestampOffset(), aby automatycznie kopiować sygnaturę czasową graniczną ze strumieni danych wejściowych do strumieni wyjściowych.

cc->SetTimestampOffset(0);

To ustawienie ma tę zaletę, że propagacja granic sygnatury czasowej następuje automatycznie, nawet jeśli pojawią się tylko granice sygnatur czasowych, a kalkulator::Proces nie jest wywoływany.

3. Można określić metodę ProcessTimestampBounds(), aby wywoływać metodę Calculator::Process w przypadku każdej nowej „rozliczonej sygnatury czasowej”, gdzie „rozliczona sygnatura czasowa” to nowa najwyższa sygnatura czasowa poniżej bieżących granic sygnatury czasowej. Bez ProcessTimestampBounds() funkcja Calculator::Process jest wywoływana tylko po otrzymaniu co najmniej jednego pakietu przychodzącego.

cc->SetProcessTimestampBounds(true);

To ustawienie pozwala kalkulatorowi wykonywać własne obliczenia i propagacje sygnatur czasowych, nawet jeśli aktualizowane są tylko wejściowe sygnatury czasowe. Można go użyć do powtórzenia działania funkcji TimestampOffset(), ale także do obliczenia sygnatury czasowej, która uwzględnia dodatkowe czynniki.

Żeby na przykład powielić SetTimestampOffset(0), kalkulator może wykonać te działania:

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

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

Harmonogram korzystania z Kalkulatora::Otwórz i Kalkulator::Zamknij

Calculator::Open jest wywoływana po utworzeniu wszystkich wymaganych pakietów dodatkowych. Wejściowe pakiety boczne można dostarczać przez aplikację otaczającą lub za pomocą „kalkulatorów pakietów bocznych” na wykresie. Pakiety boczne można określać poza wykresem za pomocą interfejsów CalculatorGraph::Initialize i CalculatorGraph::StartRun interfejsu API. Pakiety boczne można określić za pomocą kalkulatorów na wykresie za pomocą funkcji CalculatorGraphConfig::OutputSidePackets i OutputSidePacket::Set.

Kalkulator::zamknięcie jest wywoływane, gdy wszystkie strumienie wejściowe mają stan Done przez zamknięcie lub osiągnięcie sygnatury czasowej Timestamp::Done.

Uwaga: jeśli wykres zakończy wszystkie oczekujące wykonanie kalkulatora i zmieni wartość na Done, zanim niektóre strumienie zmienią się na Done, MediaPipe wywoła pozostałe wywołania Calculator::Close, aby każdy kalkulator mógł uzyskać ostateczne wyniki.

Korzystanie z tagu TimestampOffset ma wpływ na usługę Calculator::Close. Kalkulator, który określa SetTimestampOffset(0) sygnał projektowy, że wszystkie jego strumienie wyjściowe osiągnęły Timestamp::Done, gdy wszystkie strumienie wejściowe osiągnęły Timestamp::Done. W związku z tym nie będzie można uzyskać dalszych danych wyjściowych. Dzięki temu taki kalkulator nie wysyła pakietów w trakcie Calculator::Close. Jeśli kalkulator musi wygenerować pakiet podsumowania podczas funkcji Calculator::Close, Calculator::Process musi określić granice sygnatury czasowej, tak aby w funkcji Calculator::Close była dostępna co najmniej 1 sygnatura czasowa (na przykład Timestamp::Max). Oznacza to, że taki kalkulator zwykle nie może polegać na parametrze SetTimestampOffset(0) i musi w tym celu wyraźnie określić granice sygnatury czasowej za pomocą funkcji SetNextTimestampBounds().