wangrong
2025-03-19 291afb9c81aae1d15d9e3edcea618d0a2e1fc5a6
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
#include "vp_record_node.h"
 
 
namespace vp_nodes {
        
    vp_record_node::vp_record_node(std::string node_name, 
                                    std::string video_save_dir, 
                                    std::string image_save_dir,
                                    vp_objects::vp_size resolution_w_h, 
                                    bool osd,
                                    int pre_record_video_duration, 
                                    int record_video_duration,
                                    bool auto_sub_dir,
                                    int bitrate):
                                    vp_node(node_name),
                                    video_save_dir(video_save_dir),
                                    image_save_dir(image_save_dir),
                                    resolution_w_h(resolution_w_h),
                                    osd(osd),
                                    pre_record_video_duration(pre_record_video_duration),
                                    record_video_duration(record_video_duration),
                                    auto_sub_dir(auto_sub_dir),
                                    bitrate(bitrate) {
        this->initialized();
    }
    
    vp_record_node::~vp_record_node() {
        deinitialized();
    }
 
    std::shared_ptr<vp_objects::vp_meta> vp_record_node::handle_frame_meta(std::shared_ptr<vp_objects::vp_frame_meta> meta) {
        // cache fps for current channel
        if (all_fps.count(meta->channel_index) == 0) {
            all_fps[meta->channel_index] = meta->fps;
        }
        auto& fps = all_fps[meta->channel_index];
 
        // first time for current channel
        if (all_pre_records.count(meta->channel_index) == 0) {
            all_pre_records[meta->channel_index] = std::deque<std::shared_ptr<vp_objects::vp_frame_meta>>();
        }
        auto& pre_records = all_pre_records[meta->channel_index];
        
        // update pre_records for current channel
        pre_records.push_back(meta);
        auto frames_need_pre_record = fps * pre_record_video_duration;
        // keep max frames
        if (pre_records.size() > frames_need_pre_record) {
            pre_records.pop_front();
        }
        
        // first time for current channel
        if (all_record_tasks.count(meta->channel_index) == 0) {
            all_record_tasks[meta->channel_index] = std::list<std::shared_ptr<vp_nodes::vp_record_task>>();
        }
        auto& record_tasks = all_record_tasks[meta->channel_index];
 
        // then append data to all tasks of current channel
        for (auto i = record_tasks.begin(); i != record_tasks.end();) {
            if ((*i)->status == vp_nodes::vp_record_task_status::COMPLETE) {
                i = record_tasks.erase(i); // remove task which is complete already
            }
            else {
                (*i)->append_async(meta);  // no block here
                i++;
            }
        }    
        
        // done 
        return meta;
    }
 
    std::shared_ptr<vp_objects::vp_meta> vp_record_node::handle_control_meta(std::shared_ptr<vp_objects::vp_control_meta> meta) {
        
        if (meta->control_type == vp_objects::vp_control_type::IMAGE_RECORD ||
            meta->control_type == vp_objects::vp_control_type::VIDEO_RECORD) {
            /* code */
            // create record task, it will start asynchronously.
            auto_new_record_task(meta);
 
            /* no return since already handle it, do not need pass to next nodes. */
            return nullptr;
        }
        else {
            return vp_node::handle_control_meta(meta);
        }
    }
 
    void vp_record_node::auto_new_record_task(std::shared_ptr<vp_objects::vp_control_meta>& meta) {
        auto& record_tasks = all_record_tasks[meta->channel_index];
        auto& pre_records = all_pre_records[meta->channel_index];
        auto& fps = all_fps[meta->channel_index];
 
        // image record
        if (meta->control_type == vp_objects::vp_control_type::IMAGE_RECORD) {
            auto image_record_control_meta = std::dynamic_pointer_cast<vp_objects::vp_image_record_control_meta>(meta);
            
            // create image record task
            auto file_name_without_ext = image_record_control_meta->image_file_name_without_ext;
            auto _osd = image_record_control_meta->osd;
            VP_INFO(vp_utils::string_format("[%s] [record] create new image record task, file_name_without_ext is: `%s`", node_name.c_str(), file_name_without_ext.c_str()));
            auto image_record_task = std::make_shared<vp_nodes::vp_image_record_task>(meta->channel_index, 
                                                                                    file_name_without_ext, 
                                                                                    image_save_dir, 
                                                                                    auto_sub_dir, 
                                                                                    _osd, 
                                                                                    resolution_w_h, node_name);
            image_record_task->set_task_complete_hooker([this](int channel_index, vp_record_info record_info) {
                // just notify hooker which has attached on the node
                if (this->image_record_complete_hooker) {
                    this->image_record_complete_hooker(channel_index, record_info);
                }
                VP_INFO(vp_utils::string_format("[%s] [record] image record task completed, file_name_without_ext is: `%s`", node_name.c_str(), record_info.file_name_without_ext.c_str()));
            });
            record_tasks.push_back(image_record_task);
        }
 
        // video record
        if (meta->control_type == vp_objects::vp_control_type::VIDEO_RECORD) {
            auto video_record_control_meta = std::dynamic_pointer_cast<vp_objects::vp_video_record_control_meta>(meta);
            
            // create video record task
            auto file_name_without_ext = video_record_control_meta->video_file_name_without_ext;
            auto _osd = video_record_control_meta->osd;
            auto _record_video_duration = video_record_control_meta->record_video_duration;
            if (_record_video_duration == 0) {
                // use default value
                _record_video_duration = record_video_duration;
            }
            VP_INFO(vp_utils::string_format("[%s] [record] create new video record task, file_name_without_ext is: `%s`", node_name.c_str(), file_name_without_ext.c_str()));
            auto video_record_task = std::make_shared<vp_nodes::vp_video_record_task>(meta->channel_index, 
                                                                                    pre_records, 
                                                                                    file_name_without_ext, 
                                                                                    video_save_dir, 
                                                                                    auto_sub_dir, 
                                                                                    _osd, 
                                                                                    resolution_w_h, 
                                                                                    bitrate, fps, pre_record_video_duration, _record_video_duration, node_name);
            video_record_task->set_task_complete_hooker([this](int channel_index, vp_record_info record_info) {
                // just notify hooker which has attached on the node
                if (this->video_record_complete_hooker) {                
                    this->video_record_complete_hooker(channel_index, record_info);
                }
                VP_INFO(vp_utils::string_format("[%s] [record] video record task completed, file_name_without_ext is: `%s`", node_name.c_str(), record_info.file_name_without_ext.c_str()));
            });
            record_tasks.push_back(video_record_task);
        }
    }
}