Skip to content
7 changes: 7 additions & 0 deletions bmf/engine/connector/include/builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class RealNode : public std::enable_shared_from_this<RealNode> {
std::weak_ptr<RealGraph> graph_;
int id_;
std::string alias_;
std::string action_;
bmf_sdk::JsonParam option_;
std::vector<std::shared_ptr<RealStream>> inputStreams_;
std::vector<std::shared_ptr<RealStream>> outputStreams_;
Expand Down Expand Up @@ -239,6 +240,8 @@ class RealGraph : public std::enable_shared_from_this<RealGraph> {
bool dumpGraph, bool needMerge);

int Run(bool dumpGraph, bool needMerge);
int Update(std::shared_ptr<RealGraph> update_graph);
void DynamicReset(const bmf_sdk::JsonParam& node_config);
int Close();
int ForceClose();
Packet Generate(std::string streamName, bool block = true);
Expand Down Expand Up @@ -645,7 +648,11 @@ class BMF_ENGINE_API Graph {
void Start(bool dumpGraph = true, bool needMerge = true);

void Start(std::vector<Stream>& generateStreams, bool dumpGraph = true, bool needMerge = true);

int Update(const Graph& update_graph);

void DynamicReset(const bmf_sdk::JsonParam& node_config);

int Close();
int ForceClose();

Expand Down
33 changes: 33 additions & 0 deletions bmf/engine/connector/src/builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ nlohmann::json RealNode::Dump() {
info["output_streams"].push_back(s->Dump());
info["option"] = option_.json_value_;
info["scheduler"] = scheduler_;

if (!action_.empty())
info["action"] = action_;

switch (inputManager_) {
case Default:
info["input_manager"] = "default";
Expand Down Expand Up @@ -435,6 +439,27 @@ int RealGraph::Run(bool dumpGraph, bool needMerge) {
return graphInstance_->close();
}

int RealGraph::Update(std::shared_ptr<RealGraph> update_graph) {
if (!update_graph) {
throw std::logic_error("Update graph is null.");
}
std::string config_str = to_string(update_graph->Dump());
graphInstance_->update(config_str, false);
return 0;
}

void RealGraph::DynamicReset(const bmf_sdk::JsonParam& node_config) {
if (!node_config.json_value_.is_object() || !node_config.json_value_.contains("alias")) {
throw std::logic_error("Invalid configuration: missing alias.");
}

std::string alias = node_config.json_value_["alias"];
auto reset_node = AddModule(alias, bmf_sdk::JsonParam(node_config.json_value_),
{}, "", CPP, "", "", Immediate, 0);

reset_node->action_ = "reset";
}

void RealGraph::Start(
const std::vector<std::shared_ptr<internal::RealStream>> &streams,
bool dumpGraph, bool needMerge) {
Expand Down Expand Up @@ -822,6 +847,14 @@ void Graph::Start(std::vector<Stream> &generateStreams, bool dumpGraph,
graph_->Start(generateRealStreams, dumpGraph, needMerge);
}

int Graph::Update(const Graph& update_graph) {
return graph_->Update(update_graph.graph_);
}

void Graph::DynamicReset(const bmf_sdk::JsonParam& node_config) {
graph_->DynamicReset(node_config);
}

int Graph::Close() {
return graph_->Close();
}
Expand Down
86 changes: 86 additions & 0 deletions bmf/test/cpp_builder/cpp_dynamic_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include "builder.hpp"
#include <unistd.h>
#include "nlohmann/json.hpp"
#include "cpp_test_helper.h"

// Dynamic reset function test
TEST(cpp_dynamic_reset, reset_pass_through_node) {
const std::string output_file = "./output_reset_cpp.mp4";
const std::string input_file = "../../files/big_bunny_10s_30fps.mp4";
BMF_CPP_FILE_REMOVE(output_file);

// 1. Create main graph
auto main_graph = bmf::builder::Graph(bmf::builder::NormalMode);
BMFLOG(BMF_INFO) << "Main graph created.";

// 2. Add decoder node
nlohmann::json decode_para = {
{"input_path", input_file},
{"alias", "decoder0"}
};
auto decoder_node = main_graph.Decode(bmf_sdk::JsonParam(decode_para));
auto video_stream = decoder_node["video"];
auto audio_stream = decoder_node["audio"];
BMFLOG(BMF_INFO) << "Decoder node created successfully.";

// 3. Add PassThrough node to be reset
std::vector<bmf::builder::Stream> pass_through_inputs = {video_stream, audio_stream};
nlohmann::json pass_through_para = {};
bmf_sdk::JsonParam pass_through_option(nlohmann::json::object());
const std::string python_module_dir = "../../../bmf/test/dynamical_graph";
auto pass_through_node = main_graph.Module(
pass_through_inputs,
"reset_pass_through",
bmf::builder::ModuleType::Python,
pass_through_option,
"reset_pass_through",
python_module_dir,
"",
bmf::builder::InputManagerType::Immediate,
0
);
BMFLOG(BMF_INFO) << "PassThrough node created successfully.";

// 4. Non-blocking start graph
main_graph.Start(true, true);
BMFLOG(BMF_INFO) << "Waiting 20ms to ensure node initialization";
usleep(20000);

// 5. Construct dynamic reset configuration
nlohmann::json reset_config = {
{"alias", "reset_pass_through"},
{"output_path", output_file},
{"video_params", {
{"codec", "h264"},
{"width", 320},
{"height", 240},
{"crf", 23},
{"preset", "veryfast"}
}}
};
bmf_sdk::JsonParam reset_config_param(reset_config);
BMFLOG(BMF_INFO) << "Dynamic reset configuration:\n" << reset_config.dump(2);

// 6. Create empty reset graph
auto temp_graph = bmf::builder::Graph(bmf::builder::NormalMode);

// 7. Temporary graph describes reset information
temp_graph.DynamicReset(reset_config_param);

// 8. Main graph performs update
int update_ret = main_graph.Update(temp_graph);
if (update_ret != 0) {
BMFLOG(BMF_ERROR) << "Dynamic reset call failed, return code: " << update_ret;
FAIL() << "Dynamic reset node call failed.";
}
BMFLOG(BMF_INFO) << "Waiting 1 second to ensure processing is complete.";
sleep(1);

// 9. Close graph
int close_ret = main_graph.Close();
if (close_ret != 0) {
BMFLOG(BMF_ERROR) << "Graph close failed, return code: " << close_ret;
FAIL() << "Graph close failed";
}
BMFLOG(BMF_INFO) << "Main graph closed successfully.";
}