wangrong
2025-01-20 d4675814490906969a39daa3e79ac560fe705410
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
 
 
#include "vp_analysis_board.h"
#include "../vp_pipe_checker.h"
 
namespace vp_utils {
    vp_analysis_board::vp_analysis_board(std::vector<std::shared_ptr<vp_nodes::vp_node>> src_nodes_in_pipe):
        src_nodes_in_pipe(src_nodes_in_pipe) {   
        init();
    }
    
    vp_analysis_board::~vp_analysis_board() {
        // set alive to false and wait threads exits
        alive = false;
        if (display_th.joinable()) {
            display_th.join();
        }
        if (rtmp_th.joinable()) {
            rtmp_th.join();
        }
    }
 
    void vp_analysis_board::init() {
        src_nodes_on_screen.clear();
        des_nodes_on_screen.clear();
 
        // check pipe
        vp_pipe_checker pipe_checker;
        pipe_checker(src_nodes_in_pipe);
 
        // layers number and max nodes number of all layers
        pipe_width = pipe_checker.pipe_width();
        pipe_height = pipe_checker.pipe_height();
 
        // calculate the w and h of canvas
        canvas_width = pipe_width * node_width + (pipe_width - 1) * node_gap_horizontal + 2 * canvas_gap_horizontal;
        canvas_height = pipe_height * node_height + (pipe_height - 1) * node_gap_vertical + 2 * canvas_gap_vertical; 
 
        // create canvas Mat and initialize it with white
        bg_canvas.create(canvas_height, canvas_width, CV_8UC3);
        bg_canvas = cv::Scalar(255, 255, 255);
 
        // map recursively
        map_nodes(src_nodes_on_screen, 1);
 
        // render static parts starting with 1st layer
        render_layer(src_nodes_on_screen, bg_canvas);
 
        // save to local by default
        save(board_title + ".png");
    }
    void vp_analysis_board::reload(std::vector<std::shared_ptr<vp_nodes::vp_node>> new_src_nodes_in_pipe) {
        std::lock_guard<std::mutex> guard(reload_lock);
        if (new_src_nodes_in_pipe.size() != 0) {
            this->src_nodes_in_pipe = new_src_nodes_in_pipe;
        }
        init();
    }
 
    void vp_analysis_board::save(std::string path) {
        cv::imwrite(path, bg_canvas);
    }
 
