-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommon.cpp
267 lines (210 loc) · 8.03 KB
/
common.cpp
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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
//
// Created by Keaton Burleson on 11/2/23.
//
#include <libavformat/avformat.h>
#include "common.h"
using std::ifstream;
using string = std::string;
using path = std::filesystem::path;
using json = nlohmann::json;
using nvr_logger = std::shared_ptr<spdlog::logger>;
namespace fs = std::filesystem;
namespace nvr {
CameraConfig getConfig(const char* config_file) {
const string config_file_path = string(config_file);
size_t last_path_index = config_file_path.find_last_of('/');
string config_file_name = config_file_path.substr(last_path_index + 1);
size_t last_ext_index = config_file_name.find_last_of('.');
string stream_name = config_file_name.substr(0, last_ext_index);
if (access(config_file, F_OK) != 0) {
spdlog::error("Cannot read config file: {}", config_file);
exit(-1);
}
ifstream config_stream(config_file);
json config;
try {
config = json::parse(config_stream);
}
catch (json::exception&exception) {
spdlog::error("Could not parse JSON: {}", exception.what());
exit(EXIT_FAILURE);
}
string requiredFields[] = {
"splitEvery",
"id",
"streamURL",
"snapshotURL",
"outputPath",
"ipAddress",
"rtspUsername",
"rtspPassword",
"type",
};
for (auto field: requiredFields) {
if (!config.contains(field)) {
spdlog::error("Configuration is missing field", field);
exit(EXIT_FAILURE);
}
}
// Get required fields
const long clip_runtime = config["splitEvery"];
const string stream_id = config["id"];
const string stream_url = config["streamURL"];
const string snapshot_url = config["snapshotURL"];
const string output_path = config["outputPath"];
const string ip_address = config["ipAddress"];
const string rtsp_username = config["rtspUsername"];
const string rtsp_password = config["rtspPassword"];
const string raw_type = config["type"];
const StreamType type = raw_type == "h264" ? h264 : h265;
// Get optional fields
string sub_stream_url = config["streamURL"];
string hardware_enc_priority = "none";
int port = 554;
long snapshot_interval = config["splitEvery"];
if (config.contains("port"))
port = config["port"];
if (config.contains("subStreamURL"))
sub_stream_url = config["subStreamURL"];
if (config.contains("snapshotInterval"))
snapshot_interval = config["snapshotInterval"];
if (config.contains("hardwareEncoderPriority"))
hardware_enc_priority = config["hardwareEncoderPriority"];
return {
stream_url,
sub_stream_url,
snapshot_url,
output_path,
stream_name,
ip_address,
rtsp_username,
rtsp_password,
type,
stream_id,
hardware_enc_priority,
clip_runtime,
snapshot_interval,
port,
};
}
bool isReachable(const string&ip_addr) {
CURL* connection;
CURLcode res;
bool reachable = false;
connection = curl_easy_init();
string url = string("http://").append(ip_addr);
if (connection) {
curl_easy_setopt(connection, CURLOPT_URL, url.c_str());
curl_easy_setopt(connection, CURLOPT_CUSTOMREQUEST, "OPTIONS");
/* issue an OPTIONS * request (no leading slash) */
curl_easy_setopt(connection, CURLOPT_REQUEST_TARGET, "*");
res = curl_easy_perform(connection);
if (res == CURLE_OK)
reachable = true;
curl_easy_cleanup(connection);
}
return reachable;
}
nvr_logger buildLogger(const CameraConfig&config) {
string log_file_output = generateOutputFilename(config.stream_name, config.output_path, log);
try {
std::vector<spdlog::sink_ptr> sinks;
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::trace);
sinks.push_back(console_sink);
// Disables log file output if using systemd
if (!getenv("INVOCATION_ID")) {
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(log_file_output,
1024 * 1024 * 10, 3);
file_sink->set_level(spdlog::level::trace);
sinks.push_back(file_sink);
}
auto logger = std::make_shared<spdlog::logger>(config.stream_name, sinks.begin(), sinks.end());
logger->flush_on(spdlog::level::err);
std::string json_pattern = {"{\"time\": \"%Y-%m-%dT%H:%M:%S.%f%z\", \"name\": \"%n\", \"level\": \"%^%l%$\", \"message\": \"%v\"}"};
logger->set_pattern(json_pattern);
return logger;
}
catch (const spdlog::spdlog_ex&ex) {
std::cout << "Log init failed: " << ex.what() << std::endl;
return spdlog::stdout_color_mt("console");
}
}
string appendTimestamp(string¤t) {
char buf[1024];
time_t now0;
struct tm *tm, temp_buffer{};
time(&now0);
tm = localtime_r(&now0, &temp_buffer);
strftime(buf, sizeof(buf), "%Y-%m-%d_%H-%M-%S", tm);
current.append(buf);
return current;
}
string generateOutputFilename(const string&camera_id, const string&output_path, FileType file_type, bool temporary) {
string file_name;
file_name.append(camera_id);
file_name.append("-");
switch (file_type) {
case video:
file_name.append("%Y-%m-%d_%H-%M-%S");
file_name.append(".mp4");
break;
case image:
file_name = appendTimestamp(file_name);
file_name.append(".jpeg");
break;
case log:
file_name.append("log.txt");
break;
}
path file_path = temporary ? "/tmp" : output_path;
if (temporary)
file_path /= (output_path);
switch (file_type) {
case video:
file_path /= ("videos");
break;
case image:
file_path /= ("snapshots");
break;
case log:
file_path /= ("logs");
}
fs::create_directory(file_path);
file_path /= camera_id;
fs::create_directory(file_path);
file_path /= file_name;
return file_path.string();
}
int countClips(const string&output_path, const string&camera_id) {
fs::path videos_path{output_path};
videos_path /= "videos";
fs::create_directory(videos_path);
videos_path /= camera_id;
fs::create_directory(videos_path);
int clip_count = 0;
for (auto const&dir_entry: std::filesystem::directory_iterator{videos_path}) {
std::cout << dir_entry.file_size() << '\n';
clip_count += 1;
}
return clip_count;
}
string buildStreamURL(const string&url, const string&ip_address, const int port, const string&password,
const string&username) {
string base = string("rtsp://");
if (password.empty()) {
base = base.append(ip_address);
}
else {
base = base.append(username)
.append(":")
.append(password)
.append("@")
.append(ip_address);
}
return base.append(":").append(std::to_string(port)).append(url);
}
string sanitizeStreamURL(const string&stream_url, const string&password) {
return std::regex_replace(string(stream_url), std::regex(password), string(password.length(), '*'));
}
} // never