-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcamera_controller.h
357 lines (292 loc) · 11.3 KB
/
camera_controller.h
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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// Copyright 2019, The Jackson Laboratory, Bar Harbor, Maine - all rights reserved
#ifndef CAMERA_CONTROLLER_H
#define CAMERA_CONTROLLER_H
#include <atomic>
#include <chrono>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include "pixel_types.h"
namespace codecs {
static const std::vector<std::string> codec_names({"mpeg4", "libx264"});
static const std::string MPEG4 = "mpeg4";
static const std::string LIBX264 = "libx264";
/**
* @brief function to check that a string is a valid codec name
* @param type_name name to check
* @return true if valid false otherwise
*/
bool Validate(std::string type_name);
} // namespace codecs
/**
* @brief camera controller abstract base class
*
* This class provides common functionality for controlling a camera (starting
* and stopping recording threads, generating filenames, etc). It has a pure
* virtual function RecordVideo() that must be implemented by a derived class.
* RecordVideo() is executed in a separate thread managed by CameraController.
* CameraController is intended to be used from a single thread.
*
*/
class CameraController {
public:
/**
* @brief collection of recording session attributes to be passed into
* StartRecording() as a parameter.
*
* Setter functions added to perform validation -- any setter that does
* parameter validation will throw a std::invalid_argument exception for
* invalid values.
*
*/
class RecordingSessionConfig {
public:
/// get target frames per second
int target_fps() const {return target_fps_;}
/// get fragment flag
bool fragment_by_hour() const {return fragment_by_hour_;}
/// get file prefix string
const std::string& file_prefix() const {return file_prefix_;}
/// get duration of session
std::chrono::seconds duration() const {return duration_;}
/// get session ID
int session_id() const {return session_id_;}
/// get pixel format as string
const std::string& pixel_format() const {return pixel_format_;}
/// get codec name to use for encoding
const std::string& codec() const {return codec_;}
/// get the compression target preset name
const std::string& compression_target() const {return compression_target_;}
/// get the constant rate factor (CRF)
unsigned int crf() const {return crf_;}
/// get filtering flag
bool apply_filter() const {return apply_filter_;}
/// set target fps
void set_target_fps(unsigned int target_fps);
/// set fragment flag
void set_fragment_by_hour(bool fragment) {fragment_by_hour_ = fragment;}
/// set file prefix
void set_file_prefix(const std::string& prefix);
/// set session duration in seconds
void set_duration(std::chrono::seconds duration);
/// set session ID
void set_session_id(uint32_t);
/// set codec
void set_codec(std::string codec);
/// set compression preset
void set_compression_target(const std::string &target);
/// set constant rate factor (CRF)
void set_crf(unsigned int crf);
/// set filtering flag
void set_apply_filter(bool apply_filter) {apply_filter_ = apply_filter;}
private:
/// target frames per second for video acquisition
int target_fps_ = 60;
/// video files will be split into hour-long segments
bool fragment_by_hour_ = false;
/// filename prefix. All files created by this session will start with this
/// string
std::string file_prefix_;
/// duration of recording session, can be specified in units greater than
/// seconds and conversion to seconds will happen automatically
std::chrono::seconds duration_;
/// session ID
int session_id_ = -1;
/// pixel format
std::string pixel_format_ = pixel_types::YUV420P;
/// codec used for video encoding
std::string codec_ = codecs::LIBX264;
/// compression preset
std::string compression_target_ = "veryfast";
/// compression Constant Rate Factor (CRF) 0 = lossless, 51 = worst possible quality
unsigned int crf_ = 11;
/// run frames through filtering before output
bool apply_filter_ = false;
/// room string, used to generate outpput subdirectory
std::string nv_room_string_;
};
/**
* @brief construct a new CameraController
*
* @param directory base video capture directory
* @param frame_height
* @param frame_width
*/
CameraController(const std::string &directory, int frame_height, int frame_width, const std::string& nv_room_string, const std::string& rtmp_uri);
/**
* @brief destructor for CameraController, if the destructor is called
* while there is an active recording thread it will stop the thread
* and wait for it to terminate
*
*/
~CameraController();
/**
* @brief get recording status
*
* @returns true if recording thread is active, false otherwise
*/
bool recording() const {return recording_;}
/**
* @brief get streaming status
* @return true if live streaming is enabled, false otherwise
*/
bool live_streaming() const {return live_stream_;}
/**
* @brief get recording session ID
*
* @returns session ID of active recording session. return value is undefined
* if there is no active recording session
*/
int session_id() const {return session_id_;}
/**
* @brief start the recording thread
*
* @returns true if thread is started, false otherwise
*/
bool StartRecording(const RecordingSessionConfig &config);
/**
* @brief stop recording thread
*
* Signal the recording thread to stop and waits for the thread to
* finish. This is a no op if the recording thread has not been started
* and is also safe to call if the recording thread terminates on its
* own.
*/
void StopRecording();
/**
* @brief return the duration of the recording session
*
* returns the duration of the recording session in seconds. If there is
* no active session then it returns the duration of the last session
*
* @return time of recording session in seconds
*/
std::chrono::seconds elapsed_time() const;
/**
* @brief get the average frames per second
*
* uses a moving window average to calculate frames per second
*
* @return average frames per second
*/
double avg_fps();
/**
* @brief get error string set by recording thread
*
* get the error string set by the recording thread. value is undefined if
* the recording thread has not terminated with an error
*
* @return error string
*/
const std::string error_string() const;
/**
* @brief get a recording error code
*
* get error code set by the recording thread. value is undefined if the
* recording thread has not terminated
*
* @return 0 if there were no errors, non-zero value otherwise
*/
int recording_error() const;
/**
* clear information from previous recording session
*/
void ClearSession();
/**
* @brief set new frame width
*
* this setter, as well as SetFrameHeight and SetDirectory are used after
* processing a HUP signal to re-read the config file
*
* @param width new width
*/
void SetFrameWidth(int width);
/**
* @brief set new frame width
* @param height new frame width
*/
void SetFrameHeight(int height);
/**
* @brief set new output directory
* @param dir path to new output directory
*/
void SetDirectory(std::string dir);
/// set nv room string
void SetNvRoomString(std::string s) {nv_room_string_ = s;}
/// set rtmp URI
void SetRtmpUri(std::string s) {rtmp_uri_ = s;}
/// enable/disable live streaming. will not set to true if rtmp_uri_ is empty
void SetStreaming(bool stream);
protected:
std::string directory_; ///< directory for storing video
std::atomic_bool stop_recording_ {false}; ///< used to signal to the recording thread to terminate early
std::atomic_bool recording_ {false}; ///< are have we started a recording thread?
std::atomic_bool capturing_ {false}; ///< is the recording thread grabbing frames?
std::thread recording_thread_; ///< current recording thread
std::chrono::seconds elapsed_time_; ///< duration of completed recording session
std::atomic<std::chrono::high_resolution_clock::duration> session_start_;
std::vector<double> moving_avg_; ///< buffer storing fps for last N frames captured where N is the target framerate
int session_id_ {-1}; ///< stores session ID if current recording session (if there is one)
std::string err_msg_; ///< error message if recording_err_
int err_state_; ///< error state of last completed recording session
std::mutex mutex_; ///< mutex for protecting some variables shared by controlling thread and recording thread
int frame_width_; ///< frame width, loaded from config file
int frame_height_; ///< frame height, loaded from config file
std::string nv_room_string_; ///< string generated from hostname and location, for use in output subdir
std::string rtmp_uri_; ///< URL for rtmp streaming endpoint
std::atomic_bool live_stream_ {false}; ///< if true, stream video to rtmp endpoint
/**
* @brief generates a timestamp string for use in filenames.
*
* generates a string with the format YYYY-MM-DD_HH-MM-SS
*
* @param time system clock time
* @return
*/
std::string timestamp(std::chrono::time_point<std::chrono::system_clock> time);
/**
* @brief generate a timestamp string with the current time
*
* generates a string with the format YYYY-MM-DD_HH-MM-SS using the current time
*
* @return
*/
std::string timestamp();
/**
* @brief generates a date string in the format YYYY-MM-DD
*
* @param time system time to use to generate date string
* @return
*/
std::string DateString(std::chrono::time_point<std::chrono::system_clock> time);
/**
* @brief make a directory using the recording directory and a date string
* @param time sytem time to use to generate a date string
* @return path
*/
std::string MakeOutputDir(std::chrono::time_point<std::chrono::system_clock> time);
/**
* @brief get hour using a given system clock time
* @param time current time
* @return integer hour
*/
int GetCurrentHour(std::chrono::time_point<std::chrono::system_clock> time);
/**
* @brief get hour using current time
* @return integer hour
*/
int GetCurrentHour();
private:
/**
* @brief function executed in a thread by StartRecording()
*
* Pure virtual function that must be implemented by classes that
* inherit from this abstract base class to create a functioning
* CameraController. This function will handle recording video from
* the camera. It should set recording when it starts and finishes.
*/
virtual void RecordVideo(const RecordingSessionConfig&) = 0;
};
#endif