    void vp_analysis_board::display(int interval, bool block) {
        assert(interval > 0);
        if (displaying) {
            return;
        }
        
        auto display_func = [&, interval](){
            while (alive) {
                auto loop_start = std::chrono::system_clock::now();
                {
                    std::lock_guard<std::mutex> guard(reload_lock);  // in case it reloading
                    // deep copy the static background
                    cv::Mat mat_to_display = bg_canvas.clone();
                    // render dynamic parts starting with 1 st layer
                    render_layer(src_nodes_on_screen, mat_to_display, false);
                    cv::imshow(board_title, mat_to_display);
                }
 
                // calculate the time need wait for
                auto loop_cost = std::chrono::system_clock::now() - loop_start;
                auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(interval) - loop_cost);
 
                cv::waitKey(wait_time.count());
            }};
        displaying = true;
        display_th = std::thread(display_func);
        if (block) {
            display_th.join();
        }
    }
 
    void vp_analysis_board::push_rtmp(std::string rtmp, int bitrate) {
        if (displaying) {
            return;
        }
        auto fps = 10;
        auto rtmp_url = vp_utils::string_format(gst_template, bitrate, rtmp.c_str());
        // 10 fps by default
        assert(rtmp_writer.open(rtmp_url, cv::CAP_GSTREAMER, fps, {bg_canvas.cols, bg_canvas.rows}));
 
        auto display_func = [&, fps](){
            while (alive) {
                auto loop_start = std::chrono::system_clock::now();
                {
                    std::lock_guard<std::mutex> guard(reload_lock); // in case it reloading
                    // deep copy the static background
                    cv::Mat mat_to_display = bg_canvas.clone();
                    // render dynamic parts starting with 1 st layer
                    render_layer(src_nodes_on_screen, mat_to_display, false);
                    rtmp_writer.write(mat_to_display);
                }
 
                // calculate the time need wait for
                auto loop_cost = std::chrono::system_clock::now() - loop_start;
                auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::milliseconds(1000 / fps) - loop_cost);
 
                std::this_thread::sleep_for(wait_time);
            }};
        displaying = true;
        rtmp_th = std::thread(display_func);
    }
 
    void vp_analysis_board::render_layer(std::vector<std::shared_ptr<vp_node_on_screen>> nodes_in_layer, cv::Mat& canvas, bool static_parts) {
        std::vector<std::shared_ptr<vp_node_on_screen>> nodes_in_next_layer;
        for(auto& i : nodes_in_layer) {
            if (static_parts) {             
                i->render_static_parts(canvas);
            }
            else {
                i->render_dynamic_parts(canvas);
            }
            auto n = i->get_next_nodes_on_screen();
            nodes_in_next_layer.insert(nodes_in_next_layer.end(), n.begin(), n.end());
        }
        
        if (nodes_in_next_layer.size() > 0) {
            bool all_the_same = true;
            for(auto & i: nodes_in_next_layer) {
                if (i != nodes_in_next_layer[0]) {
                    all_the_same = false;
                    break;
                }
            }
            // just keep the first one if all the next nodes are the same
            if (all_the_same) {
                nodes_in_next_layer.erase(nodes_in_next_layer.begin() + 1, nodes_in_next_layer.end()); 
            }
 
            render_layer(nodes_in_next_layer, canvas, static_parts);
        }
        else { // recursion end
            
            /* global drawing */
 
            // draw layer index at the bottom of canvas
            if (static_parts) {
                for (int i = 0; i < pipe_width; i++) {
                    /* code */
                    cv::putText(canvas, "layer_" + std::to_string(i + 1), cv::Point(canvas_gap_horizontal + (node_width + node_gap_horizontal) * i, canvas_height - int(canvas_gap_vertical / 3)), 1, 1, cv::Scalar(255, 0, 0));
                }    
            }
 
            // refresh time at the top left of canvas
            if (!static_parts) {
                auto time = vp_utils::time_format(NOW, "<hour>:<min>:<sec>");
                cv::putText(canvas, time, cv::Point(20, 20), 1, 1, cv::Scalar(255, 0, 0));
            }   
        }   
    }
 
    void vp_analysis_board::map_nodes(std::vector<std::shared_ptr<vp_node_on_screen>> nodes_in_layer, int layer) {
        if (layer == 1) {  
            // here nodes_in_layer is empty
            auto num_src_nodes_in_pipe = src_nodes_in_pipe.size();
            
            auto base_left = layer_base_left_cal(layer - 1);
            auto base_top = layer_base_top_cal(num_src_nodes_in_pipe);
 
            // map nodes at 1st layer in memory to screen
            for (int i = 0; i < num_src_nodes_in_pipe; i++) {
                auto node_left = base_left;
                auto node_top = base_top + i * (node_height + node_gap_vertical);
 
                auto node_on_screen = std::make_shared<vp_node_on_screen>(src_nodes_in_pipe[i], vp_objects::vp_rect(node_left, node_top, node_width, node_height), layer);
                src_nodes_on_screen.push_back(node_on_screen);
            }
 
            map_nodes(src_nodes_on_screen, layer + 1);
        }
        else {
            std::vector<std::shared_ptr<vp_nodes::vp_node>> all_nodes_in_next_layer;
            for(auto &i: nodes_in_layer) {
                auto next_nodes = i->get_orginal_node()->next_nodes();
                all_nodes_in_next_layer.insert(all_nodes_in_next_layer.end(), next_nodes.begin(), next_nodes.end());
            }
            if (all_nodes_in_next_layer.size() > 0) {       
                bool all_the_same = true;
                for(auto & i: all_nodes_in_next_layer) {
                    if (i != all_nodes_in_next_layer[0]) {
                        all_the_same = false;
                        break;
                    }
                }
                // just keep the first one if all the next nodes are the same
                if (all_the_same) {
                    all_nodes_in_next_layer.erase(all_nodes_in_next_layer.begin() + 1, all_nodes_in_next_layer.end()); 
                }
            
                auto num_all_nodes_in_next_layer = all_nodes_in_next_layer.size();
                auto base_left = layer_base_left_cal(layer - 1);
                auto base_top = layer_base_top_cal(num_all_nodes_in_next_layer);
 
                auto index = 0;
                std::shared_ptr<vp_node_on_screen> node_on_screen = nullptr;
                std::vector<std::shared_ptr<vp_node_on_screen>> nodes_in_next_layer;
                for(int i = 0; i < nodes_in_layer.size(); i++) {
                    auto node_left = base_left;
                    auto next_nodes_in_pipe = nodes_in_layer[i]->get_orginal_node()->next_nodes();
                    for (int j = 0; j < next_nodes_in_pipe.size(); j++)
                    {
                        auto node_top = base_top +  index * (node_height + node_gap_vertical);
                        if (!all_the_same || node_on_screen == nullptr) {               
                            node_on_screen = std::make_shared<vp_node_on_screen>(next_nodes_in_pipe[j], vp_objects::vp_rect(node_left, node_top, node_width, node_height), layer);
                        }
                        nodes_in_layer[i]->get_next_nodes_on_screen().push_back(node_on_screen);
 
                        if (!all_the_same || nodes_in_next_layer.empty()) {                          
                            nodes_in_next_layer.push_back(node_on_screen);
                        }
                        index++;
                    }
                }
                // next layer
                map_nodes(nodes_in_next_layer, layer + 1);
            }
            else {
                // cache the last layer
                des_nodes_on_screen = nodes_in_layer;
            } // recursion end
        }
    }
}