实时视频俱乐部

实时时间戳

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 中获取数据包,正在等待 alpha 中的时间戳 T 的数据包。如果 A 未向 B 发送时间戳边界 针对 alpha 的更新,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,其中“结算时间” 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::InitializeCalculatorGraph::StartRun。边数据包可由 使用 CalculatorGraphConfig::OutputSidePacketsOutputSidePacket::Set

当所有输入流都变为 Done 时,系统会调用 Calculator::Close, 正在关闭或即将达到时间戳边界 Timestamp::Done

注意:如果图表完成所有待处理的计算器执行任务, Done,然后在某些流变为 Done 状态之前,MediaPipe 会调用 对 Calculator::Close 的剩余调用次数,以便每个计算器都能得出 最终输出。

使用 TimestampOffset 会对 Calculator::Close 有一些影响。答 指定 SetTimestampOffset(0) 的计算器会在设计上表明, 当其所有输入流都达到Timestamp::Done时 已达到 Timestamp::Done,因此无法进一步输出。 这样可防止此类计算器在计算过程中 Calculator::Close。如果计算器在运行 Calculator::CloseCalculator::Process 必须指定时间戳边界,例如 在查询期间,至少有一个时间戳(例如 Timestamp::Max)保持可用状态 Calculator::Close。也就是说,此类计算器通常无法 SetTimestampOffset(0),必须改为明确指定时间戳边界 使用 SetNextTimestampBounds()