实时时间戳
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 中获取数据包,正在等待
alpha 中的时间戳 T 的数据包。如果 A 未向 B 发送时间戳边界
针对 alpha 的更新,B 将继续等待 alpha 中的数据包到达。
同时,foo 的数据包队列将在 T、T+1 和
依此类推。
为了在流中输出数据包,计算器会使用 API 函数,
CalculatorContext::Outputs和OutputStream::Add。要改为输出
时间戳边界,计算器可以使用 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. 可指定 ProcessTimestampBounds() 以调用
每个新的“结算时间戳”为 Calculator::Process,其中“结算时间”
timestamp”是当前时间戳边界以下的新最高时间戳。
不使用 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。
当所有输入流都变为 Done 时,系统会调用 Calculator::Close,
正在关闭或即将达到时间戳边界 Timestamp::Done。
注意:如果图表完成所有待处理的计算器执行任务,
Done,然后在某些流变为 Done 状态之前,MediaPipe 会调用
对 Calculator::Close 的剩余调用次数,以便每个计算器都能得出
最终输出。
使用 TimestampOffset 会对 Calculator::Close 有一些影响。答
指定 SetTimestampOffset(0) 的计算器会在设计上表明,
当其所有输入流都达到Timestamp::Done时
已达到 Timestamp::Done,因此无法进一步输出。
这样可防止此类计算器在计算过程中
Calculator::Close。如果计算器在运行
Calculator::Close,Calculator::Process 必须指定时间戳边界,例如
在查询期间,至少有一个时间戳(例如 Timestamp::Max)保持可用状态
Calculator::Close。也就是说,此类计算器通常无法
SetTimestampOffset(0),必须改为明确指定时间戳边界
使用 SetNextTimestampBounds()。