실시간 타임스탬프
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
은 타임스탬프 T
의 foo
에서 패킷을 가져오고 타임스탬프 T
의 alpha
에서 패킷을 기다리고 있습니다. A
가 alpha
의 타임스탬프 경계 업데이트를 B
에 전송하지 않으면 B
는 패킷이 alpha
에 도착할 때까지 계속 대기합니다.
한편 foo
의 패킷 큐에는 T
, T+1
등에서 패킷이 누적됩니다.
스트림에서 패킷을 출력하기 위해 계산기는 CalculatorContext::Outputs
및 OutputStream::Add
API 함수를 사용합니다. 대신 스트림에 타임스탬프 경계를 출력하기 위해 계산기는 API 함수 CalculatorContext::Outputs
및 CalculatorContext::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::Initialize
및 CalculatorGraph::StartRun
를 사용하여 그래프 외부에서 지정할 수 있습니다. 사이드 패킷은 그래프 내에서 계산기에서 CalculatorGraphConfig::OutputSidePackets
및 OutputSidePacket::Set
를 사용하여 지정할 수 있습니다.
계산기::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
중에 하나 이상의 타임스탬프 (예: Timestamp::Max
)를 사용할 수 있도록 타임스탬프 경계를 지정해야 합니다. 즉, 이러한 계산기는 일반적으로 SetTimestampOffset(0)
를 사용할 수 없으며 대신 SetNextTimestampBounds()
를 사용하여 명시적으로 타임스탬프 경계를 지정해야 합니다.