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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate ci2_types;
extern crate enum_iter;
extern crate rust_cam_bui_types;

use enum_iter::EnumIter;
use rust_cam_bui_types::ClockModel;

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
pub enum RecordingFrameRate {
    Fps1,
    Fps2,
    Fps5,
    Fps10,
    Fps20,
    Fps25,
    Fps30,
    Fps40,
    Fps50,
    Fps60,
    Fps100,
    #[default]
    Unlimited,
}

impl RecordingFrameRate {
    pub fn interval(&self) -> std::time::Duration {
        use std::time::Duration;
        use RecordingFrameRate::*;
        match self {
            Fps1 => Duration::from_millis(1000),
            Fps2 => Duration::from_millis(500),
            Fps5 => Duration::from_millis(200),
            Fps10 => Duration::from_millis(100),
            Fps20 => Duration::from_millis(50),
            Fps25 => Duration::from_millis(40),
            Fps30 => Duration::from_nanos(33333333),
            Fps40 => Duration::from_millis(25),
            Fps50 => Duration::from_millis(20),
            Fps60 => Duration::from_nanos(16666667),
            Fps100 => Duration::from_millis(10),
            Unlimited => Duration::from_millis(0),
        }
    }

    pub fn as_numerator_denominator(&self) -> Option<(u32, u32)> {
        use RecordingFrameRate::*;
        Some(match self {
            Fps1 => (1, 1),
            Fps2 => (2, 1),
            Fps5 => (5, 1),
            Fps10 => (10, 1),
            Fps20 => (20, 1),
            Fps25 => (25, 1),
            Fps30 => (30, 1),
            Fps40 => (40, 1),
            Fps50 => (50, 1),
            Fps60 => (60, 1),
            Fps100 => (100, 1),
            Unlimited => {
                return None;
            }
        })
    }
}

// use Debug to impl Display
impl std::fmt::Display for RecordingFrameRate {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
        use RecordingFrameRate::*;
        let s = match self {
            Fps1 => "1 fps",
            Fps2 => "2 fps",
            Fps5 => "5 fps",
            Fps10 => "10 fps",
            Fps20 => "20 fps",
            Fps25 => "25 fps",
            Fps30 => "30 fps",
            Fps40 => "40 fps",
            Fps50 => "50 fps",
            Fps60 => "60 fps",
            Fps100 => "100 fps",
            Unlimited => "unlimited",
        };
        write!(fmt, "{s}")
    }
}

