실시간 스트림

실시간 타임스탬프

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의 패킷 큐에는 T, T+1 등에서 패킷이 누적됩니다.

스트림에서 패킷을 출력하기 위해 계산기는 CalculatorContext::OutputsOutputStream::Add API 함수를 사용합니다. 대신 스트림에 타임스탬프 경계를 출력하기 위해 계산기는 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. 새로운 '정해진 타임스탬프'마다 Calculator::Process를 호출하기 위해 ProcessTimestampBounds()를 지정할 수 있습니다. 여기서 '정해진 타임스탬프'는 현재 타임스탬프 경계 아래의 새로운 최고 타임스탬프입니다. ProcessTimestampBounds()가 없으면 Calculator::Process은 하나 이상의 도착 패킷으로만 호출됩니다.

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를 사용하여 지정할 수 있습니다.

계산기::Close는 닫히거나 타임스탬프 경계 Timestamp::Done에 도달하여 모든 입력 스트림이 Done가 되면 호출됩니다.

참고: 그래프가 대기 중인 계산기 실행을 모두 마치고 Done가 되는 경우 일부 스트림이 Done가 되기 전에 MediaPipe는 나머지 Calculator::Close 호출을 호출하여 모든 계산기가 최종 출력을 생성할 수 있도록 합니다.

TimestampOffset를 사용하면 Calculator::Close에 몇 가지 영향을 미칩니다. SetTimestampOffset(0)를 지정하는 계산기는 모든 입력 스트림이 Timestamp::Done에 도달했을 때 모든 출력 스트림이 Timestamp::Done에 도달했기 때문에 더 이상 출력이 불가능하다고 설계되었다는 신호를 받게 됩니다. 이렇게 하면 이러한 계산기가 Calculator::Close 중에 패킷을 내보내지 않습니다. 계산기가 Calculator::Close 중에 요약 패킷을 생성해야 하는 경우 Calculator::ProcessCalculator::Close 중에 하나 이상의 타임스탬프 (예: Timestamp::Max)를 사용할 수 있도록 타임스탬프 경계를 지정해야 합니다. 즉, 이러한 계산기는 일반적으로 SetTimestampOffset(0)를 사용할 수 없으며 대신 SetNextTimestampBounds()를 사용하여 명시적으로 타임스탬프 경계를 지정해야 합니다.