使直播流中的视频帧在多个客户端之间可识别
Make video frames from a livestream identifiable across multiple clients
我需要将来自实时源的视频流分发给多个客户端,并额外要求每个帧在所有客户端中都是可识别的。
我已经对该主题进行了研究,并且找到了可以分享的可能解决方案。我的解决方案似乎不是最优的,这是我第一次使用视频流,所以我想看看是否有人知道更好的方法。
我之所以需要能够识别视频流中的特定帧,是因为流媒体客户端需要能够讨论它们各自在视频流中识别的事件之间的时间差。
一个简单的例子
我想启用以下交互:
- 两个客户端应用程序 Dewey 和 Stevie 连接到流媒体服务器
- Dewey 显示流,Stevie 将其保存到磁盘
- Dewey 识别出 Stevie 感兴趣的特定视频帧,因此他想告诉 Stevie 这件事
- Dewey 从视频帧中提取一些识别信息并将其发送给 Stevie
- Stevie 使用识别信息从他当前保存的直播副本中提取相同的帧
Dewey 无法直接将帧发送给 Stevie,因为 Malcolm 和 Reese 也想告诉他具体的视频帧,而 Stevie 对他们发现的时间差感兴趣。
建议的解决方案
我找到的解决方案是使用 ffserver 广播 RTP 流并使用 RTCP 数据包中的时间戳来识别帧。这些时间戳通常用于同步音频和视频,而不是提供跨多个客户端的共享时间线,这就是为什么我怀疑这是解决我问题的最佳方法。
拥有帧数似乎也有好处,比如增加帧计数器而不是任意时间戳,它增加了一些可能不同的偏移量对于我的应用程序我还必须引用相邻帧并且计算时间差似乎更容易从帧数,而不是相反。
取决于格式。例如,MPEG 传输流使用帧的 Program Clock Reference (PCR). To get you started, with ffplay
you can get the Presentation Time Stamp (PTS) 和帧计数。我不确定帧数是否有帮助,因为它取决于播放开始时间。
ffplay -i <input stream> -vf showinfo
PCR 值会在某个时候回绕,因此您也必须考虑到这一点。
我们最终没有完成项目,遗憾的是我无法提供任何源代码,但我们在概念上开发了两个解决方案,可能对解决相同问题的其他人有用。
第一个解决方案是实现预期目标的最小努力解决方案,而第二个解决方案是更灵活的设计,它利用 RTCP 支持各种视频格式。
又快又脏
您从 MJPEG 流的现有实现或一些类似的相当简单的视频编解码器开始,这些编解码器具有您拥有源代码的独立帧,并在该格式(如 TCP 或 HTTP)下放置一个无损传输层。
1) 您在视频编解码器实现中添加一个函数,它可以从帧的图像数据中生成一个散列,如 SHA1。
2) 您将一个(持久的)映射添加到您的服务器实现中,我们称它为 framemap,它将您的哈希值作为键,returns 一个整数作为很值。
3) 当您在服务器上将视频编码为您的输出格式时,计算每一帧的哈希值并将其放入 framemap 中,其中包含一个用于标识帧的递增整数.
4) 你向你的服务器添加一些额外的 API,客户端可以在其中给你一个散列,然后你在 framemap 和 return对应递增的帧数。
5) 在客户端,如果你想知道递增的帧数,你计算帧的哈希值,向服务器询问帧哈希值API,它会发回递增的帧数数.
在此设计中,您仅在视频编解码器的某处添加散列功能,并将其他所有内容添加到另一个位置。
干净的设计
这依赖于 RTP 协议及其 RTCP 控制流。
每个 RTP 数据包都有一个时间戳,表示包含帧的预期呈现时间,但它的起始值是随机的,因此您需要查看 RTCP 控制流,它为您提供服务器的 NTP 时间戳和相应的呈现时间.由此,您应该能够根据服务器的 NTP 时钟计算出相当精确的时间戳。我们试图向 VLC 添加支持此功能的功能,结果证明这相当困难,因为 VLC 具有相当复杂的代码库,将来自不同地方的大量代码整合在一起。所以也许您想在这里扩展一个更简单的实现,具体取决于您的要求。
查看 RFC 2326 – 第 3.6 章正常播放时间和第 3.7 章绝对播放时间了解此方法。
我需要将来自实时源的视频流分发给多个客户端,并额外要求每个帧在所有客户端中都是可识别的。
我已经对该主题进行了研究,并且找到了可以分享的可能解决方案。我的解决方案似乎不是最优的,这是我第一次使用视频流,所以我想看看是否有人知道更好的方法。
我之所以需要能够识别视频流中的特定帧,是因为流媒体客户端需要能够讨论它们各自在视频流中识别的事件之间的时间差。
一个简单的例子
我想启用以下交互:
- 两个客户端应用程序 Dewey 和 Stevie 连接到流媒体服务器
- Dewey 显示流,Stevie 将其保存到磁盘
- Dewey 识别出 Stevie 感兴趣的特定视频帧,因此他想告诉 Stevie 这件事
- Dewey 从视频帧中提取一些识别信息并将其发送给 Stevie
- Stevie 使用识别信息从他当前保存的直播副本中提取相同的帧
Dewey 无法直接将帧发送给 Stevie,因为 Malcolm 和 Reese 也想告诉他具体的视频帧,而 Stevie 对他们发现的时间差感兴趣。
建议的解决方案
我找到的解决方案是使用 ffserver 广播 RTP 流并使用 RTCP 数据包中的时间戳来识别帧。这些时间戳通常用于同步音频和视频,而不是提供跨多个客户端的共享时间线,这就是为什么我怀疑这是解决我问题的最佳方法。
拥有帧数似乎也有好处,比如增加帧计数器而不是任意时间戳,它增加了一些可能不同的偏移量对于我的应用程序我还必须引用相邻帧并且计算时间差似乎更容易从帧数,而不是相反。
取决于格式。例如,MPEG 传输流使用帧的 Program Clock Reference (PCR). To get you started, with ffplay
you can get the Presentation Time Stamp (PTS) 和帧计数。我不确定帧数是否有帮助,因为它取决于播放开始时间。
ffplay -i <input stream> -vf showinfo
PCR 值会在某个时候回绕,因此您也必须考虑到这一点。
我们最终没有完成项目,遗憾的是我无法提供任何源代码,但我们在概念上开发了两个解决方案,可能对解决相同问题的其他人有用。
第一个解决方案是实现预期目标的最小努力解决方案,而第二个解决方案是更灵活的设计,它利用 RTCP 支持各种视频格式。
又快又脏
您从 MJPEG 流的现有实现或一些类似的相当简单的视频编解码器开始,这些编解码器具有您拥有源代码的独立帧,并在该格式(如 TCP 或 HTTP)下放置一个无损传输层。
1) 您在视频编解码器实现中添加一个函数,它可以从帧的图像数据中生成一个散列,如 SHA1。
2) 您将一个(持久的)映射添加到您的服务器实现中,我们称它为 framemap,它将您的哈希值作为键,returns 一个整数作为很值。
3) 当您在服务器上将视频编码为您的输出格式时,计算每一帧的哈希值并将其放入 framemap 中,其中包含一个用于标识帧的递增整数.
4) 你向你的服务器添加一些额外的 API,客户端可以在其中给你一个散列,然后你在 framemap 和 return对应递增的帧数。
5) 在客户端,如果你想知道递增的帧数,你计算帧的哈希值,向服务器询问帧哈希值API,它会发回递增的帧数数.
在此设计中,您仅在视频编解码器的某处添加散列功能,并将其他所有内容添加到另一个位置。
干净的设计
这依赖于 RTP 协议及其 RTCP 控制流。 每个 RTP 数据包都有一个时间戳,表示包含帧的预期呈现时间,但它的起始值是随机的,因此您需要查看 RTCP 控制流,它为您提供服务器的 NTP 时间戳和相应的呈现时间.由此,您应该能够根据服务器的 NTP 时钟计算出相当精确的时间戳。我们试图向 VLC 添加支持此功能的功能,结果证明这相当困难,因为 VLC 具有相当复杂的代码库,将来自不同地方的大量代码整合在一起。所以也许您想在这里扩展一个更简单的实现,具体取决于您的要求。
查看 RFC 2326 – 第 3.6 章正常播放时间和第 3.7 章绝对播放时间了解此方法。