impl EnumIter for RecordingFrameRate {
    fn variants() -> &'static [Self] {
        use RecordingFrameRate::*;
        &[
            Fps1, Fps2, Fps5, Fps10, Fps20, Fps25, Fps30, Fps40, Fps50, Fps60, Fps100, Unlimited,
        ]
    }
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum Mp4Codec {
    /// Encode data with Nvidia's NVENC.
    H264NvEnc(NvidiaH264Options),
    /// Encode data with OpenH264.
    H264OpenH264(OpenH264Options),
    /// Encode data with LessAVC.
    H264LessAvc,
    /// Data is already encoded as a raw H264 stream.
    H264RawStream,
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
pub struct OpenH264Options {
    /// Whether OpenH264 should emit debug messages
    pub debug: bool,
    pub preset: OpenH264Preset,
}

impl OpenH264Options {
    pub fn debug(&self) -> bool {
        self.debug
    }
    pub fn enable_skip_frame(&self) -> bool {
        match self.preset {
            OpenH264Preset::AllFrames => false,
            OpenH264Preset::SkipFramesBitrate(_) => true,
        }
    }
    pub fn rate_control_mode(&self) -> OpenH264RateControlMode {
        match self.preset {
            OpenH264Preset::AllFrames => OpenH264RateControlMode::Off,
            OpenH264Preset::SkipFramesBitrate(_) => OpenH264RateControlMode::Bitrate,
        }
    }
    pub fn bitrate_bps(&self) -> u32 {
        match self.preset {
            OpenH264Preset::AllFrames => 0,
            OpenH264Preset::SkipFramesBitrate(bitrate) => bitrate,
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum OpenH264Preset {
    AllFrames,
    SkipFramesBitrate(u32),
}

impl Default for OpenH264Preset {
    fn default() -> Self {
        Self::SkipFramesBitrate(5000)
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Copy)]
pub enum OpenH264RateControlMode {
    /// Quality mode.
    Quality,
    /// Bitrate mode.
    Bitrate,
    /// No bitrate control, only using buffer status, adjust the video quality.
    Bufferbased,
    /// Rate control based timestamp.
    Timestamp,
    /// Rate control off mode.
    Off,
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct NvidiaH264Options {
    /// The bitrate (used in association with the framerate).
    pub bitrate: u32,
    /// The device number of the CUDA device to use.
    pub cuda_device: i32,
}

impl Default for NvidiaH264Options {
    fn default() -> Self {
        Self {
            bitrate: 1000,
            cuda_device: 0,
        }
    }
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Mp4RecordingConfig {
    pub codec: Mp4Codec,
    /// Limits the recording to a maximum frame rate.
    pub max_framerate: RecordingFrameRate,
    pub h264_metadata: Option<H264Metadata>,
}

/// Universal identifier for our H264 metadata.
///
/// Generated with `uuid -v3 ns:URL https://strawlab.org/h264-metadata/`
pub const H264_METADATA_UUID: [u8; 16] = [
    // 0ba99cc7-f607-3851-b35e-8c7d8c04da0a
    0x0B, 0xA9, 0x9C, 0xC7, 0xF6, 0x07, 0x08, 0x51, 0x33, 0x5E, 0x8C, 0x7D, 0x8C, 0x04, 0xDA, 0x0A,
];
pub const H264_METADATA_VERSION: &str = "https://strawlab.org/h264-metadata/v1/";

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct H264Metadata {
    /// version of this structure
    ///
    /// Should be equal to H264_METADATA_VERSION.
    ///
    /// This field must always be serialized first.
    pub version: String,

    pub writing_app: String,

    pub creation_time: chrono::DateTime<chrono::FixedOffset>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub camera_name: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub gamma: Option<f32>,
}

impl H264Metadata {
    pub fn new(writing_app: &str, creation_time: chrono::DateTime<chrono::FixedOffset>) -> Self {
        Self {
            version: H264_METADATA_VERSION.to_string(),
            writing_app: writing_app.to_string(),
            creation_time,
            camera_name: None,
            gamma: None,
        }
    }
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum CsvSaveConfig {
    /// Do not save CSV
    NotSaving,
    /// Save CSV with this as a framerate limit
    Saving(Option<f32>),
}

// April tags

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
pub enum TagFamily {
    #[default]
    Family36h11,
    FamilyStandard41h12,
    Family16h5,
    Family25h9,
    FamilyCircle21h7,
    FamilyCircle49h12,
    FamilyCustom48h12,
    FamilyStandard52h13,
}

impl EnumIter for TagFamily {
    fn variants() -> &'static [Self] {
        use TagFamily::*;
        &[
            Family36h11,
            FamilyStandard41h12,
            Family16h5,
            Family25h9,
            FamilyCircle21h7,
            FamilyCircle49h12,
            FamilyCustom48h12,
            FamilyStandard52h13,
        ]
    }
}

impl std::fmt::Display for TagFamily {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use TagFamily::*;
        let fam = match self {
            Family36h11 => "36h11".to_string(),
            FamilyStandard41h12 => "standard-41h12".to_string(),
            Family16h5 => "16h5".to_string(),
            Family25h9 => "25h9".to_string(),
            FamilyCircle21h7 => "circle-21h7".to_string(),
            FamilyCircle49h12 => "circle-49h12".to_string(),
            FamilyCustom48h12 => "custom-48h12".to_string(),
            FamilyStandard52h13 => "standard-52h13".to_string(),
        };

        write!(f, "{fam}")
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub enum BitrateSelection {
    Bitrate500,
    #[default]
    Bitrate1000,
    Bitrate2000,
    Bitrate3000,
    Bitrate4000,
    Bitrate5000,
    Bitrate10000,
    BitrateUnlimited,
}

impl std::fmt::Display for BitrateSelection {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use BitrateSelection::*;
        match self {
            Bitrate500 => write!(f, "500"),
            Bitrate1000 => write!(f, "1000"),
            Bitrate2000 => write!(f, "2000"),
            Bitrate3000 => write!(f, "3000"),
            Bitrate4000 => write!(f, "4000"),
            Bitrate5000 => write!(f, "5000"),
            Bitrate10000 => write!(f, "10000"),
            BitrateUnlimited => write!(f, "Unlimited"),
        }
    }
}

impl enum_iter::EnumIter for BitrateSelection {
    fn variants() -> &'static [Self] {
        &[
            BitrateSelection::Bitrate500,
            BitrateSelection::Bitrate1000,
            BitrateSelection::Bitrate2000,
            BitrateSelection::Bitrate3000,
            BitrateSelection::Bitrate4000,
            BitrateSelection::Bitrate5000,
            BitrateSelection::Bitrate10000,
            BitrateSelection::BitrateUnlimited,
        ]
    }
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum CodecSelection {
    H264Nvenc,
    H264OpenH264,
}

impl std::fmt::Display for CodecSelection {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        let x = match self {
            CodecSelection::H264Nvenc => "H264Nvenc",
            CodecSelection::H264OpenH264 => "H264OpenH264",
        };
        write!(f, "{}", x)
    }
}

impl enum_iter::EnumIter for CodecSelection {
    fn variants() -> &'static [Self] {
        &[CodecSelection::H264Nvenc, CodecSelection::H264OpenH264]
    }
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum CamArg {
    /// Ignore future frame processing errors for this duration of seconds from current time.
    ///
    /// If seconds are not given, ignore forever.
    SetIngoreFutureFrameProcessingErrors(Option<i64>),

    SetExposureTime(f64),
    SetExposureAuto(ci2_types::AutoMode),
    SetFrameRateLimitEnabled(bool),
    SetFrameRateLimit(f64),
    SetGain(f64),
    SetGainAuto(ci2_types::AutoMode),
    SetRecordingFps(RecordingFrameRate),
    SetMp4Bitrate(BitrateSelection),
    SetMp4Codec(CodecSelection),
    SetMp4CudaDevice(String),
    SetMp4MaxFramerate(RecordingFrameRate),
    SetIsRecordingMp4(bool),
    SetIsRecordingFmf(bool),
    /// used only with image-tracker crate
    SetIsRecordingUfmf(bool),
    /// used only with image-tracker crate
    SetIsDoingObjDetection(bool),
    /// used only with image-tracker crate
    SetIsSavingObjDetectionCsv(CsvSaveConfig),
    /// used only with image-tracker crate
    SetObjDetectionConfig(String),
    CamArgSetKalmanTrackingConfig(String),
    CamArgSetLedProgramConfig(String),
    SetFrameOffset(u64),
    SetTriggerboxClockModel(Option<ClockModel>),
    SetFormatStr(String),
    ToggleCheckerboardDetection(bool),
    ToggleCheckerboardDebug(bool),
    SetCheckerboardWidth(u32),
    SetCheckerboardHeight(u32),
    ClearCheckerboards,
    PerformCheckerboardCalibration,
    DoQuit,
    PostTrigger,
    SetPostTriggerBufferSize(usize),
    ToggleAprilTagFamily(TagFamily),
    ToggleAprilTagDetection(bool),
    SetIsRecordingAprilTagCsv(bool),
    ToggleImOpsDetection(bool),
    SetImOpsDestination(std::net::SocketAddr),
    SetImOpsSource(std::net::IpAddr),
    SetImOpsCenterX(u32),
    SetImOpsCenterY(u32),
    SetImOpsThreshold(u8),
}