视频重放器 (分布式)
在本示例中,我们将之前的视频重放器应用程序扩展为多节点分布式应用程序。分布式应用程序由多个 Fragment(C++
/Python
)组成,每个 Fragment 可以在其自己的节点上运行。
在分布式案例中,我们将
创建一个 Fragment,使用 VideoStreamReplayerOp 算子从磁盘加载视频文件。
创建第二个 Fragment,使用 HolovizOp 算子显示视频。
这两个 Fragment 将被组合成一个分布式应用程序,这样视频帧的显示可以在与数据读取节点不同的节点上进行。
示例源代码和运行说明可以在 GitHub 上的 examples 目录中找到,或者在 NGC 容器和 Debian 包中的 /opt/nvidia/holoscan/examples
下找到,以及它们的可执行文件。
以下是本示例中使用的算子和工作流程图。

图 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 文件中引用的视频的视频播放。

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