NVIDIA Holoscan SDK v2.9.0

视频重放器 (分布式)

在本示例中,我们将之前的视频重放器应用程序扩展为多节点分布式应用程序。分布式应用程序由多个 Fragment(C++/Python)组成,每个 Fragment 可以在其自己的节点上运行。

在分布式案例中,我们将

  • 创建一个 Fragment,使用 VideoStreamReplayerOp 算子从磁盘加载视频文件。

  • 创建第二个 Fragment,使用 HolovizOp 算子显示视频。

这两个 Fragment 将被组合成一个分布式应用程序,这样视频帧的显示可以在与数据读取节点不同的节点上进行。

注意

示例源代码和运行说明可以在 GitHub 上的 examples 目录中找到,或者在 NGC 容器和 Debian 包中的 /opt/nvidia/holoscan/examples 下找到,以及它们的可执行文件。

以下是本示例中使用的算子和工作流程图。

graphviz-85b78b2782649644c07cf1ec3f4d0ea9dfc84e60.png

图 9 从文件加载和显示视频的工作流程

这与单 Fragment 视频重放器的工作流程相同。每个算子都分配给一个单独的 Fragment,并且 Fragment 之间现在存在网络连接。

分布式应用程序显式定义 Fragment,以隔离可以分发到不同节点的不同工作单元。在本示例中

  • 我们定义了两个从 Fragment 继承的类

    • Fragment1 包含一个名为 “replayer” 的 VideoStreamReplayerOp 实例。

    • Fragment2 包含一个名为 “holoviz” 的 HolovizOp 实例。

  • 我们创建一个应用程序 DistributedVideoReplayerApp。在其 compose 方法中

    • 我们调用 make_fragment 来初始化两个 Fragment。

    • 然后,我们将 fragment1 中 “replayer” 算子的 “output” 端口连接到 fragment2 中 “holoviz” 算子的 “receivers” 端口,以定义应用程序工作流程。

  • Fragment 中实例化的算子仍然可以使用从应用程序通过 from_config() (C++) 或 kwargs() (Python) 摄取的 YAML 配置初始化的参数进行配置。

复制
已复制!
            

#include <holoscan/holoscan.hpp> #include <holoscan/operators/holoviz/holoviz.hpp> #include <holoscan/operators/video_stream_replayer/video_stream_replayer.hpp> class Fragment1 : public holoscan::Fragment { public: void compose() override { using namespace holoscan; auto replayer = make_operator<ops::VideoStreamReplayerOp>("replayer", from_config("replayer")); add_operator(replayer); } }; class Fragment2 : public holoscan::Fragment { public: void compose() override { using namespace holoscan; auto visualizer = make_operator<ops::HolovizOp>("holoviz", from_config("holoviz")); add_operator(visualizer); } }; class DistributedVideoReplayerApp : public holoscan::Application { public: void compose() override { using namespace holoscan; auto fragment1 = make_fragment<Fragment1>("fragment1"); auto fragment2 = make_fragment<Fragment2>("fragment2"); // Define the workflow: replayer -> holoviz add_flow(fragment1, fragment2, {{"replayer.output", "holoviz.receivers"}}); } }; int main(int argc, char** argv) { // Get the yaml configuration file auto config_path = std::filesystem::canonical(argv[0]).parent_path(); config_path /= std::filesystem::path("video_replayer_distributed.yaml"); auto app = holoscan::make_application<DistributedVideoReplayerApp>(); app->config(config_path); app->run(); return 0; }

复制
已复制!
            

import os from holoscan.core import Application, Fragment from holoscan.operators import HolovizOp, VideoStreamReplayerOp sample_data_path = os.environ.get("HOLOSCAN_INPUT_PATH", "../data") class Fragment1(Fragment): def __init__(self, app, name): super().__init__(app, name) def compose(self): # Set the video source video_path = self._get_input_path() logging.info( f"Using video from{video_path}" ) # Define the replayer and holoviz operators replayer = VideoStreamReplayerOp( self, name="replayer", directory=video_path, **self.kwargs("replayer") ) self.add_operator(replayer) def _get_input_path(self): path = os.environ.get( "HOLOSCAN_INPUT_PATH", os.path.join(os.path.dirname(__file__), "data") ) return os.path.join(path, "racerx") class Fragment2(Fragment): def compose(self): visualizer = HolovizOp(self, name="holoviz", **self.kwargs("holoviz")) self.add_operator(visualizer) class DistributedVideoReplayerApp(Application): """Example of a distributed application that uses the fragments and operators defined above. This application has the following fragments: - Fragment1 - holding VideoStreamReplayerOp - Fragment2 - holding HolovizOp The VideoStreamReplayerOp reads a video file and sends the frames to the HolovizOp. The HolovizOp displays the frames. """ def compose(self): # Define the fragments fragment1 = Fragment1(self, name="fragment1") fragment2 = Fragment2(self, name="fragment2") # Define the workflow self.add_flow(fragment1, fragment2, {("replayer.output", "holoviz.receivers")}) def main(): config_file_path = os.path.join(os.path.dirname(__file__), "video_replayer_distributed.yaml") logging.info(f"Reading application configuration from{config_file_path}") app = DistributedVideoReplayerApp() app.config(config_file_path) app.run() if __name__ == "__main__": main()

这个特定的分布式应用程序每个 Fragment 只有一个算子,因此算子是通过 add_operator ( C++/ Python) 添加的。通常,每个 Fragment 可以有多个算子,Fragment 内算子之间的连接将使用 Fragment 的 compute() (C++/Python) 方法中的 add_flow() (C++/Python) 方法进行。

运行应用程序应该会启动 YAML 文件中引用的视频的视频播放。

video_replayer.png

注意

运行分布式应用程序的说明包括从“驱动程序”节点以及任何工作节点调用应用程序。有关详细信息,请参阅 GitHub 上 examples 目录或 NGC 容器和 Debian 包中的 /opt/nvidia/holoscan/examples/video_replayer_distributed 下的应用程序运行说明。

提示

在跨多个节点运行分布式应用程序时,请参阅UCX 网络接口选择

上一页 视频重放器
下一页 自带模型 (BYOM)
© 版权所有 2022-2024,NVIDIA。 上次更新于 2025 年 1 月 27 日。