1extern crate serde;
7extern crate strand_cam_bui_types;
8extern crate strand_cam_enum_iter;
9extern crate strand_cam_types;
10
11use serde::{Deserialize, Serialize};
12use strand_cam_bui_types::ClockModel;
13use strand_cam_enum_iter::EnumIter;
14
15#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
17pub enum RecordingFrameRate {
18 Fps1,
20 Fps2,
22 Fps5,
24 Fps10,
26 Fps20,
28 Fps25,
30 Fps30,
32 Fps40,
34 Fps50,
36 Fps60,
38 Fps100,
40 #[default]
42 Unlimited,
43}
44
45impl RecordingFrameRate {
46 pub fn interval(&self) -> std::time::Duration {
48 use RecordingFrameRate::*;
49 use std::time::Duration;
50 match self {
51 Fps1 => Duration::from_millis(1000),
52 Fps2 => Duration::from_millis(500),
53 Fps5 => Duration::from_millis(200),
54 Fps10 => Duration::from_millis(100),
55 Fps20 => Duration::from_millis(50),
56 Fps25 => Duration::from_millis(40),
57 Fps30 => Duration::from_nanos(33333333),
58 Fps40 => Duration::from_millis(25),
59 Fps50 => Duration::from_millis(20),
60 Fps60 => Duration::from_nanos(16666667),
61 Fps100 => Duration::from_millis(10),
62 Unlimited => Duration::from_millis(0),
63 }
64 }
65
66 pub fn as_numerator_denominator(&self) -> Option<(u32, u32)> {
68 use RecordingFrameRate::*;
69 Some(match self {
70 Fps1 => (1, 1),
71 Fps2 => (2, 1),
72 Fps5 => (5, 1),
73 Fps10 => (10, 1),
74 Fps20 => (20, 1),
75 Fps25 => (25, 1),
76 Fps30 => (30, 1),
77 Fps40 => (40, 1),
78 Fps50 => (50, 1),
79 Fps60 => (60, 1),
80 Fps100 => (100, 1),
81 Unlimited => {
82 return None;
83 }
84 })
85 }
86}
87
88impl std::fmt::Display for RecordingFrameRate {
89 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
90 use RecordingFrameRate::*;
91 let s = match self {
92 Fps1 => "1 fps",
93 Fps2 => "2 fps",
94 Fps5 => "5 fps",
95 Fps10 => "10 fps",
96 Fps20 => "20 fps",
97 Fps25 => "25 fps",
98 Fps30 => "30 fps",
99 Fps40 => "40 fps",
100 Fps50 => "50 fps",
101 Fps60 => "60 fps",
102 Fps100 => "100 fps",
103 Unlimited => "unlimited",
104 };
105 write!(fmt, "{s}")
106 }
107}
108
109impl EnumIter for RecordingFrameRate {
110 fn variants() -> Vec<Self> {
111 use RecordingFrameRate::*;
112 vec![
113 Fps1, Fps2, Fps5, Fps10, Fps20, Fps25, Fps30, Fps40, Fps50, Fps60, Fps100, Unlimited,
114 ]
115 }
116}
117
118#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
120pub enum Mp4Codec {
121 H264NvEnc(NvidiaH264Options),
123 H264OpenH264(OpenH264Options),
125 H264LessAvc,
127 H264RawStream,
129}
130
131#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
133pub struct OpenH264Options {
134 pub debug: bool,
136 pub preset: OpenH264Preset,
138}
139
140impl OpenH264Options {
141 pub fn debug(&self) -> bool {
143 self.debug
144 }
145 pub fn enable_skip_frame(&self) -> bool {
147 match self.preset {
148 OpenH264Preset::AllFrames => false,
149 OpenH264Preset::SkipFramesBitrate(_) => true,
150 }
151 }
152 pub fn rate_control_mode(&self) -> OpenH264RateControlMode {
154 match self.preset {
155 OpenH264Preset::AllFrames => OpenH264RateControlMode::Off,
156 OpenH264Preset::SkipFramesBitrate(_) => OpenH264RateControlMode::Bitrate,
157 }
158 }
159 pub fn bitrate_bps(&self) -> u32 {
161 match self.preset {
162 OpenH264Preset::AllFrames => 0,
163 OpenH264Preset::SkipFramesBitrate(bitrate) => bitrate,
164 }
165 }
166}
167
168#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
170pub enum OpenH264Preset {
171 AllFrames,
173 SkipFramesBitrate(u32),
175}
176
177impl Default for OpenH264Preset {
178 fn default() -> Self {
179 Self::SkipFramesBitrate(5000)
180 }
181}
182
183#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Copy)]
185pub enum OpenH264RateControlMode {
186 Quality,
188 Bitrate,
190 Bufferbased,
192 Timestamp,
194 Off,
196}
197
198#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
200pub struct NvidiaH264Options {
201 pub bitrate: Option<u32>,
203 pub cuda_device: i32,
205}
206
207#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
209pub struct Mp4RecordingConfig {
210 pub codec: Mp4Codec,
211 pub max_framerate: RecordingFrameRate,
213 pub h264_metadata: Option<H264Metadata>,
214}
215
216#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
218pub struct FfmpegRecordingConfig {
219 pub codec_args: FfmpegCodecArgs,
220 pub max_framerate: RecordingFrameRate,
222 pub h264_metadata: Option<H264Metadata>,
223}
224
225#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
227pub enum RecordingConfig {
228 Mp4(Mp4RecordingConfig),
230 Ffmpeg(FfmpegRecordingConfig),
232}
233
234impl Default for RecordingConfig {
235 fn default() -> Self {
236 Self::Ffmpeg(Default::default())
237 }
238}
239
240impl RecordingConfig {
241 pub fn max_framerate(&self) -> &RecordingFrameRate {
243 use RecordingConfig::*;
244 match self {
245 Mp4(c) => &c.max_framerate,
246 Ffmpeg(c) => &c.max_framerate,
247 }
248 }
249}
250
251pub const H264_METADATA_UUID: [u8; 16] = [
255 0x0B, 0xA9, 0x9C, 0xC7, 0xF6, 0x07, 0x08, 0x51, 0x33, 0x5E, 0x8C, 0x7D, 0x8C, 0x04, 0xDA, 0x0A,
257];
258pub const H264_METADATA_VERSION: &str = "https://strawlab.org/h264-metadata/v1/";
260
261#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
263pub struct H264Metadata {
264 pub version: String,
269
270 pub writing_app: String,
272
273 pub creation_time: chrono::DateTime<chrono::FixedOffset>,
275
276 #[serde(default, skip_serializing_if = "Option::is_none")]
278 pub camera_name: Option<String>,
279
280 #[serde(default, skip_serializing_if = "Option::is_none")]
282 pub gamma: Option<f32>,
283}
284
285impl H264Metadata {
286 pub fn new(writing_app: &str, creation_time: chrono::DateTime<chrono::FixedOffset>) -> Self {
287 Self {
288 version: H264_METADATA_VERSION.to_string(),
289 writing_app: writing_app.to_string(),
290 creation_time,
291 camera_name: None,
292 gamma: None,
293 }
294 }
295}
296
297#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
299pub enum CsvSaveConfig {
300 NotSaving,
302 Saving(Option<f32>),
304}
305
306#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
308pub enum TagFamily {
309 #[default]
311 Family36h11,
312 FamilyStandard41h12,
314 Family16h5,
316 Family25h9,
318 FamilyCircle21h7,
320 FamilyCircle49h12,
322 FamilyCustom48h12,
324 FamilyStandard52h13,
326}
327
328impl EnumIter for TagFamily {
329 fn variants() -> Vec<Self> {
330 use TagFamily::*;
331 vec![
332 Family36h11,
333 FamilyStandard41h12,
334 Family16h5,
335 Family25h9,
336 FamilyCircle21h7,
337 FamilyCircle49h12,
338 FamilyCustom48h12,
339 FamilyStandard52h13,
340 ]
341 }
342}
343
344impl std::fmt::Display for TagFamily {
345 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
346 use TagFamily::*;
347 let fam = match self {
348 Family36h11 => "36h11".to_string(),
349 FamilyStandard41h12 => "standard-41h12".to_string(),
350 Family16h5 => "16h5".to_string(),
351 Family25h9 => "25h9".to_string(),
352 FamilyCircle21h7 => "circle-21h7".to_string(),
353 FamilyCircle49h12 => "circle-49h12".to_string(),
354 FamilyCustom48h12 => "custom-48h12".to_string(),
355 FamilyStandard52h13 => "standard-52h13".to_string(),
356 };
357
358 write!(f, "{fam}")
359 }
360}
361
362#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
364pub enum BitrateSelection {
365 Bitrate500,
367 #[default]
369 Bitrate1000,
370 Bitrate2000,
372 Bitrate3000,
374 Bitrate4000,
376 Bitrate5000,
378 Bitrate10000,
380 BitrateUnlimited,
382}
383
384impl std::fmt::Display for BitrateSelection {
385 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
386 use BitrateSelection::*;
387 match self {
388 Bitrate500 => write!(f, "500"),
389 Bitrate1000 => write!(f, "1000"),
390 Bitrate2000 => write!(f, "2000"),
391 Bitrate3000 => write!(f, "3000"),
392 Bitrate4000 => write!(f, "4000"),
393 Bitrate5000 => write!(f, "5000"),
394 Bitrate10000 => write!(f, "10000"),
395 BitrateUnlimited => write!(f, "Unlimited"),
396 }
397 }
398}
399
400impl strand_cam_enum_iter::EnumIter for BitrateSelection {
401 fn variants() -> Vec<Self> {
402 vec![
403 BitrateSelection::Bitrate500,
404 BitrateSelection::Bitrate1000,
405 BitrateSelection::Bitrate2000,
406 BitrateSelection::Bitrate3000,
407 BitrateSelection::Bitrate4000,
408 BitrateSelection::Bitrate5000,
409 BitrateSelection::Bitrate10000,
410 BitrateSelection::BitrateUnlimited,
411 ]
412 }
413}
414
415type FfmpegCodecArgList = Option<Vec<(String, String)>>;
417
418const DEFAULT_OUTPUT_PIXFMT: &str = "yuv420p";
424
425fn default_output_pixfmt() -> Option<String> {
426 Some(DEFAULT_OUTPUT_PIXFMT.to_string())
427}
428
429#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
431pub struct FfmpegCodecArgs {
432 pub device_args: FfmpegCodecArgList,
434 pub pre_codec_args: FfmpegCodecArgList,
436 pub codec: Option<String>,
438 pub post_codec_args: FfmpegCodecArgList,
440 #[serde(default = "default_output_pixfmt")]
446 pub pixfmt: Option<String>,
447 pub max_bframes: Option<u32>,
450}
451
452impl Default for FfmpegCodecArgs {
453 fn default() -> Self {
454 Self {
455 device_args: None,
456 pre_codec_args: None,
457 codec: None,
458 post_codec_args: None,
459 pixfmt: default_output_pixfmt(),
460 max_bframes: None,
461 }
462 }
463}
464
465impl std::fmt::Display for FfmpegCodecArgs {
466 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
467 fn arg_fmt(args: Option<&Vec<(String, String)>>) -> String {
468 if let Some(args) = args {
469 args.iter()
470 .map(|(a1, a2)| format!("{a1} {a2}"))
471 .collect::<Vec<_>>()
472 .join(" ")
473 } else {
474 "".into()
475 }
476 }
477 let pre = arg_fmt(self.pre_codec_args.as_ref());
478 let codec = self
479 .codec
480 .as_ref()
481 .map(|c| format!("-c:v {c}"))
482 .unwrap_or_default();
483 let pixfmt = self
484 .pixfmt
485 .as_ref()
486 .map(|p| format!("-pix_fmt {p}"))
487 .unwrap_or_default();
488 let bframes = self
489 .max_bframes
490 .map(|n| format!("-bf {n}"))
491 .unwrap_or_default();
492 let post = arg_fmt(self.post_codec_args.as_ref());
493 write!(f, "ffmpeg {pre} {codec} {pixfmt} {bframes} {post}")
494 }
495}
496
497#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
499pub enum CodecSelection {
500 H264Nvenc,
502 H264OpenH264,
504 Ffmpeg(FfmpegCodecArgs),
506}
507
508impl CodecSelection {
509 pub fn requires(&self, what: &str) -> bool {
511 use CodecSelection::*;
512 match self {
513 H264Nvenc => what == "nvenc",
514 H264OpenH264 => false,
515 Ffmpeg(args) => {
516 if let Some(codec) = &args.codec {
517 codec.contains(what)
518 } else {
519 false
520 }
521 }
522 }
523 }
524}
525
526impl std::fmt::Display for CodecSelection {
527 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
528 use CodecSelection::*;
529 let x = match self {
530 H264Nvenc => "H264 NVENC",
531 H264OpenH264 => "OpenH264",
532 Ffmpeg(args) => {
533 return std::fmt::Display::fmt(args, f);
534 }
535 };
536 write!(f, "{x}")
537 }
538}
539
540impl strand_cam_enum_iter::EnumIter for CodecSelection {
541 fn variants() -> Vec<Self> {
542 use CodecSelection::*;
543 vec![
544 H264Nvenc,
545 H264OpenH264,
546 Ffmpeg(FfmpegCodecArgs {
549 codec: Some("h264_videotoolbox".to_string()),
550 ..Default::default()
551 }),
552 Ffmpeg(FfmpegCodecArgs {
553 codec: Some("h264_nvenc".to_string()),
554 ..Default::default()
555 }),
556 Ffmpeg(FfmpegCodecArgs {
557 codec: Some("h264_nvmpi".to_string()),
558 ..Default::default()
559 }),
560 Ffmpeg(FfmpegCodecArgs {
561 device_args: Some(vec![("-vaapi_device".into(), "/dev/dri/renderD128".into())]),
562 pre_codec_args: Some(vec![("-vf".into(), "format=nv12,hwupload".into())]),
563 codec: Some("h264_vaapi".to_string()),
564 post_codec_args: Some(vec![("-color_range".into(), "pc".into())]),
565 pixfmt: None,
569 ..Default::default()
570 }),
571 Ffmpeg(FfmpegCodecArgs {
573 codec: Some("libx264".to_string()),
574 ..Default::default()
575 }),
576 Ffmpeg(FfmpegCodecArgs {
578 codec: Some("libx264".to_string()),
579 post_codec_args: Some(vec![
580 ("-crf".into(), "22".into()),
581 ("-preset".into(), "medium".into()),
582 ]),
583 ..Default::default()
584 }),
585 ]
586 }
587}
588
589#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
591pub enum CamArg {
592 SetIngoreFutureFrameProcessingErrors(Option<i64>),
596
597 SetExposureTime(f64),
598 SetExposureAuto(strand_cam_types::AutoMode),
599 SetFrameRateLimitEnabled(bool),
600 SetFrameRateLimit(f64),
601 SetGain(f64),
602 SetGainAuto(strand_cam_types::AutoMode),
603 SetRecordingFps(RecordingFrameRate),
604 SetMp4Bitrate(BitrateSelection),
605 SetMp4Codec(CodecSelection),
606 SetMp4CudaDevice(String),
607 SetMp4MaxFramerate(RecordingFrameRate),
608 SetIsRecordingMp4(bool),
609 SetIsRecordingFmf(bool),
610 SetIsRecordingUfmf(bool),
612 SetIsDoingObjDetection(bool),
614 SetIsSavingObjDetectionCsv(CsvSaveConfig),
616 SetObjDetectionConfig(String),
618 CamArgSetKalmanTrackingConfig(String),
619 CamArgSetLedProgramConfig(String),
620 SetFrameOffset(u64),
621 SetTriggerboxClockModel(Option<ClockModel>),
622 SetFormatStr(String),
623 ToggleCheckerboardDetection(bool),
624 ToggleCheckerboardDebug(bool),
625 SetCheckerboardWidth(u32),
626 SetCheckerboardHeight(u32),
627 ClearCheckerboards,
628 PerformCheckerboardCalibration,
629 DoQuit,
630 PostTrigger,
631 SetPostTriggerBufferSize(usize),
632 ToggleAprilTagFamily(TagFamily),
633 ToggleAprilTagDetection(bool),
634 SetIsRecordingAprilTagCsv(bool),
635 ToggleImOpsDetection(bool),
636 SetImOpsDestination(std::net::SocketAddr),
637 SetImOpsSource(std::net::IpAddr),
638 SetImOpsCenterX(u32),
639 SetImOpsCenterY(u32),
640 SetImOpsThreshold(u8),
641}