diff --git a/bmf/engine/connector/include/builder.hpp b/bmf/engine/connector/include/builder.hpp index 0af08282..a9b33208 100644 --- a/bmf/engine/connector/include/builder.hpp +++ b/bmf/engine/connector/include/builder.hpp @@ -189,6 +189,7 @@ class RealNode : public std::enable_shared_from_this { std::weak_ptr graph_; int id_; std::string alias_; + std::string action_; bmf_sdk::JsonParam option_; std::vector> inputStreams_; std::vector> outputStreams_; @@ -239,6 +240,8 @@ class RealGraph : public std::enable_shared_from_this { bool dumpGraph, bool needMerge); int Run(bool dumpGraph, bool needMerge); + int Update(std::shared_ptr update_graph); + void DynamicReset(const bmf_sdk::JsonParam& node_config); int Close(); int ForceClose(); Packet Generate(std::string streamName, bool block = true); @@ -645,7 +648,11 @@ class BMF_ENGINE_API Graph { void Start(bool dumpGraph = true, bool needMerge = true); void Start(std::vector& 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(); diff --git a/bmf/engine/connector/src/builder.cpp b/bmf/engine/connector/src/builder.cpp index 60314d7c..9b2f892a 100644 --- a/bmf/engine/connector/src/builder.cpp +++ b/bmf/engine/connector/src/builder.cpp @@ -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"; @@ -435,6 +439,27 @@ int RealGraph::Run(bool dumpGraph, bool needMerge) { return graphInstance_->close(); } +int RealGraph::Update(std::shared_ptr 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> &streams, bool dumpGraph, bool needMerge) { @@ -822,6 +847,14 @@ void Graph::Start(std::vector &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(); } diff --git a/bmf/test/cpp_builder/cpp_dynamic_test.cpp b/bmf/test/cpp_builder/cpp_dynamic_test.cpp new file mode 100644 index 00000000..6c1c2470 --- /dev/null +++ b/bmf/test/cpp_builder/cpp_dynamic_test.cpp @@ -0,0 +1,86 @@ +#include "builder.hpp" +#include +#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 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."; +}